Spaces:
Running
Running
File size: 3,056 Bytes
89a70e2 | 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 | """
Niches people couldn't find in data/sectors.ts and described in their own words.
One Firestore doc per distinct niche (keyed by its normalised text), so repeat
requests add up instead of piling up as duplicates. Admins read the list in the
console to decide which niches to add. firestore.rules' default deny keeps the
collection backend-only.
"""
import re
from firebase_admin import firestore
COLLECTION = "niche_requests"
STATUSES = {"open", "added", "dismissed"}
MAX_USERS_TRACKED = 1000 # beyond this the count stops rising; plenty to rank by
def niche_key(text: str) -> str:
"""'School Stationery!' and 'school stationery' are the same request."""
return re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")[:100]
def record(uid: str, text: str, sector_id: str = "", subsector_id: str = "") -> str:
key = niche_key(text)
if not key:
raise ValueError("Niche text has no letters or numbers.")
db = firestore.client()
ref = db.collection(COLLECTION).document(key)
@firestore.transactional
def _upsert(tx):
snap = ref.get(transaction=tx)
d = snap.to_dict() if snap.exists else {}
uids = list(d.get("uids", []))
if uid not in uids and len(uids) < MAX_USERS_TRACKED:
uids.append(uid)
sectors = list(d.get("sectors", []))
where = "/".join(p for p in (sector_id, subsector_id) if p)
if where and where not in sectors:
sectors.append(where)
update = {
"text": " ".join(text.split()),
"uids": uids,
"userCount": len(uids),
"sectors": sectors[-20:],
"status": d.get("status", "open"),
"lastAt": firestore.SERVER_TIMESTAMP,
}
if not snap.exists:
update["firstAt"] = firestore.SERVER_TIMESTAMP
tx.set(ref, update, merge=True)
_upsert(db.transaction())
return key
def list_requests(status: str | None = None) -> list[dict]:
"""Most-requested first. Never returns who asked, only how many."""
out = []
for doc in firestore.client().collection(COLLECTION).stream():
d = doc.to_dict()
if status and d.get("status", "open") != status:
continue
out.append({
"id": doc.id,
"text": d.get("text", ""),
"userCount": d.get("userCount", 0),
"sectors": d.get("sectors", []),
"status": d.get("status", "open"),
"firstAt": str(d.get("firstAt", "")),
"lastAt": str(d.get("lastAt", "")),
})
return sorted(out, key=lambda r: (-r["userCount"], r["text"]))
def set_status(key: str, status: str) -> str:
"""Returns the niche text, for the audit trail."""
if status not in STATUSES:
raise ValueError(f"status must be one of {sorted(STATUSES)}")
ref = firestore.client().collection(COLLECTION).document(key)
snap = ref.get()
if not snap.exists:
raise KeyError(key)
ref.update({"status": status})
return snap.to_dict().get("text", key)
|