Skip to content

Commit df1edbb

Browse files
committed
add utils to parse imported qTypes
1 parent cf995bd commit df1edbb

File tree

1 file changed

+297
-0
lines changed

1 file changed

+297
-0
lines changed
Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
import { CourseConfig, DataShape, NameSpacer } from '@vue-skuilder/common';
2+
import { CourseDBInterface } from '@vue-skuilder/db';
3+
import { Displayable, getCurrentUser, ViewComponent } from '@vue-skuilder/common-ui';
4+
import { Course } from '@vue-skuilder/courses';
5+
6+
/**
7+
* Interface for custom questions data structure returned by allCustomQuestions()
8+
*/
9+
export interface CustomQuestionsData {
10+
courses: Course[]; // Course instances with question instances
11+
questionClasses: Displayable[]; // Question class constructors
12+
dataShapes: DataShape[]; // DataShape definitions for studio-ui
13+
views: ViewComponent[]; // Vue components for rendering
14+
meta: {
15+
questionCount: number;
16+
dataShapeCount: number;
17+
viewCount: number;
18+
courseCount: number;
19+
packageName: string;
20+
sourceDirectory: string;
21+
};
22+
}
23+
24+
/**
25+
* Interface for processed question data for registration
26+
*/
27+
export interface ProcessedQuestionData {
28+
name: string;
29+
course: string;
30+
questionClass: any;
31+
dataShapes: any[];
32+
views: any[];
33+
}
34+
35+
/**
36+
* Interface for processed data shape for registration
37+
*/
38+
export interface ProcessedDataShape {
39+
name: string;
40+
course: string;
41+
dataShape: any;
42+
}
43+
44+
/**
45+
* Check if a data shape is already registered in the course config
46+
*/
47+
export function isDataShapeRegistered(
48+
dataShape: ProcessedDataShape,
49+
courseConfig: CourseConfig
50+
): boolean {
51+
const namespacedName = NameSpacer.getDataShapeString({
52+
dataShape: dataShape.name,
53+
course: dataShape.course,
54+
});
55+
56+
return courseConfig.dataShapes.some((ds) => ds.name === namespacedName);
57+
}
58+
59+
/**
60+
* Check if a question type is already registered in the course config
61+
*/
62+
export function isQuestionTypeRegistered(
63+
question: ProcessedQuestionData,
64+
courseConfig: CourseConfig
65+
): boolean {
66+
const namespacedName = NameSpacer.getQuestionString({
67+
course: question.course,
68+
questionType: question.name,
69+
});
70+
71+
return courseConfig.questionTypes.some((qt) => qt.name === namespacedName);
72+
}
73+
74+
/**
75+
* Process custom questions data into registration-ready format
76+
*/
77+
export function processCustomQuestionsData(customQuestions: CustomQuestionsData): {
78+
dataShapes: ProcessedDataShape[];
79+
questions: ProcessedQuestionData[];
80+
} {
81+
const processedDataShapes: ProcessedDataShape[] = [];
82+
const processedQuestions: ProcessedQuestionData[] = [];
83+
84+
// Extract course names from the custom questions
85+
const courseNames = customQuestions.courses.map((course) => course.name);
86+
87+
// Process each question class
88+
customQuestions.questionClasses.forEach((questionClass) => {
89+
// Determine the course name (use first course or default to meta.packageName)
90+
const courseName = courseNames.length > 0 ? courseNames[0] : customQuestions.meta.packageName;
91+
92+
// Process data shapes from this question class
93+
if (questionClass.dataShapes && Array.isArray(questionClass.dataShapes)) {
94+
questionClass.dataShapes().forEach((dataShape) => {
95+
processedDataShapes.push({
96+
name: dataShape.name,
97+
course: courseName,
98+
dataShape: dataShape,
99+
});
100+
});
101+
}
102+
103+
// Process the question itself
104+
processedQuestions.push({
105+
name: questionClass.constructor.name,
106+
course: courseName,
107+
questionClass: questionClass,
108+
dataShapes: questionClass.dataShapes() || [],
109+
views: questionClass.views() || [],
110+
});
111+
});
112+
113+
return { dataShapes: processedDataShapes, questions: processedQuestions };
114+
}
115+
116+
/**
117+
* Register a data shape in the course config
118+
*/
119+
export function registerDataShape(
120+
dataShape: ProcessedDataShape,
121+
courseConfig: CourseConfig
122+
): boolean {
123+
if (isDataShapeRegistered(dataShape, courseConfig)) {
124+
console.log(
125+
` ℹ️ DataShape '${dataShape.name}' from '${dataShape.course}' already registered`
126+
);
127+
return false;
128+
}
129+
130+
const namespacedName = NameSpacer.getDataShapeString({
131+
dataShape: dataShape.name,
132+
course: dataShape.course,
133+
});
134+
135+
courseConfig.dataShapes.push({
136+
name: namespacedName,
137+
questionTypes: [],
138+
});
139+
140+
console.log(` ✅ Registered DataShape: ${namespacedName}`);
141+
return true;
142+
}
143+
144+
/**
145+
* Register a question type in the course config
146+
*/
147+
export function registerQuestionType(
148+
question: ProcessedQuestionData,
149+
courseConfig: CourseConfig
150+
): boolean {
151+
if (isQuestionTypeRegistered(question, courseConfig)) {
152+
console.log(
153+
` ℹ️ QuestionType '${question.name}' from '${question.course}' already registered`
154+
);
155+
return false;
156+
}
157+
158+
const namespacedQuestionName = NameSpacer.getQuestionString({
159+
course: question.course,
160+
questionType: question.name,
161+
});
162+
163+
// Build view list
164+
const viewList = question.views.map((view) => {
165+
if (view.name) {
166+
return view.name;
167+
} else {
168+
return 'unnamedComponent';
169+
}
170+
});
171+
172+
// Build data shape list
173+
const dataShapeList = question.dataShapes.map((dataShape) =>
174+
NameSpacer.getDataShapeString({
175+
course: question.course,
176+
dataShape: dataShape.name,
177+
})
178+
);
179+
180+
// Add question type to course config
181+
courseConfig.questionTypes.push({
182+
name: namespacedQuestionName,
183+
viewList: viewList,
184+
dataShapeList: dataShapeList,
185+
});
186+
187+
// Cross-reference: Add this question type to its data shapes
188+
question.dataShapes.forEach((dataShape) => {
189+
const namespacedDataShapeName = NameSpacer.getDataShapeString({
190+
course: question.course,
191+
dataShape: dataShape.name,
192+
});
193+
194+
for (const ds of courseConfig.dataShapes) {
195+
if (ds.name === namespacedDataShapeName) {
196+
ds.questionTypes.push(namespacedQuestionName);
197+
}
198+
}
199+
});
200+
201+
console.log(` ✅ Registered QuestionType: ${namespacedQuestionName}`);
202+
return true;
203+
}
204+
205+
/**
206+
* Register seed data for a question type (similar to ComponentRegistration)
207+
*/
208+
export async function registerSeedData(
209+
question: ProcessedQuestionData,
210+
courseDB: CourseDBInterface
211+
// courseName: string
212+
): Promise<void> {
213+
if (question.questionClass.seedData && Array.isArray(question.questionClass.seedData)) {
214+
console.log(` 📦 Registering seed data for question: ${question.name}`);
215+
216+
try {
217+
const currentUser = await getCurrentUser();
218+
219+
question.questionClass.seedData.forEach((seedDataItem: unknown) => {
220+
if (question.dataShapes.length > 0) {
221+
courseDB.addNote(
222+
question.course,
223+
question.dataShapes[0],
224+
seedDataItem,
225+
currentUser.getUsername(),
226+
[]
227+
);
228+
}
229+
});
230+
231+
console.log(` ✅ Seed data registered for question: ${question.name}`);
232+
} catch (error) {
233+
console.warn(
234+
` ⚠️ Failed to register seed data for question '${question.name}': ${error instanceof Error ? error.message : String(error)}`
235+
);
236+
}
237+
}
238+
}
239+
240+
/**
241+
* Main function to register all custom question types and data shapes
242+
*/
243+
export async function registerCustomQuestionTypes(
244+
customQuestions: CustomQuestionsData,
245+
courseConfig: CourseConfig,
246+
courseDB: CourseDBInterface
247+
// courseName: string
248+
): Promise<{ success: boolean; registeredCount: number; errorMessage?: string }> {
249+
try {
250+
console.log('🎨 Studio Mode: Beginning custom question registration');
251+
console.log(` 📊 Processing ${customQuestions.questionClasses.length} question classes`);
252+
253+
const { dataShapes, questions } = processCustomQuestionsData(customQuestions);
254+
255+
console.log(` 📊 Found ${dataShapes.length} data shapes and ${questions.length} questions`);
256+
257+
let registeredCount = 0;
258+
259+
// First, register all data shapes
260+
console.log(' 📋 Registering data shapes...');
261+
for (const dataShape of dataShapes) {
262+
if (registerDataShape(dataShape, courseConfig)) {
263+
registeredCount++;
264+
}
265+
}
266+
267+
// Then, register all question types
268+
console.log(' 🔧 Registering question types...');
269+
for (const question of questions) {
270+
if (registerQuestionType(question, courseConfig)) {
271+
registeredCount++;
272+
}
273+
}
274+
275+
// Update the course config in the database
276+
console.log(' 💾 Updating course configuration...');
277+
const updateResult = await courseDB.updateCourseConfig(courseConfig);
278+
279+
if (!updateResult.ok) {
280+
throw new Error(`Failed to update course config: ${JSON.stringify(updateResult)}`);
281+
}
282+
283+
// Register seed data for questions that have it
284+
console.log(' 🌱 Registering seed data...');
285+
for (const question of questions) {
286+
await registerSeedData(question, courseDB /*, courseName */);
287+
}
288+
289+
console.log(` ✅ Custom question registration complete: ${registeredCount} items registered`);
290+
291+
return { success: true, registeredCount };
292+
} catch (error) {
293+
const errorMessage = error instanceof Error ? error.message : String(error);
294+
console.error(` ❌ Custom question registration failed: ${errorMessage}`);
295+
return { success: false, registeredCount: 0, errorMessage };
296+
}
297+
}

0 commit comments

Comments
 (0)