scraper2-hj3415 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/PKG-INFO +2 -1
  2. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/pyproject.toml +2 -1
  3. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/main.py +41 -2
  4. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/LICENSE +0 -0
  5. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/README.md +0 -0
  6. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/.DS_Store +0 -0
  7. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/__init__.py +0 -0
  8. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/.DS_Store +0 -0
  9. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/__init__.py +0 -0
  10. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/playwright/__init__.py +0 -0
  11. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/playwright/browser.py +0 -0
  12. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/playwright/browser_factory.py +0 -0
  13. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/playwright/session.py +0 -0
  14. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/.DS_Store +0 -0
  15. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/__init__.py +0 -0
  16. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/c101_memory_sink.py +0 -0
  17. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/c103_memory_sink.py +0 -0
  18. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/c104_memory_sink.py +0 -0
  19. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/c106_memory_sink.py +0 -0
  20. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/c108_memory_sink.py +0 -0
  21. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/memory/store.py +0 -0
  22. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/mongo/__init__.py +0 -0
  23. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/mongo/c101_mongo_sink.py +0 -0
  24. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/mongo/c103_mongo_sink.py +0 -0
  25. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/mongo/c104_mongo_sink.py +0 -0
  26. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/mongo/c106_mongo_sink.py +0 -0
  27. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/adapters/out/sinks/mongo/c108_mongo_sink.py +0 -0
  28. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/__init__.py +0 -0
  29. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/composition.py +0 -0
  30. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/__init__.py +0 -0
  31. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/_converters.py +0 -0
  32. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/_normalize.py +0 -0
  33. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/c101_parser.py +0 -0
  34. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/c103_parser.py +0 -0
  35. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/c104_parser.py +0 -0
  36. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/c106_parser.py +0 -0
  37. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/parsing/c108_parser.py +0 -0
  38. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/__init__.py +0 -0
  39. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/browser/__init__.py +0 -0
  40. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/browser/browser_factory_port.py +0 -0
  41. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/browser/browser_port.py +0 -0
  42. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/ingest_port.py +0 -0
  43. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/__init__.py +0 -0
  44. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/base_sink_port.py +0 -0
  45. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/c101_sink_port.py +0 -0
  46. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/c103_sink_port.py +0 -0
  47. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/c104_sink_port.py +0 -0
  48. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/c106_sink_port.py +0 -0
  49. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/ports/sinks/c108_sink_port.py +0 -0
  50. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/__init__.py +0 -0
  51. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/fetch/__init__.py +0 -0
  52. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/fetch/fetch_c101.py +0 -0
  53. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/fetch/fetch_c103.py +0 -0
  54. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/fetch/fetch_c104.py +0 -0
  55. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/fetch/fetch_c106.py +0 -0
  56. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/fetch/fetch_c108.py +0 -0
  57. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/ingest/__init__.py +0 -0
  58. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/ingest/ingest_c101.py +0 -0
  59. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/ingest/ingest_c103.py +0 -0
  60. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/ingest/ingest_c104.py +0 -0
  61. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/ingest/ingest_c106.py +0 -0
  62. {scraper2_hj3415-2.1.0 → scraper2_hj3415-2.1.2}/src/scraper2/app/usecases/ingest/ingest_c108.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scraper2-hj3415
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Naver WiseReport scraper
5
5
  Keywords: example,demo
6
6
  Author-email: Hyungjin Kim <hj3415@gmail.com>
@@ -16,6 +16,7 @@ Requires-Dist: pandas-stubs>=2.3.3
16
16
  Requires-Dist: lxml>=6.0.2
17
17
  Requires-Dist: typer>=0.21.0
18
18
  Requires-Dist: db2-hj3415
19
+ Requires-Dist: contracts-hj3415
19
20
 
20
21
  # scraper2
21
22
 
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
4
4
 
5
5
  [project]
6
6
  name = "scraper2-hj3415" # PyPI 이름 (하이픈 허용)
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "Naver WiseReport scraper"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -24,6 +24,7 @@ dependencies = [
24
24
  "lxml>=6.0.2",
25
25
  "typer>=0.21.0",
26
26
  "db2-hj3415",
27
+ "contracts-hj3415",
27
28
  ]
28
29
 
29
30
  [tool.flit.module]
@@ -92,6 +92,7 @@ async def _run_ingest_with_progress(
92
92
  sleep_sec: float,
93
93
  show: bool,
94
94
  show_n: int,
95
+ asof: datetime,
95
96
  chunk_size: int = 10,
96
97
  progress_every: int = 1,
97
98
  ) -> None:
