Spaces:
Sleeping
Sleeping
File size: 5,418 Bytes
7c6808b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | """
khazna_loader.py β Space-side loading of the Khazna index.
The harvest runs in GitHub Actions (nightly, 23:00 Dubai) and publishes
khazna_index.json to an HF Dataset repo. The Space downloads it β it never
harvests, because HF Spaces have an ephemeral filesystem and a 20-minute
harvest would block startup.
Behaviour:
* startup -> download once, load into memory (non-blocking)
* daily 23:30 -> re-download (a few seconds)
* unavailable -> lookups return None, and the caller says "not checked"
rather than implying the article is absent from Khazna.
Add to requirements.txt:
huggingface_hub
apscheduler
"""
from __future__ import annotations
import asyncio
import json
import logging
import os
from pathlib import Path
from typing import Optional
logger = logging.getLogger("khazna")
HF_DATASET = os.getenv("KHAZNA_DATASET", "nikeshn/researchbee-data")
INDEX_FILE = "khazna_index.json"
LOCAL_PATH = Path("/tmp/khazna_index.json")
class KhaznaIndex:
def __init__(self) -> None:
self._idx: dict[str, dict] = {}
self._loaded_at: Optional[str] = None
# ββ loading ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def _download(self) -> Optional[Path]:
try:
from huggingface_hub import hf_hub_download
p = hf_hub_download(
repo_id=HF_DATASET,
filename=INDEX_FILE,
repo_type="dataset",
token=os.getenv("HF_TOKEN"), # omit if the dataset is public
)
return Path(p)
except Exception as e:
logger.warning("Khazna index download failed: %s", e)
return LOCAL_PATH if LOCAL_PATH.exists() else None
def reload(self) -> bool:
path = self._download()
if not path or not path.exists():
logger.warning("Khazna index unavailable β lookups will return None")
return False
try:
data = json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError as e:
logger.error("Khazna index malformed, keeping previous copy: %s", e)
return False
# Never replace a good index with an empty one.
if not data and self._idx:
logger.error("Downloaded index was empty β keeping previous copy")
return False
self._idx = data
from datetime import datetime, timezone
self._loaded_at = datetime.now(timezone.utc).isoformat(timespec="seconds")
logger.info("Khazna index loaded: %d DOIs", len(self._idx))
return True
# ββ lookup βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def get(self, doi: str) -> Optional[dict]:
d = (doi or "").strip().lower()
for p in ("https://doi.org/", "http://doi.org/", "https://dx.doi.org/", "doi:"):
if d.startswith(p):
d = d[len(p):]
break
return self._idx.get(d.strip("/"))
@property
def available(self) -> bool:
return bool(self._idx)
@property
def status(self) -> dict:
return {"available": self.available,
"records": len(self._idx),
"loaded_at": self._loaded_at,
"source": HF_DATASET}
khazna_index = KhaznaIndex()
# ββ FastAPI wiring (app.py) ββββββββββββββββββββββββββββββββββββββββββββββββ
#
# from khazna_loader import khazna_index
# from apscheduler.schedulers.asyncio import AsyncIOScheduler
# from apscheduler.triggers.cron import CronTrigger
#
# @app.on_event("startup")
# async def _startup():
# # Non-blocking: the Space serves traffic immediately; Khazna lookups
# # report "not checked" for the few seconds before the index lands.
# asyncio.create_task(asyncio.to_thread(khazna_index.reload))
#
# sched = AsyncIOScheduler(timezone="Asia/Dubai")
# sched.add_job(lambda: asyncio.create_task(asyncio.to_thread(khazna_index.reload)),
# CronTrigger(hour=23, minute=30)) # 30 min after the harvest
# sched.start()
#
# @app.get("/api/health/khazna")
# async def khazna_health():
# return khazna_index.status
#
#
# ββ Using it in the license flow βββββββββββββββββββββββββββββββββββββββββββ
#
# def khazna_status_for(doi: str) -> dict:
# """Three-valued, never guesses absence when the index is missing."""
# if not khazna_index.available:
# return {"checked": False,
# "note": "Khazna could not be checked right now."}
# rec = khazna_index.get(doi)
# if rec is None:
# return {"checked": True, "in_khazna": False}
# return {"checked": True, "in_khazna": True,
# "deposit_state": rec["deposit_state"], # open|embargoed|...|metadata_only
# "embargo_end": rec.get("embargo_end"),
# "portal_url": rec.get("portal_url"),
# "needs_deposit": rec["deposit_state"] in ("metadata_only", "closed")}
|