from datetime import date, timedelta from sqlalchemy import delete, select from sqlalchemy.orm import Session from app.models import User, WeeklyTargetRule DEFAULT_REFERENCE_WEEK_START = date(1970, 1, 5) # Montag def monday_of(day: date) -> date: return day - timedelta(days=day.weekday()) def week_starts_between(start_week_start: date, end_week_start: date) -> list[date]: weeks: list[date] = [] current = start_week_start while current <= end_week_start: weeks.append(current) current += timedelta(days=7) return weeks def list_rules_for_user(db: Session, user_id: str) -> list[WeeklyTargetRule]: stmt = ( select(WeeklyTargetRule) .where(WeeklyTargetRule.user_id == user_id) .order_by(WeeklyTargetRule.effective_from.asc()) ) return db.execute(stmt).scalars().all() def target_for_week( rules: list[WeeklyTargetRule], week_start: date, fallback_minutes: int, ) -> int: target = fallback_minutes for rule in rules: if rule.effective_from <= week_start: target = rule.weekly_target_minutes else: break return target def target_map_for_weeks( rules: list[WeeklyTargetRule], week_starts: list[date], fallback_minutes: int, ) -> dict[date, int]: result: dict[date, int] = {} for week_start in week_starts: result[week_start] = target_for_week(rules, week_start, fallback_minutes) return result def upsert_rule(db: Session, user_id: str, effective_from: date, weekly_target_minutes: int) -> None: stmt = select(WeeklyTargetRule).where( WeeklyTargetRule.user_id == user_id, WeeklyTargetRule.effective_from == effective_from, ) rule = db.execute(stmt).scalar_one_or_none() if rule: rule.weekly_target_minutes = weekly_target_minutes return db.add( WeeklyTargetRule( user_id=user_id, effective_from=effective_from, weekly_target_minutes=weekly_target_minutes, ) ) def ensure_user_has_default_target_rule(db: Session, user: User) -> None: stmt = select(WeeklyTargetRule.id).where(WeeklyTargetRule.user_id == user.id).limit(1) existing = db.execute(stmt).scalar_one_or_none() if existing: return db.add( WeeklyTargetRule( user_id=user.id, effective_from=DEFAULT_REFERENCE_WEEK_START, weekly_target_minutes=user.weekly_target_minutes, ) ) def ensure_all_users_have_default_target_rules(db: Session) -> None: users = db.execute(select(User)).scalars().all() changed = False for user in users: before_count = db.execute( select(WeeklyTargetRule.id).where(WeeklyTargetRule.user_id == user.id).limit(1) ).scalar_one_or_none() if before_count: continue ensure_user_has_default_target_rule(db, user) changed = True if changed: db.commit() def apply_weekly_target_change( db: Session, *, user: User, selected_week_start: date, new_target_minutes: int, scope: str, ) -> None: rules = list_rules_for_user(db, user.id) fallback = user.weekly_target_minutes if scope == "all_weeks": db.execute(delete(WeeklyTargetRule).where(WeeklyTargetRule.user_id == user.id)) db.add( WeeklyTargetRule( user_id=user.id, effective_from=DEFAULT_REFERENCE_WEEK_START, weekly_target_minutes=new_target_minutes, ) ) return if scope == "from_current_week": db.execute( delete(WeeklyTargetRule).where( WeeklyTargetRule.user_id == user.id, WeeklyTargetRule.effective_from >= selected_week_start, ) ) upsert_rule(db, user.id, selected_week_start, new_target_minutes) return if scope == "current_week": next_week_start = selected_week_start + timedelta(days=7) target_next_week_before = target_for_week(rules, next_week_start, fallback) upsert_rule(db, user.id, selected_week_start, new_target_minutes) upsert_rule(db, user.id, next_week_start, target_next_week_before) return raise ValueError("Ungueltiger Scope")