File size: 2,887 Bytes
c25f760
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Aggregate, privacy-preserving usage logging.

Logs the RESULT of each identification (predicted raaga + confidence + tonic + whether a
prediction was made) to a private Hugging Face Dataset. It NEVER logs the audio, an IP, or any
user identifier — just anonymous per-identification result metadata, so we can see how much the
app is used and what it's recognising in the wild (the passive real-world signal).

Robustness:
- Disabled unless HF_TOKEN is set (a write token, as a Space secret). Without it the app runs
  exactly as before, just without logging.
- Every call is wrapped so a logging failure can NEVER break recognition.
- Uses huggingface_hub.CommitScheduler: appends locally, flushes to the dataset every few minutes
  (one commit per identification would be far too many).
"""
from __future__ import annotations

import json
import os
from pathlib import Path

DATASET = "twelveswaras/usage-logs"
LOG_DIR = Path("usage_logs")
LOG_FILE = LOG_DIR / "identifications.jsonl"

_scheduler = None
_tried = False


def _scheduler_or_none():
    global _scheduler, _tried
    if _scheduler is not None or _tried:
        return _scheduler
    _tried = True
    if not os.environ.get("HF_TOKEN"):
        return None                       # logging off; app still works
    try:
        from huggingface_hub import CommitScheduler
        LOG_DIR.mkdir(exist_ok=True)
        _scheduler = CommitScheduler(repo_id=DATASET, repo_type="dataset", private=True,
                                     folder_path=str(LOG_DIR), path_in_repo="data", every=5)
    except Exception:                     # noqa: BLE001  (bad token / no access -> stay disabled)
        _scheduler = None
    return _scheduler


def record(*, top1=None, confidence=None, top3=None, tonic_hz=None,
           heard_seconds=None, no_prediction=False, elapsed_s=None) -> None:
    """Append one identification's RESULT metadata. No audio, no PII. Never raises."""
    try:
        sch = _scheduler_or_none()
        if sch is None:
            return
        from datetime import datetime, timezone
        row = {
            "ts": datetime.now(timezone.utc).isoformat(timespec="seconds"),
            "top1": top1,
            "confidence": round(float(confidence), 3) if confidence is not None else None,
            "top3": top3,
            "tonic_hz": round(float(tonic_hz)) if tonic_hz else None,
            "heard_s": round(float(heard_seconds), 1) if heard_seconds is not None else None,
            "no_prediction": bool(no_prediction),
            "elapsed_s": round(float(elapsed_s), 2) if elapsed_s is not None else None,
        }
        with sch.lock:
            with open(LOG_FILE, "a", encoding="utf-8") as fh:
                fh.write(json.dumps(row, ensure_ascii=False) + "\n")
    except Exception:                     # noqa: BLE001  (logging must never break recognition)
        pass