From abe0ce4a44c958c9aff8d67c9c8fb5da5aab341d Mon Sep 17 00:00:00 2001 From: Carifio24 Date: Tue, 27 Aug 2024 17:19:30 -0400 Subject: [PATCH 1/5] Create stage SQL and model. --- src/models/stage.ts | 31 +++++++++++++++++++++++++++++++ src/sql/create_stage_table.sql | 13 +++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/models/stage.ts create mode 100644 src/sql/create_stage_table.sql diff --git a/src/models/stage.ts b/src/models/stage.ts new file mode 100644 index 0000000..587704b --- /dev/null +++ b/src/models/stage.ts @@ -0,0 +1,31 @@ +import { Sequelize, DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize"; + + +export class Stage extends Model, InferCreationAttributes> { + declare story_name: string; + declare stage_name: string; + declare stage_index: CreationOptional; +} + +export function initializeStageModel(sequelize: Sequelize) { + Stage.init({ + story_name: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + stage_name: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + }, + stage_index: { + type: DataTypes.INTEGER.UNSIGNED, + allowNull: true, + defaultValue: null, + } + }, { + sequelize, + engine: "InnoDB", + }); +} diff --git a/src/sql/create_stage_table.sql b/src/sql/create_stage_table.sql new file mode 100644 index 0000000..0bb8c5d --- /dev/null +++ b/src/sql/create_stage_table.sql @@ -0,0 +1,13 @@ +CREATE TABLE Stages ( + story_name varchar(50) NOT NULL UNIQUE, + stage_name varchar(50) NOT NULL UNIQUE, + stage_index int(11) UNSIGNED DEFAULT NULL, + + PRIMARY KEY(story_name, stage_name), + INDEX(story_name), + INDEX(stage_name), + FOREIGN KEY(story_name) + REFERENCES Stories(name) + ON UPDATE CASCADE + ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci PACK_KEYS=0; From 6a09c49a9dcebb0db0ac2f3a522c29b459d83445 Mon Sep 17 00:00:00 2001 From: Carifio24 Date: Tue, 27 Aug 2024 17:24:34 -0400 Subject: [PATCH 2/5] Add index information to stage model. --- src/models/stage.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/models/stage.ts b/src/models/stage.ts index 587704b..8bc5a82 100644 --- a/src/models/stage.ts +++ b/src/models/stage.ts @@ -1,4 +1,5 @@ import { Sequelize, DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from "sequelize"; +import { Story } from "./story"; export class Stage extends Model, InferCreationAttributes> { @@ -13,11 +14,17 @@ export function initializeStageModel(sequelize: Sequelize) { type: DataTypes.STRING, allowNull: false, unique: true, + primaryKey: true, }, stage_name: { type: DataTypes.STRING, allowNull: false, unique: true, + primaryKey: true, + references: { + model: Story, + key: "name", + } }, stage_index: { type: DataTypes.INTEGER.UNSIGNED, @@ -27,5 +34,19 @@ export function initializeStageModel(sequelize: Sequelize) { }, { sequelize, engine: "InnoDB", + indexes: [ + { + unique: true, + fields: ["story_name", "stage_name"], + }, + { + unique: true, + fields: ["story_name"], + }, + { + unique: true, + fields: ["stage_name"], + }, + ] }); } From caed7ccab847461d1fcc52a432d82dad9935e978 Mon Sep 17 00:00:00 2001 From: Carifio24 Date: Tue, 27 Aug 2024 17:46:33 -0400 Subject: [PATCH 3/5] Add endpoints for getting stages and stage states. --- src/database.ts | 27 +++++++++++++++++++++++++++ src/server.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/database.ts b/src/database.ts index 4150173..3138ae8 100644 --- a/src/database.ts +++ b/src/database.ts @@ -36,6 +36,7 @@ import { initializeModels } from "./models"; import { StudentOption, StudentOptions } from "./models/student_options"; import { Question } from "./models/question"; import { logger } from "./logger"; +import { Stage } from "./models/stage"; type SequelizeError = { parent: { code: string } }; @@ -337,6 +338,19 @@ export async function getAllEducators(): Promise { return Educator.findAll(); } +export async function getStory(storyName: string): Promise { + return Story.findOne({ where: { name: storyName } }); +} + +export async function getStages(storyName: string): Promise { + return Stage.findAll({ + where: { + story_name: storyName, + }, + order: [['stage_index', 'ASC']], + }); +} + export async function getStoryState(studentID: number, storyName: string): Promise { const result = await StoryState.findOne({ where: { @@ -427,6 +441,19 @@ export async function deleteStageState(studentID: number, storyName: string, sta }); } +export async function getStageStates(studentID: number, storyName: string): Promise> { + const stages = await getStages(storyName); + const stageNames = stages.map(stage => stage.stage_name); + const stageStates: Record = {}; + for (const name of stageNames) { + const state = await getStageState(studentID, storyName, name); + if (state !== null) { + stageStates[name] = state; + } + } + return stageStates; +} + export async function getClassesForEducator(educatorID: number): Promise { return Class.findAll({ where: { diff --git a/src/server.ts b/src/server.ts index 3ab65c2..fb60eca 100644 --- a/src/server.ts +++ b/src/server.ts @@ -33,6 +33,9 @@ import { updateStageState, deleteStageState, findClassById, + getStages, + getStory, + getStageStates, } from "./database"; import { getAPIKey, hasPermission } from "./authorization"; @@ -462,6 +465,47 @@ app.put("/story-state/:studentID/:storyName", async (req, res) => { }); }); +app.get("/stages/:storyName", async (req, res) => { + const storyName = req.params.storyName; + const story = await getStory(storyName); + + if (story === null) { + res.status(404).json({ + error: `No story found with name ${storyName}` + }); + return; + } + + const stages = await getStages(req.params.storyName); + res.json({ + stages, + }); +}); + +app.get("/stage-states/:studentID/:storyName", async (req, res) => { + const storyName = req.params.storyName; + const story = await getStory(storyName); + + if (story === null) { + res.status(404).json({ + error: `No story found with name ${storyName}` + }); + return; + } + + const studentID = Number(req.params.studentID); + const student = await findStudentById(studentID); + if (student === null) { + res.status(404).json({ + error: `No student found with ID ${studentID}` + }); + return; + } + + const stageStates = await getStageStates(studentID, storyName); + res.json(stageStates); +}); + app.get("/stage-state/:studentID/:storyName/:stageName", async (req, res) => { const params = req.params; const studentID = Number(params.studentID); From 83ea34a028845066143946dc6667edcf7d4a5a98 Mon Sep 17 00:00:00 2001 From: Carifio24 Date: Wed, 28 Aug 2024 01:10:37 -0400 Subject: [PATCH 4/5] Make story and stage names be not unique in table. Initialize stage model in server. --- src/models/index.ts | 3 +++ src/server.ts | 2 +- src/sql/create_stage_table.sql | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/models/index.ts b/src/models/index.ts index d2396a2..b99a62e 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -5,6 +5,7 @@ import { Educator, initializeEducatorModel } from "./educator"; import { IgnoreStudent, initializeIgnoreStudentModel } from "./ignore_student"; import { ClassStories, initializeClassStoryModel } from "./story_class"; import { CosmicDSSession, initializeSessionModel } from "./session"; +import { Stage, initializeStageModel } from "./stage"; import { StageState, initializeStageStateModel } from "./stage_state"; import { StoryState, initializeStoryStateModel } from "./story_state"; import { Story, initializeStoryModel } from "./story"; @@ -23,6 +24,7 @@ export { DummyClass, Educator, IgnoreStudent, + Stage, StageState, Story, StoryState, @@ -38,6 +40,7 @@ export function initializeModels(db: Sequelize) { initializeStoryModel(db); initializeClassStoryModel(db); initializeDummyClassModel(db); + initializeStageModel(db); initializeStageStateModel(db); initializeStoryStateModel(db); initializeStudentClassModel(db); diff --git a/src/server.ts b/src/server.ts index fb60eca..6f7f821 100644 --- a/src/server.ts +++ b/src/server.ts @@ -47,7 +47,7 @@ import { VerificationResult, } from "./request_results"; -import { CosmicDSSession, StageState } from "./models"; +import { CosmicDSSession } from "./models"; import { ParsedQs } from "qs"; import express, { Request, Response as ExpressResponse, NextFunction } from "express"; diff --git a/src/sql/create_stage_table.sql b/src/sql/create_stage_table.sql index 0bb8c5d..77d60a0 100644 --- a/src/sql/create_stage_table.sql +++ b/src/sql/create_stage_table.sql @@ -1,6 +1,6 @@ CREATE TABLE Stages ( - story_name varchar(50) NOT NULL UNIQUE, - stage_name varchar(50) NOT NULL UNIQUE, + story_name varchar(50) NOT NULL, + stage_name varchar(50) NOT NULL, stage_index int(11) UNSIGNED DEFAULT NULL, PRIMARY KEY(story_name, stage_name), From 72aeb3f61415807953e12d78d91d10755b9aca18 Mon Sep 17 00:00:00 2001 From: Carifio24 Date: Wed, 28 Aug 2024 01:31:25 -0400 Subject: [PATCH 5/5] Fix linting issues. --- src/database.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database.ts b/src/database.ts index 3138ae8..1601db1 100644 --- a/src/database.ts +++ b/src/database.ts @@ -347,7 +347,7 @@ export async function getStages(storyName: string): Promise { where: { story_name: storyName, }, - order: [['stage_index', 'ASC']], + order: [["stage_index", "ASC"]], }); }