from datetime import date from decimal import Decimal PHASEN = [ (1, 'Recherche & Unterlagen'), (2, 'Teilnahmeentscheidung'), (3, 'Detaillierte Durchsicht'), (4, 'Bieterfragen & Klärung'), (5, 'Preismodell'), (6, 'Unterlagen finalisieren'), (7, 'Abgabe'), (8, 'Zuschlag / Nachbetrachtung'), ] # Maps Ausschreibung.status integer to phase number STATUS_TO_PHASE = { 1: 1, 2: 1, # Recherche 3: 2, # Teilnahmeentscheidung 4: 3, 5: 3, # Durchsicht 6: 4, 7: 4, # Bieterfragen 8: 5, # Preise 9: 6, # Finalisierung 10: 7, 11: 7, # Abgabe 12: 8, 13: 8, # Nachbetrachtung } def build_phase_nav(ausschreibung, current_url=''): aktuelle_phase = STATUS_TO_PHASE.get(ausschreibung.status, 1) base = f'/ausschreibungen/{ausschreibung.pk}' phase_urls = { 1: f'{base}/', 2: f'{base}/teilnahmeentscheidung/', 3: f'{base}/anforderungen/', 4: f'{base}/bieterfragen/', 5: f'{base}/preise/', 6: f'{base}/dokumente/', 7: f'{base}/abgabe/', 8: f'{base}/nachbetrachtung/', } return [ { 'nummer': num, 'name': name, 'url': phase_urls[num], 'aktiv': num == aktuelle_phase, 'erledigt': num < aktuelle_phase, 'warnung': False, } for num, name in PHASEN ] def get_deadline_warnings(ausschreibung): """Gibt Liste von Warnungen für nahende Fristen zurück.""" warnings = [] heute = date.today() if ausschreibung.bieterfragen_bis: delta = (ausschreibung.bieterfragen_bis - heute).days if delta <= 3: warnings.append({ 'typ': 'bieterfragen', 'tage': delta, 'farbe': 'red' if delta <= 1 else 'amber', }) if ausschreibung.abgabe_bis: abgabe_date = ( ausschreibung.abgabe_bis.date() if hasattr(ausschreibung.abgabe_bis, 'date') else ausschreibung.abgabe_bis ) delta = (abgabe_date - heute).days if delta <= 14: warnings.append({ 'typ': 'abgabe', 'tage': delta, 'farbe': 'red' if delta <= 3 else 'amber', }) return warnings def gewichteter_durchschnitt(preispunkte, feld='einzelpreis'): """Berechnet gewichteten Durchschnitt für Preispunkte. Punkte mit Gewicht 0,0 werden ausgeschlossen. Gibt None zurück wenn keine verwertbaren Punkte vorhanden. """ relevante = [ p for p in preispunkte if getattr(p, feld) is not None and p.vergleichsgewicht > 0 ] if not relevante: return None summe_gewichte = sum(p.vergleichsgewicht for p in relevante) if summe_gewichte == 0: return None summe = sum(getattr(p, feld) * p.vergleichsgewicht for p in relevante) werte = [getattr(p, feld) for p in relevante] return { 'wert': summe / summe_gewichte, 'summe_gewichte': summe_gewichte, 'anzahl': len(relevante), 'minimum': min(werte), 'maximum': max(werte), 'ungewichtet': sum(werte) / len(werte), }