SPB Git forge

spb/lou-ka

Public

Lou·Ka — tous les logements à louer du Québec, un seul endroit.

232commits 1branches 0releases
172.9 MBsize
maindefault branch
2 days agolast push
HTML 98.9% Python 0.6%
5.0 KB · 124 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Lou-Ka — Agrégateur de logements à louer (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/humano.py : connecteur Humano District (humanodistrict.ca)5#   Appartements locatifs au cœur de Sherbrooke — 2 bâtiments (Galt, 1820 rue6#   Galt Ouest, et Maison Générale) sur l'ancien campus des sœurs. Le site WP7#   embarque le widget SmartCondo Plans (silo.immo) : l'API JSON8#   /v2/building/get-project-data?project=humano-district renvoie toutes les9#   unités (prix, superficie, chambres, sdb, étage, disponibilité 1=dispo /10#   2=loué / 3=réservé + availability_date, plan PDF, photo). Granularité :11#   unité.12# -----------------------------------------------------------------------------13from __future__ import annotations1415import re16import unicodedata1718from .base import BaseConnector19from ..schema import Listing2021SITE = "https://humanodistrict.ca/appartements/"22API = ("https://smartcondoplans.silo.immo/v2/building/get-project-data"23       "?project=humano-district&language=fr")24CDN = "https://smartcondoplans.silo.immo/"2526CITY = "Sherbrooke"27ADDRESSES = {28    "galt": "1820, rue Galt Ouest, Sherbrooke",29    "maison-generale": "rue Galt Ouest, Sherbrooke",30}3132TYPE_BY_ROOMS = {0: "Studio", 1: "3½", 2: "4½", 3: "5½"}333435def _slug(s: str) -> str:36    s = unicodedata.normalize("NFKD", str(s)).encode("ascii", "ignore").decode()37    return re.sub(r"-+", "-", re.sub(r"[^a-z0-9]+", "-", s.lower())).strip("-")383940class HumanoConnector(BaseConnector):41    source_id = "humano"42    request_delay = 0.64344    def fetch(self) -> list[Listing]:45        listings: list[Listing] = []46        try:47            data = self.get(API).json()48        except Exception:49            return listings5051        # url de bâtiment par id (les unités référencent building via floors)52        bld_by_id = {b.get("id"): b for b in data.get("buildings", [])}5354        for u in data.get("project_units", []):55            try:56                if u.get("hide") or u.get("isnonunit") or u.get("not_unit"):57                    continue58                avail = u.get("availability")59                date = (u.get("availability_date") or "").strip()60                if avail == 1:61                    availability = (f"Disponible le {date}" if date62                                    else "Disponible")63                elif avail == 2 and date:64                    availability = f"Disponible le {date}"   # loué → se libère65                else:66                    continue                # loué sans date / réservé6768                name = str(u.get("name") or "").strip()69                if not name:70                    continue71                bname = (u.get("building_name") or "").strip()72                burl = _slug(bname) if bname else "x"73                # retrouver l'url canonique du bâtiment si connue74                for b in bld_by_id.values():75                    if b.get("name") == bname and b.get("url"):76                        burl = b["url"]77                        break7879                rooms = u.get("room")80                bathrooms = u.get("bathroom")81                unit_type = TYPE_BY_ROOMS.get(rooms, "")82                price = float(u["price"]) if u.get("price") else None83                area = float(u["area"]) if u.get("area") else None8485                furnished = bool(re.search(r"meubl", name, re.I))86                unit_no = re.sub(r"\s*-\s*Meubl[ée]s?$", "", name, flags=re.I)8788                details: dict = {}89                if u.get("floor_name"):90                    details["floor"] = u["floor_name"]91                if bname:92                    details["building"] = bname93                if furnished:94                    details["furnished"] = True95                if u.get("file"):96                    details["plan_pdf"] = CDN + u["file"].lstrip("/")9798                images = []99                if u.get("image"):100                    images.append(CDN + u["image"].lstrip("/"))101102                listings.append(Listing(103                    source=self.source_id,104                    external_id=f"{burl}-{_slug(name)}",105                    url=SITE,106                    title=f"Unité {unit_no}"107                          + (f" ({unit_type})" if unit_type else "")108                          + (f" — {bname}, Humano District" if bname109                             else " — Humano District"),110                    address=ADDRESSES.get(burl, "rue Galt Ouest, Sherbrooke"),111                    city=CITY,112                    unit_type=unit_type,113                    bedrooms=rooms if isinstance(rooms, int) else None,114                    bathrooms=bathrooms if isinstance(bathrooms, int) else None,115                    price=price,116                    availability=availability,117                    area_sqft=area,118                    details=details,119                    images=images,120                ))121            except Exception:122                continue123        return listings124