osmsg 1.2.3__tar.gz → 1.2.4__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.
- {osmsg-1.2.3 → osmsg-1.2.4}/PKG-INFO +1 -1
- osmsg-1.2.4/osmsg/__version__.py +1 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/_http.py +7 -3
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/gui.py +26 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/pipeline.py +26 -6
- {osmsg-1.2.3 → osmsg-1.2.4}/pyproject.toml +1 -1
- osmsg-1.2.3/osmsg/__version__.py +0 -1
- {osmsg-1.2.3 → osmsg-1.2.4}/LICENSE +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/README.md +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/__init__.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/_tick.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/auth.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/boundary.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/cli.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/db/__init__.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/db/duckdb_schema.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/db/ingest.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/db/queries.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/db/schema.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/exceptions.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/export/__init__.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/export/csv.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/export/json.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/export/markdown.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/export/parquet.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/export/psql.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/fetch.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/geofabrik.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/handlers.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/history.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/__init__.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/cli.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/convert.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/manifest.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/month.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/parquet.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/maintain/pbf_split.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/models.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/pg_schema.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/py.typed +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/replication.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/tm.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/ui.py +0 -0
- {osmsg-1.2.3 → osmsg-1.2.4}/osmsg/workers.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.4"
|
|
@@ -11,7 +11,7 @@ from requests.adapters import HTTPAdapter
|
|
|
11
11
|
from urllib3.util.retry import Retry
|
|
12
12
|
|
|
13
13
|
USER_AGENT = "osmsg"
|
|
14
|
-
DEFAULT_TIMEOUT = (
|
|
14
|
+
DEFAULT_TIMEOUT = (30, 120) # (connect, read) seconds
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class _TimeoutSession(requests.Session):
|
|
@@ -26,10 +26,14 @@ def make_session() -> requests.Session:
|
|
|
26
26
|
"""Fresh session with the standard timeout + retry policy (use when a flow needs its own cookie jar)."""
|
|
27
27
|
s = _TimeoutSession()
|
|
28
28
|
retry = Retry(
|
|
29
|
-
total=
|
|
30
|
-
|
|
29
|
+
total=10,
|
|
30
|
+
connect=10,
|
|
31
|
+
read=10,
|
|
32
|
+
backoff_factor=1.0,
|
|
33
|
+
backoff_max=120,
|
|
31
34
|
status_forcelist=(429, 500, 502, 503, 504),
|
|
32
35
|
allowed_methods=frozenset({"GET", "POST", "HEAD"}),
|
|
36
|
+
respect_retry_after_header=True,
|
|
33
37
|
)
|
|
34
38
|
adapter = HTTPAdapter(max_retries=retry, pool_maxsize=32)
|
|
35
39
|
s.mount("https://", adapter)
|
|
@@ -7,14 +7,21 @@ import os
|
|
|
7
7
|
import queue
|
|
8
8
|
import sys
|
|
9
9
|
import threading
|
|
10
|
+
import webbrowser
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from typing import Any
|
|
12
13
|
|
|
14
|
+
from .__version__ import __version__
|
|
13
15
|
from .exceptions import NoDataFoundError, OsmsgError
|
|
14
16
|
from .pipeline import RunConfig, run
|
|
15
17
|
|
|
16
18
|
UTC = dt.UTC
|
|
17
19
|
FORMATS = ["parquet", "csv", "json", "markdown"]
|
|
20
|
+
ABOUT_LINKS = [
|
|
21
|
+
("Star osmsg on GitHub", "https://github.com/osgeonepal/osmsg"),
|
|
22
|
+
("Report a bug or request a feature", "https://github.com/osgeonepal/osmsg/issues"),
|
|
23
|
+
("Sponsor the developer", "https://github.com/sponsors/kshitijrajsharma"),
|
|
24
|
+
]
|
|
18
25
|
PRESETS = ["Last hour", "Last day", "Last week", "Last month", "Last year", "All time"]
|
|
19
26
|
_PRESET_DELTAS = {
|
|
20
27
|
"Last hour": dt.timedelta(hours=1),
|
|
@@ -109,6 +116,7 @@ class App:
|
|
|
109
116
|
from tkinter import filedialog, scrolledtext, ttk
|
|
110
117
|
|
|
111
118
|
self._tk = tk
|
|
119
|
+
self._ttk = ttk
|
|
112
120
|
self._filedialog = filedialog
|
|
113
121
|
self.events: queue.Queue = queue.Queue()
|
|
114
122
|
self.out_dir = str(Path.home() / "osmsg")
|
|
@@ -164,8 +172,26 @@ class App:
|
|
|
164
172
|
|
|
165
173
|
self.log = scrolledtext.ScrolledText(frame, width=70, height=14, state="disabled")
|
|
166
174
|
self.log.grid(row=10, column=0, columnspan=4, sticky="nsew")
|
|
175
|
+
|
|
176
|
+
ttk.Button(frame, text="About", command=self._show_about).grid(row=11, column=0, pady=(6, 0), sticky="w")
|
|
177
|
+
ttk.Label(frame, text="A project of OSGeo Nepal").grid(row=11, column=1, columnspan=3, pady=(6, 0), sticky="e")
|
|
167
178
|
self.root.after(120, self._drain)
|
|
168
179
|
|
|
180
|
+
def _show_about(self) -> None:
|
|
181
|
+
tk, ttk = self._tk, self._ttk
|
|
182
|
+
win = tk.Toplevel(self.root)
|
|
183
|
+
win.title("About osmsg")
|
|
184
|
+
box = ttk.Frame(win, padding=16)
|
|
185
|
+
box.grid(sticky="nsew")
|
|
186
|
+
ttk.Label(box, text=f"osmsg {__version__}", font=("", 12, "bold")).grid(sticky="w")
|
|
187
|
+
ttk.Label(box, text="OpenStreetMap Stats Generator").grid(sticky="w")
|
|
188
|
+
ttk.Label(box, text="A project of OSGeo Nepal").grid(sticky="w", pady=(0, 10))
|
|
189
|
+
for text, url in ABOUT_LINKS:
|
|
190
|
+
link = ttk.Label(box, text=text, foreground="#1a73e8", cursor="hand2")
|
|
191
|
+
link.grid(sticky="w", pady=2)
|
|
192
|
+
link.bind("<Button-1>", lambda _event, target=url: webbrowser.open(target))
|
|
193
|
+
ttk.Button(box, text="Close", command=win.destroy).grid(sticky="e", pady=(12, 0))
|
|
194
|
+
|
|
169
195
|
def _apply_preset(self, name: str) -> None:
|
|
170
196
|
start, end = preset_range(name)
|
|
171
197
|
self.vars["start"].set(_fmt(start))
|
|
@@ -14,6 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
16
16
|
import duckdb
|
|
17
|
+
import requests
|
|
17
18
|
from platformdirs import user_cache_dir
|
|
18
19
|
from shapely.ops import unary_union
|
|
19
20
|
|
|
@@ -539,6 +540,11 @@ def _processing_config(cfg: RunConfig, *, parquet_dir: Path, geom_wkt: str | Non
|
|
|
539
540
|
}
|
|
540
541
|
|
|
541
542
|
|
|
543
|
+
# Replication servers throttle many concurrent connections, so downloads stay polite regardless of
|
|
544
|
+
# the worker count used for local parsing. Already-downloaded files are cached, so a rerun resumes.
|
|
545
|
+
_DOWNLOAD_WORKERS = 4
|
|
546
|
+
|
|
547
|
+
|
|
542
548
|
def _download_all(
|
|
543
549
|
urls: list[str],
|
|
544
550
|
mode: str,
|
|
@@ -548,12 +554,19 @@ def _download_all(
|
|
|
548
554
|
label: str,
|
|
549
555
|
description: str = "downloading",
|
|
550
556
|
) -> None:
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
+
workers = min(max_workers, _DOWNLOAD_WORKERS)
|
|
558
|
+
try:
|
|
559
|
+
with (
|
|
560
|
+
progress_bar(len(urls), unit=label, description=description) as advance,
|
|
561
|
+
concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool,
|
|
562
|
+
):
|
|
563
|
+
for _ in pool.map(lambda u: download_osm_file(u, mode=mode, cookie=cookie, cache_dir=cache_dir), urls):
|
|
564
|
+
advance()
|
|
565
|
+
except requests.exceptions.RequestException as exc:
|
|
566
|
+
raise OsmsgError(
|
|
567
|
+
f"Network error downloading {label} after retries ({type(exc).__name__}). "
|
|
568
|
+
"Re-run to resume: finished downloads are cached, so it continues from where it stopped."
|
|
569
|
+
) from exc
|
|
557
570
|
|
|
558
571
|
|
|
559
572
|
def _process_all(
|
|
@@ -727,6 +740,13 @@ def run(cfg: RunConfig) -> dict[str, Any]:
|
|
|
727
740
|
else f"first run with {cfg.changeset_pad_hours}h backward pad"
|
|
728
741
|
)
|
|
729
742
|
info(f"Changesets: {len(urls)} files (seq {cs_start}-{cs_end}), {pad_note}.")
|
|
743
|
+
if len(urls) > 5000:
|
|
744
|
+
warn(
|
|
745
|
+
f"Hashtag/changeset filtering downloads the per-minute changeset stream for the live "
|
|
746
|
+
f"tail ({len(urls):,} files here). This is slow over a busy network and resumes from "
|
|
747
|
+
f"cache if interrupted; a shorter range or waiting for the dataset to cover more months "
|
|
748
|
+
f"reduces it."
|
|
749
|
+
)
|
|
730
750
|
|
|
731
751
|
cs_frontier_ts = cs_repl.sequence_to_timestamp(cs_end)
|
|
732
752
|
|
osmsg-1.2.3/osmsg/__version__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.2.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|