build-small-hackathon/microfactory-lab
2
1"""Email signup for Microfactory updates.2 3Lightweight, privacy-first opt-in collection. Stores one JSONL row per signup:4{ts, email, consent, source, app_version}. Local runs write to data/signups.jsonl.5Space runs can additionally push to a private HF dataset via CommitScheduler if6SIGNUPS_DATASET and HF_TOKEN are set. Never writes without explicit consent.7"""8 9from __future__ import annotations10 11import json12import os13import re14import threading15from datetime import datetime, timezone16from pathlib import Path17from typing import Any18 19SIGNUPS_FILE = Path(__file__).resolve().parent.parent / "data" / "signups.jsonl"20SIGNUPS_DATASET = os.environ.get("SIGNUPS_DATASET", "kylebrodeur/microfactory-signups").strip()21FLUSH_MINUTES = 522 23_scheduler: Any = None24_lock = threading.Lock()25 26_EMAIL_RE = re.compile(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$")27 28 29def _get_scheduler():30 """Lazy-init CommitScheduler for the private signup dataset."""31 global _scheduler32 token = os.environ.get("HF_TOKEN", "").strip()33 if not token:34 return None35 if not SIGNUPS_DATASET or "/" not in SIGNUPS_DATASET:36 return None37 if _scheduler is None:38 with _lock:39 if _scheduler is None:40 try:41 from huggingface_hub import CommitScheduler42 except ImportError:43 return None44 SIGNUPS_FILE.parent.mkdir(parents=True, exist_ok=True)45 if not SIGNUPS_FILE.exists():46 SIGNUPS_FILE.write_text("", encoding="utf-8")47 _scheduler = CommitScheduler(48 repo_id=SIGNUPS_DATASET,49 repo_type="dataset",50 folder_path=str(SIGNUPS_FILE.parent),51 every=FLUSH_MINUTES,52 token=token,53 allow_patterns=["signups.jsonl"],54 )55 return _scheduler56 57 58def is_active() -> bool:59 """True if signup syncing to HF is configured and active."""60 return _get_scheduler() is not None61 62 63def validate_email(email: str) -> str | None:64 """Return normalized email or None if invalid."""65 if not email:66 return None67 email = email.strip().lower()68 if not _EMAIL_RE.match(email):69 return None70 return email71 72 73def record_signup(email: str, consent: bool, source: str = "local") -> tuple[bool, str]:74 """Record one signup. Returns (ok, message).75 76 Requires explicit consent. Email is validated. Writes locally always;77 pushes to HF only when HF_TOKEN + SIGNUPS_DATASET are present.78 """79 if not consent:80 return False, "Please check the box to opt in before submitting."81 normalized = validate_email(email)82 if normalized is None:83 return False, "Enter a valid email address."84 85 row = {86 "ts": datetime.now(timezone.utc).isoformat(),87 "email": normalized,88 "consent": True,89 "source": source,90 "app_version": os.environ.get("CHIEF_ENGINEER_VERSION", "0.1.0"),91 }92 93 try:94 SIGNUPS_FILE.parent.mkdir(parents=True, exist_ok=True)95 with _lock:96 with SIGNUPS_FILE.open("a", encoding="utf-8") as f:97 f.write(json.dumps(row, ensure_ascii=False) + "\n")98 sched = _get_scheduler()99 if sched is not None:100 try:101 sched.trigger()102 except Exception:103 pass104 return True, "You're on the list. Microfactory updates will hit that inbox."105 except Exception as e:106 return False, f"Could not save signup: {e}"107 108 109def privacy_notice() -> str:110 return (111 "<div class='ce-sub' style='font-size:10px;opacity:0.7;margin-top:4px;'>"112 "📬 Microfactory updates: one email, no spam, unsub any time. "113 "We store only your email and this timestamp. No print data, no uploaded files.</div>"114 )115 