Source code for src.core.routes.surveys

# Copyright (C) 2025 Raccoon Survey org
# This file is part of Raccoon Survey.
# Raccoon Survey is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License v3 as published by
# the Free Software Foundation.
# See the LICENSE file distributed with this program for details.

from __future__ import annotations

from datetime import datetime

from flask import Blueprint, jsonify, request

from src.core.middlewares.rbac import role_required
from src.core.models import Survey
from src.core.services import surveys_service

bp = Blueprint("surveys", __name__)


[docs] def serialize_survey(s: Survey) -> dict: """Serialize a survey. Args: s (Survey): Survey instance. Returns: dict: Serialized survey. """ return { "id": s.id, "team_id": s.team_id, "created_by_user_id": s.created_by_user_id, "title": s.title, "description": s.description, "is_anonymous": s.is_anonymous, "state": s.state, "expires_at": s.expires_at.isoformat() if s.expires_at else None, "created_at": s.created_at.isoformat() if s.created_at else None, }
[docs] @bp.get("/") @role_required("admin", "rrhh") def list_surveys() -> tuple[dict, int]: """List surveys. Returns: tuple[dict, int]: Serialized surveys and HTTP status code. """ team_id = request.args.get("team_id", type=int) surveys = surveys_service.list_surveys(team_id) return jsonify([serialize_survey(s) for s in surveys]), 200
[docs] @bp.post("/") @role_required("admin", "rrhh") def create_survey() -> tuple[dict, int]: """Create a new survey. Returns: tuple[dict, int]: Serialized survey and HTTP status code. """ payload = request.get_json(silent=True) or {} title = payload.get("title") team_id_raw = payload.get("team_id") team_id = team_id_raw.get("id") if isinstance(team_id_raw, dict) else team_id_raw if not title or not team_id: return jsonify({"message": "title and team_id are required"}), 400 description = payload.get("description") is_anonymous = bool(payload.get("is_anonymous", True)) expires_at = payload.get("expires_at") created_by_user_id = payload.get("created_by_user_id") expires_dt = None if expires_at: try: expires_dt = datetime.fromisoformat(expires_at) except ValueError: return jsonify({"message": "invalid expires_at format"}), 400 survey = surveys_service.create_survey( { "team_id": team_id, "created_by_user_id": created_by_user_id, "title": title, "description": description, "is_anonymous": is_anonymous, "expires_at": expires_dt, } ) return jsonify(serialize_survey(survey)), 201
[docs] @bp.get("/<int:survey_id>") @role_required("admin", "rrhh") def get_survey(survey_id: int) -> tuple[dict, int]: """Get a survey. Args: survey_id (int): Survey ID. Returns: tuple[dict, int]: Serialized survey and HTTP status code. """ survey = surveys_service.get_survey(survey_id) if not survey: return jsonify({"message": "survey not found"}), 404 return jsonify(serialize_survey(survey)), 200
[docs] @bp.put("/<int:survey_id>") @role_required("admin", "rrhh") def update_survey(survey_id: int) -> tuple[dict, int]: """Update survey. Args: survey_id (int): Survey ID. Returns: tuple[dict, int]: Serialized survey with updated fields and HTTP status code. """ survey = surveys_service.get_survey(survey_id) if not survey: return jsonify({"message": "survey not found"}), 404 payload = request.get_json(silent=True) or {} update_data: dict = {} for field in [ "title", "description", "is_anonymous", "team_id", "created_by_user_id", ]: if field in payload and payload[field] is not None: update_data[field] = payload[field] if "expires_at" in payload: expires_at = payload.get("expires_at") if expires_at: try: update_data["expires_at"] = datetime.fromisoformat(expires_at) except ValueError: return jsonify({"message": "invalid expires_at format"}), 400 else: update_data["expires_at"] = None if "state" in payload and payload["state"] is not None: update_data["state"] = bool(payload["state"]) survey = surveys_service.update_survey(survey_id, update_data) return jsonify(serialize_survey(survey))
[docs] @bp.patch("/<int:survey_id>/state") @role_required("admin", "rrhh") def change_survey_state(survey_id: int) -> tuple[dict, int]: """Change survey state. Args: survey_id (int): Survey ID. Returns: tuple[dict, int]: Serialized survey with updated state and HTTP status code. """ survey = surveys_service.get_survey(survey_id) if not survey: return jsonify({"message": "survey not found"}), 404 payload = request.get_json(silent=True) or {} state = payload.get("state") if state is None: return jsonify({"message": "state is required"}), 400 survey = surveys_service.set_survey_state(survey_id, bool(state)) return jsonify(serialize_survey(survey)), 200