-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
156 lines (129 loc) · 4.57 KB
/
Copy pathmain.py
File metadata and controls
156 lines (129 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import argparse
import asyncio
import json
import re
from datetime import date, timedelta
from config import EXPORT_PATH
from logger import get_logger, setup_logging
_SCRIPT_RE = re.compile(r"<script[^>]*>.*?</script>", re.DOTALL | re.IGNORECASE)
_log = get_logger(__name__)
_HTML_RE = re.compile(r"<[^>]+>")
def _sanitize_str(s: str) -> str:
s = _SCRIPT_RE.sub("", s)
s = _HTML_RE.sub("", s)
return s
def _sanitize_event(e: dict) -> dict:
for key in ("title", "venue", "category", "city"):
if key in e and isinstance(e[key], str):
e[key] = _sanitize_str(e[key])
return e
async def cmd_scrape(args):
from pipeline.orchestrator import Orchestrator
orch = Orchestrator()
if args.target == "all":
await orch.scrape_all()
elif args.target == "ticketing":
await orch.scrape_ticketing()
elif args.target == "social":
await orch.scrape_social()
def cmd_export(args):
from db.store import get_all_events, get_session
session = get_session()
try:
events = get_all_events(session)
today = date.today().isoformat()
# Filter: drop events that have definitely ended
active = []
for e in events:
if not e.start_date:
continue
if e.end_date:
cutoff = e.end_date
else:
# No end date — assume runs 7 days
try:
cutoff = (date.fromisoformat(e.start_date) + timedelta(days=7)).isoformat()
except ValueError:
# Malformed/LLM-generated start date — keep the event, treat as active today
cutoff = today
if cutoff >= today and e.status != "已结束":
active.append(e)
data = [_sanitize_event(_event_to_dict(e)) for e in active]
with open(EXPORT_PATH, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
_log.info(f"Exported {len(data)} events (filtered {len(events)-len(data)} expired)")
finally:
session.close()
def cmd_notify(args):
from pipeline.orchestrator import Orchestrator
orch = Orchestrator()
asyncio.run(orch.check_and_notify())
def cmd_run(args):
asyncio.run(cmd_scrape(argparse.Namespace(target="all")))
cmd_export(None)
cmd_notify(None)
def cmd_stats(args):
from db.store import get_all_events, get_session
from pipeline.orchestrator import get_stats
_log.info("=== Platform Health ===")
for k, v in get_stats().items():
_log.info(f" {k}: {v}")
session = get_session()
try:
events = get_all_events(session)
by_source = {}
for e in events:
by_source[e.source_name] = by_source.get(e.source_name, 0) + 1
_log.info(f"=== Events: {len(events)} total ===")
for src, cnt in sorted(by_source.items()):
_log.info(f" {src}: {cnt}")
finally:
session.close()
def _event_to_dict(event) -> dict:
return {
"id": event.id,
"sourceType": event.source_type,
"sourceName": event.source_name,
"title": event.title,
"category": event.category,
"city": event.city,
"venue": event.venue,
"startDate": event.start_date,
"endDate": event.end_date,
"priceRange": event.price_range,
"ticketUrl": event.ticket_url,
"imageUrl": event.image_url,
"status": event.status,
"confidence": event.confidence,
"canonicalId": event.canonical_id,
"scrapedAt": event.scraped_at,
}
def main():
parser = argparse.ArgumentParser(prog="anime-scraper")
parser.add_argument("--log-level", default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR"])
sub = parser.add_subparsers(dest="command")
scrape_p = sub.add_parser("scrape")
scrape_p.add_argument("target", nargs="?", default="all", choices=["all", "ticketing", "social"])
sub.add_parser("export")
sub.add_parser("notify")
sub.add_parser("run")
sub.add_parser("stats")
args = parser.parse_args()
setup_logging(level=args.log_level)
from config import validate_config
for warning in validate_config():
_log.warning(warning)
if args.command == "scrape":
asyncio.run(cmd_scrape(args))
elif args.command == "export":
cmd_export(args)
elif args.command == "notify":
cmd_notify(args)
elif args.command == "run":
cmd_run(args)
elif args.command == "stats":
cmd_stats(args)
else:
parser.print_help()
if __name__ == "__main__":
main()