mirror of
https://github.com/marcogll/molding_assesment.git
synced 2026-03-15 16:25:06 +00:00
Update assessments, scripts, and documentation
- Updated JSON assessment files for all levels - Modified formbricks_assistant.py script - Updated markdown assessment documents - Added comprehensive Form_requirements.md documentation - Updated tasks.md with current status
This commit is contained in:
@@ -2,3 +2,4 @@ FORMBRICKS_BASEURL=https://feedback.soul23.cloud
|
||||
FORMBRICKS_API_KEY=fbk_re7r3JGIlwrFXUgxXv4-YHbNnvjc3gD9aVm13N2LvzE
|
||||
FORMBRICKS_ENVIRONMENT_ID=cmbjyaka9000fqr01fxyfn8f3
|
||||
|
||||
|
||||
|
||||
491
questions/formbricks/Form_requirements.md
Normal file
491
questions/formbricks/Form_requirements.md
Normal file
@@ -0,0 +1,491 @@
|
||||
# Formbricks Survey Requirements - API v1
|
||||
|
||||
## Overview
|
||||
|
||||
Este documento es la **guía definitiva** para crear encuestas en Formbricks utilizando la API REST para las evaluaciones técnicas de moldeo por inyección. Incluye todos los requisitos de formato, ejemplos completos y solución de problemas comunes.
|
||||
|
||||
## Instalación y Configuración
|
||||
|
||||
- **Idioma**: NO multiidioma. Todos los contenidos se proporcionan en español únicamente.
|
||||
- **Versión API**: v1 (compatible con Formbricks actual)
|
||||
- **Formato Requerido**: Todos los campos de texto DEBEN usar formato objeto `{"default": "texto"}`, no strings directos.
|
||||
|
||||
## Variables de Entorno Requeridas
|
||||
|
||||
```env
|
||||
FORMBRICKS_BASEURL=https://app.formbricks.com
|
||||
FORMBRICKS_API_KEY=tu-api-key
|
||||
FORMBRICKS_ENVIRONMENT_ID=tu-environment-id
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Crear Encuesta (Create Survey)
|
||||
|
||||
```
|
||||
POST https://{baseurl}/api/v1/management/surveys
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
- `Content-Type: application/json`
|
||||
- `x-api-key: <API_KEY>`
|
||||
|
||||
**Respuesta Éxito**: 200 OK con datos de la encuesta creada
|
||||
**Respuesta Error**: 400 Bad Request si campos mal formateados
|
||||
|
||||
### Actualizar Encuesta (Update Survey)
|
||||
|
||||
```
|
||||
PUT https://{baseurl}/api/v1/management/surveys/{surveyId}
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
- `Content-Type: application/json`
|
||||
- `x-api-key: <API_KEY>`
|
||||
|
||||
### Obtener Encuesta (Get Survey)
|
||||
|
||||
```
|
||||
GET https://{baseurl}/api/v1/management/surveys/{surveyId}
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
- `x-api-key: <API_KEY>`
|
||||
|
||||
## Estructura del Payload
|
||||
|
||||
### Campos Principales
|
||||
|
||||
| Campo | Tipo | Requerido | Descripción |
|
||||
|-------|------|-----------|-------------|
|
||||
| `environmentId` | string | Sí | ID del entorno de Formbricks |
|
||||
| `name` | string | Sí | Nombre de la encuesta |
|
||||
| `status` | enum | Sí | `draft`, `inProgress`, `paused`, `completed` |
|
||||
| `type` | enum | Sí | `link`, `app` |
|
||||
| `displayOption` | enum | No | `displayOnce`, `displayMultiple`, `respondMultiple`, `displaySome` |
|
||||
| `questions` | array | Sí | Array de preguntas (ver formato abajo) |
|
||||
| `welcomeCard` | object | No | Tarjeta de bienvenida (ver formato abajo) |
|
||||
| `endings` | array | No | Pantallas de finalización (ver formato abajo) |
|
||||
| `languages` | array | Sí | Array vacío `[]` (no multiidioma) |
|
||||
|
||||
### ⚠️ REQUISITO CRÍTICO: Formato de Texto
|
||||
|
||||
**TODOS** los campos de texto deben usar el formato objeto, NO strings directos:
|
||||
|
||||
```json
|
||||
// ✅ CORRECTO
|
||||
"headline": {"default": "Texto de la pregunta"}
|
||||
|
||||
// ❌ INCORRECTO
|
||||
"headline": "Texto de la pregunta"
|
||||
```
|
||||
|
||||
Campos afectados: `headline`, `subheader`, `buttonLabel`, `backButtonLabel`, `placeholder`, `label`, `html`
|
||||
|
||||
### Welcome Card
|
||||
|
||||
```json
|
||||
{
|
||||
"welcomeCard": {
|
||||
"enabled": true,
|
||||
"headline": {
|
||||
"default": "Evaluación Técnica de Moldeo"
|
||||
},
|
||||
"html": {
|
||||
"default": "<b>Nivel Básico</b><br>Prueba de conocimientos fundamentales.<br><br>• Preguntas: 51<br>• Tiempo estimado: 45 min"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"showResponseCount": false,
|
||||
"timeToFinish": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**IMPORTANTE**: TODOS los campos de texto (`headline`, `html`, `buttonLabel`) DEBEN ser objetos con un campo `default`.
|
||||
|
||||
### Ending Card
|
||||
|
||||
```json
|
||||
{
|
||||
"endings": [
|
||||
{
|
||||
"id": "p73t62dgwq0cvmtt6ug0hmfc",
|
||||
"type": "endScreen",
|
||||
"headline": {
|
||||
"default": "¡Gracias!"
|
||||
},
|
||||
"subheader": {
|
||||
"default": "Tu evaluación ha sido enviada correctamente."
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Completar"
|
||||
},
|
||||
"buttonLink": "https://example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Nota**: Los campos `headline`, `subheader`, `buttonLabel` usan formato objeto.
|
||||
|
||||
## Mapeo de Preguntas
|
||||
|
||||
### Estructura General
|
||||
|
||||
Las preguntas del JSON original se mapean a Formbricks. **TODOS los campos de texto requieren formato objeto**.
|
||||
|
||||
| Campo Original | Campo Formbricks | Formato Requerido | Notas |
|
||||
|----------------|------------------|-------------------|--------|
|
||||
| `id` | `id` | string | ID único de la pregunta |
|
||||
| `question` | `headline` | `{"default": "texto"}` | Texto principal de la pregunta |
|
||||
| `description` | `subheader` | `{"default": "texto"}` | Contexto adicional |
|
||||
| `type` | `type` | string | Mapeado a tipos de Formbricks |
|
||||
| `required` | `required` | boolean | Campo obligatorio |
|
||||
| `options` | `choices` | array de objetos | Opciones con `label: {"default": "texto"}` |
|
||||
|
||||
**CRÍTICO**: Los campos `headline`, `subheader`, `buttonLabel`, `backButtonLabel`, `placeholder`, `label` (en choices) DEBEN usar formato objeto.
|
||||
|
||||
### Tipos de Preguntas
|
||||
|
||||
#### 1. Texto Abierto (openText)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "employee_number",
|
||||
"type": "openText",
|
||||
"inputType": "text",
|
||||
"headline": {
|
||||
"default": "Número de empleado"
|
||||
},
|
||||
"subheader": {
|
||||
"default": ""
|
||||
},
|
||||
"required": true,
|
||||
"placeholder": {
|
||||
"default": "Ingresa tu número de empleado"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Nota**: Todos los campos de texto usan formato objeto.
|
||||
|
||||
#### 2. Selección Múltiple Única (multipleChoiceSingle)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "mach_1",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": {
|
||||
"default": "¿Cuál es el componente que gira para transportar el material?"
|
||||
},
|
||||
"subheader": {
|
||||
"default": "La unidad de plastificación consta de varios elementos clave para procesar el material."
|
||||
},
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "c0",
|
||||
"label": {
|
||||
"default": "El barril reforzado (cilindro)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"label": {
|
||||
"default": "El tornillo (husillo)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
},
|
||||
"shuffleOption": "none"
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Selección Múltiple (multipleChoiceMulti)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "qual_1",
|
||||
"type": "multipleChoiceMulti",
|
||||
"headline": {
|
||||
"default": "¿Qué defectos pueden ocurrir en el proceso?"
|
||||
},
|
||||
"subheader": {
|
||||
"default": "Selecciona todas las opciones aplicables."
|
||||
},
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "c0",
|
||||
"label": {
|
||||
"default": "Tiro Corto"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"label": {
|
||||
"default": "Rebaba"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
},
|
||||
"shuffleOption": "none"
|
||||
}
|
||||
```
|
||||
|
||||
## Ejemplo Completo de Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"environmentId": "clygwxsbh01v5aga1sdien2th",
|
||||
"name": "Evaluación Técnica de Moldeo - Nivel Básico",
|
||||
"type": "link",
|
||||
"status": "draft",
|
||||
"displayOption": "displayOnce",
|
||||
"welcomeCard": {
|
||||
"enabled": true,
|
||||
"headline": {
|
||||
"default": "Evaluación Técnica de Moldeo"
|
||||
},
|
||||
"html": "<b>Nivel Básico</b><br>Prueba de conocimientos fundamentales sobre maquinaria, proceso, calidad y seguridad.<br><br>• Preguntas: 51<br>• Tiempo estimado: 45 min",
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"showResponseCount": false,
|
||||
"timeToFinish": false
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"id": "employee_number",
|
||||
"type": "openText",
|
||||
"headline": {
|
||||
"default": "Número de empleado"
|
||||
},
|
||||
"subheader": "",
|
||||
"required": true,
|
||||
"inputType": "text",
|
||||
"placeholder": {
|
||||
"default": "Ingresa tu número de empleado"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mach_1",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": {
|
||||
"default": "En el sistema de plastificación, ¿cuál es el componente que gira para cizallar, fundir y transportar el material hacia adelante?"
|
||||
},
|
||||
"subheader": "La unidad de plastificación consta de varios elementos clave para procesar el material.",
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "c0",
|
||||
"label": {
|
||||
"default": "El barril reforzado (cilindro)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"label": {
|
||||
"default": "El tornillo (husillo)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"label": {
|
||||
"default": "La válvula check (anillo)"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"label": {
|
||||
"default": "La banda calefactora cerámica"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
},
|
||||
"shuffleOption": "none"
|
||||
}
|
||||
],
|
||||
"endings": [
|
||||
{
|
||||
"id": "p73t62dgwq0cvmtt6ug0hmfc",
|
||||
"type": "endScreen",
|
||||
"headline": {
|
||||
"default": "¡Gracias!"
|
||||
},
|
||||
"subheader": {
|
||||
"default": "Tu evaluación ha sido enviada correctamente."
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Completar"
|
||||
},
|
||||
"buttonLink": "https://example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Mapeo de Tipos de Preguntas
|
||||
|
||||
| Tipo Original | Tipo Formbricks | Descripción |
|
||||
|--------------|-----------------|-------------|
|
||||
| N/A (texto abierto) | `openText` | Campo de texto libre |
|
||||
| Opción única | `multipleChoiceSingle` | Una sola respuesta |
|
||||
| Múltiples opciones | `multipleChoiceMulti` | Selección múltiple |
|
||||
|
||||
## Reglas de Validación
|
||||
|
||||
1. **IDs Únicos**: Cada pregunta debe tener un ID único
|
||||
2. **Choices IDs**: Cada opción debe tener un ID único (ej. `c0`, `c1`, `c2`, `c3`)
|
||||
3. **Required**: Las preguntas marcadas como requeridas deben tener `required: true`
|
||||
4. **No Multiidioma**: El campo `languages` debe ser un array vacío `[]`
|
||||
5. **Formato de Objetos CRÍTICO**: TODOS los campos de texto DEBEN usar `{"default": "texto"}`
|
||||
- `headline`, `subheader`, `buttonLabel`, `backButtonLabel`, `placeholder`, `label`, `html`
|
||||
- Error 400 si se usa string directo
|
||||
|
||||
### Conversión Automática
|
||||
|
||||
Usa el script `formbricks_assitant.py` que automáticamente convierte strings a objetos. Los JSON fuente pueden mantener strings para legibilidad, la conversión ocurre en runtime.
|
||||
|
||||
## Campos Opcionales
|
||||
|
||||
### shuffleOption
|
||||
|
||||
Controla si las opciones se muestran en orden aleatorio:
|
||||
- `none`: Orden original
|
||||
- `random`: Orden aleatorio
|
||||
|
||||
### displayLimit
|
||||
|
||||
Número máximo de veces que se puede mostrar la encuesta.
|
||||
|
||||
### delay
|
||||
|
||||
Retraso en segundos antes de mostrar la encuesta.
|
||||
|
||||
## Configuración de Entorno
|
||||
|
||||
Las variables de entorno requeridas están en el archivo `.env`:
|
||||
|
||||
```env
|
||||
FORMBRICKS_BASEURL=https://app.formbricks.com
|
||||
FORMBRICKS_API_KEY=tu-api-key
|
||||
FORMBRICKS_ENVIRONMENT_ID=tu-environment-id
|
||||
```
|
||||
|
||||
## Ejemplo de cURL
|
||||
|
||||
```bash
|
||||
curl --request POST \
|
||||
--url https://app.formbricks.com/api/v1/management/surveys \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: YOUR_API_KEY' \
|
||||
--data @survey_payload.json
|
||||
```
|
||||
|
||||
## Estructura de Archivos y Scripts
|
||||
|
||||
### Archivos JSON
|
||||
Los archivos generados para Formbricks tienen esta estructura:
|
||||
|
||||
```
|
||||
formbricks/
|
||||
├── .env # Variables de entorno
|
||||
├── Form_requirements.md # Este documento (guía definitiva)
|
||||
├── assessment_conv.py # Script conversor JSON a Formbricks
|
||||
├── formbricks_assitant.py # Script de creación de encuestas (con conversión automática)
|
||||
├── convert_json.py # Utilidad de conversión de archivos JSON
|
||||
├── funnel_registration_formbricks.json # Nivel 0
|
||||
├── basic_v2_formbricks.json # Nivel 1 (57 preguntas)
|
||||
├── medium_v2_formbricks.json # Nivel 2 (60 preguntas)
|
||||
└── advanced_v2_formbricks.json # Nivel 3 (43 preguntas)
|
||||
```
|
||||
|
||||
### Scripts Disponibles
|
||||
|
||||
#### `formbricks_assitant.py` (Principal)
|
||||
Crea encuestas en Formbricks con conversión automática de formato:
|
||||
```bash
|
||||
cd /home/marco/Work/Carol/questions/formbricks
|
||||
python3 formbricks_assitant.py
|
||||
```
|
||||
|
||||
#### `assessment_conv.py` (Conversor)
|
||||
Convierte archivos JSON originales a Formbricks:
|
||||
```bash
|
||||
cd /home/marco/Work/Carol/questions
|
||||
python3 assessment_conv.py
|
||||
```
|
||||
|
||||
#### `convert_json.py` (Utilidad)
|
||||
Convierte archivos JSON existentes al formato API-compliant:
|
||||
```bash
|
||||
cd /home/marco/Work/Carol
|
||||
python3 questions/formbricks/convert_json.py
|
||||
```
|
||||
|
||||
## Referencias
|
||||
|
||||
- [Documentación API REST de Formbricks](https://formbricks.com/docs/api-reference/rest-api)
|
||||
- [API Reference - Survey](https://formbricks.com/docs/api-reference/management-api--survey/)
|
||||
|
||||
## Errores Comunes de la API
|
||||
|
||||
### Error 400: "Fields are missing or incorrectly formatted"
|
||||
|
||||
Este error ocurre cuando los campos de texto no tienen el formato correcto de objeto.
|
||||
|
||||
**Causa común**: Usar string directo en lugar de objeto para cualquier campo de texto.
|
||||
|
||||
**Incorrecto**:
|
||||
```json
|
||||
{
|
||||
"headline": "Evaluación Técnica",
|
||||
"buttonLabel": "Siguiente"
|
||||
}
|
||||
```
|
||||
|
||||
**Correcto**:
|
||||
```json
|
||||
{
|
||||
"headline": {"default": "Evaluación Técnica"},
|
||||
"buttonLabel": {"default": "Siguiente"}
|
||||
}
|
||||
```
|
||||
|
||||
### Errores típicos de validación:
|
||||
- `welcomeCard.headline: Expected object, received string`
|
||||
- `questions[0].headline: Expected object, received string`
|
||||
- `questions[0].choices[0].label: Expected object, received string`
|
||||
|
||||
### Solución
|
||||
Ejecuta el script `formbricks_assitant.py` - maneja automáticamente la conversión de formato.
|
||||
34
questions/formbricks/README.md
Normal file
34
questions/formbricks/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Formbricks Survey Integration
|
||||
|
||||
This directory contains scripts and data for creating technical assessment surveys in Formbricks.
|
||||
|
||||
## Files
|
||||
|
||||
### Scripts
|
||||
- `formbricks_assitant.py` - Main script for creating surveys via Formbricks API
|
||||
- `assessment_conv.py` - Script for converting original JSON assessments to Formbricks format
|
||||
|
||||
### Data Files
|
||||
- `basic_v2_formbricks.json` - Basic level assessment (57 questions)
|
||||
- `medium_v2_formbricks.json` - Medium level assessment (60 questions)
|
||||
- `advanced_v2_formbricks.json` - Advanced level assessment (43 questions)
|
||||
- `funnel_registration_formbricks.json` - Registration funnel
|
||||
|
||||
### Documentation
|
||||
- `Form_requirements.md` - Complete API documentation and requirements
|
||||
- `.env` - Environment variables (API keys, etc.)
|
||||
|
||||
### Archive
|
||||
- `archive/` - Contains previous versions of JSON files
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Create a survey
|
||||
python3 formbricks_assitant.py
|
||||
|
||||
# Convert assessments
|
||||
python3 assessment_conv.py
|
||||
```
|
||||
|
||||
See `Form_requirements.md` for detailed documentation.
|
||||
File diff suppressed because it is too large
Load Diff
1592
questions/formbricks/archive/advanced_v2_formbricks.json.old
Normal file
1592
questions/formbricks/archive/advanced_v2_formbricks.json.old
Normal file
File diff suppressed because it is too large
Load Diff
1384
questions/formbricks/archive/basic_v2_formbricks.json.old
Normal file
1384
questions/formbricks/archive/basic_v2_formbricks.json.old
Normal file
File diff suppressed because it is too large
Load Diff
2204
questions/formbricks/archive/medium_v2_formbricks.json.old
Normal file
2204
questions/formbricks/archive/medium_v2_formbricks.json.old
Normal file
File diff suppressed because it is too large
Load Diff
203
questions/formbricks/assessment_conv.py
Normal file
203
questions/formbricks/assessment_conv.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Conversor de JSON a Formbricks
|
||||
Convierte archivos de preguntas JSON originales al formato Formbricks API
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def calculate_time_estimated(num_questions):
|
||||
"""Calcula el tiempo estimado basado en el numero de preguntas"""
|
||||
# Aproximadamente 45-60 segundos por pregunta
|
||||
minutes = (num_questions * 45) // 60
|
||||
return max(minutes, 1)
|
||||
|
||||
|
||||
def create_welcome_card(level_name, num_questions):
|
||||
"""Crea el welcomeCard para Formbricks"""
|
||||
level_descriptions = {
|
||||
"Basico": "Prueba de conocimientos fundamentales sobre maquinaria, proceso, calidad y seguridad.",
|
||||
"Intermedio": "Evaluacion de diagnostico de fallas, parametros de proceso y eficiencia operativa.",
|
||||
"Avanzado": "Prueba para Ingenieros y Tecnicos Sr. enfocada en optimizacion, reologia, defectos complejos y diseno de moldes.",
|
||||
}
|
||||
|
||||
time_estimated = calculate_time_estimated(num_questions)
|
||||
|
||||
return {
|
||||
"enabled": True,
|
||||
"headline": {"default": "Evaluacion Tecnica de Moldeo"},
|
||||
"html": f"<b>Nivel {level_name}</b><br>{level_descriptions.get(level_name, '')}<br><br>• Preguntas: {num_questions}<br>• Tiempo estimado: {time_estimated} min",
|
||||
"showResponseCount": False,
|
||||
"timeToFinish": False,
|
||||
"buttonLabel": {"default": "Siguiente"},
|
||||
}
|
||||
|
||||
time_estimated = calculate_time_estimated(num_questions)
|
||||
|
||||
return {
|
||||
"enabled": True,
|
||||
"headline": "Evaluacion Tecnica de Moldeo",
|
||||
"html": f"<b>Nivel {level_name}</b><br>{level_descriptions.get(level_name, '')}<br><br>• Preguntas: {num_questions}<br>• Tiempo estimado: {time_estimated} min",
|
||||
"showResponseCount": False,
|
||||
"timeToFinish": False,
|
||||
"buttonLabel": "Siguiente",
|
||||
}
|
||||
|
||||
time_estimated = calculate_time_estimated(num_questions)
|
||||
|
||||
return {
|
||||
"enabled": True,
|
||||
"headline": "Evaluación Técnica de Moldeo",
|
||||
"html": f"<b>Nivel {level_name}</b><br>{level_descriptions.get(level_name, '')}<br><br>• Preguntas: {num_questions}<br>• Tiempo estimado: {time_estimated} min",
|
||||
"showResponseCount": False,
|
||||
"timeToFinish": False,
|
||||
"buttonLabel": "Siguiente",
|
||||
}
|
||||
|
||||
|
||||
def convert_question(original_q):
|
||||
"""Convierte una pregunta original al formato Formbricks"""
|
||||
# Obtener description y manejar si es None o vacío
|
||||
description = original_q.get("description", "")
|
||||
if description is None:
|
||||
description = ""
|
||||
|
||||
fb_question = {
|
||||
"id": original_q["id"],
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": {"default": original_q["question"]},
|
||||
"subheader": description,
|
||||
"required": True,
|
||||
"buttonLabel": {"default": "Siguiente"},
|
||||
"backButtonLabel": {"default": "Anterior"},
|
||||
"shuffleOption": "none",
|
||||
"choices": [],
|
||||
}
|
||||
|
||||
# Convertir opciones
|
||||
for idx, option in enumerate(original_q.get("options", [])):
|
||||
fb_question["choices"].append({"id": f"c{idx}", "label": {"default": option}})
|
||||
|
||||
return fb_question
|
||||
|
||||
|
||||
def create_ending():
|
||||
"""Crea el ending screen"""
|
||||
return {
|
||||
"id": "end_screen",
|
||||
"type": "endScreen",
|
||||
"headline": {"default": "Evaluación Completada"},
|
||||
"subheader": {
|
||||
"default": "Tus respuestas han sido enviadas exitosamente. El departamento de entrenamiento revisará tus resultados."
|
||||
},
|
||||
"buttonLabel": {"default": "Finalizar"},
|
||||
}
|
||||
|
||||
|
||||
def convert_json_to_formbricks(input_file, output_file, level_name):
|
||||
"""Convierte un archivo JSON original a Formbricks"""
|
||||
|
||||
# Leer archivo original
|
||||
with open(input_file, "r", encoding="utf-8") as f:
|
||||
original_data = json.load(f)
|
||||
|
||||
questions = (
|
||||
original_data
|
||||
if isinstance(original_data, list)
|
||||
else original_data.get("questions", [])
|
||||
)
|
||||
num_questions = len(questions)
|
||||
|
||||
# Crear estructura Formbricks
|
||||
formbricks_data = {
|
||||
"name": f"Evaluación Técnica de Moldeo - Nivel {level_name}",
|
||||
"type": "link",
|
||||
"status": "draft",
|
||||
"displayOption": "displayOnce",
|
||||
"welcomeCard": create_welcome_card(level_name, num_questions),
|
||||
"questions": [],
|
||||
}
|
||||
|
||||
# Agregar pregunta de número de empleado al inicio
|
||||
formbricks_data["questions"].append(
|
||||
{
|
||||
"id": "employee_number",
|
||||
"type": "openText",
|
||||
"headline": {"default": "Número de empleado"},
|
||||
"subheader": "",
|
||||
"required": True,
|
||||
"inputType": "text",
|
||||
"placeholder": {"default": "Ingresa tu número de empleado"},
|
||||
"buttonLabel": {"default": "Siguiente"},
|
||||
"backButtonLabel": {"default": "Anterior"},
|
||||
}
|
||||
)
|
||||
|
||||
# Convertir y agregar preguntas
|
||||
for question in questions:
|
||||
fb_question = convert_question(question)
|
||||
formbricks_data["questions"].append(fb_question)
|
||||
|
||||
# Agregar ending
|
||||
formbricks_data["endings"] = [create_ending()]
|
||||
|
||||
# Escribir archivo Formbricks
|
||||
with open(output_file, "w", encoding="utf-8") as f:
|
||||
json.dump(formbricks_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"✓ {output_file.name} creado exitosamente ({num_questions} preguntas)")
|
||||
|
||||
|
||||
def main():
|
||||
"""Función principal"""
|
||||
# Definir rutas
|
||||
base_dir = Path(__file__).parent
|
||||
json_dir = base_dir / "json"
|
||||
formbricks_dir = base_dir / "formbricks"
|
||||
|
||||
# Definir archivos a convertir
|
||||
conversions = [
|
||||
{
|
||||
"input": json_dir / "basic_v2.json",
|
||||
"output": formbricks_dir / "basic_v2_formbricks.json",
|
||||
"level": "Básico",
|
||||
},
|
||||
{
|
||||
"input": json_dir / "medium_v2.json",
|
||||
"output": formbricks_dir / "medium_v2_formbricks.json",
|
||||
"level": "Intermedio",
|
||||
},
|
||||
{
|
||||
"input": json_dir / "advanced_v2.json",
|
||||
"output": formbricks_dir / "advanced_v2_formbricks.json",
|
||||
"level": "Avanzado",
|
||||
},
|
||||
]
|
||||
|
||||
print("=" * 60)
|
||||
print("Conversor de JSON a Formbricks")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Procesar cada archivo
|
||||
for conversion in conversions:
|
||||
if conversion["input"].exists():
|
||||
print(f"Procesando: {conversion['input'].name}")
|
||||
convert_json_to_formbricks(
|
||||
conversion["input"], conversion["output"], conversion["level"]
|
||||
)
|
||||
print()
|
||||
else:
|
||||
print(f"✗ Archivo no encontrado: {conversion['input']}")
|
||||
print()
|
||||
|
||||
print("=" * 60)
|
||||
print("Conversión completada!")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import json
|
||||
import uuid
|
||||
import requests
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
@@ -37,107 +38,130 @@ if not all([BASEURL, API_KEY, ENV_ID]):
|
||||
raise RuntimeError("Variables de entorno incompletas")
|
||||
|
||||
LEVEL_FILE_MAP = {
|
||||
"0": "funnel_registration_formbricks.json",
|
||||
"1": "basic_v2_formbricks.json",
|
||||
"2": "medium_v2_formbricks.json",
|
||||
"3": "advanced_v2_formbricks.json",
|
||||
}
|
||||
|
||||
def parse_survey(raw_items):
|
||||
|
||||
def convert_to_object_format(data):
|
||||
"""Recursively convert string text fields to object format required by Formbricks API"""
|
||||
if isinstance(data, dict):
|
||||
for key, value in data.items():
|
||||
if key in [
|
||||
"headline",
|
||||
"subheader",
|
||||
"buttonLabel",
|
||||
"backButtonLabel",
|
||||
"placeholder",
|
||||
"label",
|
||||
"html",
|
||||
] and isinstance(value, str):
|
||||
data[key] = {"default": value}
|
||||
elif key == "choices" and isinstance(value, list):
|
||||
for choice in value:
|
||||
if (
|
||||
isinstance(choice, dict)
|
||||
and "label" in choice
|
||||
and isinstance(choice["label"], str)
|
||||
):
|
||||
choice["label"] = {"default": choice["label"]}
|
||||
else:
|
||||
convert_to_object_format(value)
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
convert_to_object_format(item)
|
||||
|
||||
|
||||
def parse_questions(raw_items):
|
||||
questions = []
|
||||
welcome_card = None
|
||||
ending = None
|
||||
|
||||
for i, item in enumerate(raw_items):
|
||||
t = item.get("type")
|
||||
q = item.copy()
|
||||
if "id" not in q:
|
||||
q["id"] = f"q{i}"
|
||||
questions.append(q)
|
||||
|
||||
if t == "welcome":
|
||||
welcome_card = {
|
||||
"enabled": True,
|
||||
"headline": item.get("headline"),
|
||||
"html": item.get("html"),
|
||||
"showResponseCount": False,
|
||||
"timeToFinish": False,
|
||||
}
|
||||
return questions
|
||||
|
||||
elif t == "thankYou":
|
||||
ending = {
|
||||
"id": item.get("id", "end_default"),
|
||||
"type": "endScreen",
|
||||
"headline": item.get("headline"),
|
||||
"subheader": item.get("html"),
|
||||
}
|
||||
|
||||
else:
|
||||
q = {
|
||||
"id": item.get("id", f"q{i}"),
|
||||
"type": item.get("type"),
|
||||
"required": item.get("required", True),
|
||||
"headline": item.get("headline"),
|
||||
"shuffleOption": item.get("shuffleOption", "none"),
|
||||
"choices": [
|
||||
{
|
||||
"id": c.get("id", f"{i}_{idx}"),
|
||||
"label": c.get("label"),
|
||||
}
|
||||
for idx, c in enumerate(item.get("choices", []))
|
||||
],
|
||||
}
|
||||
questions.append(q)
|
||||
def main():
|
||||
# ───────── Inputs ─────────
|
||||
company = Prompt.ask("🏭 Compañía", default="Empresa")
|
||||
level = Prompt.ask("🎚️ Nivel [0/1/2/3]", choices=["0", "1", "2", "3"], default="1")
|
||||
start_date = Prompt.ask("📅 Fecha inicio (YYYY-MM-DD o vacío)", default="")
|
||||
end_date = Prompt.ask("📅 Fecha término (YYYY-MM-DD o vacío)", default="")
|
||||
|
||||
return questions, welcome_card, ending
|
||||
file_name = LEVEL_FILE_MAP[level]
|
||||
|
||||
# ───────── Inputs ─────────
|
||||
company = Prompt.ask("🏭 Compañía", default="Empresa")
|
||||
level = Prompt.ask("🎚️ Nivel [1/2/3]", choices=["1", "2", "3"], default="1")
|
||||
start_date = Prompt.ask("📅 Fecha inicio (YYYY-MM-DD o vacío)", default="")
|
||||
end_date = Prompt.ask("📅 Fecha término (YYYY-MM-DD o vacío)", default="")
|
||||
with open(file_name, "r", encoding="utf-8") as f:
|
||||
raw_file = json.load(f)
|
||||
|
||||
file_name = LEVEL_FILE_MAP[level]
|
||||
raw_questions = raw_file.get("questions", [])
|
||||
if not raw_questions:
|
||||
raise RuntimeError("El archivo no contiene preguntas")
|
||||
|
||||
with open(file_name, "r", encoding="utf-8") as f:
|
||||
raw_file = json.load(f)
|
||||
questions = parse_questions(raw_questions)
|
||||
convert_to_object_format(questions)
|
||||
|
||||
raw_questions = raw_file.get("questions", [])
|
||||
if not raw_questions:
|
||||
raise RuntimeError("El archivo no contiene preguntas")
|
||||
welcome_card = raw_file.get("welcomeCard")
|
||||
if welcome_card:
|
||||
convert_to_object_format(welcome_card)
|
||||
|
||||
questions, welcome_card, ending = parse_survey(raw_questions)
|
||||
endings = raw_file.get("endings", [])
|
||||
# Set valid id and add required fields for endings
|
||||
for ending in endings:
|
||||
ending["id"] = "p73t62dgwq0cvmtt6ug0hmfc"
|
||||
if "buttonLabel" not in ending:
|
||||
ending["buttonLabel"] = {"default": "Completar"}
|
||||
if "buttonLink" not in ending:
|
||||
ending["buttonLink"] = "https://example.com"
|
||||
convert_to_object_format(endings)
|
||||
|
||||
title = f"{company} | Evaluacion de moldeo L{level}"
|
||||
if level == "0":
|
||||
title = f"{company} | Funnel L0"
|
||||
else:
|
||||
title = f"{company} | Evaluacion de moldeo L{level}"
|
||||
|
||||
payload = {
|
||||
"environmentId": ENV_ID,
|
||||
"name": title,
|
||||
"status": "draft",
|
||||
"type": "link",
|
||||
"displayOption": "displayOnce",
|
||||
"languages": [],
|
||||
"questions": questions,
|
||||
}
|
||||
payload = {
|
||||
"environmentId": ENV_ID,
|
||||
"name": title,
|
||||
"status": "draft",
|
||||
"type": "link",
|
||||
"displayOption": "displayOnce",
|
||||
"languages": [],
|
||||
"questions": questions,
|
||||
}
|
||||
|
||||
if welcome_card:
|
||||
payload["welcomeCard"] = welcome_card
|
||||
if ending:
|
||||
payload["endings"] = [ending]
|
||||
if welcome_card:
|
||||
payload["welcomeCard"] = welcome_card
|
||||
if endings:
|
||||
payload["endings"] = endings
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": API_KEY,
|
||||
}
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": API_KEY,
|
||||
}
|
||||
|
||||
console.print(Panel(f"📄 {file_name}", title="Archivo seleccionado"))
|
||||
console.print(Panel(f"📝 {title}", title="Creando encuesta (DRAFT)"))
|
||||
console.print(Panel(f"📄 {file_name}", title="Archivo seleccionado"))
|
||||
console.print(Panel(f"📝 {title}", title="Creando encuesta (DRAFT)"))
|
||||
|
||||
response = requests.post(
|
||||
f"{BASEURL}/api/v1/management/surveys",
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=30,
|
||||
)
|
||||
response = requests.post(
|
||||
f"{BASEURL}/api/v1/management/surveys",
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
if response.ok:
|
||||
console.print(Panel("✅ Encuesta creada correctamente", border_style=COLORS["green"]))
|
||||
else:
|
||||
console.print(Panel("❌ Error al crear encuesta", border_style=COLORS["red"]))
|
||||
console.print(response.status_code, response.text)
|
||||
if response.ok:
|
||||
console.print(
|
||||
Panel("✅ Encuesta creada correctamente", border_style=COLORS["green"])
|
||||
)
|
||||
else:
|
||||
console.print(Panel("❌ Error al crear encuesta", border_style=COLORS["red"]))
|
||||
console.print(response.status_code, response.text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
221
questions/formbricks/funnel_registration_formbricks.json
Normal file
221
questions/formbricks/funnel_registration_formbricks.json
Normal file
@@ -0,0 +1,221 @@
|
||||
{
|
||||
"name": "Registro de Perfil y Autoevaluación",
|
||||
"type": "link",
|
||||
"status": "draft",
|
||||
"displayOption": "displayOnce",
|
||||
"welcomeCard": {
|
||||
"enabled": true,
|
||||
"headline": {
|
||||
"default": "Registro de Perfil y Autoevaluación"
|
||||
},
|
||||
"html": {
|
||||
"default": "Por favor completa tu información para asignarte la evaluación técnica adecuada."
|
||||
},
|
||||
"showResponseCount": false,
|
||||
"timeToFinish": false,
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
}
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"id": "full_name",
|
||||
"type": "openText",
|
||||
"inputType": "text",
|
||||
"headline": {
|
||||
"default": "Nombre Completo"
|
||||
},
|
||||
"subheader": {
|
||||
"default": ""
|
||||
},
|
||||
"required": true,
|
||||
"placeholder": {
|
||||
"default": "Ingresa tu nombre completo"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "employee_id",
|
||||
"type": "openText",
|
||||
"inputType": "text",
|
||||
"headline": {
|
||||
"default": "Número de Empleado"
|
||||
},
|
||||
"subheader": {
|
||||
"default": ""
|
||||
},
|
||||
"required": true,
|
||||
"placeholder": {
|
||||
"default": "Ingresa tu número de empleado"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "department",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": {
|
||||
"default": "Departamento"
|
||||
},
|
||||
"subheader": {
|
||||
"default": ""
|
||||
},
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "c0",
|
||||
"label": {
|
||||
"default": "Producción"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"label": {
|
||||
"default": "Mantenimiento"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"label": {
|
||||
"default": "Calidad"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"label": {
|
||||
"default": "Ingeniería"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"label": {
|
||||
"default": "Taller de Moldes"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
},
|
||||
"shuffleOption": "none"
|
||||
},
|
||||
{
|
||||
"id": "job_role",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": {
|
||||
"default": "Puesto Actual"
|
||||
},
|
||||
"subheader": {
|
||||
"default": ""
|
||||
},
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "c0",
|
||||
"label": {
|
||||
"default": "Operador General"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c1",
|
||||
"label": {
|
||||
"default": "Técnico de Montaje"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"label": {
|
||||
"default": "Técnico de Procesos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c3",
|
||||
"label": {
|
||||
"default": "Ingeniero de Procesos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c4",
|
||||
"label": {
|
||||
"default": "Supervisor"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
},
|
||||
"shuffleOption": "none"
|
||||
},
|
||||
{
|
||||
"id": "years_experience",
|
||||
"type": "openText",
|
||||
"inputType": "number",
|
||||
"headline": {
|
||||
"default": "Años de Experiencia en Moldeo"
|
||||
},
|
||||
"subheader": {
|
||||
"default": ""
|
||||
},
|
||||
"required": true,
|
||||
"placeholder": {
|
||||
"default": "Ingresa el número de años"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "self_evaluation",
|
||||
"type": "openText",
|
||||
"inputType": "number",
|
||||
"headline": {
|
||||
"default": "¿Cómo calificarías tu nivel de conocimiento técnico actual?"
|
||||
},
|
||||
"subheader": {
|
||||
"default": "0% = Principiante, 100% = Experto Mundial"
|
||||
},
|
||||
"required": true,
|
||||
"placeholder": {
|
||||
"default": "Ingresa un valor entre 0 y 100"
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Siguiente"
|
||||
},
|
||||
"backButtonLabel": {
|
||||
"default": "Anterior"
|
||||
}
|
||||
}
|
||||
],
|
||||
"endings": [
|
||||
{
|
||||
"id": "end_screen",
|
||||
"type": "endScreen",
|
||||
"headline": {
|
||||
"default": "¡Gracias!"
|
||||
},
|
||||
"subheader": {
|
||||
"default": "Tu información ha sido registrada. Recibirás la evaluación correspondiente pronto."
|
||||
},
|
||||
"buttonLabel": {
|
||||
"default": "Finalizar"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user