@@ -101,7 +102,7 @@ async def _run_ingest_with_progress(
101
102
  return
102
103
 
103
104
  t0 = time.perf_counter() # ✅ 시작 시각
104
- run_asof = datetime.now(timezone.utc)
105
+ run_asof = asof
105
106
 
106
107
  def _chunks(xs: list[str], n: int):
107
108
  for i in range(0, len(xs), n):
@@ -159,6 +160,39 @@ def _format_elapsed(sec: float) -> str:
159
160
  h, m = divmod(m, 60)
160
161
  return f"{h}h {m}m {s}s"
161
162
 
163
+ def _parse_asof(asof: str | None) -> datetime:
164
+ """
165
+ Parse ISO8601 string to timezone-aware UTC datetime.
166
+ Accepts:
167
+ - 2026-01-09T05:00:00+00:00
168
+ - 2026-01-09T05:00:00Z
169
+ - 2026-01-09T05:00:00 (treated as UTC)
170
+ """
171
+ if not asof:
172
+ return datetime.now(timezone.utc)
173
+
174
+ s = asof.strip()
175
+ if not s:
176
+ return datetime.now(timezone.utc)
177
+
178
+ # allow trailing 'Z'
179
+ if s.endswith("Z"):
180
+ s = s[:-1] + "+00:00"
181
+
182
+ try:
183
+ dt = datetime.fromisoformat(s)
184
+ except ValueError as e:
185
+ raise typer.BadParameter(
186
+ f"--asof must be ISO8601 datetime (e.g. 2026-01-09T05:00:00Z). got={asof!r}"
187
+ ) from e
188
+
189
+ # if naive, assume UTC
190
+ if dt.tzinfo is None:
191
+ dt = dt.replace(tzinfo=timezone.utc)
192
+
193
+ # normalize to UTC
194
+ return dt.astimezone(timezone.utc)
195
+
162
196
  # -------------------------
163
197
  # nfs subcommands: one / all
164
198
  # -------------------------
@@ -170,6 +204,7 @@ def nfs_one(
170
204
  sleep_sec: float = typer.Option(2.0, "--sleep"),
171
205
  sink: Sink = typer.Option("memory", "--sink"),
172
206
  show: bool = typer.Option(True, "--show/--no-show", help="결과 DTO 출력"),
207
+ asof: str | None = typer.Option(None, "--asof", help="배치 기준시각(ISO8601, UTC 권장). 예: 2026-01-09T05:00:00Z"),
173
208
  ):
174
209
  code = code.strip()
175
210
  if not code:
@@ -184,7 +219,7 @@ def nfs_one(
184
219
  await _mongo_bootstrap(ucs.db)
185
220
 
186
221
  try:
187
- run_asof = datetime.now(timezone.utc)
222
+ run_asof = _parse_asof(asof)
188
223
  for ep in _endpoint_list(endpoint):
189
224
  ingest_uc = cast(IngestPort, getattr(ucs.ingest, ep))
190
225
  results = await ingest_uc.execute_many([code], sleep_sec=sleep_sec, asof=run_asof)
@@ -214,6 +249,7 @@ def nfs_all(
214
249
  chunk_size: int = typer.Option(5, "--chunk", help="진행률 표시용 배치 크기"),
215
250
  show: bool = typer.Option(False, "--show/--no-show", help="일부 코드만 출력"),
216
251
  show_n: int = typer.Option(3, "--show-n"),
252
+ asof: str | None = typer.Option(None, "--asof", help="배치 기준시각(ISO8601). 예: 2026-01-09T05:00:00Z"),
217
253
  ):
218
254
  async def _run():
219
255
  ucs = build_usecases(sink_kind=sink)
@@ -228,6 +264,8 @@ def nfs_all(
228
264
  if limit and limit > 0:
229
265
  codes = codes[:limit]
230
266
 
267
+ run_asof = _parse_asof(asof)
268
+
231
269
  typer.echo(f"\n=== NFS ALL === universe={universe}, endpoint={endpoint}, codes={len(codes)}, sink={sink}")
232
270
 
233
271
  try:
@@ -238,6 +276,7 @@ def nfs_all(
238
276
  sleep_sec=sleep_sec,
239
277
  show=show,
240
278
  show_n=show_n,
279
+ asof=run_asof,
241
280
  chunk_size=chunk_size,
242
281
  progress_every=1, # chunk마다
243
282
  )
File without changes