Jul 13 - Update web API to support iOS app on scheduled inspection and issue's handler
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
app/api/scheduled.py
|
||||
--------------------
|
||||
Mobile API endpoint for planned/recurring inspection assignments (phase36).
|
||||
|
||||
GET /api/v1/scheduled-inspections
|
||||
Returns ACTIVE scheduled inspections the caller is responsible for.
|
||||
- inspector : only schedules where inspector_id == the caller
|
||||
- admin / director / project_manager : all active schedules
|
||||
Powers the "Scheduled" section on the iPad Dashboard and My Inspections
|
||||
lists. The iPad taps "Start", which opens the normal new-inspection flow
|
||||
with the facility + template preselected (client-side); the schedule
|
||||
lifecycle (fulfil / roll-forward) continues to be driven by the web app.
|
||||
|
||||
A ScheduledInspection is a PLAN, not an inspection — see
|
||||
app/models/scheduled_inspection.py for the full lifecycle.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from flask import Blueprint, request, g
|
||||
from app.models.scheduled_inspection import ScheduledInspection
|
||||
from app.api.errors import api_ok, api_error
|
||||
from app.api.decorators import jwt_required
|
||||
from app.utils.scope import get_inspector_scope
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
bp = Blueprint('api_scheduled', __name__)
|
||||
|
||||
_ALLOWED_ROLES = {'admin', 'director', 'inspector', 'project_manager'}
|
||||
|
||||
|
||||
def _scheduled_payload(s):
|
||||
"""Serialise a ScheduledInspection to the dict returned in list responses."""
|
||||
return {
|
||||
'id': s.id,
|
||||
'facility_id': s.facility_id,
|
||||
'facility_name': s.facility.name if s.facility else None,
|
||||
'template_id': s.template_id,
|
||||
'template_name': s.template.name if s.template else None,
|
||||
'inspector_id': s.inspector_id,
|
||||
'frequency': s.frequency,
|
||||
'frequency_label': s.frequency_label,
|
||||
'next_due_date': s.next_due_date.isoformat() if s.next_due_date else None,
|
||||
'is_overdue': s.is_overdue(),
|
||||
'notes': s.notes or None,
|
||||
}
|
||||
|
||||
|
||||
# ── List Scheduled Inspections ────────────────────────────────────────────────
|
||||
|
||||
@bp.route('/scheduled-inspections', methods=['GET'])
|
||||
@jwt_required
|
||||
def list_scheduled():
|
||||
"""
|
||||
Return active scheduled inspections for the authenticated user.
|
||||
|
||||
Query parameters
|
||||
----------------
|
||||
limit int Default 100, max 200.
|
||||
offset int Default 0.
|
||||
|
||||
Response 200
|
||||
------------
|
||||
{
|
||||
"ok": true,
|
||||
"data": {
|
||||
"scheduled": [...],
|
||||
"total": 3,
|
||||
"limit": 100,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
"""
|
||||
user = g.api_user
|
||||
if user.role not in _ALLOWED_ROLES:
|
||||
return api_error('Access denied', 403)
|
||||
|
||||
limit = min(int(request.args.get('limit', 100)), 200)
|
||||
offset = max(int(request.args.get('offset', 0)), 0)
|
||||
|
||||
query = ScheduledInspection.query.filter(ScheduledInspection.active.is_(True))
|
||||
|
||||
if user.role == 'inspector':
|
||||
# Inspectors only see schedules assigned directly to them.
|
||||
query = query.filter(ScheduledInspection.inspector_id == user.id)
|
||||
|
||||
total = query.count()
|
||||
rows = (
|
||||
query
|
||||
.order_by(ScheduledInspection.next_due_date.asc())
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
payload = [_scheduled_payload(s) for s in rows]
|
||||
|
||||
logger.info('API SCHEDULED | list | user=%s | count=%d | total=%d',
|
||||
user.username, len(payload), total)
|
||||
|
||||
return api_ok({'scheduled': payload, 'total': total,
|
||||
'limit': limit, 'offset': offset})
|
||||
Reference in New Issue
Block a user