import datetime from sqlalchemy import create_engine, func from sqlalchemy.orm import sessionmaker from .models import ProposalJob, ProposedSlot, Base import uuid # Konfiguration DB_PATH = 'sqlite:///trading_twins/trading_twins.db' MAX_PROPOSALS_PER_SLOT = 3 # Aggressiver Faktor 3 class TradingTwinsManager: def __init__(self, db_path=DB_PATH): self.engine = create_engine(db_path) self.Session = sessionmaker(bind=self.engine) Base.metadata.create_all(self.engine) def create_proposal_job(self, customer_email, customer_name, customer_company): """Erstellt einen neuen Job, sucht Slots und speichert alles.""" session = self.Session() try: # 1. Freie Slots finden (Mock für jetzt) # Später: real_slots = self.fetch_calendar_availability() candidate_slots = self._mock_calendar_availability() # 2. Beste Slots auswählen (mit Overbooking-Check) selected_slots = self._select_best_slots(session, candidate_slots) if not selected_slots: # Fallback: Wenn alles "voll" ist (sehr unwahrscheinlich bei Faktor 3), # nehmen wir trotzdem den am wenigsten gebuchten Slot. selected_slots = candidate_slots[:2] # 3. Job anlegen job_uuid = str(uuid.uuid4()) new_job = ProposalJob( job_uuid=job_uuid, customer_email=customer_email, customer_name=customer_name, customer_company=customer_company, status='pending' ) session.add(new_job) session.flush() # ID generieren # 4. Slots speichern for slot in selected_slots: new_slot = ProposedSlot( job_id=new_job.id, start_time=slot['start'], end_time=slot['end'] ) session.add(new_slot) session.commit() return new_job.job_uuid, selected_slots except Exception as e: session.rollback() raise e finally: session.close() def _select_best_slots(self, session, candidate_slots): """Wählt Slots aus, die noch nicht 'voll' sind (Faktor 3).""" valid_slots = [] # Wir betrachten nur Vorschläge der letzten 24h als "aktiv" yesterday = datetime.datetime.now() - datetime.timedelta(days=1) for slot in candidate_slots: # Wie oft wurde dieser Start-Zeitpunkt in den letzten 24h vorgeschlagen? count = session.query(func.count(ProposedSlot.id)).filter(ProposedSlot.start_time == slot['start']).filter(ProposedSlot.job.has(ProposalJob.created_at >= yesterday)).scalar() if count < MAX_PROPOSALS_PER_SLOT: valid_slots.append(slot) if len(valid_slots) >= 2: break return valid_slots def _mock_calendar_availability(self): """Simuliert freie Termine für morgen.""" tomorrow = datetime.date.today() + datetime.timedelta(days=1) # Ein Slot Vormittags (10:30), einer Nachmittags (14:00) return [ { 'start': datetime.datetime.combine(tomorrow, datetime.time(10, 30)), 'end': datetime.datetime.combine(tomorrow, datetime.time(11, 15)) }, { 'start': datetime.datetime.combine(tomorrow, datetime.time(14, 0)), 'end': datetime.datetime.combine(tomorrow, datetime.time(14, 45)) } ] def get_job_status(self, job_uuid): session = self.Session() job = session.query(ProposalJob).filter_by(job_uuid=job_uuid).first() status = job.status if job else None session.close() return status def get_job_details(self, job_uuid): """Holt alle Details zu einem Job inklusive der Slots.""" session = self.Session() job = session.query(ProposalJob).filter_by(job_uuid=job_uuid).first() if not job: session.close() return None # Wir müssen die Daten extrahieren, bevor die Session geschlossen wird details = { 'uuid': job.job_uuid, 'email': job.customer_email, 'name': job.customer_name, 'company': job.customer_company, 'status': job.status, 'slots': [{'start': s.start_time, 'end': s.end_time} for s in job.slots] } session.close() return details def update_job_status(self, job_uuid, new_status): session = self.Session() job = session.query(ProposalJob).filter_by(job_uuid=job_uuid).first() if job: job.status = new_status if new_status == 'approved': job.approved_at = datetime.datetime.now() session.commit() session.close()