Schulportal-Konventionen übernommen: Dark Mode (ThemeToggle), beispieldaten.py + generated.json, kimi.md, mail-server (Postfix-Relay + API), Deploy-Workflow (pscp/plink, npm_default), Screenshots in preview/
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Hocks Mieterportal – Beispieldaten-Generator.
|
||||
|
||||
Erzeugt deterministisch die Demo-Daten des Portals und schreibt sie nach
|
||||
``src/data/generated.json`` (Units + Transaktionen). Die handschriftlichen
|
||||
Inhalte (Tickets, Dokumente, Nachrichten, Ankündigungen) liegen weiterhin in
|
||||
``src/data/seed.ts``.
|
||||
|
||||
Aufruf: python beispieldaten.py (Standard-Seed 20261002)
|
||||
python beispieldaten.py 42 (eigener Seed zum Experimentieren)
|
||||
|
||||
Achtung: Der Demo-Mieter „Anna Berger" (Whg. Hauptstraße 12 Nr. 7) und die
|
||||
Algorithmen sind synchron zu src/data/seed.ts zu halten.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SEED = int(sys.argv[1]) if len(sys.argv) > 1 else 20261002
|
||||
|
||||
BUILDINGS = [
|
||||
{"name": "Hauptstraße 12", "prefix": "HS", "count": 20, "baseRent": 9.8},
|
||||
{"name": "Ringweg 3", "prefix": "RW", "count": 15, "baseRent": 8.9},
|
||||
{"name": "Am Park 5", "prefix": "AP", "count": 15, "baseRent": 10.4},
|
||||
]
|
||||
|
||||
FIRST_NAMES = [
|
||||
"Anna", "Jonas", "Marta", "Leon", "Sofia", "Felix", "Lena", "Paul",
|
||||
"Emma", "Noah", "Clara", "Ben", "Nora", "Max", "Julia", "Tim",
|
||||
"Sarah", "Finn", "Laura", "Jan", "Mia", "Tom", "Ela", "Kim",
|
||||
]
|
||||
LAST_NAMES = [
|
||||
"Berger", "Klein", "Wolf", "Neumann", "Schäfer", "Krüger", "Brandt",
|
||||
"Vogel", "Otto", "Horn", "Busch", "Thomas", "Pohl", "Sander", "Lindner",
|
||||
]
|
||||
|
||||
DEMO_TENANT = "Anna Berger"
|
||||
DEMO_UNIT_ID = "hs-7"
|
||||
|
||||
MONTHS = [
|
||||
"Januar", "Februar", "März", "April", "Mai", "Juni",
|
||||
"Juli", "August", "September", "Oktober", "November", "Dezember",
|
||||
]
|
||||
|
||||
|
||||
def gen_units() -> list[dict]:
|
||||
units: list[dict] = []
|
||||
name_idx = 0
|
||||
|
||||
def next_name() -> str:
|
||||
nonlocal name_idx
|
||||
n = f"{FIRST_NAMES[name_idx % len(FIRST_NAMES)]} {LAST_NAMES[name_idx % len(LAST_NAMES)]}"
|
||||
name_idx += 1
|
||||
return n
|
||||
|
||||
for b in BUILDINGS:
|
||||
for i in range(b["count"]):
|
||||
num = i + 1
|
||||
size = 38 + ((num * 7 + len(b["prefix"]) * 3) % 78) # 38–115 m²
|
||||
cold = round(size * b["baseRent"])
|
||||
warm = cold + round(2.4 * size) + (35 if num % 3 == 0 else 0)
|
||||
floor = 0 if num <= 2 else min(4, (num - 1) // 4)
|
||||
occupied = num % 9 != 0 # ~89 % Vermietung
|
||||
if b["prefix"] == "HS" and num == 7:
|
||||
tenant: str | None = DEMO_TENANT
|
||||
elif occupied:
|
||||
tenant = next_name()
|
||||
else:
|
||||
tenant = None
|
||||
units.append({
|
||||
"id": f"{b['prefix'].lower()}-{num}",
|
||||
"building": b["name"],
|
||||
"number": str(num),
|
||||
"floor": floor,
|
||||
"size": size,
|
||||
"coldRent": cold,
|
||||
"warmRent": warm,
|
||||
"tenant": tenant,
|
||||
"status": "vermietet" if occupied else "frei",
|
||||
})
|
||||
return units
|
||||
|
||||
|
||||
def gen_transactions(units: list[dict]) -> list[dict]:
|
||||
txs: list[dict] = []
|
||||
for u in units:
|
||||
if u["status"] != "vermietet":
|
||||
continue
|
||||
for m in range(12):
|
||||
due = m + 1
|
||||
if due <= 9:
|
||||
status = "überfällig" if (ord(u["id"][3]) + m) % 11 == 0 else "bezahlt"
|
||||
else:
|
||||
status = "offen"
|
||||
txs.append({
|
||||
"id": f"tx-{u['id']}-{m}",
|
||||
"unitId": u["id"],
|
||||
"tenant": u["tenant"],
|
||||
"month": MONTHS[m],
|
||||
"year": 2026,
|
||||
"amount": u["warmRent"],
|
||||
"type": "Miete",
|
||||
"status": status,
|
||||
**({"paidAt": f"0{(m % 8) + 1}.0{due}.2026"} if status == "bezahlt" else {}),
|
||||
})
|
||||
return txs
|
||||
|
||||
|
||||
def main() -> None:
|
||||
units = gen_units()
|
||||
data = {
|
||||
"meta": {"seed": SEED, "units": len(units),
|
||||
"rented": sum(1 for u in units if u["status"] == "vermietet")},
|
||||
"units": units,
|
||||
"transactions": gen_transactions(units),
|
||||
}
|
||||
out = Path(__file__).parent / "src" / "data" / "generated.json"
|
||||
out.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(f"{out} geschrieben: {data['meta']['units']} Wohnungen "
|
||||
f"({data['meta']['rented']} vermietet), {len(data['transactions'])} Buchungen")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user