Source code for src.core.routes.tokens

# 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

import datetime as dt

from flask import Blueprint, jsonify, request

from src.core.middlewares.rbac import role_required
from src.core.services import tokens_service

bp = Blueprint("tokens", __name__)


[docs] @bp.post("/<int:survey_id>/generate") @role_required("admin", "rrhh") def generate_tokens(survey_id: int) -> tuple[list[dict], int]: """Generate survey tokens. Args: survey_id (int): Survey ID Returns: tuple[list[dict], int]: List of generated tokens, status code """ payload = request.get_json(silent=True) or {} count = int(payload.get("count", 1)) expires_at_str = payload.get("expires_at") if not expires_at_str: return jsonify({"message": "expires_at is required (ISO format)"}), 400 try: expires_at = dt.datetime.fromisoformat(expires_at_str) except ValueError: return jsonify({"message": "invalid expires_at format"}), 400 team_id = payload.get("team_id") employee_identifiers = payload.get("employee_identifiers") if employee_identifiers is not None and not isinstance(employee_identifiers, list): return jsonify({"message": "employee_identifiers must be a list"}), 400 try: tokens = tokens_service.generate_tokens_for_survey( survey_id, count, expires_at=expires_at, team_id=team_id, employee_identifiers=employee_identifiers, ) except LookupError: return jsonify({"message": "survey not found"}), 404 except ValueError as e: return jsonify({"message": str(e)}), 400 except RuntimeError as e: return jsonify({"message": str(e)}), 500 result = [ { "id": t.id, "token": t.token, "employee_identifier": t.employee_identifier, "expires_at": t.expires_at.isoformat() if t.expires_at else None, "created_at": t.created_at.isoformat() if t.created_at else None, "survey_id": t.survey_id, "team_id": t.team_id, "is_used": t.is_used, "used_at": t.used_at.isoformat() if t.used_at else None, } for t in tokens ] return jsonify(result), 201
[docs] @bp.get("/<int:survey_id>/list") @role_required("admin", "rrhh") def list_tokens(survey_id: int) -> tuple[list[dict], int]: """List survey tokens. Args: survey_id (int): Survey ID Returns: tuple[list[dict], int]: List of tokens, status code """ is_used = request.args.get("is_used") include_expired = request.args.get("include_expired") if is_used is not None: is_used = is_used.lower() in ("true", "1", "yes") include_expired = ( True if include_expired is None else include_expired.lower() in ("true", "1", "yes") ) rows = tokens_service.list_tokens( survey_id, is_used=is_used, include_expired=include_expired ) return jsonify(rows), 200
[docs] @bp.get("/<int:survey_id>/export") @role_required("admin", "rrhh") def export_tokens(survey_id: int) -> tuple[str, int, dict]: """Export survey tokens to CSV. Args: survey_id (int): Survey ID Returns: tuple[str, int, dict]: CSV content, status code, headers """ is_used = request.args.get("is_used") include_expired = request.args.get("include_expired") if is_used is not None: is_used = is_used.lower() in ("true", "1", "yes") include_expired = ( True if include_expired is None else include_expired.lower() in ("true", "1", "yes") ) csv_content = tokens_service.export_tokens_csv( survey_id, is_used=is_used, include_expired=include_expired ) return ( csv_content, 200, { "Content-Type": "text/csv; charset=utf-8", "Content-Disposition": f"attachment; filename=survey_{survey_id}_tokens.csv", }, )