from flask import Blueprint, request, jsonify from tinydb import Query from api.utils import send_event_for_current_user, get_validated_user_id from db.db import routine_db, child_db, pending_confirmations_db from db.child_overrides import delete_overrides_for_entity from db.routine_items import delete_for_routine from db.routine_schedules import delete_schedules_for_routine from db.routine_extensions import delete_extensions_for_routine from events.types.event import Event from events.types.event_types import EventType from events.types.routine_modified import RoutineModified from events.types.child_routines_set import ChildRoutinesSet from models.routine import Routine routine_api = Blueprint('routine_api', __name__) @routine_api.route('/routine/add', methods=['PUT']) def add_routine(): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401 data = request.get_json() or {} name = data.get('name') points = data.get('points') image = data.get('image_id', '') if not name or points is None: return jsonify({'error': 'Name and points are required'}), 400 routine = Routine(name=name, points=points, image_id=image, user_id=user_id) routine_db.insert(routine.to_dict()) send_event_for_current_user( Event(EventType.ROUTINE_MODIFIED.value, RoutineModified(routine.id, RoutineModified.OPERATION_ADD)) ) return jsonify({'message': f'Routine {name} added.', 'routine': routine.to_dict()}), 201 @routine_api.route('/routine/', methods=['GET']) def get_routine(id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401 q = Query() result = routine_db.search((q.id == id) & ((q.user_id == user_id) | (q.user_id == None))) if not result: return jsonify({'error': 'Routine not found'}), 404 return jsonify(result[0]), 200 @routine_api.route('/routine/list', methods=['GET']) def list_routines(): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401 ids_param = request.args.get('ids') q = Query() routines = routine_db.search((q.user_id == user_id) | (q.user_id == None)) if ids_param is not None: if ids_param.strip() == '': routines = [] else: ids = set(ids_param.split(',')) routines = [routine for routine in routines if routine.get('id') in ids] user_routines = {r['name'].strip().lower(): r for r in routines if r.get('user_id') == user_id} filtered_routines = [] for routine in routines: if routine.get('user_id') is None and routine['name'].strip().lower() in user_routines: continue filtered_routines.append(routine) user_created = sorted( [r for r in filtered_routines if r.get('user_id') == user_id], key=lambda x: x['name'].lower(), ) default_items = sorted( [r for r in filtered_routines if r.get('user_id') is None], key=lambda x: x['name'].lower(), ) return jsonify({'routines': user_created + default_items}), 200 @routine_api.route('/routine//edit', methods=['PUT']) def edit_routine(id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401 q = Query() existing = routine_db.get((q.id == id) & ((q.user_id == user_id) | (q.user_id == None))) if not existing: return jsonify({'error': 'Routine not found'}), 404 routine = Routine.from_dict(existing) data = request.get_json(force=True) or {} is_dirty = False if 'name' in data: name = data.get('name', '').strip() if not name: return jsonify({'error': 'Name cannot be empty'}), 400 routine.name = name is_dirty = True if 'points' in data: points = data.get('points') if not isinstance(points, int) or points <= 0: return jsonify({'error': 'Points must be a positive integer'}), 400 routine.points = points is_dirty = True if 'image_id' in data: routine.image_id = data.get('image_id', '') is_dirty = True if not is_dirty: return jsonify({'error': 'No valid fields to update'}), 400 if routine.user_id is None: new_routine = Routine(name=routine.name, points=routine.points, image_id=routine.image_id, user_id=user_id) routine_db.insert(new_routine.to_dict()) send_event_for_current_user( Event(EventType.ROUTINE_MODIFIED.value, RoutineModified(new_routine.id, RoutineModified.OPERATION_ADD)) ) return jsonify(new_routine.to_dict()), 200 routine_db.update(routine.to_dict(), q.id == id) send_event_for_current_user( Event(EventType.ROUTINE_MODIFIED.value, RoutineModified(id, RoutineModified.OPERATION_EDIT)) ) return jsonify(routine.to_dict()), 200 @routine_api.route('/routine/', methods=['DELETE']) def delete_routine(id): user_id = get_validated_user_id() if not user_id: return jsonify({'error': 'Unauthorized', 'code': 'UNAUTHORIZED'}), 401 q = Query() routine = routine_db.get(q.id == id) if not routine: return jsonify({'error': 'Routine not found'}), 404 if routine.get('user_id') is None: return jsonify({'error': 'System routines cannot be deleted.'}), 403 removed = routine_db.remove((q.id == id) & (q.user_id == user_id)) if not removed: return jsonify({'error': 'Routine not found'}), 404 delete_overrides_for_entity(id) delete_for_routine(id) delete_schedules_for_routine(id) delete_extensions_for_routine(id) pending_q = Query() pending_confirmations_db.remove( (pending_q.entity_id == id) & (pending_q.entity_type == 'routine') & (pending_q.user_id == user_id) ) child_q = Query() children = child_db.search(child_q.user_id == user_id) for child in children: routine_ids = child.get('routines', []) if id in routine_ids: routine_ids = [rid for rid in routine_ids if rid != id] child_db.update({'routines': routine_ids}, child_q.id == child.get('id')) send_event_for_current_user( Event(EventType.CHILD_ROUTINES_SET.value, ChildRoutinesSet(child.get('id'), routine_ids)) ) send_event_for_current_user( Event(EventType.ROUTINE_MODIFIED.value, RoutineModified(id, RoutineModified.OPERATION_DELETE)) ) return jsonify({'message': f'Routine {id} deleted.'}), 200