viernes, 9 de octubre de 2026

/** VideoQuiz CBTA 1 — Google Apps Script. * Crea este proyecto DESDE la hoja de cálculo de resultados: * Extensiones > Apps Script. */ const CONFIG = { titulo: 'Inglés I · VideoQuiz: Possessive Adjectives', actividad: 'INGLES1-POSSESSIVE-ADJECTIVES-MRPEA', videoId: 'XVUnAwXUj9I', // Las pausas se calculan como una fracción de la duración real del video. preguntas: [ {id:'p1',fraccion:0.12,texto:'Choose the correct possessive adjective: I have a notebook. This is ___ notebook.',opciones:['your','my','his','our'],correcta:1}, {id:'p2',fraccion:0.24,texto:'Ana is my sister. ___ favorite color is green.',opciones:['his','their','her','its'],correcta:2}, {id:'p3',fraccion:0.36,texto:'Pedro has a bicycle. ___ bicycle is red.',opciones:['his','her','our','your'],correcta:0}, {id:'p4',fraccion:0.48,texto:'My friends and I study at CBTA 1. ___ school is in Torreón.',opciones:['their','my','your','our'],correcta:3}, {id:'p5',fraccion:0.60,texto:'The dog is eating ___ food.',opciones:['it’s','its','their','his'],correcta:1}, {id:'p6',fraccion:0.72,texto:'Luis and Sofía have English books. ___ books are new.',opciones:['our','his','her','their'],correcta:3}, {id:'p7',fraccion:0.84,texto:'Choose the grammatically correct sentence.',opciones:['She is her my teacher.','He name is Carlos.','Is this your pencil?','They house is big.'],correcta:2} ] }; function doGet(){ if (CONFIG.preguntas.some(q => q.texto.includes('PREGUNTA DE PRUEBA'))) { return HtmlService.createHtmlOutput('

Actividad en preparación

La docente debe editar las preguntas y sus tiempos en Code.gs antes de publicarla.

'); } const tpl = HtmlService.createTemplateFromFile('Index'); tpl.config = JSON.stringify({ titulo:CONFIG.titulo, actividad:CONFIG.actividad, videoId:CONFIG.videoId, preguntas:CONFIG.preguntas.map(q=>({id:q.id,fraccion:q.fraccion,texto:q.texto,opciones:q.opciones})) }); return tpl.evaluate().setTitle(CONFIG.titulo) .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL); } function registrar(payload){ if (!payload || typeof payload !== 'object') throw new Error('Solicitud inválida'); const nombre=String(payload.nombre||'').trim().slice(0,120); const grupo=String(payload.grupo||'').trim().slice(0,40); const grado=String(payload.grado||'').trim().slice(0,20); if(nombre.length<5 || !grupo || !grado) throw new Error('Completa nombre, grado y grupo.'); const respuestas=payload.respuestas || {}; if(CONFIG.preguntas.some(q=>!Number.isInteger(respuestas[q.id]) || respuestas[q.id]<0 || respuestas[q.id]>=q.opciones.length)){ throw new Error('Debes contestar todas las preguntas.'); } const correctas=CONFIG.preguntas.reduce((n,q)=>n+(respuestas[q.id]===q.correcta?1:0),0); const puntos=Math.round(correctas/CONFIG.preguntas.length*100); const ss=SpreadsheetApp.getActiveSpreadsheet(); if(!ss) throw new Error('El proyecto debe crearse desde una hoja de Google Sheets.'); const lock=LockService.getScriptLock();lock.waitLock(20000); try{ let hoja=ss.getSheetByName('VideoQuiz'); if(!hoja){hoja=ss.insertSheet('VideoQuiz');hoja.appendRow(['Fecha','Actividad','Nombre','Grado','Grupo','Correctas','Total','Calificación /100','Respuestas','Folio']);hoja.setFrozenRows(1);} const fecha=new Date(); const folio=Utilities.getUuid().slice(0,8).toUpperCase(); // Los campos del estudiante se fuerzan a texto para evitar inyección de fórmulas. const seguro=s=>/^\s*[=+@\-]/.test(s)?"'"+s:s; hoja.appendRow([fecha,CONFIG.actividad,seguro(nombre),seguro(grado),seguro(grupo),correctas,CONFIG.preguntas.length,puntos,JSON.stringify(respuestas),folio]); return {ok:true,fecha:Utilities.formatDate(fecha,Session.getScriptTimeZone(),'yyyy-MM-dd HH:mm'),actividad:CONFIG.actividad,folio,nombre,grado,grupo,correctas,total:CONFIG.preguntas.length,puntos}; }finally{lock.releaseLock();} }

No hay comentarios:

Publicar un comentario