From f4883ee6ead6dd52044db3546ea9e754f7796499 Mon Sep 17 00:00:00 2001 From: primakov Date: Tue, 25 Mar 2025 19:23:01 +0300 Subject: [PATCH] sticky qrcode --- src/pages/lesson-details.tsx | 3 + stubs/api/index.js | 417 ++++++------------ stubs/api/mock-generator.js | 264 ----------- .../create/with-rapid-reactions.json | 100 ----- 4 files changed, 131 insertions(+), 653 deletions(-) delete mode 100644 stubs/api/mock-generator.js delete mode 100644 stubs/mocks/lessons/access-code/create/with-rapid-reactions.json diff --git a/src/pages/lesson-details.tsx b/src/pages/lesson-details.tsx index 544932b..ece73df 100644 --- a/src/pages/lesson-details.tsx +++ b/src/pages/lesson-details.tsx @@ -278,6 +278,9 @@ const LessonDetail = () => { borderRadius="xl" bg={colorMode === "light" ? "gray.50" : "gray.700"} boxShadow="md" + position="sticky" + top="20px" + zIndex="2" > {formatDate(accessCode?.body?.lesson?.date, t('journal.pl.lesson.dateFormat'))}{' '} {t('journal.pl.common.marked')} - diff --git a/stubs/api/index.js b/stubs/api/index.js index 9bcd154..b6b8966 100644 --- a/stubs/api/index.js +++ b/stubs/api/index.js @@ -1,21 +1,88 @@ const router = require('express').Router() const fs = require('node:fs') const path = require('node:path') -const mockGenerator = require('./mock-generator') + +// Функция для чтения JSON файла и случайной модификации содержимого +function readAndModifyJson(filePath) { + try { + // Используем fs.readFileSync вместо require для избежания кэширования + const fullPath = path.resolve(__dirname, filePath); + const fileContent = fs.readFileSync(fullPath, 'utf8'); + const jsonContent = JSON.parse(fileContent); + + // Если это список учеников, немного перемешаем их + if (jsonContent.body && Array.isArray(jsonContent.body.students)) { + jsonContent.body.students.sort(() => 0.5 - Math.random()); + } + + // Если это список реакций, обновим время создания и слегка перемешаем + if (jsonContent.body && Array.isArray(jsonContent.body.reactions)) { + const now = Date.now(); + jsonContent.body.reactions.forEach((reaction, index) => { + // Интервал от 10 секунд до 2 минут назад + const randomTime = now - Math.floor(Math.random() * (120000 - 10000) + 10000); + reaction.created = new Date(randomTime).toISOString(); + }); + + // Сортируем реакции по времени создания (новые сверху) + jsonContent.body.reactions.sort((a, b) => + new Date(b.created) - new Date(a.created) + ); + } + + // Если это список уроков, обновим даты + if (jsonContent.body && Array.isArray(jsonContent.body) && jsonContent.body[0] && jsonContent.body[0].name) { + jsonContent.body.forEach((lesson) => { + // Случайная дата в пределах последних 3 месяцев + const randomDate = new Date(); + randomDate.setMonth(randomDate.getMonth() - Math.random() * 3); + lesson.date = randomDate.toISOString(); + lesson.created = new Date(randomDate.getTime() - 86400000).toISOString(); // Создан за день до даты + }); + } + + // Если это список курсов, добавим случайные данные + if (jsonContent.body && Array.isArray(jsonContent.body) && jsonContent.body[0] && jsonContent.body[0].id) { + jsonContent.body.forEach((course) => { + course.startDt = new Date(new Date().getTime() - Math.random() * 31536000000).toISOString(); // В пределах года + course.created = new Date(new Date(course.startDt).getTime() - 604800000).toISOString(); // Создан за неделю до начала + }); + } + + return jsonContent; + } catch (error) { + console.error(`Error reading/modifying file ${filePath}:`, error); + return { success: false, error: "Failed to read file" }; + } +} + +// Функция для чтения JSON без модификации +function readJsonFile(filePath) { + try { + const fullPath = path.resolve(__dirname, filePath); + const fileContent = fs.readFileSync(fullPath, 'utf8'); + return JSON.parse(fileContent); + } catch (error) { + console.error(`Error reading file ${filePath}:`, error); + return { success: false, error: "Failed to read file" }; + } +} const timer = (time = 1000) => (_req, _res, next) => setTimeout(next, time) -router.use(timer()) +// Небольшая задержка для имитации реальной сети +router.use(timer(100)); const config = { examCreated: false } router.get('/course/list', (req, res) => { - res.send(require('../mocks/courses/list/success.json')) + const modifiedData = readAndModifyJson('../mocks/courses/list/success.json'); + res.send(modifiedData); }) router.get('/course/:id', (req, res) => { @@ -23,18 +90,30 @@ router.get('/course/:id', (req, res) => { return res.status(400).send({ success: false, error: 'Invalid course id' }) if (config.examCreated) { - config.examCreated = false - return res.send(require('../mocks/courses/by-id/with-exam.json')) + config.examCreated = false; + const modifiedData = readAndModifyJson('../mocks/courses/by-id/with-exam.json'); + return res.send(modifiedData); } - res.send(require('../mocks/courses/by-id/success.json')) + + const modifiedData = readAndModifyJson('../mocks/courses/by-id/success.json'); + res.send(modifiedData); }) router.get('/course/students/:courseId', (req, res) => { - res.send(require('../mocks/courses/all-students/success.json')) + const modifiedData = readAndModifyJson('../mocks/courses/all-students/success.json'); + res.send(modifiedData); }) router.post('/course', (req, res) => { - res.send(require('../mocks/courses/create/success.json')) + const baseData = readJsonFile('../mocks/courses/create/success.json'); + + // Добавляем данные из запроса + if (baseData.body) { + baseData.body.name = req.body.name || baseData.body.name; + baseData.body.created = new Date().toISOString(); + } + + res.send(baseData); }) router.post('/course/toggle-exam-with-jury/:id', (req, res) => { @@ -43,35 +122,62 @@ router.post('/course/toggle-exam-with-jury/:id', (req, res) => { }) router.get('/lesson/list/:courseId', (req, res) => { - res.send(require('../mocks/lessons/list/success.json')) + const modifiedData = readAndModifyJson('../mocks/lessons/list/success.json'); + res.send(modifiedData); }) - -// https://platform.bro-js.ru/jrnl-bh/api/lesson/67cf0c9f2f4241c6fc29f464/ai/generate-lessons router.get('/lesson/:courseId/ai/generate-lessons', timer(3000), (req, res) => { - res.send(require('../mocks/lessons/generate/success.json')) + const modifiedData = readAndModifyJson('../mocks/lessons/generate/success.json'); + res.send(modifiedData); }) router.post('/lesson', (req, res) => { - res.send(require('../mocks/lessons/create/success.json')) + const baseData = readJsonFile('../mocks/lessons/create/success.json'); + + // Добавляем данные из запроса + if (baseData.body) { + baseData.body.name = req.body.name || baseData.body.name; + baseData.body.date = req.body.date || new Date().toISOString(); + baseData.body.created = new Date().toISOString(); + } + + res.send(baseData); }) router.post('/lesson/access-code', (req, res) => { - // Generate random students and reactions dynamically - const dynamicData = mockGenerator.generateDynamicAccessCodeResponse(); - res.send(dynamicData); + const modifiedData = readAndModifyJson('../mocks/lessons/access-code/create/success.json'); + + // Обновляем дату истечения через час от текущего времени + if (modifiedData.body) { + modifiedData.body.expires = new Date(Date.now() + 60 * 60 * 1000).toISOString(); + modifiedData.body.created = new Date().toISOString(); + } + + res.send(modifiedData); }) router.get('/lesson/access-code/:accessCode', (req, res) => { - // Generate dynamic data for the access code lookup - const dynamicData = mockGenerator.generateDynamicAccessLookupResponse(req.params.accessCode); - res.send(dynamicData); + const modifiedData = readAndModifyJson('../mocks/lessons/access-code/get/success.json'); + + // Обновляем дату истечения через час от текущего времени + if (modifiedData.body && modifiedData.body.accessCode) { + modifiedData.body.accessCode.expires = new Date(Date.now() + 60 * 60 * 1000).toISOString(); + modifiedData.body.accessCode.created = new Date().toISOString(); + } + + res.send(modifiedData); }) router.get('/lesson/:lessonId', (req, res) => { - // Generate dynamic lesson data using the same helpers - const dynamicData = mockGenerator.generateDynamicLessonResponse(req.params.lessonId); - res.send(dynamicData); + const modifiedData = readAndModifyJson('../mocks/lessons/byid/success.json'); + + // Обновляем даты + if (modifiedData.body) { + modifiedData.body.date = new Date().toISOString(); + modifiedData.body.created = new Date(Date.now() - 86400000).toISOString(); // Создан день назад + } + + res.send(modifiedData); }) router.delete('/lesson/:lessonId', (req, res) => { @@ -103,270 +209,3 @@ router.post('/lesson/reaction/:lessonId', (req, res) => { }); module.exports = router - -// Database of potential students -const potentialStudents = [ - { - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "email_verified": true, - "name": "Мария Капитанова", - "preferred_username": "maryaKapitan@gmail.com", - "given_name": "Мария", - "family_name": "Капитанова", - "email": "maryaKapitan@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJgIjjOFD2YUSyRF5kH4jaysE6X5p-kq0Cg0CFncfMi=s96-c" - }, - { - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "email_verified": true, - "name": "Евгения Жужова", - "preferred_username": "zhuzhova@gmail.com", - "given_name": "Евгения", - "family_name": "Жужова", - "email": "zhuzhova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJUtJBAVBm642AxoGpMDDMV8CPu3MEoLjU3hmO7oisG=s96-c" - }, - { - "sub": "a723b8c2-f1d7-4620-9c35-1d48c821afb7", - "email_verified": true, - "name": "Иван Петров", - "preferred_username": "ivan.petrov@gmail.com", - "given_name": "Иван", - "family_name": "Петров", - "email": "ivan.petrov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJKHmMLFXY1s0Lkj_KKf9ZEsHl-rW6FnDs4vPHUl2aF=s96-c" - }, - { - "sub": "e4f9d328-7b2e-49c1-b5e8-12f78c54a63d", - "email_verified": true, - "name": "Алексей Смирнов", - "preferred_username": "alexey.smirnov@gmail.com", - "given_name": "Алексей", - "family_name": "Смирнов", - "email": "alexey.smirnov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocK9Nfj_jT4DLjG5hVQWS2bz8_QTZ3cHVJ6K8mD8aqWr=s96-c" - }, - { - "sub": "b9d7e1f5-6a3c-47d0-9bce-3c54e28a0ef2", - "email_verified": true, - "name": "Ольга Иванова", - "preferred_username": "olga.ivanova@gmail.com", - "given_name": "Ольга", - "family_name": "Иванова", - "email": "olga.ivanova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocI48DY7C2ZXbMvHrEjKmY6w9JdF5PLKwEDgTR9x1jY2=s96-c" - }, - { - "sub": "c5e8d4f3-2b1a-4c9d-8e7f-6a5b4c3d2e1f", - "email_verified": true, - "name": "Дмитрий Кузнецов", - "preferred_username": "dmitry.kuznetsov@gmail.com", - "given_name": "Дмитрий", - "family_name": "Кузнецов", - "email": "dmitry.kuznetsov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocLqZD7KjXy3B1P2VsRn6Z9tY8XMhCJ6F5gK7sD1qB3t=s96-c" - }, - { - "sub": "d6f9e8d7-3c2b-4a1d-9e8f-7a6b5c4d3e2f", - "email_verified": true, - "name": "Анна Соколова", - "preferred_username": "anna.sokolova@gmail.com", - "given_name": "Анна", - "family_name": "Соколова", - "email": "anna.sokolova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocK3dN5mYwLjE1qFvX9pZ8rY1hJ5L2mN3oP6gR7tUb4s=s96-c" - }, - { - "sub": "e7f8g9h0-4d3c-2b1a-0f9e-8d7c6b5a4e3d", - "email_verified": true, - "name": "Сергей Новиков", - "preferred_username": "sergey.novikov@gmail.com", - "given_name": "Сергей", - "family_name": "Новиков", - "email": "sergey.novikov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocI7P2dF3vQ5wR9jH6tN8bZ1cM4kD6yL2jN5oR8tYb5r=s96-c" - }, - { - "sub": "f8g9h0i1-5e4d-3c2b-1a0f-9e8d7c6b5a4e", - "email_verified": true, - "name": "Екатерина Морозова", - "preferred_username": "ekaterina.morozova@gmail.com", - "given_name": "Екатерина", - "family_name": "Морозова", - "email": "ekaterina.morozova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJ6N5oR7sD8tUb4rY1hJ5L2mN3oP6gR7tUb4s9pZ8=s96-c" - }, - { - "sub": "g9h0i1j2-6f5e-4d3c-2b1a-0f9e8d7c6b5a", - "email_verified": true, - "name": "Андрей Волков", - "preferred_username": "andrey.volkov@gmail.com", - "given_name": "Андрей", - "family_name": "Волков", - "email": "andrey.volkov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocK4e3d2c1b0a9f8e7d6c5b4a3e2d1c0b9a8f7e6d5=s96-c" - } -]; - -// Available reaction types -const reactionTypes = ['thumbs_up', 'heart', 'laugh', 'wow', 'clap']; - -// Function to generate a random integer between min and max (inclusive) -function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -// Function to generate a random subset of students -function getRandomStudents() { - const totalStudents = potentialStudents.length; - const count = getRandomInt(1, Math.min(8, totalStudents)); - - // Shuffle array and take a subset - const shuffled = [...potentialStudents].sort(() => 0.5 - Math.random()); - return shuffled.slice(0, count); -} - -// Function to generate a random reaction -function generateReaction(studentSub, index) { - const reactionType = reactionTypes[getRandomInt(0, reactionTypes.length - 1)]; - - return { - "_id": `r-${Date.now()}-${index}`, - "sub": studentSub, - "reaction": reactionType, - "created": new Date().toISOString() - }; -} - -// Function to generate random reactions for each student -function generateReactions(students) { - const reactions = []; - let reactionIndex = 0; - - students.forEach(student => { - // Small chance (20%) of a "reaction burst" - multiple reactions in rapid succession - const hasBurst = Math.random() < 0.2; - - if (hasBurst) { - // Generate a burst of 2-5 rapid reactions - const burstCount = getRandomInt(2, 5); - const now = Date.now(); - - for (let i = 0; i < burstCount; i++) { - // Reactions spaced 0.5-2 seconds apart - const timeOffset = i * getRandomInt(500, 2000); - const reactionTime = new Date(now - timeOffset); - - reactions.push({ - "_id": `r-burst-${now}-${i}-${reactionIndex++}`, - "sub": student.sub, - "reaction": reactionTypes[getRandomInt(0, reactionTypes.length - 1)], - "created": reactionTime.toISOString() - }); - } - } else { - // Each student may have 0-3 random reactions - const reactionCount = getRandomInt(0, 3); - - for (let i = 0; i < reactionCount; i++) { - // Space out regular reactions by 5-30 seconds - const timeOffset = getRandomInt(5000, 30000); - const reactionTime = new Date(Date.now() - timeOffset); - - reactions.push({ - "_id": `r-${Date.now()}-${reactionIndex++}`, - "sub": student.sub, - "reaction": reactionTypes[getRandomInt(0, reactionTypes.length - 1)], - "created": reactionTime.toISOString() - }); - } - } - }); - - // Sort reactions by creation time (newest first) - return reactions.sort((a, b) => new Date(b.created) - new Date(a.created)); -} - -// Function to generate the entire dynamic response -function generateDynamicAccessCodeResponse() { - // Base template from the static file - const baseTemplate = { - "success": true, - "body": { - "expires": new Date(Date.now() + 60 * 60 * 1000).toISOString(), // 1 hour from now - "lesson": { - "_id": "65df996c584b172772d69706", - "name": "ВВОДНАЯ ПО JS.ПРИМЕНЕНИЕ И СПОСОБЫ ПОДКЛЮЧЕНИЯ НА СТРАНИЦЕ. LET, CONST. БАЗОВЫЕ ТИПЫ ДАННЫХ, ПРИВЕДЕНИЕ ТИПОВ. ПЕРЕМЕННЫЕ, ОБЛАСТЬ ВИДИМОСТИ ПЕРЕМЕННЫХ", - "date": new Date().toISOString(), - "created": new Date().toISOString(), - "__v": 0 - }, - "_id": `access-${Date.now()}`, - "created": new Date().toISOString(), - "__v": 0 - } - }; - - // Generate random students - const students = getRandomStudents(); - baseTemplate.body.lesson.students = students; - - // Generate random reactions for those students - baseTemplate.body.lesson.reactions = generateReactions(students); - - return baseTemplate; -} - -// Function to generate a dynamic lesson response -function generateDynamicLessonResponse(lessonId) { - // Base template for lesson response - const baseTemplate = { - "success": true, - "body": { - "_id": lessonId || "65df996c584b172772d69706", - "name": "ВВОДНАЯ ПО JS.ПРИМЕНЕНИЕ И СПОСОБЫ ПОДКЛЮЧЕНИЯ НА СТРАНИЦЕ. LET, CONST. БАЗОВЫЕ ТИПЫ ДАННЫХ, ПРИВЕДЕНИЕ ТИПОВ. ПЕРЕМЕННЫЕ, ОБЛАСТЬ ВИДИМОСТИ ПЕРЕМЕННЫХ", - "date": new Date().toISOString(), - "created": new Date().toISOString(), - "__v": 0 - } - }; - - // Generate random students - const students = getRandomStudents(); - baseTemplate.body.students = students; - - // Generate random reactions for those students - baseTemplate.body.reactions = generateReactions(students); - - return baseTemplate; -} - -// Function to generate a dynamic access code lookup response -function generateDynamicAccessLookupResponse(accessCode) { - // Generate a lesson with students and reactions - const lessonData = generateDynamicLessonResponse(); - - // Create a mock user - const mockUser = { - sub: `user-${Date.now()}`, - email_verified: true, - name: "Текущий Пользователь", - preferred_username: "current.user@example.com", - email: "current.user@example.com" - }; - - // Combine into the expected format - return { - "success": true, - "body": { - "user": mockUser, - "accessCode": { - "expires": new Date(Date.now() + 60 * 60 * 1000).toISOString(), - "lesson": lessonData.body, - "_id": accessCode || `access-${Date.now()}`, - "created": new Date().toISOString(), - "__v": 0 - } - } - }; -} diff --git a/stubs/api/mock-generator.js b/stubs/api/mock-generator.js deleted file mode 100644 index 322b204..0000000 --- a/stubs/api/mock-generator.js +++ /dev/null @@ -1,264 +0,0 @@ -// Database of potential students -const potentialStudents = [ - { - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "email_verified": true, - "name": "Мария Капитанова", - "preferred_username": "maryaKapitan@gmail.com", - "given_name": "Мария", - "family_name": "Капитанова", - "email": "maryaKapitan@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJgIjjOFD2YUSyRF5kH4jaysE6X5p-kq0Cg0CFncfMi=s96-c" - }, - { - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "email_verified": true, - "name": "Евгения Жужова", - "preferred_username": "zhuzhova@gmail.com", - "given_name": "Евгения", - "family_name": "Жужова", - "email": "zhuzhova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJUtJBAVBm642AxoGpMDDMV8CPu3MEoLjU3hmO7oisG=s96-c" - }, - { - "sub": "a723b8c2-f1d7-4620-9c35-1d48c821afb7", - "email_verified": true, - "name": "Иван Петров", - "preferred_username": "ivan.petrov@gmail.com", - "given_name": "Иван", - "family_name": "Петров", - "email": "ivan.petrov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJKHmMLFXY1s0Lkj_KKf9ZEsHl-rW6FnDs4vPHUl2aF=s96-c" - }, - { - "sub": "e4f9d328-7b2e-49c1-b5e8-12f78c54a63d", - "email_verified": true, - "name": "Алексей Смирнов", - "preferred_username": "alexey.smirnov@gmail.com", - "given_name": "Алексей", - "family_name": "Смирнов", - "email": "alexey.smirnov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocK9Nfj_jT4DLjG5hVQWS2bz8_QTZ3cHVJ6K8mD8aqWr=s96-c" - }, - { - "sub": "b9d7e1f5-6a3c-47d0-9bce-3c54e28a0ef2", - "email_verified": true, - "name": "Ольга Иванова", - "preferred_username": "olga.ivanova@gmail.com", - "given_name": "Ольга", - "family_name": "Иванова", - "email": "olga.ivanova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocI48DY7C2ZXbMvHrEjKmY6w9JdF5PLKwEDgTR9x1jY2=s96-c" - }, - { - "sub": "c5e8d4f3-2b1a-4c9d-8e7f-6a5b4c3d2e1f", - "email_verified": true, - "name": "Дмитрий Кузнецов", - "preferred_username": "dmitry.kuznetsov@gmail.com", - "given_name": "Дмитрий", - "family_name": "Кузнецов", - "email": "dmitry.kuznetsov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocLqZD7KjXy3B1P2VsRn6Z9tY8XMhCJ6F5gK7sD1qB3t=s96-c" - }, - { - "sub": "d6f9e8d7-3c2b-4a1d-9e8f-7a6b5c4d3e2f", - "email_verified": true, - "name": "Анна Соколова", - "preferred_username": "anna.sokolova@gmail.com", - "given_name": "Анна", - "family_name": "Соколова", - "email": "anna.sokolova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocK3dN5mYwLjE1qFvX9pZ8rY1hJ5L2mN3oP6gR7tUb4s=s96-c" - }, - { - "sub": "e7f8g9h0-4d3c-2b1a-0f9e-8d7c6b5a4e3d", - "email_verified": true, - "name": "Сергей Новиков", - "preferred_username": "sergey.novikov@gmail.com", - "given_name": "Сергей", - "family_name": "Новиков", - "email": "sergey.novikov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocI7P2dF3vQ5wR9jH6tN8bZ1cM4kD6yL2jN5oR8tYb5r=s96-c" - }, - { - "sub": "f8g9h0i1-5e4d-3c2b-1a0f-9e8d7c6b5a4e", - "email_verified": true, - "name": "Екатерина Морозова", - "preferred_username": "ekaterina.morozova@gmail.com", - "given_name": "Екатерина", - "family_name": "Морозова", - "email": "ekaterina.morozova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJ6N5oR7sD8tUb4rY1hJ5L2mN3oP6gR7tUb4s9pZ8=s96-c" - }, - { - "sub": "g9h0i1j2-6f5e-4d3c-2b1a-0f9e8d7c6b5a", - "email_verified": true, - "name": "Андрей Волков", - "preferred_username": "andrey.volkov@gmail.com", - "given_name": "Андрей", - "family_name": "Волков", - "email": "andrey.volkov@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocK4e3d2c1b0a9f8e7d6c5b4a3e2d1c0b9a8f7e6d5=s96-c" - } -]; - -// Available reaction types -const reactionTypes = ['thumbs_up', 'heart', 'laugh', 'wow', 'clap']; - -// Function to generate a random integer between min and max (inclusive) -function getRandomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -// Function to generate a random subset of students -function getRandomStudents() { - const totalStudents = potentialStudents.length; - const count = getRandomInt(1, Math.min(8, totalStudents)); - - // Shuffle array and take a subset - const shuffled = [...potentialStudents].sort(() => 0.5 - Math.random()); - return shuffled.slice(0, count); -} - -// Function to generate random reactions for each student -function generateReactions(students) { - const reactions = []; - let reactionIndex = 0; - - students.forEach(student => { - // Small chance (20%) of a "reaction burst" - multiple reactions in rapid succession - const hasBurst = Math.random() < 0.2; - - if (hasBurst) { - // Generate a burst of 2-5 rapid reactions - const burstCount = getRandomInt(2, 5); - const now = Date.now(); - - for (let i = 0; i < burstCount; i++) { - // Reactions spaced 0.5-2 seconds apart - const timeOffset = i * getRandomInt(500, 2000); - const reactionTime = new Date(now - timeOffset); - - reactions.push({ - "_id": `r-burst-${now}-${i}-${reactionIndex++}`, - "sub": student.sub, - "reaction": reactionTypes[getRandomInt(0, reactionTypes.length - 1)], - "created": reactionTime.toISOString() - }); - } - } else { - // Each student may have 0-3 random reactions - const reactionCount = getRandomInt(0, 3); - - for (let i = 0; i < reactionCount; i++) { - // Space out regular reactions by 5-30 seconds - const timeOffset = getRandomInt(5000, 30000); - const reactionTime = new Date(Date.now() - timeOffset); - - reactions.push({ - "_id": `r-${Date.now()}-${reactionIndex++}`, - "sub": student.sub, - "reaction": reactionTypes[getRandomInt(0, reactionTypes.length - 1)], - "created": reactionTime.toISOString() - }); - } - } - }); - - // Sort reactions by creation time (newest first) - return reactions.sort((a, b) => new Date(b.created) - new Date(a.created)); -} - -// Function to generate the entire dynamic response -function generateDynamicAccessCodeResponse() { - // Base template from the static file - const baseTemplate = { - "success": true, - "body": { - "expires": new Date(Date.now() + 60 * 60 * 1000).toISOString(), // 1 hour from now - "lesson": { - "_id": "65df996c584b172772d69706", - "name": "ВВОДНАЯ ПО JS.ПРИМЕНЕНИЕ И СПОСОБЫ ПОДКЛЮЧЕНИЯ НА СТРАНИЦЕ. LET, CONST. БАЗОВЫЕ ТИПЫ ДАННЫХ, ПРИВЕДЕНИЕ ТИПОВ. ПЕРЕМЕННЫЕ, ОБЛАСТЬ ВИДИМОСТИ ПЕРЕМЕННЫХ", - "date": new Date().toISOString(), - "created": new Date().toISOString(), - "__v": 0 - }, - "_id": `access-${Date.now()}`, - "created": new Date().toISOString(), - "__v": 0 - } - }; - - // Generate random students - const students = getRandomStudents(); - baseTemplate.body.lesson.students = students; - - // Generate random reactions for those students - baseTemplate.body.lesson.reactions = generateReactions(students); - - return baseTemplate; -} - -// Function to generate a dynamic lesson response -function generateDynamicLessonResponse(lessonId) { - // Base template for lesson response - const baseTemplate = { - "success": true, - "body": { - "_id": lessonId || "65df996c584b172772d69706", - "name": "ВВОДНАЯ ПО JS.ПРИМЕНЕНИЕ И СПОСОБЫ ПОДКЛЮЧЕНИЯ НА СТРАНИЦЕ. LET, CONST. БАЗОВЫЕ ТИПЫ ДАННЫХ, ПРИВЕДЕНИЕ ТИПОВ. ПЕРЕМЕННЫЕ, ОБЛАСТЬ ВИДИМОСТИ ПЕРЕМЕННЫХ", - "date": new Date().toISOString(), - "created": new Date().toISOString(), - "__v": 0 - } - }; - - // Generate random students - const students = getRandomStudents(); - baseTemplate.body.students = students; - - // Generate random reactions for those students - baseTemplate.body.reactions = generateReactions(students); - - return baseTemplate; -} - -// Function to generate a dynamic access code lookup response -function generateDynamicAccessLookupResponse(accessCode) { - // Generate a lesson with students and reactions - const lessonData = generateDynamicLessonResponse(); - - // Create a mock user - const mockUser = { - sub: `user-${Date.now()}`, - email_verified: true, - name: "Текущий Пользователь", - preferred_username: "current.user@example.com", - email: "current.user@example.com" - }; - - // Combine into the expected format - return { - "success": true, - "body": { - "user": mockUser, - "accessCode": { - "expires": new Date(Date.now() + 60 * 60 * 1000).toISOString(), - "lesson": lessonData.body, - "_id": accessCode || `access-${Date.now()}`, - "created": new Date().toISOString(), - "__v": 0 - } - } - }; -} - -// Export all the necessary functions -module.exports = { - getRandomStudents, - generateReactions, - generateDynamicAccessCodeResponse, - generateDynamicLessonResponse, - generateDynamicAccessLookupResponse, - reactionTypes -}; \ No newline at end of file diff --git a/stubs/mocks/lessons/access-code/create/with-rapid-reactions.json b/stubs/mocks/lessons/access-code/create/with-rapid-reactions.json deleted file mode 100644 index ec0c772..0000000 --- a/stubs/mocks/lessons/access-code/create/with-rapid-reactions.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "success": true, - "body": { - "expires": "2024-03-01T07:52:16.374Z", - "lesson": { - "_id": "65df996c584b172772d69706", - "name": "ВВОДНАЯ ПО JS.ПРИМЕНЕНИЕ И СПОСОБЫ ПОДКЛЮЧЕНИЯ НА СТРАНИЦЕ. LET, CONST. БАЗОВЫЕ ТИПЫ ДАННЫХ, ПРИВЕДЕНИЕ ТИПОВ. ПЕРЕМЕННЫЕ, ОБЛАСТЬ ВИДИМОСТИ ПЕРЕМЕННЫХ", - "students": [ - { - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "email_verified": true, - "name": "Мария Капитанова", - "preferred_username": "maryaKapitan@gmail.com", - "given_name": "Мария", - "family_name": "Капитанова", - "email": "maryaKapitan@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJgIjjOFD2YUSyRF5kH4jaysE6X5p-kq0Cg0CFncfMi=s96-c" - }, - { - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "email_verified": true, - "name": "Евгения Жужова", - "preferred_username": "zhuzhova@gmail.com", - "given_name": "Евгения", - "family_name": "Жужова", - "email": "zhuzhova@gmail.com", - "picture": "https://lh3.googleusercontent.com/a/ACg8ocJUtJBAVBm642AxoGpMDDMV8CPu3MEoLjU3hmO7oisG=s96-c" - } - ], - "reactions": [ - { - "_id": "r1-rapid-001", - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "reaction": "thumbs_up", - "created": "2024-03-08T10:00:00.000Z" - }, - { - "_id": "r1-rapid-002", - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "reaction": "heart", - "created": "2024-03-08T10:00:01.500Z" - }, - { - "_id": "r1-rapid-003", - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "reaction": "laugh", - "created": "2024-03-08T10:00:02.800Z" - }, - { - "_id": "r1-rapid-004", - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "reaction": "wow", - "created": "2024-03-08T10:00:04.200Z" - }, - { - "_id": "r1-rapid-005", - "sub": "fcde3f22-d9ba-412a-a572-c59e515a290f", - "reaction": "clap", - "created": "2024-03-08T10:00:05.500Z" - }, - { - "_id": "r2-rapid-001", - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "reaction": "thumbs_up", - "created": "2024-03-08T10:01:00.000Z" - }, - { - "_id": "r2-rapid-002", - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "reaction": "heart", - "created": "2024-03-08T10:01:01.200Z" - }, - { - "_id": "r2-rapid-003", - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "reaction": "wow", - "created": "2024-03-08T10:01:02.300Z" - }, - { - "_id": "r2-rapid-004", - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "reaction": "laugh", - "created": "2024-03-08T10:01:04.100Z" - }, - { - "_id": "r2-rapid-005", - "sub": "8555885b-715c-4dee-a7c5-9563a6a05211", - "reaction": "clap", - "created": "2024-03-08T10:01:05.300Z" - } - ], - "date": "2024-02-28T20:37:00.057Z", - "created": "2024-02-28T20:37:00.057Z", - "__v": 0 - }, - "_id": "65e18926584b172772d69722", - "created": "2024-03-01T07:52:06.375Z", - "__v": 0 - } -} \ No newline at end of file