CoolFace
Apppublic

build-small-hackathon/microfactory-lab

sourceHugging Facemitupdated 3mo agoView on Hugging Face
2likes
signups.py115 linesDownload Raw Back to core
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