From 032ea688c54521bac2d72a9d88d8995dc070088c Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 14 Apr 2023 17:10:56 +0200 Subject: [PATCH 01/50] Add multi threading --- Epsilon.Host.WebApi/Controllers/ComponentController.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index dbd2e63b..ffd4d59f 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -11,6 +11,8 @@ namespace Epsilon.Host.WebApi.Controllers; [Route("[controller]")] public class ComponentController : ControllerBase { + private static readonly IHboIDomain s_hboIDomain2018 = new HboIDomain2018(); + private readonly IGraphQlHttpService _graphQlService; private readonly IConfiguration _configuration; private readonly ICompetenceProfileConverter _competenceProfileConverter; @@ -29,11 +31,13 @@ public async Task> GetCompetenceProfile() { var studentId = _configuration["Canvas:StudentId"]; var query = QueryConstants.GetAllUserCoursesSubmissionOutcomes.Replace("$studentIds", $"{studentId}"); - var queryResult = await _graphQlService.Query(query); - var terms = await _accountHttpService.GetAllTerms(1); + var queryResult = _graphQlService.Query(query); + var terms = _accountHttpService.GetAllTerms(1); + + await Task.WhenAll(queryResult, terms); - var competenceProfile = _competenceProfileConverter.ConvertFrom(queryResult, new HboIDomain2018(), terms); + var competenceProfile = _competenceProfileConverter.ConvertFrom(queryResult.Result, s_hboIDomain2018, terms.Result); return competenceProfile; } } \ No newline at end of file From 7641206e7e2efb84301c58a1add1c9abedb4c859 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 14 Apr 2023 17:11:13 +0200 Subject: [PATCH 02/50] Restyle KPI Table and cleanup code --- .../components/{KpiLegend => }/KpiLegend.vue | 47 +- .../components/{Competance => }/KpiMatrix.vue | 22 +- .../components/{Competance => }/KpiTable.vue | 74 +- .../PersonalDevelopmentMatrix.vue | 4 +- Epsilon.Host.Frontend/src/logic/Api.ts | 733 +++++++++--------- .../src/views/PerformanceDashboard.vue | 59 +- 6 files changed, 461 insertions(+), 478 deletions(-) rename Epsilon.Host.Frontend/src/components/{KpiLegend => }/KpiLegend.vue (68%) rename Epsilon.Host.Frontend/src/components/{Competance => }/KpiMatrix.vue (87%) rename Epsilon.Host.Frontend/src/components/{Competance => }/KpiTable.vue (58%) rename Epsilon.Host.Frontend/src/components/{Competance => }/PersonalDevelopmentMatrix.vue (96%) diff --git a/Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue b/Epsilon.Host.Frontend/src/components/KpiLegend.vue similarity index 68% rename from Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue rename to Epsilon.Host.Frontend/src/components/KpiLegend.vue index c47cb156..9455f984 100644 --- a/Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue +++ b/Epsilon.Host.Frontend/src/components/KpiLegend.vue @@ -1,19 +1,19 @@ @@ -54,31 +54,31 @@ function getCellColor(arId: string, acId: string): MasteryLevel | undefined { diff --git a/Epsilon.Host.Frontend/src/components/Competance/PersonalDevelopmentMatrix.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentMatrix.vue similarity index 96% rename from Epsilon.Host.Frontend/src/components/Competance/PersonalDevelopmentMatrix.vue rename to Epsilon.Host.Frontend/src/components/PersonalDevelopmentMatrix.vue index 88de686c..f4b9c343 100644 --- a/Epsilon.Host.Frontend/src/components/Competance/PersonalDevelopmentMatrix.vue +++ b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentMatrix.vue @@ -79,11 +79,11 @@ const chartOptions = { } } -props.domain.professionalSkills.forEach(skill =>{ +props.domain.professionalSkills.forEach(skill => { chartOptions.xaxis.categories.push(skill.shortName) }) \ No newline at end of file + diff --git a/Epsilon.Host.Frontend/src/logic/Api.ts b/Epsilon.Host.Frontend/src/logic/Api.ts index f35e2098..e7251bb6 100644 --- a/Epsilon.Host.Frontend/src/logic/Api.ts +++ b/Epsilon.Host.Frontend/src/logic/Api.ts @@ -1,5 +1,6 @@ /* eslint-disable */ /* tslint:disable */ + /* * --------------------------------------------------------------- * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## @@ -10,344 +11,344 @@ */ export interface Activity { - name?: string | null; - color?: string | null; + name?: string | null; + color?: string | null; } export interface ArchitectureLayer { - name?: string | null; - shortName?: string | null; - color?: string | null; + name?: string | null; + shortName?: string | null; + color?: string | null; } export interface AssessmentRating { - /** @format double */ - points?: number | null; - outcome?: Outcome; + /** @format double */ + points?: number | null; + outcome?: Outcome; } export interface Assignment { - name?: string | null; - modules?: Module[] | null; + name?: string | null; + modules?: Module[] | null; } export interface CompetenceProfile { - hboIDomain?: HboIDomain; - professionalTaskOutcomes?: ProfessionalTaskOutcome[] | null; - professionalSkillOutcomes?: ProfessionalSkillOutcome[] | null; + hboIDomain?: HboIDomain; + professionalTaskOutcomes?: ProfessionalTaskOutcome[] | null; + professionalSkillOutcomes?: ProfessionalSkillOutcome[] | null; } export interface Course { - name?: string | null; - submissionsConnection?: SubmissionsConnection; + name?: string | null; + submissionsConnection?: SubmissionsConnection; } export interface CourseData { - course?: Course; + course?: Course; } export interface GetUserSubmissionOutcomes { - data?: CourseData; + data?: CourseData; } export interface HboIDomain { - architectureLayers?: Record; - activities?: Record; - professionalSkills?: Record; - masteryLevels?: Record; + architectureLayers?: Record; + activities?: Record; + professionalSkills?: Record; + masteryLevels?: Record; } export interface LtiOpenIdConnectCallback { - authenticityToken?: string | null; - idToken?: string | null; - state?: string | null; - ltiStorageTarget?: string | null; + authenticityToken?: string | null; + idToken?: string | null; + state?: string | null; + ltiStorageTarget?: string | null; } export interface LtiOpenIdConnectLaunch { - issuer?: string | null; - loginHint?: string | null; - clientId?: string | null; - /** @format uri */ - targetLinkUri?: string | null; - encodedLtiMessageHint?: string | null; - ltiStorageTarget?: string | null; + issuer?: string | null; + loginHint?: string | null; + clientId?: string | null; + /** @format uri */ + targetLinkUri?: string | null; + encodedLtiMessageHint?: string | null; + ltiStorageTarget?: string | null; } export interface MasteryLevel { - /** @format int32 */ - level?: number; - color?: string | null; + /** @format int32 */ + level?: number; + color?: string | null; } export interface Module { - name?: string | null; + name?: string | null; } export interface Node { - assignment?: Assignment; - rubricAssessmentsConnection?: RubricAssessmentsConnection; + assignment?: Assignment; + rubricAssessmentsConnection?: RubricAssessmentsConnection; } export interface Outcome { - title?: string | null; + title?: string | null; } export interface ProfessionalSkill { - name?: string | null; - shortName?: string | null; - color?: string | null; + name?: string | null; + shortName?: string | null; + color?: string | null; } export interface ProfessionalSkillOutcome { - /** @format int32 */ - professionalSkillId?: number; - /** @format int32 */ - masteryLevel?: number; - /** @format int32 */ - grade?: number; - /** @format date-time */ - assessedAt?: string; + /** @format int32 */ + professionalSkillId?: number; + /** @format int32 */ + masteryLevel?: number; + /** @format int32 */ + grade?: number; + /** @format date-time */ + assessedAt?: string; } export interface ProfessionalTaskOutcome { - /** @format int32 */ - architectureLayerId?: number; - /** @format int32 */ - activityId?: number; - /** @format int32 */ - masteryLevelId?: number; - /** @format int32 */ - grade?: number; - /** @format date-time */ - assessedAt?: string; + /** @format int32 */ + architectureLayerId?: number; + /** @format int32 */ + activityId?: number; + /** @format int32 */ + masteryLevelId?: number; + /** @format int32 */ + grade?: number; + /** @format date-time */ + assessedAt?: string; } export interface RubricAssessmentNode { - assessmentRatings?: AssessmentRating[] | null; - user?: User; + assessmentRatings?: AssessmentRating[] | null; + user?: User; } export interface RubricAssessmentsConnection { - nodes?: RubricAssessmentNode[] | null; + nodes?: RubricAssessmentNode[] | null; } export interface SubmissionsConnection { - nodes?: Node[] | null; + nodes?: Node[] | null; } export interface User { - name?: string | null; + name?: string | null; } export type QueryParamsType = Record; export type ResponseFormat = keyof Omit; export interface FullRequestParams extends Omit { - /** set parameter to `true` for call `securityWorker` for this request */ - secure?: boolean; - /** request path */ - path: string; - /** content type of request body */ - type?: ContentType; - /** query params */ - query?: QueryParamsType; - /** format of response (i.e. response.json() -> format: "json") */ - format?: ResponseFormat; - /** request body */ - body?: unknown; - /** base url */ - baseUrl?: string; - /** request cancellation token */ - cancelToken?: CancelToken; + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseFormat; + /** request body */ + body?: unknown; + /** base url */ + baseUrl?: string; + /** request cancellation token */ + cancelToken?: CancelToken; } export type RequestParams = Omit; export interface ApiConfig { - baseUrl?: string; - baseApiParams?: Omit; - securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; - customFetch?: typeof fetch; + baseUrl?: string; + baseApiParams?: Omit; + securityWorker?: (securityData: SecurityDataType | null) => Promise | RequestParams | void; + customFetch?: typeof fetch; } export interface HttpResponse extends Response { - data: D; - error: E; + data: D; + error: E; } type CancelToken = Symbol | string | number; export enum ContentType { - Json = "application/json", - FormData = "multipart/form-data", - UrlEncoded = "application/x-www-form-urlencoded", - Text = "text/plain", + Json = "application/json", + FormData = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded", + Text = "text/plain", } export class HttpClient { - public baseUrl: string = "https://localhost:7084"; - private securityData: SecurityDataType | null = null; - private securityWorker?: ApiConfig["securityWorker"]; - private abortControllers = new Map(); - private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); - - private baseApiParams: RequestParams = { - credentials: "same-origin", - headers: {}, - redirect: "follow", - referrerPolicy: "no-referrer", - }; - - constructor(apiConfig: ApiConfig = {}) { - Object.assign(this, apiConfig); - } - - public setSecurityData = (data: SecurityDataType | null) => { - this.securityData = data; - }; - - protected encodeQueryParam(key: string, value: any) { - const encodedKey = encodeURIComponent(key); - return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; - } - - protected addQueryParam(query: QueryParamsType, key: string) { - return this.encodeQueryParam(key, query[key]); - } - - protected addArrayQueryParam(query: QueryParamsType, key: string) { - const value = query[key]; - return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); - } - - protected toQueryString(rawQuery?: QueryParamsType): string { - const query = rawQuery || {}; - const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); - return keys - .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) - .join("&"); - } - - protected addQueryParams(rawQuery?: QueryParamsType): string { - const queryString = this.toQueryString(rawQuery); - return queryString ? `?${queryString}` : ""; - } - - private contentFormatters: Record any> = { - [ContentType.Json]: (input: any) => - input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, - [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), - [ContentType.FormData]: (input: any) => - Object.keys(input || {}).reduce((formData, key) => { - const property = input[key]; - formData.append( - key, - property instanceof Blob - ? property - : typeof property === "object" && property !== null - ? JSON.stringify(property) - : `${property}`, - ); - return formData; - }, new FormData()), - [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), - }; - - protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { - return { - ...this.baseApiParams, - ...params1, - ...(params2 || {}), - headers: { - ...(this.baseApiParams.headers || {}), - ...(params1.headers || {}), - ...((params2 && params2.headers) || {}), - }, + public baseUrl: string = "https://localhost:7084"; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig["securityWorker"]; + private abortControllers = new Map(); + private customFetch = (...fetchParams: Parameters) => fetch(...fetchParams); + + private baseApiParams: RequestParams = { + credentials: "same-origin", + headers: {}, + redirect: "follow", + referrerPolicy: "no-referrer", }; - } - protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { - if (this.abortControllers.has(cancelToken)) { - const abortController = this.abortControllers.get(cancelToken); - if (abortController) { - return abortController.signal; - } - return void 0; + constructor(apiConfig: ApiConfig = {}) { + Object.assign(this, apiConfig); } - const abortController = new AbortController(); - this.abortControllers.set(cancelToken, abortController); - return abortController.signal; - }; + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; - public abortRequest = (cancelToken: CancelToken) => { - const abortController = this.abortControllers.get(cancelToken); + protected encodeQueryParam(key: string, value: any) { + const encodedKey = encodeURIComponent(key); + return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`; + } - if (abortController) { - abortController.abort(); - this.abortControllers.delete(cancelToken); + protected addQueryParam(query: QueryParamsType, key: string) { + return this.encodeQueryParam(key, query[key]); } - }; - - public request = async ({ - body, - secure, - path, - type, - query, - format, - baseUrl, - cancelToken, - ...params - }: FullRequestParams): Promise> => { - const secureParams = - ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && - this.securityWorker && - (await this.securityWorker(this.securityData))) || - {}; - const requestParams = this.mergeRequestParams(params, secureParams); - const queryString = query && this.toQueryString(query); - const payloadFormatter = this.contentFormatters[type || ContentType.Json]; - const responseFormat = format || requestParams.format; - - return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { - ...requestParams, - headers: { - ...(requestParams.headers || {}), - ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}), - }, - signal: cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal, - body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), - }).then(async (response) => { - const r = response as HttpResponse; - r.data = null as unknown as T; - r.error = null as unknown as E; - - const data = !responseFormat - ? r - : await response[responseFormat]() - .then((data) => { - if (r.ok) { - r.data = data; - } else { - r.error = data; - } - return r; - }) - .catch((e) => { - r.error = e; - return r; - }); - - if (cancelToken) { - this.abortControllers.delete(cancelToken); - } - - if (!response.ok) throw data; - return data; - }); - }; + + protected addArrayQueryParam(query: QueryParamsType, key: string) { + const value = query[key]; + return value.map((v: any) => this.encodeQueryParam(key, v)).join("&"); + } + + protected toQueryString(rawQuery?: QueryParamsType): string { + const query = rawQuery || {}; + const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]); + return keys + .map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key))) + .join("&"); + } + + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ""; + } + + private contentFormatters: Record any> = { + [ContentType.Json]: (input: any) => + input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input, + [ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input), + [ContentType.FormData]: (input: any) => + Object.keys(input || {}).reduce((formData, key) => { + const property = input[key]; + formData.append( + key, + property instanceof Blob + ? property + : typeof property === "object" && property !== null + ? JSON.stringify(property) + : `${property}`, + ); + return formData; + }, new FormData()), + [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), + }; + + protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => { + if (this.abortControllers.has(cancelToken)) { + const abortController = this.abortControllers.get(cancelToken); + if (abortController) { + return abortController.signal; + } + return void 0; + } + + const abortController = new AbortController(); + this.abortControllers.set(cancelToken, abortController); + return abortController.signal; + }; + + public abortRequest = (cancelToken: CancelToken) => { + const abortController = this.abortControllers.get(cancelToken); + + if (abortController) { + abortController.abort(); + this.abortControllers.delete(cancelToken); + } + }; + + public request = async ({ + body, + secure, + path, + type, + query, + format, + baseUrl, + cancelToken, + ...params + }: FullRequestParams): Promise> => { + const secureParams = + ((typeof secure === "boolean" ? secure : this.baseApiParams.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const queryString = query && this.toQueryString(query); + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; + const responseFormat = format || requestParams.format; + + return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData ? {"Content-Type": type} : {}), + }, + signal: cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal, + body: typeof body === "undefined" || body === null ? null : payloadFormatter(body), + }).then(async (response) => { + const r = response as HttpResponse; + r.data = null as unknown as T; + r.error = null as unknown as E; + + const data = !responseFormat + ? r + : await response[responseFormat]() + .then((data) => { + if (r.ok) { + r.data = data; + } else { + r.error = data; + } + return r; + }) + .catch((e) => { + r.error = e; + return r; + }); + + if (cancelToken) { + this.abortControllers.delete(cancelToken); + } + + if (!response.ok) throw data; + return data; + }); + }; } /** @@ -355,124 +356,124 @@ export class HttpClient { * @version 1.0 */ export class Api extends HttpClient { - auth = { - /** - * No description - * - * @tags Auth - * @name ChallengeList - * @request GET:/Auth/challenge - */ - challengeList: (params: RequestParams = {}) => - this.request({ - path: `/Auth/challenge`, - method: "GET", - ...params, - }), - - /** - * No description - * - * @tags Auth - * @name CallbackList - * @request GET:/Auth/callback - */ - callbackList: (params: RequestParams = {}) => - this.request({ - path: `/Auth/callback`, - method: "GET", - ...params, - }), - }; - component = { - /** - * No description - * - * @tags Component - * @name CompetenceProfileList - * @request GET:/Component/competence_profile - */ - competenceProfileList: ( - query?: { - courseId?: string; - /** @format date-time */ - startDate?: string; - /** @format date-time */ - endDate?: string; - }, - params: RequestParams = {}, - ) => - this.request({ - path: `/Component/competence_profile`, - method: "GET", - query: query, - format: "json", - ...params, - }), - - /** - * No description - * - * @tags Component - * @name CompetenceProfileMockList - * @request GET:/Component/competence_profile_mock - */ - competenceProfileMockList: ( - query?: { - /** @format date-time */ - startDate?: string; - /** @format date-time */ - endDate?: string; - }, - params: RequestParams = {}, - ) => - this.request({ - path: `/Component/competence_profile_mock`, - method: "GET", - query: query, - format: "json", - ...params, - }), - }; - lti = { - /** - * No description - * - * @tags Lti - * @name OidcAuthCreate - * @request POST:/lti/oidc/auth - */ - oidcAuthCreate: ( - query?: { - launchRequest?: LtiOpenIdConnectLaunch; - }, - params: RequestParams = {}, - ) => - this.request({ - path: `/lti/oidc/auth`, - method: "POST", - query: query, - ...params, - }), - - /** - * No description - * - * @tags Lti - * @name OidcCallbackCreate - * @request POST:/lti/oidc/callback - */ - oidcCallbackCreate: ( - query?: { - callback?: LtiOpenIdConnectCallback; - }, - params: RequestParams = {}, - ) => - this.request({ - path: `/lti/oidc/callback`, - method: "POST", - query: query, - ...params, - }), - }; + auth = { + /** + * No description + * + * @tags Auth + * @name ChallengeList + * @request GET:/Auth/challenge + */ + challengeList: (params: RequestParams = {}) => + this.request({ + path: `/Auth/challenge`, + method: "GET", + ...params, + }), + + /** + * No description + * + * @tags Auth + * @name CallbackList + * @request GET:/Auth/callback + */ + callbackList: (params: RequestParams = {}) => + this.request({ + path: `/Auth/callback`, + method: "GET", + ...params, + }), + }; + component = { + /** + * No description + * + * @tags Component + * @name CompetenceProfileList + * @request GET:/Component/competence_profile + */ + competenceProfileList: ( + query?: { + courseId?: string; + /** @format date-time */ + startDate?: string; + /** @format date-time */ + endDate?: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/Component/competence_profile`, + method: "GET", + query: query, + format: "json", + ...params, + }), + + /** + * No description + * + * @tags Component + * @name CompetenceProfileMockList + * @request GET:/Component/competence_profile_mock + */ + competenceProfileMockList: ( + query?: { + /** @format date-time */ + startDate?: string; + /** @format date-time */ + endDate?: string; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/Component/competence_profile_mock`, + method: "GET", + query: query, + format: "json", + ...params, + }), + }; + lti = { + /** + * No description + * + * @tags Lti + * @name OidcAuthCreate + * @request POST:/lti/oidc/auth + */ + oidcAuthCreate: ( + query?: { + launchRequest?: LtiOpenIdConnectLaunch; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/lti/oidc/auth`, + method: "POST", + query: query, + ...params, + }), + + /** + * No description + * + * @tags Lti + * @name OidcCallbackCreate + * @request POST:/lti/oidc/callback + */ + oidcCallbackCreate: ( + query?: { + callback?: LtiOpenIdConnectCallback; + }, + params: RequestParams = {}, + ) => + this.request({ + path: `/lti/oidc/callback`, + method: "POST", + query: query, + ...params, + }), + }; } diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index 87f187e3..b580d6a1 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -1,58 +1,43 @@ From 8d9d49acf5d20a7271fd17fe5cf89f4c21c657f3 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 14 Apr 2023 17:12:46 +0200 Subject: [PATCH 03/50] Rename to correspond to definitions --- .../{KpiMatrix.vue => CompetenceGraph.vue} | 0 .../{KpiTable.vue => CompetenceProfile.vue} | 44 ++++++++++--------- ...Legend.vue => CompetenceProfileLegend.vue} | 0 ...atrix.vue => PersonalDevelopmentGraph.vue} | 0 .../src/views/PerformanceDashboard.vue | 14 +++--- 5 files changed, 31 insertions(+), 27 deletions(-) rename Epsilon.Host.Frontend/src/components/{KpiMatrix.vue => CompetenceGraph.vue} (100%) rename Epsilon.Host.Frontend/src/components/{KpiTable.vue => CompetenceProfile.vue} (67%) rename Epsilon.Host.Frontend/src/components/{KpiLegend.vue => CompetenceProfileLegend.vue} (100%) rename Epsilon.Host.Frontend/src/components/{PersonalDevelopmentMatrix.vue => PersonalDevelopmentGraph.vue} (100%) diff --git a/Epsilon.Host.Frontend/src/components/KpiMatrix.vue b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue similarity index 100% rename from Epsilon.Host.Frontend/src/components/KpiMatrix.vue rename to Epsilon.Host.Frontend/src/components/CompetenceGraph.vue diff --git a/Epsilon.Host.Frontend/src/components/KpiTable.vue b/Epsilon.Host.Frontend/src/components/CompetenceProfile.vue similarity index 67% rename from Epsilon.Host.Frontend/src/components/KpiTable.vue rename to Epsilon.Host.Frontend/src/components/CompetenceProfile.vue index 9b490fc8..fb51e224 100644 --- a/Epsilon.Host.Frontend/src/components/KpiTable.vue +++ b/Epsilon.Host.Frontend/src/components/CompetenceProfile.vue @@ -4,29 +4,33 @@ class="kpitable" > - - - - {{ activity.name }} - - + + + + {{ activity.name }} + + - - - {{ architectureLayer.name }} - - - {{ getKpis(i, x).length }} - - + + {{ architectureLayer.name }} + + + {{ getKpis(i, j).length }} + + diff --git a/Epsilon.Host.Frontend/src/components/KpiLegend.vue b/Epsilon.Host.Frontend/src/components/CompetenceProfileLegend.vue similarity index 100% rename from Epsilon.Host.Frontend/src/components/KpiLegend.vue rename to Epsilon.Host.Frontend/src/components/CompetenceProfileLegend.vue diff --git a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentMatrix.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue similarity index 100% rename from Epsilon.Host.Frontend/src/components/PersonalDevelopmentMatrix.vue rename to Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index b580d6a1..5fb88b11 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -1,8 +1,8 @@ diff --git a/Epsilon.Host.Frontend/src/components/CompetenceProfileLegend.vue b/Epsilon.Host.Frontend/src/components/CompetenceProfileLegend.vue index d265179d..635e1b7e 100644 --- a/Epsilon.Host.Frontend/src/components/CompetenceProfileLegend.vue +++ b/Epsilon.Host.Frontend/src/components/CompetenceProfileLegend.vue @@ -1,32 +1,28 @@ + + \ No newline at end of file diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index 31152fd8..28057f34 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -7,7 +7,9 @@ :domain="data.hboIDomain" :data="data.professionalTaskOutcomes" /> - + -
- - - - - - -
+ + + \ No newline at end of file diff --git a/Epsilon.Host.Frontend/src/router/index.ts b/Epsilon.Host.Frontend/src/router/index.ts index 20ce1631..724b1c8e 100644 --- a/Epsilon.Host.Frontend/src/router/index.ts +++ b/Epsilon.Host.Frontend/src/router/index.ts @@ -1,6 +1,6 @@ import {createRouter, createWebHistory} from 'vue-router' import PerformanceDashboard from "@/views/PerformanceDashboard.vue"; -import Authorize from "@/views/Authorize.vue"; +import AuthorizeUser from "@/views/AuthorizeUser.vue"; const routes = [ { @@ -11,7 +11,7 @@ const routes = [ { path: '/auth', name: 'Authorize', - component: Authorize + component: AuthorizeUser }, ] const router = createRouter({ diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index 28057f34..bee5dc5b 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -3,13 +3,18 @@ v-if="data" class="performance-dashboard" > + +
From 5041972ffbb5b334c79e728dc6a039cd3f5b0c94 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Sun, 16 Apr 2023 19:22:19 +0200 Subject: [PATCH 16/50] Fix competence profile being empty and cleanup code --- .../src/components/EnrollmentTermButtons.vue | 21 ++++++++++--------- .../src/views/PerformanceDashboard.vue | 17 +++++++++------ 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Epsilon.Host.Frontend/src/components/EnrollmentTermButtons.vue b/Epsilon.Host.Frontend/src/components/EnrollmentTermButtons.vue index 66e7e572..1f308ece 100644 --- a/Epsilon.Host.Frontend/src/components/EnrollmentTermButtons.vue +++ b/Epsilon.Host.Frontend/src/components/EnrollmentTermButtons.vue @@ -1,26 +1,27 @@ @@ -46,4 +47,4 @@ function handleClick(this: any, term: EnrollmentTerm) { width: fit-content; } - \ No newline at end of file + diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index bee5dc5b..470ef190 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -4,8 +4,8 @@ class="performance-dashboard" > -
+
From 3ea3a3f838712e030a2f9c9d10df66efb96d2d17 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Sun, 16 Apr 2023 19:32:49 +0200 Subject: [PATCH 17/50] Make personal development height the same as competence graph --- Epsilon.Host.Frontend/src/components/CompetenceProfile.vue | 3 +-- .../src/components/PersonalDevelopmentGraph.vue | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Epsilon.Host.Frontend/src/components/CompetenceProfile.vue b/Epsilon.Host.Frontend/src/components/CompetenceProfile.vue index 191e30dd..abb077b9 100644 --- a/Epsilon.Host.Frontend/src/components/CompetenceProfile.vue +++ b/Epsilon.Host.Frontend/src/components/CompetenceProfile.vue @@ -65,7 +65,6 @@ function getCellColor(arId: string, acId: string): MasteryLevel | undefined { diff --git a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue index b5622df0..9d0d1c5d 100644 --- a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue +++ b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue @@ -1,7 +1,7 @@ @@ -28,6 +31,7 @@ import CompetenceGraph from "@/components/CompetenceGraph.vue" import { computed, onMounted, Ref, ref } from "vue" import RoundLoader from "@/components/RoundLoader.vue" import EnrollmentTermButtons from "@/components/EnrollmentTermButtons.vue" +import PersonalDevelopmentGraph from "@/components/PersonalDevelopmentGraph.vue" const data: Ref = ref(undefined) const App = new Api() @@ -35,32 +39,32 @@ const App = new Api() const selectedTerm = ref(null) const filteredProfessionalTaskOutcomes = computed(() => { - if (!data.value) { + if (!data.value?.professionalTaskOutcomes) { return [] } - if (!selectedTerm.value) { + if (!selectedTerm.value?.end_at) { return data.value?.professionalTaskOutcomes } return data.value.professionalTaskOutcomes.filter( - (o) => o.assessedAt < selectedTerm.value.end_at + (o) => o.assessedAt < selectedTerm.value?.end_at ) }) -// const filteredProfessionalSkillOutcomes = computed(() => { -// if (!data.value) { -// return [] -// } -// -// if (!selectedTerm.value) { -// return data.value?.professionalTaskOutcomes -// } -// -// return data.value?.professionalTaskOutcomes?.filter( -// (o) => o.assessedAt < selectedTerm?.value?.end_at, -// ) -// }) +const filteredProfessionalSkillOutcomes = computed(() => { + if (!data.value?.professionalSkillOutcomes) { + return [] + } + + if (!selectedTerm.value?.end_at) { + return data.value?.professionalSkillOutcomes + } + + return data.value?.professionalSkillOutcomes?.filter( + (o) => o.assessedAt < selectedTerm.value?.end_at + ) +}) onMounted(() => { App.component From bddee025c06e112fd13949c7637bf08e8a8a83ac Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 11 May 2023 11:10:31 +0200 Subject: [PATCH 41/50] Warning fixes --- Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts index a1127d37..9664ec3b 100644 --- a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts +++ b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts @@ -10,7 +10,7 @@ export class DecayingAverageLogic { domain: IHboIDomain ): DecayingAveragePerSkill[] { const listOfResults = Object.entries( - this.groupBy(taskResults, (r) => r.outcomeId) + this.groupBy(taskResults, (r) => r.outcomeId as unknown as string) ).map(([, j]) => { return { decayingAverage: this.GetDecayingAverageFromOneOutcomeType(j), @@ -98,7 +98,7 @@ export class DecayingAverageLogic { taskResults.filter( (layer) => layer.architectureLayer === l.id ), - (r) => r.outcomeId + (r) => r.outcomeId as unknown as string ) ).map(([i, j]) => { return { @@ -147,7 +147,7 @@ export class DecayingAverageLogic { private static groupBy( arr: T[], - fn: (item: T) => any + fn: (item: T) => number | string ): Record { return arr.reduce>((prev, curr) => { const groupKey = fn(curr) From c090f5c4db2ec6d7dfcbc907b37b75d072848806 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 11 May 2023 12:12:07 +0200 Subject: [PATCH 42/50] Mastery level indication in PersonalDevelopmentGraph.vue --- .../components/PersonalDevelopmentGraph.vue | 28 +++++++++++++++---- .../src/logic/DecayingAverageLogic.ts | 7 +++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue index da787065..b9828d49 100644 --- a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue +++ b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue @@ -9,7 +9,11 @@ diff --git a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts index 9664ec3b..31c2acf3 100644 --- a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts +++ b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts @@ -15,6 +15,9 @@ export class DecayingAverageLogic { return { decayingAverage: this.GetDecayingAverageFromOneOutcomeType(j), skill: j.at(0)?.skill, + masteryLevel: j + .sort((a) => a.masteryLevel as never as number) + .at(0)?.masteryLevel, } }) @@ -29,6 +32,9 @@ export class DecayingAverageLogic { return { decayingAverage: score / filteredResults.length, skill: s.id, + masteryLevel: filteredResults + .sort((a) => a.masteryLevel as never as number) + .at(0)?.masteryLevel, } as DecayingAveragePerSkill }) as DecayingAveragePerSkill[] } @@ -172,4 +178,5 @@ export interface DecayingAveragePerLayer { export interface DecayingAveragePerSkill { skill: number decayingAverage: number + masteryLevel: number } From ee8c67b1f4f327d8479644d9cf11af7906c0b91a Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Fri, 12 May 2023 00:11:34 +0200 Subject: [PATCH 43/50] Fixed the mess --- .../src/logic/DecayingAverageLogic.ts | 12 ++++-------- .../Converters/CompetenceProfileConverter.cs | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts index 31c2acf3..f722985c 100644 --- a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts +++ b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts @@ -129,19 +129,15 @@ export class DecayingAverageLogic { ): number { let totalGradeScore = 0.0 - const recentResult = results.at(0) + const recentResult = results.reverse().pop() if (recentResult && recentResult.grade) { - const pastResults = results.slice(1, results.length - 1) - if (pastResults.length > 0) { - pastResults.forEach( + if (results.length > 0) { + results.forEach( (r) => (totalGradeScore += r.grade ? r.grade : 0) ) totalGradeScore = - parseFloat( - (totalGradeScore / pastResults.length).toFixed(2) - ) * - 0.35 + + (totalGradeScore / results.length) * 0.35 + recentResult.grade * 0.65 } else { totalGradeScore = recentResult.grade diff --git a/Epsilon/Component/Converters/CompetenceProfileConverter.cs b/Epsilon/Component/Converters/CompetenceProfileConverter.cs index ca29e5a5..d789d1aa 100644 --- a/Epsilon/Component/Converters/CompetenceProfileConverter.cs +++ b/Epsilon/Component/Converters/CompetenceProfileConverter.cs @@ -83,8 +83,8 @@ ar is return new CompetenceProfile( domain, - taskResults, - professionalResults, + taskResults.OrderByDescending(task => task.AssessedAt), + professionalResults.OrderByDescending(skill => skill.AssessedAt), filteredTerms ); } From 9b43516bcb1fd99a66b3bf15c364ad6bcec7e7c1 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Fri, 12 May 2023 09:19:44 +0200 Subject: [PATCH 44/50] Change AssessedAt to SubmittedAt --- .../Model/CompetenceOutcomeResult.cs | 2 +- .../Converters/CompetenceProfileConverter.cs | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Epsilon.Abstractions/Model/CompetenceOutcomeResult.cs b/Epsilon.Abstractions/Model/CompetenceOutcomeResult.cs index fd8b69d2..7d6e8cc5 100644 --- a/Epsilon.Abstractions/Model/CompetenceOutcomeResult.cs +++ b/Epsilon.Abstractions/Model/CompetenceOutcomeResult.cs @@ -3,5 +3,5 @@ namespace Epsilon.Abstractions.Model; public record CompetenceOutcomeResult( int OutcomeId, double Grade, - DateTime AssessedAt + DateTime SubmittedAt ); \ No newline at end of file diff --git a/Epsilon/Component/Converters/CompetenceProfileConverter.cs b/Epsilon/Component/Converters/CompetenceProfileConverter.cs index d789d1aa..a53de9fe 100644 --- a/Epsilon/Component/Converters/CompetenceProfileConverter.cs +++ b/Epsilon/Component/Converters/CompetenceProfileConverter.cs @@ -35,11 +35,10 @@ public CompetenceProfile ConvertFrom( ar is { Points: not null, Criterion.MasteryPoints: not null, - Criterion.Outcome: not null + Criterion.Outcome: not null, } && ar.Points >= ar.Criterion.MasteryPoints))) { - if (FhictConstants.ProfessionalTasks.TryGetValue(assessmentRating.Criterion.Outcome.Id, - out var professionalTask)) + if (FhictConstants.ProfessionalTasks.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalTask)) { taskResults.Add( new ProfessionalTaskResult( @@ -52,8 +51,7 @@ ar is ) ); } - else if (FhictConstants.ProfessionalSkills.TryGetValue(assessmentRating.Criterion.Outcome.Id, - out var professionalSkill)) + else if (FhictConstants.ProfessionalSkills.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalSkill)) { professionalResults.Add( new ProfessionalSkillResult( @@ -73,18 +71,16 @@ ar is var filteredTerms = enrollmentTerms .Where(static term => term is { StartAt: not null, EndAt: not null }) .Where(term => taskResults.Any(taskOutcome => - taskOutcome.AssessedAt >= term.StartAt && taskOutcome.AssessedAt <= term.EndAt) + taskOutcome.SubmittedAt >= term.StartAt && taskOutcome.SubmittedAt <= term.EndAt) || professionalResults.Any(skillOutcome => - skillOutcome.AssessedAt > term.StartAt && skillOutcome.AssessedAt < term.EndAt)) + skillOutcome.SubmittedAt > term.StartAt && skillOutcome.SubmittedAt < term.EndAt)) .Distinct() .OrderByDescending(static term => term.StartAt); - ; - return new CompetenceProfile( domain, - taskResults.OrderByDescending(task => task.AssessedAt), - professionalResults.OrderByDescending(skill => skill.AssessedAt), + taskResults.OrderByDescending(task => task.SubmittedAt), + professionalResults.OrderByDescending(skill => skill.SubmittedAt), filteredTerms ); } From f020dd77cdc467afe1abd4b3287f235222b09044 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Mon, 15 May 2023 10:07:32 +0200 Subject: [PATCH 45/50] Added comments --- .../src/logic/DecayingAverageLogic.ts | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts index f722985c..970731b9 100644 --- a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts +++ b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts @@ -4,7 +4,30 @@ import { ProfessionalTaskResult, } from "/@/logic/Api" +export interface DecayingAveragePerActivity { + outcome: number + activity: number + decayingAverage: number +} + +export interface DecayingAveragePerLayer { + architectureLayer: number + layerActivities: DecayingAveragePerActivity[] +} + +export interface DecayingAveragePerSkill { + skill: number + decayingAverage: number + masteryLevel: number +} + export class DecayingAverageLogic { + /** + * Calculate the averages for each skill type + * @param taskResults + * @param domain + * @constructor + */ public static GetAverageSkillOutcomeScores( taskResults: ProfessionalSkillResult[], domain: IHboIDomain @@ -18,7 +41,7 @@ export class DecayingAverageLogic { masteryLevel: j .sort((a) => a.masteryLevel as never as number) .at(0)?.masteryLevel, - } + } as DecayingAveragePerSkill }) return domain.professionalSkills?.map((s) => { @@ -39,6 +62,12 @@ export class DecayingAverageLogic { }) as DecayingAveragePerSkill[] } + /** + * Calculate the averages for each task type divided in architecture layers. + * @param taskResults + * @param domain + * @constructor + */ public static GetAverageTaskOutcomeScores( taskResults: ProfessionalTaskResult[], domain: IHboIDomain @@ -92,6 +121,13 @@ export class DecayingAverageLogic { }) as DecayingAveragePerLayer[] } + /** + * Calculate average of given tasks divided in architectural layers + * @param taskResults + * @param domain + * @constructor + * @private + */ private static GetDecayingAverageForAllOutcomes( taskResults: ProfessionalTaskResult[], domain: IHboIDomain @@ -101,12 +137,14 @@ export class DecayingAverageLogic { architectureLayer: l.id, layerActivities: Object.entries( this.groupBy( + //Ensure that given results are only relined on the architecture that is currently being used. taskResults.filter( (layer) => layer.architectureLayer === l.id ), (r) => r.outcomeId as unknown as string ) ).map(([i, j]) => { + //From all selected outcomes calculate the decaying average, Give outcome id and activity layer. return { outcome: i, activity: j.at(0)?.activity, @@ -119,7 +157,8 @@ export class DecayingAverageLogic { } /** - * Explanation of process can be found here: https://community.canvaslms.com/t5/Canvas-Basics-Guide/What-are-Outcomes/ta-p/75#decaying_average + * Calculate decaying average described by Canvas: https://community.canvaslms.com/t5/Canvas-Basics-Guide/What-are-Outcomes/ta-p/75#decaying_average + * !IMPORTANT, The list of results will always have to be a list of the same outcome id. Not a list of equal activity and architecture layer. * @param results * @constructor * @private @@ -135,7 +174,6 @@ export class DecayingAverageLogic { results.forEach( (r) => (totalGradeScore += r.grade ? r.grade : 0) ) - totalGradeScore = (totalGradeScore / results.length) * 0.35 + recentResult.grade * 0.65 @@ -159,20 +197,3 @@ export class DecayingAverageLogic { }, {}) } } - -export interface DecayingAveragePerActivity { - outcome: number - activity: number - decayingAverage: number -} - -export interface DecayingAveragePerLayer { - architectureLayer: number - layerActivities: DecayingAveragePerActivity[] -} - -export interface DecayingAveragePerSkill { - skill: number - decayingAverage: number - masteryLevel: number -} From 7bf4e0d21bc3237b7cf3117737a62b3c46e7a451 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Wed, 17 May 2023 17:35:31 +0200 Subject: [PATCH 46/50] Cleanup code --- .../Converters/CompetenceProfileConverter.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Epsilon/Component/Converters/CompetenceProfileConverter.cs b/Epsilon/Component/Converters/CompetenceProfileConverter.cs index a53de9fe..ee7f7a92 100644 --- a/Epsilon/Component/Converters/CompetenceProfileConverter.cs +++ b/Epsilon/Component/Converters/CompetenceProfileConverter.cs @@ -23,7 +23,7 @@ public CompetenceProfile ConvertFrom( { var submission = submissionsConnection.SubmissionsHistories.Nodes .Where(static h => h.RubricAssessments.Nodes.Any()) - .OrderByDescending(h => h.SubmittedAt) + .OrderByDescending(static h => h.SubmittedAt) .MaxBy(static h => h.Attempt); if (submission != null) @@ -70,17 +70,17 @@ ar is var filteredTerms = enrollmentTerms .Where(static term => term is { StartAt: not null, EndAt: not null }) - .Where(term => taskResults.Any(taskOutcome => - taskOutcome.SubmittedAt >= term.StartAt && taskOutcome.SubmittedAt <= term.EndAt) - || professionalResults.Any(skillOutcome => - skillOutcome.SubmittedAt > term.StartAt && skillOutcome.SubmittedAt < term.EndAt)) + .Where(term => + taskResults.Any(taskOutcome => taskOutcome.SubmittedAt >= term.StartAt && taskOutcome.SubmittedAt <= term.EndAt) + || professionalResults.Any(skillOutcome => skillOutcome.SubmittedAt > term.StartAt && skillOutcome.SubmittedAt < term.EndAt) + ) .Distinct() .OrderByDescending(static term => term.StartAt); return new CompetenceProfile( domain, - taskResults.OrderByDescending(task => task.SubmittedAt), - professionalResults.OrderByDescending(skill => skill.SubmittedAt), + taskResults.OrderByDescending(static task => task.SubmittedAt), + professionalResults.OrderByDescending(static skill => skill.SubmittedAt), filteredTerms ); } From 6f4378740297697416b6c11bfd71ca9e39e48222 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:49:04 +0200 Subject: [PATCH 47/50] MOved functions inside mount function --- Epsilon.Host.Frontend/src/components/CompetenceGraph.vue | 3 +-- .../src/components/PersonalDevelopmentGraph.vue | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue index 04e8ddcd..c57a6d2f 100644 --- a/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue +++ b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue @@ -110,7 +110,6 @@ function loadChartData(): void { onMounted(() => { loadChartData() + watch(() => loadChartData()) }) - -watch(() => loadChartData()) diff --git a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue index b9828d49..a3987270 100644 --- a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue +++ b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue @@ -82,6 +82,7 @@ const chartOptions = { onMounted(() => { loadChartData() + watch(() => loadChartData()) }) function loadChartData(): void { @@ -118,6 +119,4 @@ function getMastery(masteryId: number): MasteryLevel | undefined { (masteryLevel) => masteryLevel.id == masteryId ) } - -watch(() => loadChartData()) From 20c63655ebbf9b2bd9cac3f075464843f8989cbc Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:50:29 +0200 Subject: [PATCH 48/50] Changed function names --- .../src/components/CompetenceGraph.vue | 2 +- .../src/components/PersonalDevelopmentGraph.vue | 2 +- .../src/logic/DecayingAverageLogic.ts | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue index c57a6d2f..142243a3 100644 --- a/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue +++ b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue @@ -93,7 +93,7 @@ function loadChartData(): void { }) } - DecayingAverageLogic.GetAverageTaskOutcomeScores( + DecayingAverageLogic.getAverageTaskOutcomeScores( props.data, props.domain ).map((layer: DecayingAveragePerLayer) => { diff --git a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue index a3987270..72ff687b 100644 --- a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue +++ b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue @@ -97,7 +97,7 @@ function loadChartData(): void { // Add data series.push({ name: "Score", - data: DecayingAverageLogic.GetAverageSkillOutcomeScores( + data: DecayingAverageLogic.getAverageSkillOutcomeScores( props.data, props.domain )?.map((d) => { diff --git a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts index 970731b9..186262b9 100644 --- a/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts +++ b/Epsilon.Host.Frontend/src/logic/DecayingAverageLogic.ts @@ -28,7 +28,7 @@ export class DecayingAverageLogic { * @param domain * @constructor */ - public static GetAverageSkillOutcomeScores( + public static getAverageSkillOutcomeScores( taskResults: ProfessionalSkillResult[], domain: IHboIDomain ): DecayingAveragePerSkill[] { @@ -36,7 +36,7 @@ export class DecayingAverageLogic { this.groupBy(taskResults, (r) => r.outcomeId as unknown as string) ).map(([, j]) => { return { - decayingAverage: this.GetDecayingAverageFromOneOutcomeType(j), + decayingAverage: this.getDecayingAverageFromOneOutcomeType(j), skill: j.at(0)?.skill, masteryLevel: j .sort((a) => a.masteryLevel as never as number) @@ -68,11 +68,11 @@ export class DecayingAverageLogic { * @param domain * @constructor */ - public static GetAverageTaskOutcomeScores( + public static getAverageTaskOutcomeScores( taskResults: ProfessionalTaskResult[], domain: IHboIDomain ): DecayingAveragePerLayer[] { - const canvasDecaying = this.GetDecayingAverageForAllOutcomes( + const canvasDecaying = this.getDecayingAverageForAllOutcomes( taskResults, domain ) @@ -128,7 +128,7 @@ export class DecayingAverageLogic { * @constructor * @private */ - private static GetDecayingAverageForAllOutcomes( + private static getDecayingAverageForAllOutcomes( taskResults: ProfessionalTaskResult[], domain: IHboIDomain ): DecayingAveragePerLayer[] { @@ -149,7 +149,7 @@ export class DecayingAverageLogic { outcome: i, activity: j.at(0)?.activity, decayingAverage: - this.GetDecayingAverageFromOneOutcomeType(j), + this.getDecayingAverageFromOneOutcomeType(j), } }) as unknown as DecayingAveragePerActivity[], } @@ -163,7 +163,7 @@ export class DecayingAverageLogic { * @constructor * @private */ - private static GetDecayingAverageFromOneOutcomeType( + private static getDecayingAverageFromOneOutcomeType( results: ProfessionalTaskResult[] | ProfessionalSkillResult[] ): number { let totalGradeScore = 0.0 From b0375b6d95909a63cd2f94691698479c40773d71 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:57:52 +0200 Subject: [PATCH 49/50] Changes and removed unused file --- Epsilon.Abstractions/Model/OutcomeResult.cs | 9 --------- Epsilon.Host.Frontend/src/components/CompetenceGraph.vue | 2 +- .../src/components/PersonalDevelopmentGraph.vue | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/OutcomeResult.cs diff --git a/Epsilon.Abstractions/Model/OutcomeResult.cs b/Epsilon.Abstractions/Model/OutcomeResult.cs deleted file mode 100644 index 666d93a7..00000000 --- a/Epsilon.Abstractions/Model/OutcomeResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public abstract record OutcomeResult -{ - public int MasteryLevel { get; set; } - public double Grade{ get; set; } - public DateTime AssessedAt{ get; set; } - public double DecayingAverage{ get; set; } -} \ No newline at end of file diff --git a/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue index 142243a3..44185abb 100644 --- a/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue +++ b/Epsilon.Host.Frontend/src/components/CompetenceGraph.vue @@ -110,6 +110,6 @@ function loadChartData(): void { onMounted(() => { loadChartData() - watch(() => loadChartData()) }) +watch(() => loadChartData()) diff --git a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue index 72ff687b..6190ea02 100644 --- a/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue +++ b/Epsilon.Host.Frontend/src/components/PersonalDevelopmentGraph.vue @@ -82,8 +82,8 @@ const chartOptions = { onMounted(() => { loadChartData() - watch(() => loadChartData()) }) +watch(() => loadChartData()) function loadChartData(): void { series = [] From cb09d7f6c7c43ed33a54a65a874e3e070bfb1b32 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:59:26 +0200 Subject: [PATCH 50/50] Project setting changes --- Epsilon.Abstractions/Epsilon.Abstractions.csproj | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Epsilon.Abstractions/Epsilon.Abstractions.csproj b/Epsilon.Abstractions/Epsilon.Abstractions.csproj index 2ce703a6..8f641cfd 100644 --- a/Epsilon.Abstractions/Epsilon.Abstractions.csproj +++ b/Epsilon.Abstractions/Epsilon.Abstractions.csproj @@ -9,9 +9,4 @@ - - - - -