All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m0s
- Add backend routines management with add, get, update, delete, and list functionalities. - Create models for Routine, RoutineItem, RoutineSchedule, and RoutineExtension. - Develop event types for routine confirmation and modification. - Implement frontend components for routine assignment, confirmation dialog, and routine management views. - Add unit tests for routine API and integration tests for routine CRUD flow. - Create end-to-end test plan for routines feature covering parent and child interactions.
186 lines
6.6 KiB
Python
186 lines
6.6 KiB
Python
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/<id>', 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/<id>/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/<id>', 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
|