fix: question service + refactor loID
This commit is contained in:
		
							parent
							
								
									dbc1da741c
								
							
						
					
					
						commit
						bd75ab8af9
					
				
					 16 changed files with 86 additions and 81 deletions
				
			
		|  | @ -6,9 +6,9 @@ import attachmentService from '../services/learning-objects/attachment-service.j | |||
| import { BadRequestException } from '../exceptions/bad-request-exception.js'; | ||||
| import { NotFoundException } from '../exceptions/not-found-exception.js'; | ||||
| import { envVars, getEnvVar } from '../util/envVars.js'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| 
 | ||||
| function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIdentifier { | ||||
| function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIdentifierDTO { | ||||
|     if (!req.params.hruid) { | ||||
|         throw new BadRequestException('HRUID is required.'); | ||||
|     } | ||||
|  |  | |||
|  | @ -61,4 +61,13 @@ export class QuestionRepository extends DwengoEntityRepository<Question> { | |||
|             orderBy: { timestamp: 'DESC' }, // New to old
 | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public async findByLearningObjectAndSequenceNumber(loId: LearningObjectIdentifier, sequenceNumber: number){ | ||||
|         return this.findOne({ | ||||
|             learningObjectHruid: loId.hruid, | ||||
|             learningObjectLanguage: loId.language, | ||||
|             learningObjectVersion: loId.version, | ||||
|             sequenceNumber | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,9 +1,11 @@ | |||
| import { Question } from '../entities/questions/question.entity.js'; | ||||
| import { mapToStudentDTO } from './student.js'; | ||||
| import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question'; | ||||
| import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js'; | ||||
| 
 | ||||
| function getLearningObjectIdentifier(question: Question): LearningObjectIdentifier { | ||||
| 
 | ||||
| function getLearningObjectIdentifier(question: Question): LearningObjectIdentifierDTO { | ||||
|     return { | ||||
|         hruid: question.learningObjectHruid, | ||||
|         language: question.learningObjectLanguage, | ||||
|  | @ -11,6 +13,14 @@ function getLearningObjectIdentifier(question: Question): LearningObjectIdentifi | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| export function mapToLearningObjectID(loID: LearningObjectIdentifierDTO): LearningObjectIdentifier { | ||||
|     return { | ||||
|         hruid: loID.hruid, | ||||
|         language: loID.language, | ||||
|         version: loID.version ?? 1 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Convert a Question entity to a DTO format. | ||||
|  */ | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import { getAttachmentRepository } from '../../data/repositories.js'; | ||||
| import { Attachment } from '../../entities/content/attachment.entity.js'; | ||||
| 
 | ||||
| import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| 
 | ||||
| const attachmentService = { | ||||
|     async getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> { | ||||
|     async getAttachment(learningObjectId: LearningObjectIdentifierDTO, attachmentName: string): Promise<Attachment | null> { | ||||
|         const attachmentRepo = getAttachmentRepository(); | ||||
| 
 | ||||
|         if (learningObjectId.version) { | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ import processingService from './processing/processing-service.js'; | |||
| import { NotFoundError } from '@mikro-orm/core'; | ||||
| import learningObjectService from './learning-object-service.js'; | ||||
| import { getLogger, Logger } from '../../logging/initalize.js'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| 
 | ||||
| const logger: Logger = getLogger(); | ||||
| 
 | ||||
|  | @ -40,7 +40,7 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| async function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> { | ||||
| async function findLearningObjectEntityById(id: LearningObjectIdentifierDTO): Promise<LearningObject | null> { | ||||
|     const learningObjectRepo = getLearningObjectRepository(); | ||||
| 
 | ||||
|     return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language); | ||||
|  | @ -53,7 +53,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = { | |||
|     /** | ||||
|      * Fetches a single learning object by its HRUID | ||||
|      */ | ||||
|     async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> { | ||||
|     async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> { | ||||
|         const learningObject = await findLearningObjectEntityById(id); | ||||
|         return convertLearningObject(learningObject); | ||||
|     }, | ||||
|  | @ -61,7 +61,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = { | |||
|     /** | ||||
|      * Obtain a HTML-rendering of the learning object with the given identifier (as a string). | ||||
|      */ | ||||
|     async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> { | ||||
|     async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> { | ||||
|         const learningObjectRepo = getLearningObjectRepository(); | ||||
| 
 | ||||
|         const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language); | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import { LearningObjectProvider } from './learning-object-provider.js'; | |||
| import { getLogger, Logger } from '../../logging/initalize.js'; | ||||
| import { | ||||
|     FilteredLearningObject, | ||||
|     LearningObjectIdentifier, | ||||
|     LearningObjectIdentifierDTO, | ||||
|     LearningObjectMetadata, | ||||
|     LearningObjectNode, | ||||
|     LearningPathIdentifier, | ||||
|  | @ -67,7 +67,7 @@ async function fetchLearningObjects(learningPathId: LearningPathIdentifier, full | |||
| 
 | ||||
|         const objects = await Promise.all( | ||||
|             nodes.map(async (node) => { | ||||
|                 const learningObjectId: LearningObjectIdentifier = { | ||||
|                 const learningObjectId: LearningObjectIdentifierDTO = { | ||||
|                     hruid: node.learningobject_hruid, | ||||
|                     language: learningPathId.language, | ||||
|                 }; | ||||
|  | @ -85,7 +85,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { | |||
|     /** | ||||
|      * Fetches a single learning object by its HRUID | ||||
|      */ | ||||
|     async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> { | ||||
|     async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> { | ||||
|         const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`; | ||||
|         const metadata = await fetchWithLogging<LearningObjectMetadata>( | ||||
|             metadataUrl, | ||||
|  | @ -121,7 +121,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { | |||
|      * Obtain a HTML-rendering of the learning object with the given identifier (as a string). For learning objects | ||||
|      * from the Dwengo API, this means passing through the HTML rendering from there. | ||||
|      */ | ||||
|     async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> { | ||||
|     async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> { | ||||
|         const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw`; | ||||
|         const html = await fetchWithLogging<string>(htmlUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, { | ||||
|             params: { ...id }, | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| 
 | ||||
| export interface LearningObjectProvider { | ||||
|     /** | ||||
|      * Fetches a single learning object by its HRUID | ||||
|      */ | ||||
|     getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null>; | ||||
|     getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null>; | ||||
| 
 | ||||
|     /** | ||||
|      * Fetch full learning object data (metadata) | ||||
|  | @ -19,5 +19,5 @@ export interface LearningObjectProvider { | |||
|     /** | ||||
|      * Obtain a HTML-rendering of the learning object with the given identifier (as a string). | ||||
|      */ | ||||
|     getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null>; | ||||
|     getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null>; | ||||
| } | ||||
|  |  | |||
|  | @ -2,9 +2,9 @@ import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provid | |||
| import { LearningObjectProvider } from './learning-object-provider.js'; | ||||
| import { envVars, getEnvVar } from '../../util/envVars.js'; | ||||
| import databaseLearningObjectProvider from './database-learning-object-provider.js'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| 
 | ||||
| function getProvider(id: LearningObjectIdentifier): LearningObjectProvider { | ||||
| function getProvider(id: LearningObjectIdentifierDTO): LearningObjectProvider { | ||||
|     if (id.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) { | ||||
|         return databaseLearningObjectProvider; | ||||
|     } | ||||
|  | @ -18,7 +18,7 @@ const learningObjectService = { | |||
|     /** | ||||
|      * Fetches a single learning object by its HRUID | ||||
|      */ | ||||
|     async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> { | ||||
|     async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> { | ||||
|         return getProvider(id).getLearningObjectById(id); | ||||
|     }, | ||||
| 
 | ||||
|  | @ -39,7 +39,7 @@ const learningObjectService = { | |||
|     /** | ||||
|      * Obtain a HTML-rendering of the learning object with the given identifier (as a string). | ||||
|      */ | ||||
|     async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> { | ||||
|     async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> { | ||||
|         return getProvider(id).getLearningObjectHTML(id); | ||||
|     }, | ||||
| }; | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ import Image = marked.Tokens.Image; | |||
| import Heading = marked.Tokens.Heading; | ||||
| import Link = marked.Tokens.Link; | ||||
| import RendererObject = marked.RendererObject; | ||||
| import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { Language } from '@dwengo-1/common/util/language'; | ||||
| 
 | ||||
| const prefixes = { | ||||
|  | @ -25,7 +25,7 @@ const prefixes = { | |||
|     blockly: '@blockly', | ||||
| }; | ||||
| 
 | ||||
| function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier { | ||||
| function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifierDTO { | ||||
|     const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split('/'); | ||||
|     return { | ||||
|         hruid, | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ import { LearningObject } from '../../../entities/content/learning-object.entity | |||
| import Processor from './processor.js'; | ||||
| import { DwengoContentType } from './content-type.js'; | ||||
| import { replaceAsync } from '../../../util/async.js'; | ||||
| import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { Language } from '@dwengo-1/common/util/language'; | ||||
| 
 | ||||
| const EMBEDDED_LEARNING_OBJECT_PLACEHOLDER = /<learning-object hruid="([^"]+)" language="([^"]+)" version="([^"]+)"\/>/g; | ||||
|  | @ -50,7 +50,7 @@ class ProcessingService { | |||
|      */ | ||||
|     async render( | ||||
|         learningObject: LearningObject, | ||||
|         fetchEmbeddedLearningObjects?: (loId: LearningObjectIdentifier) => Promise<LearningObject | null> | ||||
|         fetchEmbeddedLearningObjects?: (loId: LearningObjectIdentifierDTO) => Promise<LearningObject | null> | ||||
|     ): Promise<string> { | ||||
|         const html = this.processors.get(learningObject.contentType)!.renderLearningObject(learningObject); | ||||
|         if (fetchEmbeddedLearningObjects) { | ||||
|  |  | |||
|  | @ -1,22 +1,21 @@ | |||
| import { getAnswerRepository, getQuestionRepository } from '../data/repositories.js'; | ||||
| import { mapToQuestionDTO, mapToQuestionDTOId } from '../interfaces/question.js'; | ||||
| import {mapToLearningObjectID, mapToQuestionDTO, mapToQuestionDTOId} from '../interfaces/question.js'; | ||||
| import { Question } from '../entities/questions/question.entity.js'; | ||||
| import { Answer } from '../entities/questions/answer.entity.js'; | ||||
| import { mapToAnswerDTO, mapToAnswerDTOId } from '../interfaces/answer.js'; | ||||
| import { QuestionRepository } from '../data/questions/question-repository.js'; | ||||
| import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js'; | ||||
| import { mapToStudent } from '../interfaces/student.js'; | ||||
| import {mapToStudent, mapToStudentDTO} from '../interfaces/student.js'; | ||||
| import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question'; | ||||
| import { AnswerDTO, AnswerId } from '@dwengo-1/common/interfaces/answer'; | ||||
| import {NotFoundException} from "../exceptions/not-found-exception"; | ||||
| import {fetchStudent} from "./students"; | ||||
| import {Student} from "../entities/users/student.entity"; | ||||
| 
 | ||||
| export async function getAllQuestions(id: LearningObjectIdentifier, full: boolean): Promise<QuestionDTO[] | QuestionId[]> { | ||||
|     const questionRepository: QuestionRepository = getQuestionRepository(); | ||||
|     const questions = await questionRepository.findAllQuestionsAboutLearningObject(id); | ||||
| 
 | ||||
|     if (!questions) { | ||||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     if (full) { | ||||
|         return questions.map(mapToQuestionDTO); | ||||
|     } | ||||
|  | @ -24,24 +23,22 @@ export async function getAllQuestions(id: LearningObjectIdentifier, full: boolea | |||
|     return questions.map(mapToQuestionDTOId); | ||||
| } | ||||
| 
 | ||||
| async function fetchQuestion(questionId: QuestionId): Promise<Question | null> { | ||||
| async function fetchQuestion(questionId: QuestionId): Promise<Question> { | ||||
|     const questionRepository = getQuestionRepository(); | ||||
| 
 | ||||
|     return await questionRepository.findOne({ | ||||
|         learningObjectHruid: questionId.learningObjectIdentifier.hruid, | ||||
|         learningObjectLanguage: questionId.learningObjectIdentifier.language, | ||||
|         learningObjectVersion: questionId.learningObjectIdentifier.version, | ||||
|         sequenceNumber: questionId.sequenceNumber, | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| export async function getQuestion(questionId: QuestionId): Promise<QuestionDTO | null> { | ||||
|     const question = await fetchQuestion(questionId); | ||||
|     const question = await questionRepository.findByLearningObjectAndSequenceNumber( | ||||
|         mapToLearningObjectID(questionId.learningObjectIdentifier), | ||||
|         questionId.sequenceNumber | ||||
|     ); | ||||
| 
 | ||||
|     if (!question){ | ||||
|         return null; | ||||
|         throw new NotFoundException('Question with loID and sequence number not found'); | ||||
|     } | ||||
| 
 | ||||
|     return question; | ||||
| } | ||||
| 
 | ||||
| export async function getQuestion(questionId: QuestionId): Promise<QuestionDTO> { | ||||
|     const question = await fetchQuestion(questionId); | ||||
|     return mapToQuestionDTO(question); | ||||
| } | ||||
| 
 | ||||
|  | @ -49,16 +46,8 @@ export async function getAnswersByQuestion(questionId: QuestionId, full: boolean | |||
|     const answerRepository = getAnswerRepository(); | ||||
|     const question = await fetchQuestion(questionId); | ||||
| 
 | ||||
|     if (!question) { | ||||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     const answers: Answer[] = await answerRepository.findAllAnswersToQuestion(question); | ||||
| 
 | ||||
|     if (!answers) { | ||||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     if (full) { | ||||
|         return answers.map(mapToAnswerDTO); | ||||
|     } | ||||
|  | @ -68,23 +57,20 @@ export async function getAnswersByQuestion(questionId: QuestionId, full: boolean | |||
| 
 | ||||
| export async function createQuestion(questionDTO: QuestionDTO): Promise<QuestionDTO | null> { | ||||
|     const questionRepository = getQuestionRepository(); | ||||
|     let author: Student; | ||||
|     if (typeof questionDTO.author === "string" ){ | ||||
|         author = await fetchStudent(questionDTO.author); | ||||
|     } else { | ||||
|         author = mapToStudent(questionDTO.author) | ||||
|     } | ||||
| 
 | ||||
|     const author = mapToStudent(questionDTO.author); | ||||
| 
 | ||||
|     const loId: LearningObjectIdentifier = { | ||||
|         ...questionDTO.learningObjectIdentifier, | ||||
|         version: questionDTO.learningObjectIdentifier.version ?? 1, | ||||
|     }; | ||||
| 
 | ||||
|     try { | ||||
|     await questionRepository.createQuestion({ | ||||
|             loId, | ||||
|         loId: mapToLearningObjectID(questionDTO.learningObjectIdentifier), | ||||
|         author, | ||||
|         content: questionDTO.content, | ||||
|     }); | ||||
|     } catch (_) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     return questionDTO; | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| 
 | ||||
| export function isValidHttpUrl(url: string): boolean { | ||||
|     try { | ||||
|  | @ -9,7 +9,7 @@ export function isValidHttpUrl(url: string): boolean { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| export function getUrlStringForLearningObject(learningObjectId: LearningObjectIdentifier): string { | ||||
| export function getUrlStringForLearningObject(learningObjectId: LearningObjectIdentifierDTO): string { | ||||
|     let url = `/learningObject/${learningObjectId.hruid}/html?language=${learningObjectId.language}`; | ||||
|     if (learningObjectId.version) { | ||||
|         url += `&version=${learningObjectId.version}`; | ||||
|  | @ -17,7 +17,7 @@ export function getUrlStringForLearningObject(learningObjectId: LearningObjectId | |||
|     return url; | ||||
| } | ||||
| 
 | ||||
| export function getUrlStringForLearningObjectHTML(learningObjectIdentifier: LearningObjectIdentifier): string { | ||||
| export function getUrlStringForLearningObjectHTML(learningObjectIdentifier: LearningObjectIdentifierDTO): string { | ||||
|     let url = `/learningObject/${learningObjectIdentifier.hruid}/html?language=${learningObjectIdentifier.language}`; | ||||
|     if (learningObjectIdentifier.version) { | ||||
|         url += `&version=${learningObjectIdentifier.version}`; | ||||
|  |  | |||
|  | @ -7,11 +7,11 @@ import learningObjectService from '../../../src/services/learning-objects/learni | |||
| import { envVars, getEnvVar } from '../../../src/util/envVars'; | ||||
| import { LearningPath } from '../../../src/entities/content/learning-path.entity'; | ||||
| import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; | ||||
| import { LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content'; | ||||
| import { Language } from '@dwengo-1/common/util/language'; | ||||
| 
 | ||||
| const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = 'Werken met notebooks'; | ||||
| const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { | ||||
| const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifierDTO = { | ||||
|     hruid: 'pn_werkingnotebooks', | ||||
|     language: Language.Dutch, | ||||
|     version: 3, | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ export interface Transition { | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| export interface LearningObjectIdentifier { | ||||
| export interface LearningObjectIdentifierDTO { | ||||
|     hruid: string; | ||||
|     language: Language; | ||||
|     version?: number; | ||||
|  |  | |||
|  | @ -1,15 +1,15 @@ | |||
| import { LearningObjectIdentifier } from './learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from './learning-content'; | ||||
| import { StudentDTO } from './student'; | ||||
| 
 | ||||
| export interface QuestionDTO { | ||||
|     learningObjectIdentifier: LearningObjectIdentifier; | ||||
|     learningObjectIdentifier: LearningObjectIdentifierDTO; | ||||
|     sequenceNumber?: number; | ||||
|     author: StudentDTO; | ||||
|     author: StudentDTO | string; | ||||
|     timestamp?: string; | ||||
|     content: string; | ||||
| } | ||||
| 
 | ||||
| export interface QuestionId { | ||||
|     learningObjectIdentifier: LearningObjectIdentifier; | ||||
|     learningObjectIdentifier: LearningObjectIdentifierDTO; | ||||
|     sequenceNumber: number; | ||||
| } | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import { GroupDTO } from './group'; | ||||
| import { LearningObjectIdentifier } from './learning-content'; | ||||
| import { LearningObjectIdentifierDTO } from './learning-content'; | ||||
| import { StudentDTO } from './student'; | ||||
| import { Language } from '../util/language'; | ||||
| 
 | ||||
| export interface SubmissionDTO { | ||||
|     learningObjectIdentifier: LearningObjectIdentifier; | ||||
|     learningObjectIdentifier: LearningObjectIdentifierDTO; | ||||
| 
 | ||||
|     submissionNumber?: number; | ||||
|     submitter: StudentDTO; | ||||
|  |  | |||
		Reference in a new issue
	
	 Gabriellvl
						Gabriellvl