HK-UTM-LLM / src /regx /fetch.py
GordonUK's picture
Create src/regx/fetch.py
06d8cb8 verified
Raw
History Blame Contribute Delete
2.05 kB
from __future__ import annotations
import httpx, hashlib, json, os, mimetypes
from datetime import datetime
from pydantic import HttpUrl
from .models import SourceMeta
def _sha256_bytes(b: bytes) -> str:
return hashlib.sha256(b).hexdigest()
def fetch_url(url: str, out_dir: str = "data") -> str:
os.makedirs(out_dir, exist_ok=True)
ts = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
filename_base = url.replace("://", "_").replace("/", "_")
target_path = os.path.join(out_dir, f"{filename_base}")
manifest_path = target_path + ".json"
with httpx.Client(follow_redirects=True, timeout=60) as client:
r = client.get(url)
r.raise_for_status()
media_type = r.headers.get("content-type", "").split(";")[0] or mimetypes.guess_type(url)[0] or "application/octet-stream"
b = r.content
sha = _sha256_bytes(b)
# write raw artifact
ext = mimetypes.guess_extension(media_type) or ".bin"
artifact_path = target_path + ext
with open(artifact_path, "wb") as f:
f.write(b)
# write manifest
meta = {
"url": url,
"retrieved_at": ts,
"status_code": r.status_code,
"redirect_chain": [str(h.request.url) for h in r.history] + [str(r.request.url)],
"etag": r.headers.get("etag"),
"last_modified": r.headers.get("last-modified"),
"media_type": media_type,
"sha256": sha,
"bytes": len(b),
"artifact_path": artifact_path,
}
with open(manifest_path, "w") as f:
json.dump(meta, f, indent=2)
return manifest_path
def manifest_to_source(meta_path: str, title: str | None = None, jurisdiction: str = "OTHER") -> SourceMeta:
with open(meta_path) as f:
m = json.load(f)
return SourceMeta(
jurisdiction=jurisdiction, title=title, url=m["url"],
media_type=m["media_type"], etag=m.get("etag"), last_modified=m.get("last_modified"),
sha256=m.get("sha256")
)