continuous-gtfs 0.1.0__py3-none-any.whl
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.
- continuous_gtfs/__init__.py +24 -0
- continuous_gtfs/analysis_storage.py +76 -0
- continuous_gtfs/builtins/__init__.py +0 -0
- continuous_gtfs/builtins/realtime/__init__.py +25 -0
- continuous_gtfs/builtins/realtime/transforms.py +637 -0
- continuous_gtfs/builtins/schedule/__init__.py +35 -0
- continuous_gtfs/builtins/schedule/removals.py +573 -0
- continuous_gtfs/builtins/schedule/transforms.py +540 -0
- continuous_gtfs/cli.py +1479 -0
- continuous_gtfs/context.py +246 -0
- continuous_gtfs/executor.py +195 -0
- continuous_gtfs/parsers.py +96 -0
- continuous_gtfs/pipelines/__init__.py +12 -0
- continuous_gtfs/pipelines/realtime.py +106 -0
- continuous_gtfs/pipelines/schedule.py +232 -0
- continuous_gtfs/prep.py +244 -0
- continuous_gtfs/py.typed +0 -0
- continuous_gtfs/results.py +44 -0
- continuous_gtfs/rt_compare/__init__.py +7 -0
- continuous_gtfs/rt_compare/compare.py +656 -0
- continuous_gtfs/rt_compare/config.py +26 -0
- continuous_gtfs/rt_compare/match.py +144 -0
- continuous_gtfs/rt_compare/report.py +147 -0
- continuous_gtfs/scanner.py +564 -0
- continuous_gtfs/schedule_helpers.py +256 -0
- continuous_gtfs/schedule_pairing.py +465 -0
- continuous_gtfs/schedule_records.py +477 -0
- continuous_gtfs/schedule_semantics.py +550 -0
- continuous_gtfs/step.py +333 -0
- continuous_gtfs/testing.py +304 -0
- continuous_gtfs-0.1.0.dist-info/METADATA +165 -0
- continuous_gtfs-0.1.0.dist-info/RECORD +36 -0
- continuous_gtfs-0.1.0.dist-info/WHEEL +4 -0
- continuous_gtfs-0.1.0.dist-info/entry_points.txt +3 -0
- continuous_gtfs-0.1.0.dist-info/licenses/LICENSE +202 -0
- continuous_gtfs-0.1.0.dist-info/licenses/NOTICE +5 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Continuous GTFS transform framework."""
|
|
2
|
+
|
|
3
|
+
from .context import PipelineContext
|
|
4
|
+
from .executor import execute_pipeline
|
|
5
|
+
from .prep import PreparedRun, load_inputs, prepare_pipeline_run
|
|
6
|
+
from .results import ExecutionResult, StepResult
|
|
7
|
+
from .scanner import dag_edges, export_reactflow, resolve_dag, scan_pipeline
|
|
8
|
+
from .step import Step, step
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Step",
|
|
12
|
+
"step",
|
|
13
|
+
"PipelineContext",
|
|
14
|
+
"StepResult",
|
|
15
|
+
"ExecutionResult",
|
|
16
|
+
"execute_pipeline",
|
|
17
|
+
"scan_pipeline",
|
|
18
|
+
"dag_edges",
|
|
19
|
+
"resolve_dag",
|
|
20
|
+
"export_reactflow",
|
|
21
|
+
"PreparedRun",
|
|
22
|
+
"prepare_pipeline_run",
|
|
23
|
+
"load_inputs",
|
|
24
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""GCS customTime stamping for analysis-bucket writes.
|
|
2
|
+
|
|
3
|
+
Every object the pipeline worker writes to the analysis bucket (canonical
|
|
4
|
+
form today; semantic/pairing tables later) must carry the retention
|
|
5
|
+
deadline of the source asset version it derives from, per
|
|
6
|
+
specs/data-retention.md §Analysis bucket. The deadline rides the run
|
|
7
|
+
dispatch (orchestrator's ``retention.ts`` ``dispatchDeadline()``, wired
|
|
8
|
+
onto ``DispatchRequest.deadline``) since the worker has no way to compute
|
|
9
|
+
a retention class itself.
|
|
10
|
+
|
|
11
|
+
Stamping happens at OBJECT CREATION, never as a follow-up metadata patch:
|
|
12
|
+
the pipeline service account holds create-only IAM on the analysis bucket
|
|
13
|
+
(``tf/modules/platform/analysis-storage.tf`` — objectCreator + objectViewer,
|
|
14
|
+
no update/delete), so ``customTime`` must be part of each object's initial
|
|
15
|
+
upload. gcsfs's ``fixed_key_metadata={"custom_time": ...}`` does exactly
|
|
16
|
+
that (see gcsfs.GCSFile — the value is folded into the initial
|
|
17
|
+
simple/resumable upload body, not a PATCH), so the helper here wraps
|
|
18
|
+
whatever filesystem ``gtfs_digester.write_exploded()`` would otherwise
|
|
19
|
+
resolve, injecting that metadata into every file it opens for write. A
|
|
20
|
+
``None`` deadline (permanent/null-window source version) is a pure
|
|
21
|
+
passthrough: no customTime is ever set, matching "permanent and
|
|
22
|
+
null-window derived objects carry no customTime" in the spec.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from datetime import datetime
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
import fsspec
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _StampingFilesystem:
|
|
34
|
+
"""Wraps an fsspec filesystem so every file opened for write is created
|
|
35
|
+
with a fixed GCS ``customTime``. Wraps rather than subclasses — fsspec
|
|
36
|
+
filesystem instances are commonly cached per URL, and wrapping avoids
|
|
37
|
+
fighting that cache. Delegates every other attribute/method (mkdirs,
|
|
38
|
+
exists, ls, ...) straight through to the wrapped filesystem.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, fs: fsspec.AbstractFileSystem, deadline: datetime) -> None:
|
|
42
|
+
self._fs = fs
|
|
43
|
+
self._deadline = deadline
|
|
44
|
+
|
|
45
|
+
def __getattr__(self, name: str) -> Any:
|
|
46
|
+
return getattr(self._fs, name)
|
|
47
|
+
|
|
48
|
+
def open(self, path: str, mode: str = "rb", *args: Any, **kwargs: Any):
|
|
49
|
+
if "w" in mode or "x" in mode:
|
|
50
|
+
fixed = dict(kwargs.pop("fixed_key_metadata", None) or {})
|
|
51
|
+
fixed.setdefault("custom_time", self._deadline.isoformat())
|
|
52
|
+
kwargs["fixed_key_metadata"] = fixed
|
|
53
|
+
return self._fs.open(path, mode, *args, **kwargs)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def stamped_filesystem(
|
|
57
|
+
base_path: str,
|
|
58
|
+
deadline: datetime | None,
|
|
59
|
+
) -> tuple[fsspec.AbstractFileSystem, str]:
|
|
60
|
+
"""Resolve the fsspec filesystem for ``base_path``, wrapped so every
|
|
61
|
+
subsequently-written file is stamped with ``customTime = deadline`` at
|
|
62
|
+
creation. Pass both return values straight through to
|
|
63
|
+
``gtfs_digester.write_exploded(filesystem=fs, base_path=resolved, ...)``
|
|
64
|
+
— ``write_exploded`` only resolves its own filesystem when ``None`` is
|
|
65
|
+
passed in, so handing it an already-resolved (and already-wrapped) one
|
|
66
|
+
skips that and uses ours for every write.
|
|
67
|
+
|
|
68
|
+
``deadline=None`` (a permanent/null-window source version) returns the
|
|
69
|
+
plain, unwrapped filesystem: no customTime is ever set, matching
|
|
70
|
+
specs/data-retention.md's "permanent and null-window derived objects
|
|
71
|
+
carry no customTime."
|
|
72
|
+
"""
|
|
73
|
+
fs, resolved = fsspec.core.url_to_fs(base_path)
|
|
74
|
+
if deadline is None:
|
|
75
|
+
return fs, resolved
|
|
76
|
+
return _StampingFilesystem(fs, deadline), resolved
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Realtime transform builtins — parameterized Steps for GTFS-RT protobuf data."""
|
|
2
|
+
|
|
3
|
+
from .transforms import (
|
|
4
|
+
CombineFeeds,
|
|
5
|
+
ConvertScheduledToNew,
|
|
6
|
+
ExpireCancelledTrips,
|
|
7
|
+
FilterStopsByID,
|
|
8
|
+
InsertMissingCancellations,
|
|
9
|
+
PassThrough,
|
|
10
|
+
RenameVehicles,
|
|
11
|
+
TransformTripId,
|
|
12
|
+
UpdateFeedHeader,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"PassThrough",
|
|
17
|
+
"FilterStopsByID",
|
|
18
|
+
"CombineFeeds",
|
|
19
|
+
"RenameVehicles",
|
|
20
|
+
"UpdateFeedHeader",
|
|
21
|
+
"TransformTripId",
|
|
22
|
+
"ExpireCancelledTrips",
|
|
23
|
+
"InsertMissingCancellations",
|
|
24
|
+
"ConvertScheduledToNew",
|
|
25
|
+
]
|