11import logging
22from collections import defaultdict
33from collections .abc import AsyncIterable , Iterable
4- from contextlib import suppress
54from io import StringIO
65from pathlib import Path
76from typing import Any , Never
109from aiofile import async_open
1110from datashare_python .exceptions import ArtifactNotFound
1211from datashare_python .objects import Document , WorkerPaths
12+ from datashare_python .types_ import RawAsyncProgressHandler
1313from datashare_python .utils import async_read_artifact
14- from temporalio import activity
1514
1615from .objects import ExportFormat , PassportArtifact , Passports
1716
@@ -25,40 +24,51 @@ async def export_passports_act(
2524 * ,
2625 output_path : Path ,
2726 ds_base_url : str ,
27+ progress : RawAsyncProgressHandler | None = None ,
2828) -> None :
2929 match export_format :
3030 case ExportFormat .CSV :
31- lines = export_as_csv (docs , ds_base_url = ds_base_url , paths = paths )
31+ lines = export_as_csv (
32+ docs , ds_base_url = ds_base_url , paths = paths , progress = progress
33+ )
3234 case ExportFormat .JSONL :
33- lines = export_as_jsonl (docs , paths )
35+ lines = export_as_jsonl (docs , paths , progress = progress )
3436 case _:
3537 raise ValueError (f"unsupported export format { export_format } " )
36- l_count = 0
3738 async with async_open (output_path , "w" ) as f :
3839 async for line in lines :
3940 await f .write (line + "\n " )
40- l_count += 1
41- if l_count % 1000 == 0 :
42- with suppress (RuntimeError ):
43- activity .heartbeat ()
4441
4542
4643async def export_as_jsonl (
47- docs : AsyncIterable [Document ], paths : WorkerPaths
44+ docs : AsyncIterable [Document ],
45+ paths : WorkerPaths ,
46+ progress : RawAsyncProgressHandler | None = None ,
4847) -> AsyncIterable [str ]:
48+ n_docs = 0
4949 async for _ , p in _read_passport_artifacts (paths , docs ):
5050 yield p
51+ n_docs += 1
52+ if progress is not None and n_docs % 100 == 0 :
53+ await progress (n_docs )
5154
5255
5356async def export_as_csv (
54- docs : AsyncIterable [Document ], ds_base_url : str , paths : WorkerPaths
57+ docs : AsyncIterable [Document ],
58+ ds_base_url : str ,
59+ paths : WorkerPaths ,
60+ progress : RawAsyncProgressHandler | None = None ,
5561) -> AsyncIterable [str ]:
5662 out = StringIO ()
63+ n_docs = 0
5764 records_by_project = defaultdict (list )
5865 async for doc , passports in _read_passport_artifacts (paths , docs ):
59- passports = Passports .model_validate_json (passports )
66+ passports = Passports .model_validate_json (passports ) # noqa: PLW2901
6067 for r in _record_from_passports (doc , passports ):
6168 records_by_project [r ["project" ]].append ((doc , r ))
69+ n_docs += 1
70+ if progress is not None and n_docs % 100 == 0 :
71+ await progress (n_docs )
6272 recs = []
6373 for project , records in records_by_project .items ():
6474 for doc , r in records :
@@ -82,7 +92,6 @@ async def _read_passport_artifacts(
8292 paths : WorkerPaths ,
8393 docs : AsyncIterable [Document ],
8494) -> AsyncIterable [Document , bytes ]:
85- n_docs = 0
8695 async for doc in docs :
8796 try :
8897 passports = await async_read_artifact (
@@ -91,9 +100,6 @@ async def _read_passport_artifacts(
91100 except ArtifactNotFound :
92101 continue
93102 yield doc , passports
94- n_docs += 1
95- if n_docs % 100 == 0 :
96- logger .info (f"processed { n_docs } docs" )
97103
98104
99105def _record_from_passports (
0 commit comments