from flask import Blueprint, request, jsonify from tinydb import Query from api.error_codes import ErrorCodes from api.utils import get_validated_user_id, send_event_for_current_user from db.db import child_db, pending_confirmations_db from db.routine_extensions import add_extension, delete_extension_for_child_routine, get_extension from db.routine_schedules import delete_schedule, get_schedule, upsert_schedule from events.types.child_routine_confirmation import ChildRoutineConfirmation from events.types.event import Event from events.types.event_types import EventType from events.types.routine_schedule_modified import RoutineScheduleModified from events.types.routine_time_extended import RoutineTimeExtended from models.routine_extension import RoutineExtension from models.routine_schedule import RoutineSchedule routine_schedule_api = Blueprint('routine_schedule_api', __name__) def _validate_child(child_id: str, user_id: str): q = Query() result = child_db.search((q.id == child_id) & (q.user_id == user_id)) return result[0] if result else None @routine_schedule_api.route('/child//routine//schedule', methods=['GET']) def get_routine_schedule(child_id, routine_id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': ErrorCodes.UNAUTHORIZED}), 401 if not _validate_child(child_id, user_id): return jsonify({'error': 'Child not found', 'code': ErrorCodes.CHILD_NOT_FOUND}), 404 schedule = get_schedule(child_id, routine_id) if not schedule: return jsonify({'error': 'Schedule not found'}), 404 return jsonify(schedule.to_dict()), 200 @routine_schedule_api.route('/child//routine//schedule', methods=['PUT']) def set_routine_schedule(child_id, routine_id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': ErrorCodes.UNAUTHORIZED}), 401 if not _validate_child(child_id, user_id): return jsonify({'error': 'Child not found', 'code': ErrorCodes.CHILD_NOT_FOUND}), 404 data = request.get_json() or {} mode = data.get('mode') if mode not in ('days', 'interval'): return jsonify({'error': 'mode must be "days" or "interval"', 'code': ErrorCodes.INVALID_VALUE}), 400 enabled = data.get('enabled', True) if not isinstance(enabled, bool): return jsonify({'error': 'enabled must be a boolean', 'code': ErrorCodes.INVALID_VALUE}), 400 if mode == 'days': day_configs = data.get('day_configs', []) if not isinstance(day_configs, list): return jsonify({'error': 'day_configs must be a list', 'code': ErrorCodes.INVALID_VALUE}), 400 schedule = RoutineSchedule( child_id=child_id, routine_id=routine_id, mode='days', day_configs=day_configs, default_hour=data.get('default_hour', 8), default_minute=data.get('default_minute', 0), default_has_deadline=data.get('default_has_deadline', True), enabled=enabled, ) else: interval_days = data.get('interval_days', 2) anchor_date = data.get('anchor_date', '') interval_has_deadline = data.get('interval_has_deadline', True) interval_hour = data.get('interval_hour', 0) interval_minute = data.get('interval_minute', 0) if not isinstance(interval_days, int) or not (1 <= interval_days <= 7): return jsonify({'error': 'interval_days must be an integer between 1 and 7', 'code': ErrorCodes.INVALID_VALUE}), 400 schedule = RoutineSchedule( child_id=child_id, routine_id=routine_id, mode='interval', interval_days=interval_days, anchor_date=anchor_date, interval_has_deadline=interval_has_deadline, interval_hour=interval_hour, interval_minute=interval_minute, enabled=enabled, ) delete_extension_for_child_routine(child_id, routine_id) upsert_schedule(schedule) pending_q = Query() pending_routines = pending_confirmations_db.search( (pending_q.child_id == child_id) & (pending_q.entity_id == routine_id) & (pending_q.entity_type == 'routine') & (pending_q.status == 'pending') ) for _ in pending_routines: pending_confirmations_db.remove( (pending_q.child_id == child_id) & (pending_q.entity_id == routine_id) & (pending_q.entity_type == 'routine') & (pending_q.status == 'pending') ) send_event_for_current_user( Event( EventType.CHILD_ROUTINE_CONFIRMATION.value, ChildRoutineConfirmation(child_id, routine_id, ChildRoutineConfirmation.OPERATION_RESET) ) ) send_event_for_current_user( Event( EventType.ROUTINE_SCHEDULE_MODIFIED.value, RoutineScheduleModified(child_id, routine_id, RoutineScheduleModified.OPERATION_SET) ) ) return jsonify(schedule.to_dict()), 200 @routine_schedule_api.route('/child//routine//schedule', methods=['DELETE']) def delete_routine_schedule(child_id, routine_id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': ErrorCodes.UNAUTHORIZED}), 401 if not _validate_child(child_id, user_id): return jsonify({'error': 'Child not found', 'code': ErrorCodes.CHILD_NOT_FOUND}), 404 removed = delete_schedule(child_id, routine_id) if not removed: return jsonify({'error': 'Schedule not found'}), 404 send_event_for_current_user( Event( EventType.ROUTINE_SCHEDULE_MODIFIED.value, RoutineScheduleModified(child_id, routine_id, RoutineScheduleModified.OPERATION_DELETED) ) ) return jsonify({'message': 'Schedule deleted'}), 200 @routine_schedule_api.route('/child//routine//extend', methods=['POST']) def extend_routine_time(child_id, routine_id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': ErrorCodes.UNAUTHORIZED}), 401 if not _validate_child(child_id, user_id): return jsonify({'error': 'Child not found', 'code': ErrorCodes.CHILD_NOT_FOUND}), 404 data = request.get_json() or {} date = data.get('date') if not date or not isinstance(date, str): return jsonify({'error': 'date is required (ISO date string)', 'code': ErrorCodes.MISSING_FIELD}), 400 existing = get_extension(child_id, routine_id, date) if existing: return jsonify({'error': 'Routine already extended for this date', 'code': 'ALREADY_EXTENDED'}), 409 delete_extension_for_child_routine(child_id, routine_id) extension = RoutineExtension(child_id=child_id, routine_id=routine_id, date=date) add_extension(extension) send_event_for_current_user( Event( EventType.ROUTINE_TIME_EXTENDED.value, RoutineTimeExtended(child_id, routine_id) ) ) return jsonify(extension.to_dict()), 200