feat(sources): real-time file progress for sync and upload — background upload with success toast
This commit is contained in:
@@ -26,11 +26,15 @@ no longer exist **or no longer match the format filter** — this is how
|
||||
previously-imported junk (e.g. dot-dir READMEs) leaves the index. Per-file
|
||||
logging uses the verbs ``added | updated | unchanged | pruned`` plus a
|
||||
summary line with per-format counts (PLAN §9).
|
||||
|
||||
``import_sources`` accepts an optional per-file ``progress`` callback
|
||||
(phase 64, task 01) reporting the file being processed right now.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
@@ -148,12 +152,26 @@ async def import_sources(
|
||||
prune: bool = False,
|
||||
limit: int | None = None,
|
||||
session: Session | None = None,
|
||||
progress: Callable[[str, str, int, int], None] | None = None,
|
||||
) -> ImportSummary:
|
||||
"""Import every A9-format file under *sources* (see module docstring).
|
||||
|
||||
``session`` may be supplied (tests); a private one is opened and closed
|
||||
otherwise. ``limit`` caps the number of files processed (debug only) and
|
||||
disables pruning, since an incomplete walk must not drive deletions.
|
||||
|
||||
``progress`` (phase 64, task 01) is an optional per-file hook called
|
||||
once per importable file, immediately before that file's
|
||||
``_index_file`` — with ``(source, rel_posix_path, done, total)``: the
|
||||
same POSIX *rel* the document rows use, ``done`` = the 1-based index of
|
||||
the current file **across all sources**, and ``total`` = the combined
|
||||
pre-walk count of importable files across all *sources* roots. The
|
||||
pre-walk (same extension/exclusion rules, directory stats only, no file
|
||||
reads) happens **only when *progress* is provided**: callers passing
|
||||
nothing pay no extra walk and behave exactly as before. Under
|
||||
``limit``, the hook still fires per processed file only — ``done`` never
|
||||
exceeds the limit, but ``total`` stays the full pre-walk count (an
|
||||
incomplete walk must not misreport the denominator).
|
||||
"""
|
||||
if limit is not None and limit <= 0:
|
||||
raise ValueError("limit must be >= 1")
|
||||
@@ -163,6 +181,14 @@ async def import_sources(
|
||||
session = SessionLocal()
|
||||
seen: set[tuple[str, str]] = set()
|
||||
source_names: set[str] = set()
|
||||
# phase 64 (task 01): the hook's combined denominator, walked with the
|
||||
# exact same rules as the processing loop below (directory stats only,
|
||||
# no file reads). Skipped entirely for ``progress=None`` callers — no
|
||||
# extra pass, byte-identical behaviour and cost.
|
||||
total = 0
|
||||
if progress is not None:
|
||||
for root in sources:
|
||||
total += len(iter_importable_files(root, llm.settings.import_extension_set))
|
||||
try:
|
||||
for root in sources:
|
||||
if not root.is_dir():
|
||||
@@ -180,6 +206,12 @@ async def import_sources(
|
||||
summary.files += 1
|
||||
ext = path.suffix.lower().lstrip(".") or "unknown"
|
||||
summary.formats[ext] = summary.formats.get(ext, 0) + 1
|
||||
if progress is not None:
|
||||
# phase 64: report the file *before* indexing it — a
|
||||
# file that then errors or turns out unchanged was
|
||||
# already the "current file". No try/except around the
|
||||
# call: the hooks in this repo only assign fields.
|
||||
progress(source, rel, summary.files, total)
|
||||
try:
|
||||
await _index_file(
|
||||
session, source=source, rel=rel, full_path=path, llm=llm,
|
||||
|
||||
Reference in New Issue
Block a user