Source code for src.core.routes.questions

# 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 flask import Blueprint, jsonify, request

from src.core.middlewares.rbac import role_required
from src.core.models import Question
from src.core.services import questions_service, surveys_service

bp = Blueprint("questions", __name__)


[docs] def serialize_question(q: Question) -> dict: """Serialize a question. Args: q (Question): Question instance. Returns: dict: Serialized question. """ return { "id": q.id, "survey_id": q.survey_id, "text": q.text, "type": q.type, "options": q.options, "is_required": q.is_required, "order_position": q.order_position, "state": q.state, "created_at": q.created_at.isoformat() if q.created_at else None, }
[docs] @bp.get("/") @role_required("admin", "rrhh") def list_questions() -> tuple[dict, int]: """List questions. Returns: tuple[dict, int]: Serialized questions and HTTP status code. """ survey_id = request.args.get("survey_id", type=int) questions = questions_service.list_questions(survey_id) return jsonify([serialize_question(q) for q in questions]), 200
[docs] @bp.post("/") @role_required("admin", "rrhh") def create_question() -> tuple[dict, int]: """Create a new question. Returns: tuple[dict, int]: Serialized question with created fields and HTTP status code. """ payload = request.get_json(silent=True) or {} required_fields = ["survey_id", "text", "type"] missing = [f for f in required_fields if not payload.get(f)] if missing: return ( jsonify({"message": f"missing fields: {', '.join(missing)}"}), 400, ) # Validate that the referenced survey exists to avoid FK violations survey = surveys_service.get_survey(payload["survey_id"]) if not survey: return jsonify({"message": "survey not found"}), 404 question = questions_service.create_question(payload) return jsonify(serialize_question(question)), 201
[docs] @bp.get("/<int:question_id>") @role_required("admin", "rrhh") def get_question(question_id: int) -> tuple[dict, int]: """Get question by ID. Args: question_id (int): Question ID. Returns: tuple[dict, int]: Serialized question and HTTP status code. """ question = questions_service.get_question(question_id) if not question: return jsonify({"message": "question not found"}), 404 return jsonify(serialize_question(question))
[docs] @bp.put("/<int:question_id>") @role_required("admin", "rrhh") def update_question(question_id: int) -> tuple[dict, int]: """Update question. Args: question_id (int): Question ID. Returns: tuple[dict, int]: Serialized question with updated fields and HTTP status code. """ question = questions_service.get_question(question_id) if not question: return jsonify({"message": "question not found"}), 404 payload = request.get_json(silent=True) or {} # If the payload attempts to change the survey_id, validate it exists first if "survey_id" in payload and payload["survey_id"] is not None: survey = surveys_service.get_survey(payload["survey_id"]) if not survey: return jsonify({"message": "survey not found"}), 404 question = questions_service.update_question(question_id, payload) return jsonify(serialize_question(question))
[docs] @bp.patch("/<int:question_id>/state") @role_required("admin", "rrhh") def change_question_state(question_id: int) -> tuple[dict, int]: """Change question state. Args: question_id (int): Question ID. Returns: tuple[dict, int]: Serialized question with updated state and HTTP status code. """ question = questions_service.get_question(question_id) if not question: return jsonify({"message": "question 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 question = questions_service.set_question_state(question_id, bool(state)) return jsonify(serialize_question(question))