================================== Documentación para desarrolladores ================================== A continuación se presenta información enfocada a los desarrolladores Proceso de desarrollo ===================== Las plataformas de Evaluador, Preparación y presentación de clases, Parque Sabios y portal de presentación de Sabios están integradas en un proyecto con aplicaciones que interactúan. Para mantener los ambientes de producción y desarrollo funcionales con una base sólida y estable en producción y el ambiente de de desarrollo con los modelos de datos más recientes y funcionalidades actualizadas contando con la posibilidad de aplicar hotfixes a producción o adición de funcionalidades en producción se empleará dos ramas: * master * dev La rama master con la integración continua hará el despliegue sobre producción, mientras que la rama dev apuntará a desarrollo. Siempre que se hagan fixes en master se harán mezclas de master hacia dev, las cuales serán backports. Estructura de datos JSON en respuestas AJAX =========================================== En la medida de lo posible, se entrega un diccionario con 3 entradas:: { response_message: "mensaje mostrado al usuario", response_status: 0, obj_list: [], } obj_list es una lista de objetos; la estructura de cada objeto en la lista está a discreción del programador. response_status debe ser 0 si el request tuvo éxito, y diferente de 0 en otro caso. Los valores particulares (diferentes de cero) están a discreción del programador. response_message es una cadena unicode, con el mensaje apropiado para el usuario, bien sea para casos de error o de éxito. Usuarios ======== Para minimizar las consultas en la base de datos se está almacenando información de las consignaciones de puntos que se hace a los estudiantes en un campo dentro de la tabla usuario cuyo formato es::: { 'operations': [{ 'amount': value 'timestamp': date 'description': char 'game': can be None, game that issued the operation 'user': can be None, user that issued the operation 'transaction_type': retiro o depósito }] } Padres de familia ----------------- El padre de familia o acudiente va a tener el hijo actual guardado en la variable de session CURRENT_CHILD , cuando se autentica va a buscar la variable de sesión CURRENT_CHILD, en caso de que no esté buscará el primer hijo o acudido y lo colocará en CURRENT_CHILD. Habrá un método que permitirá que el acudiente pueda cambiar de hijo, se establecerá el hijo que está en la sesión y en caso contrario se obtendrá el primer "hijo" disponible. El userprofile de un padre de familia deberá proporcionar un método que ofrezca un listado de los hijos del colegio actual. Exámenes ======== Para reducir la cantidad de relaciones en la base de datos, las respuestas dadas por los usuarios se serializan en el campo **answers** de la clase UserAnswers. Para dar un resultado más rápido en los reportes se precalculan los puntajes obtenidos por los estudiantes. La estructura del campo answers, en el que se almacena el listado de preguntas, y sus respuestas, es:: { 'questions': [ { 'question': DB id of the question, 'subject': DB id of question's subject, 'status': one of: QUESTION_STATUS_UNANSWERED, QUESTION_STATUS_SKIPPED, QUESTION_STATUS_ANSWERED (defined in evaluation/data.py) 'answers_presentation_order': randomized list of the following elements: [ 'answer', 'distractor_1', 'distractor_2', 'distractor_3' ], 'answer': integer, index for the 'answers_presentation_order' array; range: [0, 3]; answer will be correct if element at this position matches the string 'answer' When no answer has been provided, it holds the None special value } ] } Creación automatizada de preguntas ---------------------------------- Si se requiere creación automatizada de preguntas para hacer pruebas, se puede usar:: from django_dynamic_fixture import G from evaluation.data import QUESTIONSET_CHOICE_NOT_GRADABLE from evaluation.tests import _create_questions user = User.objects.get(email='carlos@axiacore.com') for axiscontent in AxisContent.objects.all(): _create_questions( user, axiscontent=axiscontent, questionset=QUESTIONSET_CHOICE_NOT_GRADABLE ) Carga Masiva ============ Se está empleando Celery para la carga masiva de estudiantes, hay dos etapas en la carga de estudiantes: * Revisión de consistencia interna del archivo * Revisión y cargue de datos frente al sistema La revisión de la consistencia del archivo es rápida y debe tardar menos de 30 segundos para un archivo con miles de registros. La carga de cada registro tarda entre 2 y 3 segundos y se verifica consistencia de datos para evitar escalamiento de permisos en diferentes colegios, por ejemplo, no es admisible que un administrador sea estudiante de colegio alguno. Para garantizar que los procesos terminan estos son encolados y solamente puede ejecutarse una carga a la vez en todo el sistema, esto está controlado por un semáforo y reintentos de Sentry. Juegos ====== Comentarios de foro =================== Además de añadir una respuesta a un foro, es posible responder a una de las respuestas de un foro. Dichas respuestas anidadas, o subrespuestas/subcomentarios, se almacenan como datos JSON dentro de cada respuesta; ver campo related_answers del modelo communication.ForumAnswer. El formato de dicho campo es el siguiente: El campo contiene un único objeto, un diccionario con una entrada (llave) 'related_answers', cuyo contenido es la lista de respuestas (related_answer). Es decir:: { related_answers: [related_answer, related_answer, ...] } Cada related_answer es un diccionario, que fundamentalmente imita la estructura del modelo UserAnswer:: { description: cadena unicode con texto markdown: el comentario. status: entero. Es uno de los estados definidos en communication.data.FORUM_ANS_STATUSES created_at: marca de tiempo de la creación del subcomentario, almacenada como una cadena de texto con la fecha en formato ISO 8601 incluyendo la zona horaria (YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM) created_by: Entero. id del usuario (userprofile.User) que creó el comentario. } Estilos ======= En esta sección se presentan algunos estilos para emplear a lo largo de la aplicación Menús ----- Para hacer sobresalir a un menú o submenú, se le añade la clase *current* Para ubicar las pestañas de los menús a izquierda, centra o derecha se usan respectivamente las clases *popup_content left*, *popup_content* y *popup_content right* Uso de componentes js ===================== Uso de componente Markdown -------------------------- Para que un textarea con id id_description, que esté dentro de un div con id div_id_description tenga la opción de convertirse en un control markdown se usa el siguiente código javascript:: $('#id_description').attach_markdown({ container: '#div_id_description' }); Para desplegar el contenido se envuelve en un div con un código similar a:
{{ description|markdown }}
Uso de componentes de archivos ------------------------------ Para usar un componente de archivo se emplea la función configure_file_field, la cual fue inyectada en la librería jquery, para emplearla:: $.configure_file_field( '.section_form', 'div.upload_image', '#id_image', '.filename_holder' ); Donde recibe como primer parámetro un selector del contenedor del formulario para en caso de que se recargue por ajax puede mantener las reacciones, como segundo parámetro el div que reemplaza el widget nativo, como tercer parametro el selector del id del componente nativo y finalmente el widget original donde aparecería el nombre del archivo. Uso de mensajes informativos ---------------------------- Para ofrecer un mensaje en la página emplee la función show_message de la siguiente forma:: $.show_message('No fue posible enviar el mensaje', 'error'); El segundo parámetro es opcional y puede ser también warning. Uso de mensajes de confirmación ------------------------------- Los mensajes de confirmación se apoyan en la librería Messi, deberá usar el callback para continuar el flujo de la operación de acuerdo a la respuesta del usuario:: new Messi('¿Estás seguro de que deseas desactivar el evento?', { title: 'Cancelar', buttons: [ {id: 0, label: 'Sí', val: 'Y'}, {id: 1, label: 'No', val: 'N'} ], callback: function(val) { $.show_message('elegiste ' + val); } }); Comunicaciones ============== Para llevar el conteo de los eventos por leer de un usuario se estructuró una API que permite: Aumentar el conteo dada una cantidad de elementos, si no recibe parámetro se incrementa en un elemento:: add_counter(counter, users, quantity) Donde **counter** es una cadena de texto 'messages'|'events'|'news'|'memos', **users** es un iterable del ids de usuarios a los que se les incrementará el conteo de eventos, **quantity** es la cantidad de elemntos que se añadieron para el listado de usuarios. add_counter se ejecutará como una tarea en background para evitar demorar otros procesos como el envío de una circular para todo el colegio o la notificación de una noticia. reset_counter(counter, user) **counter es como se especificó anteriormente y user es el usuario de la aplicación al cual se le añadirán los eventos correspondientes. El almacenamiento de esta información se hará en redis con elemento hkeys, los nombres de las llaves principales serán:: 'noti.userid' Donde se reemplazará userid por el id del usuario. Adicionalmente se emplea un prefijo para evitar colisionar con otros servicios, la constante que especifica tal prefijo en settings es: COMMUNICATION_PREFIX Para incrementar el contador de mensajes:: from communication.signals import add_counter add_counter.send( sender=user, counter='messages', users=[user.id], quantity=3, ) Incrementaría en 3 el contador para el usuario con user.id, cabe notar que users es una lista para permitir que varios usuarios puedan incrementar el contador. Durante el desarrollo ===================== En esta sección se encuentran algunos comandos frecuentes durante el desarrollo Celery local ------------ Se aplica el comando:: manage.py celery worker --loglevel=info Compilar sass ============= Una vez instalada la aplicación localmente, se debe compilar el sass para que se muestren los estilos y las imágenes correctamente, para hacerlo deberan estar situados en el directorio del proyecto y ejecutar el siguiente comando: compass compile app/static/