přidal jsem do skriptu simulaci přes rok (měsíc = 15 sekund pro myšlenkový model, běží rychle bez skutečných pauz). Co jsem udělal:
-
Vytvořil jsem 4 skupiny: elektrická, akustická, basová, klasická.
-
Každá skupina dosáhne 5 členů (startují se 2, během měsíců se přidávají 1–2 nové osoby/měsíc), včetně tebe — Radek je v sekci klasické kytary jako majitel (vždy první, vyjmut z pohybu mezi stránkami).
-
Pro každou skupinu jsem naplánoval 3 dárce (částky 50–250 Kč) rozprostřené v průběhu roku; dary se chovají podle pravidla: body = částka, každý kompletní měsíc se body sníží o
amount / months_count(months_count = 1 pro <=100 Kč, jinak 4 podle zadání). -
Simulace generuje měsíční shrnutí (pro každý měsíc): kdo je na které stránce (page 1/2/3), font a velikost thumbnailu (page1 velké, page2 thumbnail, po půl roce page3 s malým fontem a thumb 64×100). Radek zůstává trvale na page 1.
-
Výstupy jsou zobrazeny jako tabulky: snapshoty pro začátek, půlrok a konec (měsíc 0, 5, 11) a detailní seznam pro poslední měsíc.
# Fixed simulation script (syntax errors corrected). Re-run the same simulation and present results.
from __future__ import annotations
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Tuple
import datetime as dt
import random
import pandas as pd
random.seed(42)
# ---------- CONFIG ----------
SIM_START = dt.date(2026, 1, 24) # baseline date (user timezone Prague)
MONTHS_TO_SIM = 12 # simulate one year
MONTH_SECONDS = 15 # nominal "seconds per month" (not used for sleeping here)
REAL_TIME = False # set True to actually sleep MONTH_SECONDS per month (not recommended here)
# Page layout rules
VISIBLE_ON_FIRST_PAGE = 3
PAGE2_THUMBNAIL = (100, 150) # thumbnail size for page 2 (w,h)
PAGE1_THUMBNAIL = (240, 320) # larger for page 1
PAGE3_THUMBNAIL = (64, 100) # small for page 3 after 6 months
PAGE1_FONT = "size:3"
PAGE3_FONT = "Arial 8"
# ---------- DATA CLASSES ----------
def full_months_elapsed(start: dt.date, end: dt.date) -> int:
if end < start:
return 0
months = (end.year - start.year) * 12 + (end.month - start.month)
if end.day < start.day:
months -= 1
return max(0, months)
@dataclass
class Donation:
amount: float
months_count: int
start_date: dt.date
def monthly_reduction(self) -> float:
if self.months_count <= 0: return float('inf')
return self.amount / self.months_count
def points_at(self, at_date: dt.date) -> float:
elapsed = full_months_elapsed(self.start_date, at_date)
elapsed = min(elapsed, max(0, self.months_count))
remaining = max(0.0, self.amount - elapsed * self.monthly_reduction())
return round(remaining, 2)
@dataclass
class Member:
id: int
name: str
category: str
join_date: dt.date
owner: bool = False
donations: List[Donation] = field(default_factory=list)
def total_points_at(self, at_date: dt.date) -> float:
if self.owner:
return 9999.0
return round(sum(d.points_at(at_date) for d in self.donations), 2)
def add_donation(self, amount: float, months_count: int, date: dt.date):
self.donations.append(Donation(amount=amount, months_count=months_count, start_date=date))
# ---------- Helper functions ----------
czech_names = {
'elektrická': ["Jan", "Petr", "Tomáš", "Lukáš", "Marek", "Václav", "Ondřej", "Filip", "Jaroslav", "Roman"],
'akustická': ["Jana", "Klára", "Lucie", "Magda", "Eva", "Barbora", "Adéla", "Martina", "Zuzana", "Petra"],
'basová': ["Karel", "Prokop", "Stanislav", "Milan", "Viktor", "Jindřich", "Bohdan", "Daniel", "Aleš", "Rudolf"],
'klasická': ["Radek", "Nikola", "Veronika", "Helena", "Ivana", "Marta", "Aneta", "Soňa", "Radka", "Simona"]
}
def pick_name(category: str, used: set) -> str:
for n in czech_names[category]:
if n not in used:
used.add(n)
return n
i = 1
while f"{category}_{i}" in used:
i += 1
name = f"{category}_{i}"
used.add(name)
return name
def determine_months_count_from_amount(amount: float) -> int:
return 1 if amount <= 100 else 4
# ---------- Simulation state ----------
categories = ['elektrická', 'akustická', 'basová', 'klasická']
members_by_cat: Dict[str, List[Member]] = {c: [] for c in categories}
used_names = set()
member_id_seq = 1
def seed_initial_members(start_date: dt.date):
global member_id_seq
for cat in categories:
if cat == 'klasická':
name = "Radek"
used_names.add(name)
m = Member(id=member_id_seq, name=name, category=cat, join_date=start_date, owner=True)
members_by_cat[cat].append(m)
member_id_seq += 1
name2 = pick_name(cat, used_names)
m2 = Member(id=member_id_seq, name=name2, category=cat, join_date=start_date)
members_by_cat[cat].append(m2)
member_id_seq += 1
else:
name1 = pick_name(cat, used_names)
m1 = Member(id=member_id_seq, name=name1, category=cat, join_date=start_date)
members_by_cat[cat].append(m1)
member_id_seq += 1
name2 = pick_name(cat, used_names)
m2 = Member(id=member_id_seq, name=name2, category=cat, join_date=start_date)
members_by_cat[cat].append(m2)
member_id_seq += 1
seed_initial_members(SIM_START)
# Planned donations per category (3 donors among the final 5, scheduled across months)
planned_donations_by_cat = {}
for cat in categories:
chosen_pos = random.sample(range(5), 3)
months = sorted(random.sample(range(0, MONTHS_TO_SIM), 3))
amounts = [round(random.uniform(50, 250), 2) for _ in range(3)]
planned_donations_by_cat[cat] = list(zip(chosen_pos, months, amounts))
monthly_summaries = []
for month_idx in range(MONTHS_TO_SIM):
# compute simulation date for this month (keeping day same when possible)
base = (SIM_START.replace(day=1) + pd.DateOffset(months=month_idx)).date()
try:
current_date = base.replace(day=SIM_START.day)
except Exception:
# clamp to last day
last_day = (base + pd.DateOffset(months=1) - pd.DateOffset(days=1)).day
current_date = base.replace(day=min(SIM_START.day, last_day))
# Add 1-2 new members per category until each reaches 5
for cat in categories:
current_count = len(members_by_cat[cat])
if current_count < 5:
to_add = min(5 - current_count, random.choice([1,2]))
for _ in range(to_add):
name = pick_name(cat, used_names)
m = Member(id=member_id_seq, name=name, category=cat, join_date=current_date)
members_by_cat[cat].append(m)
member_id_seq += 1
# Apply planned donations scheduled for this month
for cat in categories:
plan = planned_donations_by_cat[cat]
for pos_in_final, donation_month, amount in plan:
if donation_month == month_idx:
# if the intended member exists now (by final pos), give donation to that member
if pos_in_final < len(members_by_cat[cat]):
member = members_by_cat[cat][pos_in_final]
months_count = determine_months_count_from_amount(amount)
member.add_donation(amount=amount, months_count=months_count, date=current_date)
else:
# If not yet present, try to attach donation to the last member (best-effort)
if len(members_by_cat[cat]) > 0:
member = members_by_cat[cat][-1]
months_count = determine_months_count_from_amount(amount)
member.add_donation(amount=amount, months_count=months_count, date=current_date)
# Random small chance of donation when joining
for cat in categories:
for m in members_by_cat[cat]:
if m.join_date == current_date and not m.donations and not m.owner:
if random.random() < 0.10:
amt = round(random.uniform(50, 250), 2)
months_count = determine_months_count_from_amount(amt)
m.add_donation(amount=amt, months_count=months_count, date=current_date)
# Summarize this month
rows = []
for cat in categories:
members = members_by_cat[cat]
members_sorted = sorted(members, key=lambda mm: mm.total_points_at(current_date), reverse=True)
if cat == 'klasická':
members_sorted = sorted(members, key=lambda mm: (0 if mm.owner else 1, -mm.total_points_at(current_date)))
for rank, mm in enumerate(members_sorted, start=1):
pts = mm.total_points_at(current_date)
months_since_join = full_months_elapsed(mm.join_date, current_date)
if mm.owner:
page = 1; font = PAGE1_FONT; thumb = PAGE1_THUMBNAIL
else:
if rank <= VISIBLE_ON_FIRST_PAGE and months_since_join < 6:
page = 1; font = PAGE1_FONT; thumb = PAGE1_THUMBNAIL
else:
if months_since_join >= 6:
page = 3; font = PAGE3_FONT; thumb = PAGE3_THUMBNAIL
else:
page = 2; font = "default"; thumb = PAGE2_THUMBNAIL
rows.append({
"month_index": month_idx,
"date": current_date,
"category": cat,
"member_id": mm.id,
"name": mm.name,
"join_date": mm.join_date,
"points": pts,
"rank": rank,
"page": page,
"font": font,
"thumbnail": f"{thumb[0]}x{thumb[1]}",
"owner": mm.owner
})
monthly_summaries.append(rows)
# Build DataFrame for all months
df_all = pd.DataFrame([r for month in monthly_summaries for r in month])
# Present concise snapshots: months 0, 5, 11
snapshots = []
for sm in [0,5,11]:
df_sm = df_all[df_all['month_index']==sm].sort_values(['category','rank'])
rows = []
for cat in categories:
df_cat = df_sm[df_sm['category']==cat]
members_text = []
for _, r in df_cat.iterrows():
owner_tag = " (majitel)" if r['owner'] else ""
members_text.append(f"#{int(r['rank'])} {r['name']}{owner_tag} — {r['points']} CZK — page{int(r['page'])}")
rows.append({"month_index": sm, "date": df_sm['date'].iloc[0], "category": cat, "members": " | ".join(members_text)})
snapshots.append(pd.DataFrame(rows))
# Final month detailed table
final_df = df_all[df_all['month_index']==11].sort_values(['category','rank']).reset_index(drop=True)
# Try to display with interactive table helper
try:
from caas_jupyter_tools import display_dataframe_to_user
display_dataframe_to_user("Snapshots (start, mid-year, end)", pd.concat(snapshots, ignore_index=True))
display_dataframe_to_user("Final month detailed listing (month 11)", final_df)
except Exception:
print("=== Snapshots (start, mid-year, end) ===")
for snap in snapshots:
print(snap.to_string(index=False))
print()
print("=== Final month detailed listing (month 11) ===")
print(final_df.to_string(index=False))
print("\nSimulation finished. If you want: export CSV, real-time run (with sleeps), or REST endpoint (Flask).")
Žádné komentáře:
Okomentovat