SPB Git forge

spb/rent-ka

Public
8commits 1branches 0releases
7.4 MBsize
maindefault branch
19 days agolast push
Python 68.8% TypeScript 18.6% CSS 8.7% JavaScript 3.3% HTML 0.6%
3.6 KB · 96 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Rent-Ka — Rental listings aggregator (Canada, outside Québec)3# Author: Simon-Pierre Boucher — contact@spboucher.ai4# history.py : « Historique Rent-Ka » d'une annonce — timeline construite à5#              partir des observations réelles des crawls :6#   - price_log (depuis la v1) : chaque changement de prix observé ;7#   - listing_events (db.py/_log_events) : description, superficie, dispo,8#     inclusions, photos, disparition/réapparition.9# Chaque élément est étiqueté `observed` : rien n'est estimé ni inventé.10# -----------------------------------------------------------------------------11from __future__ import annotations1213import json14import time1516from . import db171819def timeline(uid: str, con=None) -> dict | None:20    """Historique complet d'une annonce (fiche + API).2122    Retourne : première/dernière observation, jours en ligne, prix initial et23    courant, variation, nombre de modifications et la timeline fusionnée24    (prix + événements), du plus récent au plus ancien.25    """26    own = con is None27    if own:28        con = db.connect()29    row = con.execute(30        "SELECT uid, price, first_seen, last_seen, active FROM listings"31        " WHERE uid=?", (uid,)).fetchone()32    if row is None:33        if own:34            con.close()35        return None3637    prices = con.execute(38        "SELECT ts, price FROM price_log WHERE uid=? ORDER BY ts", (uid,)).fetchall()39    events = con.execute(40        "SELECT ts, event, old, new FROM listing_events WHERE uid=? ORDER BY ts",41        (uid,)).fetchall()4243    items: list[dict] = []44    prev_price = None45    for p in prices:46        it = {"ts": p["ts"], "type": "prix", "prix": p["price"],47              "statut": "observed"}48        if prev_price is not None and p["price"] is not None:49            it["prix_avant"] = prev_price50        items.append(it)51        if p["price"] is not None:52            prev_price = p["price"]53    for e in events:54        def _load(v):55            try:56                return json.loads(v) if v is not None else None57            except (ValueError, TypeError):58                return None59        items.append({"ts": e["ts"], "type": e["event"],60                      "avant": _load(e["old"]), "apres": _load(e["new"]),61                      "statut": "observed"})62    items.sort(key=lambda x: x["ts"], reverse=True)6364    first_price = next((p["price"] for p in prices if p["price"] is not None), None)65    cur_price = row["price"]66    variation = None67    if first_price and cur_price and first_price > 0:68        variation = round((cur_price - first_price) / first_price, 4)6970    now = time.time()71    fin = row["last_seen"] if not row["active"] else now72    jours = max(0, round((fin - (row["first_seen"] or fin)) / 86400))7374    # modifications = éléments de timeline hors point de départ du prix75    n_modif = max(0, len([p for p in prices]) - 1) + len(76        [e for e in events if e["event"] not in ("disparition", "reapparition")])7778    out = {79        "uid": uid,80        "premiere_observation": row["first_seen"],81        "derniere_observation": row["last_seen"],82        "active": bool(row["active"]),83        "jours_en_ligne": jours,84        "prix_initial": first_price,85        "prix_actuel": cur_price,86        "variation": variation,87        "modifications": n_modif,88        "timeline": items[:60],89        "methode": ("Direct observations from Rent-Ka syncs (price, "90                    "description, area, availability, inclusions, photos, "91                    "removals/returns). No estimation."),92    }93    if own:94        con.close()95    return out96