From 8427658b8890eef81392887179ed149e495ca458 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Fri, 21 Apr 2023 12:53:09 +0200 Subject: [PATCH 01/72] Created KpiMatrix component --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 + .../Model/GraphQl/AssessmentRating.cs | 3 +- .../Model/GraphQl/Outcome.cs | 3 +- .../GraphQl/SubmissionsConnectionNode.cs | 4 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 + Epsilon.Host.Frontend/pnpm-lock.yaml | 604 +++++++++--------- .../Controllers/ComponentController.cs | 10 + Epsilon.Host.WebApi/Program.cs | 2 + Epsilon/Component/KpiMatrixComponent.cs | 83 +++ 9 files changed, 447 insertions(+), 299 deletions(-) create mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs create mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs new file mode 100644 index 00000000..87ca98e9 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix.cs @@ -0,0 +1,25 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes + ); + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments + ); + diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs index 5354ed79..d4be774b 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record AssessmentRating( [property: JsonPropertyName("criterion")] Criterion Criterion, - [property: JsonPropertyName("points")] double? Points + [property: JsonPropertyName("points")] double? Points, + [property: JsonPropertyName("outcome")] Outcome? Outcome ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index d9caddc1..6bdbd704 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -3,5 +3,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( - [property: JsonPropertyName("_id")] int Id + [property: JsonPropertyName("_id")] int Id, + [property: JsonPropertyName("title")] string Title ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs index b5aded9c..013fdbb2 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs @@ -4,5 +4,7 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record SubmissionsConnectionNode( [property: JsonPropertyName("postedAt")] DateTime? PostedAt, - [property: JsonPropertyName("submissionHistoriesConnection")] SubmissionsHistoriesConnection SubmissionsHistories + [property: JsonPropertyName("assignment")] Assignment? Assignment, + [property: JsonPropertyName("submissionHistoriesConnection")] SubmissionsHistoriesConnection SubmissionsHistories, + [property: JsonPropertyName("rubricAssessmentsConnection")] RubricAssessmentsConnection RubricAssessmentsConnection ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs new file mode 100644 index 00000000..ca2dc6ad --- /dev/null +++ b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Epsilon.Canvas.Abstractions.Model.GraphQl; + +namespace Epsilon.Canvas.Abstractions.QueryResponse; +public record GetUserKpiMatrixOutcomes( + [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data +) +{ + public record CourseData( + [property: JsonPropertyName("course")] Course Course + ); +}; \ No newline at end of file diff --git a/Epsilon.Host.Frontend/pnpm-lock.yaml b/Epsilon.Host.Frontend/pnpm-lock.yaml index e59ead27..7cf285cd 100644 --- a/Epsilon.Host.Frontend/pnpm-lock.yaml +++ b/Epsilon.Host.Frontend/pnpm-lock.yaml @@ -1,57 +1,69 @@ -lockfileVersion: 5.4 - -specifiers: - '@types/node': ^18.15.3 - '@typescript-eslint/eslint-plugin': ^5.55.0 - '@typescript-eslint/parser': ^5.55.0 - '@vitejs/plugin-vue': ^4.0.0 - '@vue/eslint-config-typescript': ^11.0.2 - apexcharts: ^3.37.3 - eslint: ^8.36.0 - eslint-plugin-vue: ^9.9.0 - ts-node: ^10.9.1 - typescript: ^4.9.5 - vite: ^4.1.0 - vue: ^3.2.45 - vue-router: ^4.1.6 - vue3-apexcharts: ^1.4.1 +lockfileVersion: '6.0' dependencies: - apexcharts: 3.37.3 - typescript: 4.9.5 - vue: 3.2.47 - vue-router: 4.1.6_vue@3.2.47 - vue3-apexcharts: 1.4.1_w3ch7w2j635wji4x6c2377ue4i + apexcharts: + specifier: ^3.37.3 + version: 3.37.3 + typescript: + specifier: ^4.9.5 + version: 4.9.5 + vue: + specifier: ^3.2.45 + version: 3.2.47 + vue-router: + specifier: ^4.1.6 + version: 4.1.6(vue@3.2.47) + vue3-apexcharts: + specifier: ^1.4.1 + version: 1.4.1(apexcharts@3.37.3)(vue@3.2.47) devDependencies: - '@types/node': 18.15.3 - '@typescript-eslint/eslint-plugin': 5.55.0_342y7v4tc7ytrrysmit6jo4wri - '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m - '@vitejs/plugin-vue': 4.0.0_vite@4.1.4+vue@3.2.47 - '@vue/eslint-config-typescript': 11.0.2_75cttubc7yphoaabg6k3yrjnke - eslint: 8.36.0 - eslint-plugin-vue: 9.9.0_eslint@8.36.0 - ts-node: 10.9.1_cbfmry4sbbh4vatmdrsmatfg5a - vite: 4.1.4_@types+node@18.15.3 + '@types/node': + specifier: ^18.15.3 + version: 18.15.3 + '@typescript-eslint/eslint-plugin': + specifier: ^5.55.0 + version: 5.55.0(@typescript-eslint/parser@5.55.0)(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.55.0 + version: 5.55.0(eslint@8.36.0)(typescript@4.9.5) + '@vitejs/plugin-vue': + specifier: ^4.0.0 + version: 4.0.0(vite@4.1.4)(vue@3.2.47) + '@vue/eslint-config-typescript': + specifier: ^11.0.2 + version: 11.0.2(eslint-plugin-vue@9.9.0)(eslint@8.36.0)(typescript@4.9.5) + eslint: + specifier: ^8.36.0 + version: 8.36.0 + eslint-plugin-vue: + specifier: ^9.9.0 + version: 9.9.0(eslint@8.36.0) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.15.3)(typescript@4.9.5) + vite: + specifier: ^4.1.0 + version: 4.1.4(@types/node@18.15.3) packages: - /@babel/helper-string-parser/7.19.4: + /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier/7.19.1: + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - /@babel/parser/7.21.3: + /@babel/parser@7.21.3: resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: '@babel/types': 7.21.3 - /@babel/types/7.21.3: + /@babel/types@7.21.3: resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} engines: {node: '>=6.9.0'} dependencies: @@ -59,32 +71,32 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - /@cspotcode/source-map-support/0.8.1: + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@esbuild/android-arm/0.16.17: - resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} + /@esbuild/android-arm64@0.16.17: + resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-arm64/0.16.17: - resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} + /@esbuild/android-arm@0.16.17: + resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} engines: {node: '>=12'} - cpu: [arm64] + cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-x64/0.16.17: + /@esbuild/android-x64@0.16.17: resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} engines: {node: '>=12'} cpu: [x64] @@ -93,7 +105,7 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64/0.16.17: + /@esbuild/darwin-arm64@0.16.17: resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} engines: {node: '>=12'} cpu: [arm64] @@ -102,7 +114,7 @@ packages: dev: true optional: true - /@esbuild/darwin-x64/0.16.17: + /@esbuild/darwin-x64@0.16.17: resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} engines: {node: '>=12'} cpu: [x64] @@ -111,7 +123,7 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64/0.16.17: + /@esbuild/freebsd-arm64@0.16.17: resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} engines: {node: '>=12'} cpu: [arm64] @@ -120,7 +132,7 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64/0.16.17: + /@esbuild/freebsd-x64@0.16.17: resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} engines: {node: '>=12'} cpu: [x64] @@ -129,25 +141,25 @@ packages: dev: true optional: true - /@esbuild/linux-arm/0.16.17: - resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + /@esbuild/linux-arm64@0.16.17: + resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-arm64/0.16.17: - resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + /@esbuild/linux-arm@0.16.17: + resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} engines: {node: '>=12'} - cpu: [arm64] + cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-ia32/0.16.17: + /@esbuild/linux-ia32@0.16.17: resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} engines: {node: '>=12'} cpu: [ia32] @@ -156,7 +168,7 @@ packages: dev: true optional: true - /@esbuild/linux-loong64/0.16.17: + /@esbuild/linux-loong64@0.16.17: resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} engines: {node: '>=12'} cpu: [loong64] @@ -165,7 +177,7 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el/0.16.17: + /@esbuild/linux-mips64el@0.16.17: resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} engines: {node: '>=12'} cpu: [mips64el] @@ -174,7 +186,7 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64/0.16.17: + /@esbuild/linux-ppc64@0.16.17: resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} engines: {node: '>=12'} cpu: [ppc64] @@ -183,7 +195,7 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64/0.16.17: + /@esbuild/linux-riscv64@0.16.17: resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} engines: {node: '>=12'} cpu: [riscv64] @@ -192,7 +204,7 @@ packages: dev: true optional: true - /@esbuild/linux-s390x/0.16.17: + /@esbuild/linux-s390x@0.16.17: resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} engines: {node: '>=12'} cpu: [s390x] @@ -201,7 +213,7 @@ packages: dev: true optional: true - /@esbuild/linux-x64/0.16.17: + /@esbuild/linux-x64@0.16.17: resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} engines: {node: '>=12'} cpu: [x64] @@ -210,7 +222,7 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64/0.16.17: + /@esbuild/netbsd-x64@0.16.17: resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} engines: {node: '>=12'} cpu: [x64] @@ -219,7 +231,7 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64/0.16.17: + /@esbuild/openbsd-x64@0.16.17: resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} engines: {node: '>=12'} cpu: [x64] @@ -228,7 +240,7 @@ packages: dev: true optional: true - /@esbuild/sunos-x64/0.16.17: + /@esbuild/sunos-x64@0.16.17: resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} engines: {node: '>=12'} cpu: [x64] @@ -237,7 +249,7 @@ packages: dev: true optional: true - /@esbuild/win32-arm64/0.16.17: + /@esbuild/win32-arm64@0.16.17: resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} engines: {node: '>=12'} cpu: [arm64] @@ -246,7 +258,7 @@ packages: dev: true optional: true - /@esbuild/win32-ia32/0.16.17: + /@esbuild/win32-ia32@0.16.17: resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} engines: {node: '>=12'} cpu: [ia32] @@ -255,7 +267,7 @@ packages: dev: true optional: true - /@esbuild/win32-x64/0.16.17: + /@esbuild/win32-x64@0.16.17: resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} engines: {node: '>=12'} cpu: [x64] @@ -264,7 +276,7 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils/4.2.0_eslint@8.36.0: + /@eslint-community/eslint-utils@4.2.0(eslint@8.36.0): resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -274,12 +286,12 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@eslint-community/regexpp/4.4.0: + /@eslint-community/regexpp@4.4.0: resolution: {integrity: sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc/2.0.1: + /@eslint/eslintrc@2.0.1: resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -296,12 +308,12 @@ packages: - supports-color dev: true - /@eslint/js/8.36.0: + /@eslint/js@8.36.0: resolution: {integrity: sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@humanwhocodes/config-array/0.11.8: + /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} dependencies: @@ -312,32 +324,32 @@ packages: - supports-color dev: true - /@humanwhocodes/module-importer/1.0.1: + /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema/1.2.1: + /@humanwhocodes/object-schema@1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@jridgewell/resolve-uri/3.1.0: + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.14: + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true - /@jridgewell/trace-mapping/0.3.9: + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@nodelib/fs.scandir/2.1.5: + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} dependencies: @@ -345,12 +357,12 @@ packages: run-parallel: 1.2.0 dev: true - /@nodelib/fs.stat/2.0.5: + /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} dev: true - /@nodelib/fs.walk/1.2.8: + /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} dependencies: @@ -358,35 +370,35 @@ packages: fastq: 1.15.0 dev: true - /@tsconfig/node10/1.0.9: + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12/1.0.11: + /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14/1.0.3: + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16/1.0.3: + /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@types/json-schema/7.0.11: + /@types/json-schema@7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true - /@types/node/18.15.3: + /@types/node@18.15.3: resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} dev: true - /@types/semver/7.3.13: + /@types/semver@7.3.13: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true - /@typescript-eslint/eslint-plugin/5.55.0_342y7v4tc7ytrrysmit6jo4wri: + /@typescript-eslint/eslint-plugin@5.55.0(@typescript-eslint/parser@5.55.0)(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -398,23 +410,23 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.4.0 - '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/parser': 5.55.0(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 5.55.0 - '@typescript-eslint/type-utils': 5.55.0_vgl77cfdswitgr47lm5swmv43m - '@typescript-eslint/utils': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/type-utils': 5.55.0(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.55.0(eslint@8.36.0)(typescript@4.9.5) debug: 4.3.4 eslint: 8.36.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.9.5 + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/5.55.0_vgl77cfdswitgr47lm5swmv43m: + /@typescript-eslint/parser@5.55.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -426,7 +438,7 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.55.0 '@typescript-eslint/types': 5.55.0 - '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5 + '@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5) debug: 4.3.4 eslint: 8.36.0 typescript: 4.9.5 @@ -434,7 +446,7 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager/5.55.0: + /@typescript-eslint/scope-manager@5.55.0: resolution: {integrity: sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -442,7 +454,7 @@ packages: '@typescript-eslint/visitor-keys': 5.55.0 dev: true - /@typescript-eslint/type-utils/5.55.0_vgl77cfdswitgr47lm5swmv43m: + /@typescript-eslint/type-utils@5.55.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -452,22 +464,22 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5 - '@typescript-eslint/utils': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.55.0(eslint@8.36.0)(typescript@4.9.5) debug: 4.3.4 eslint: 8.36.0 - tsutils: 3.21.0_typescript@4.9.5 + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.55.0: + /@typescript-eslint/types@5.55.0: resolution: {integrity: sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.5: + /@typescript-eslint/typescript-estree@5.55.0(typescript@4.9.5): resolution: {integrity: sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -482,24 +494,24 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.9.5 + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.55.0_vgl77cfdswitgr47lm5swmv43m: + /@typescript-eslint/utils@5.55.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@eslint-community/eslint-utils': 4.2.0(eslint@8.36.0) '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.55.0 '@typescript-eslint/types': 5.55.0 - '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5 + '@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5) eslint: 8.36.0 eslint-scope: 5.1.1 semver: 7.3.8 @@ -508,7 +520,7 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys/5.55.0: + /@typescript-eslint/visitor-keys@5.55.0: resolution: {integrity: sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -516,18 +528,18 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@vitejs/plugin-vue/4.0.0_vite@4.1.4+vue@3.2.47: + /@vitejs/plugin-vue@4.0.0(vite@4.1.4)(vue@3.2.47): resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.1.4_@types+node@18.15.3 + vite: 4.1.4(@types/node@18.15.3) vue: 3.2.47 dev: true - /@vue/compiler-core/3.2.47: + /@vue/compiler-core@3.2.47: resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: '@babel/parser': 7.21.3 @@ -535,13 +547,13 @@ packages: estree-walker: 2.0.2 source-map: 0.6.1 - /@vue/compiler-dom/3.2.47: + /@vue/compiler-dom@3.2.47: resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} dependencies: '@vue/compiler-core': 3.2.47 '@vue/shared': 3.2.47 - /@vue/compiler-sfc/3.2.47: + /@vue/compiler-sfc@3.2.47: resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: '@babel/parser': 7.21.3 @@ -555,17 +567,17 @@ packages: postcss: 8.4.21 source-map: 0.6.1 - /@vue/compiler-ssr/3.2.47: + /@vue/compiler-ssr@3.2.47: resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} dependencies: '@vue/compiler-dom': 3.2.47 '@vue/shared': 3.2.47 - /@vue/devtools-api/6.5.0: + /@vue/devtools-api@6.5.0: resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} dev: false - /@vue/eslint-config-typescript/11.0.2_75cttubc7yphoaabg6k3yrjnke: + /@vue/eslint-config-typescript@11.0.2(eslint-plugin-vue@9.9.0)(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: @@ -576,17 +588,17 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.55.0_342y7v4tc7ytrrysmit6jo4wri - '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/eslint-plugin': 5.55.0(@typescript-eslint/parser@5.55.0)(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.55.0(eslint@8.36.0)(typescript@4.9.5) eslint: 8.36.0 - eslint-plugin-vue: 9.9.0_eslint@8.36.0 + eslint-plugin-vue: 9.9.0(eslint@8.36.0) typescript: 4.9.5 - vue-eslint-parser: 9.1.0_eslint@8.36.0 + vue-eslint-parser: 9.1.0(eslint@8.36.0) transitivePeerDependencies: - supports-color dev: true - /@vue/reactivity-transform/3.2.47: + /@vue/reactivity-transform@3.2.47: resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: '@babel/parser': 7.21.3 @@ -595,25 +607,25 @@ packages: estree-walker: 2.0.2 magic-string: 0.25.9 - /@vue/reactivity/3.2.47: + /@vue/reactivity@3.2.47: resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==} dependencies: '@vue/shared': 3.2.47 - /@vue/runtime-core/3.2.47: + /@vue/runtime-core@3.2.47: resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==} dependencies: '@vue/reactivity': 3.2.47 '@vue/shared': 3.2.47 - /@vue/runtime-dom/3.2.47: + /@vue/runtime-dom@3.2.47: resolution: {integrity: sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==} dependencies: '@vue/runtime-core': 3.2.47 '@vue/shared': 3.2.47 csstype: 2.6.21 - /@vue/server-renderer/3.2.47_vue@3.2.47: + /@vue/server-renderer@3.2.47(vue@3.2.47): resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==} peerDependencies: vue: 3.2.47 @@ -622,10 +634,10 @@ packages: '@vue/shared': 3.2.47 vue: 3.2.47 - /@vue/shared/3.2.47: + /@vue/shared@3.2.47: resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} - /acorn-jsx/5.3.2_acorn@8.8.2: + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -633,18 +645,18 @@ packages: acorn: 8.8.2 dev: true - /acorn-walk/8.2.0: + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn/8.8.2: + /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /ajv/6.12.6: + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: fast-deep-equal: 3.1.3 @@ -653,19 +665,19 @@ packages: uri-js: 4.4.1 dev: true - /ansi-regex/5.0.1: + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true - /ansi-styles/4.3.0: + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 dev: true - /apexcharts/3.37.3: + /apexcharts@3.37.3: resolution: {integrity: sha512-+rnUui9uC3Mvh9qbQxUfqBnuJ0nAJOYTp+yKnA5bVmmndKXj5X/Q+OVIxkq0Jr5ysiK200Dsg1Tuz/OUG+DEpw==} dependencies: svg.draggable.js: 2.2.2 @@ -676,47 +688,47 @@ packages: svg.select.js: 3.0.1 dev: false - /arg/4.1.3: + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /argparse/2.0.1: + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true - /array-union/2.1.0: + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true - /balanced-match/1.0.2: + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /boolbase/1.0.0: + /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true - /brace-expansion/1.1.11: + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 dev: true - /braces/3.0.2: + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} dependencies: fill-range: 7.0.1 dev: true - /callsites/3.1.0: + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} dev: true - /chalk/4.1.2: + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: @@ -724,26 +736,26 @@ packages: supports-color: 7.2.0 dev: true - /color-convert/2.0.1: + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 dev: true - /color-name/1.1.4: + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /concat-map/0.0.1: + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /create-require/1.1.1: + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /cross-spawn/7.0.3: + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: @@ -752,16 +764,16 @@ packages: which: 2.0.2 dev: true - /cssesc/3.0.0: + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true dev: true - /csstype/2.6.21: + /csstype@2.6.21: resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} - /debug/4.3.4: + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -773,30 +785,30 @@ packages: ms: 2.1.2 dev: true - /deep-is/0.1.4: + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /diff/4.0.2: + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /dir-glob/3.0.1: + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dependencies: path-type: 4.0.0 dev: true - /doctrine/3.0.0: + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} dependencies: esutils: 2.0.3 dev: true - /esbuild/0.16.17: + /esbuild@0.16.17: resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} engines: {node: '>=12'} hasBin: true @@ -826,30 +838,30 @@ packages: '@esbuild/win32-x64': 0.16.17 dev: true - /escape-string-regexp/4.0.0: + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - /eslint-plugin-vue/9.9.0_eslint@8.36.0: + /eslint-plugin-vue@9.9.0(eslint@8.36.0): resolution: {integrity: sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: eslint: 8.36.0 - eslint-utils: 3.0.0_eslint@8.36.0 + eslint-utils: 3.0.0(eslint@8.36.0) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.11 semver: 7.3.8 - vue-eslint-parser: 9.1.0_eslint@8.36.0 + vue-eslint-parser: 9.1.0(eslint@8.36.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color dev: true - /eslint-scope/5.1.1: + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} dependencies: @@ -857,7 +869,7 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope/7.1.1: + /eslint-scope@7.1.1: resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -865,7 +877,7 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.36.0: + /eslint-utils@3.0.0(eslint@8.36.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: @@ -875,22 +887,22 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /eslint-visitor-keys/2.1.0: + /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} dev: true - /eslint-visitor-keys/3.3.0: + /eslint-visitor-keys@3.3.0: resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.36.0: + /eslint@8.36.0: resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@eslint-community/eslint-utils': 4.2.0(eslint@8.36.0) '@eslint-community/regexpp': 4.4.0 '@eslint/eslintrc': 2.0.1 '@eslint/js': 8.36.0 @@ -934,52 +946,52 @@ packages: - supports-color dev: true - /espree/9.5.0: + /espree@9.5.0: resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.8.2 - acorn-jsx: 5.3.2_acorn@8.8.2 + acorn-jsx: 5.3.2(acorn@8.8.2) eslint-visitor-keys: 3.3.0 dev: true - /esquery/1.5.0: + /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 dev: true - /esrecurse/4.3.0: + /esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} dependencies: estraverse: 5.3.0 dev: true - /estraverse/4.3.0: + /estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} dev: true - /estraverse/5.3.0: + /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} dev: true - /estree-walker/2.0.2: + /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - /esutils/2.0.3: + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} dev: true - /fast-deep-equal/3.1.3: + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true - /fast-glob/3.2.12: + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} dependencies: @@ -990,35 +1002,35 @@ packages: micromatch: 4.0.5 dev: true - /fast-json-stable-stringify/2.1.0: + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: true - /fast-levenshtein/2.0.6: + /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fastq/1.15.0: + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 dev: true - /file-entry-cache/6.0.1: + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: flat-cache: 3.0.4 dev: true - /fill-range/7.0.1: + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 dev: true - /find-up/5.0.0: + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} dependencies: @@ -1026,7 +1038,7 @@ packages: path-exists: 4.0.0 dev: true - /flat-cache/3.0.4: + /flat-cache@3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: @@ -1034,15 +1046,15 @@ packages: rimraf: 3.0.2 dev: true - /flatted/3.2.7: + /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true - /fs.realpath/1.0.0: + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents/2.3.2: + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -1050,25 +1062,25 @@ packages: dev: true optional: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /glob-parent/5.1.2: + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 dev: true - /glob-parent/6.0.2: + /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 dev: true - /glob/7.2.3: + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 @@ -1079,14 +1091,14 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals/13.20.0: + /globals@13.20.0: resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 dev: true - /globby/11.1.0: + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} dependencies: @@ -1098,28 +1110,28 @@ packages: slash: 3.0.0 dev: true - /grapheme-splitter/1.0.4: + /grapheme-splitter@1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true - /has-flag/4.0.0: + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: true - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /ignore/5.2.4: + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} dev: true - /import-fresh/3.3.0: + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} dependencies: @@ -1127,74 +1139,74 @@ packages: resolve-from: 4.0.0 dev: true - /imurmurhash/0.1.4: + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} dev: true - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 dev: true - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true - /is-core-module/2.11.0: + /is-core-module@2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true - /is-extglob/2.1.1: + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} dev: true - /is-glob/4.0.3: + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 dev: true - /is-number/7.0.0: + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true - /is-path-inside/3.0.3: + /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} dev: true - /isexe/2.0.0: + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /js-sdsl/4.3.0: + /js-sdsl@4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} dev: true - /js-yaml/4.1.0: + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 dev: true - /json-schema-traverse/0.4.1: + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true - /json-stable-stringify-without-jsonify/1.0.1: + /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /levn/0.4.1: + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} dependencies: @@ -1202,43 +1214,43 @@ packages: type-check: 0.4.0 dev: true - /locate-path/6.0.0: + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} dependencies: p-locate: 5.0.0 dev: true - /lodash.merge/4.6.2: + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /lodash/4.17.21: + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /lru-cache/6.0.0: + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} dependencies: yallist: 4.0.0 dev: true - /magic-string/0.25.9: + /magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: sourcemap-codec: 1.4.8 - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /merge2/1.4.1: + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} dev: true - /micromatch/4.0.5: + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} dependencies: @@ -1246,42 +1258,42 @@ packages: picomatch: 2.3.1 dev: true - /minimatch/3.1.2: + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true - /ms/2.1.2: + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true - /nanoid/3.3.4: + /nanoid@3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /natural-compare-lite/1.4.0: + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true - /natural-compare/1.4.0: + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /nth-check/2.1.1: + /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: boolbase: 1.0.0 dev: true - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true - /optionator/0.9.1: + /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} dependencies: @@ -1293,60 +1305,60 @@ packages: word-wrap: 1.2.3 dev: true - /p-limit/3.1.0: + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 dev: true - /p-locate/5.0.0: + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} dependencies: p-limit: 3.1.0 dev: true - /parent-module/1.0.1: + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} dependencies: callsites: 3.1.0 dev: true - /path-exists/4.0.0: + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} dev: true - /path-is-absolute/1.0.1: + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} dev: true - /path-key/3.1.1: + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /path-type/4.0.0: + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} dev: true - /picocolors/1.0.0: + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - /picomatch/2.3.1: + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: true - /postcss-selector-parser/6.0.11: + /postcss-selector-parser@6.0.11: resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} engines: {node: '>=4'} dependencies: @@ -1354,7 +1366,7 @@ packages: util-deprecate: 1.0.2 dev: true - /postcss/8.4.21: + /postcss@8.4.21: resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} engines: {node: ^10 || ^12 || >=14} dependencies: @@ -1362,26 +1374,26 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 - /prelude-ls/1.2.1: + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true - /punycode/2.3.0: + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} dev: true - /queue-microtask/1.2.3: + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /resolve-from/4.0.0: + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} dev: true - /resolve/1.22.1: + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true dependencies: @@ -1390,19 +1402,19 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /reusify/1.0.4: + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true - /rimraf/3.0.2: + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 dev: true - /rollup/3.19.1: + /rollup@3.19.1: resolution: {integrity: sha512-lAbrdN7neYCg/8WaoWn/ckzCtz+jr70GFfYdlf50OF7387HTg+wiuiqJRFYawwSPpqfqDNYqK7smY/ks2iAudg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true @@ -1410,13 +1422,13 @@ packages: fsevents: 2.3.2 dev: true - /run-parallel/1.2.0: + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /semver/7.3.8: + /semver@7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} hasBin: true @@ -1424,92 +1436,92 @@ packages: lru-cache: 6.0.0 dev: true - /shebang-command/2.0.0: + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 dev: true - /shebang-regex/3.0.0: + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} dev: true - /slash/3.0.0: + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} dev: true - /source-map-js/1.0.2: + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - /source-map/0.6.1: + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - /sourcemap-codec/1.4.8: + /sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead - /strip-ansi/6.0.1: + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 dev: true - /strip-json-comments/3.1.1: + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true - /supports-color/7.2.0: + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /svg.draggable.js/2.2.2: + /svg.draggable.js@2.2.2: resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.easing.js/2.0.0: + /svg.easing.js@2.0.0: resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.filter.js/2.0.2: + /svg.filter.js@2.0.2: resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.js/2.7.1: + /svg.js@2.7.1: resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==} dev: false - /svg.pathmorphing.js/0.1.3: + /svg.pathmorphing.js@0.1.3: resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.resize.js/1.4.3: + /svg.resize.js@1.4.3: resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==} engines: {node: '>= 0.8.0'} dependencies: @@ -1517,36 +1529,36 @@ packages: svg.select.js: 2.1.2 dev: false - /svg.select.js/2.1.2: + /svg.select.js@2.1.2: resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.select.js/3.0.1: + /svg.select.js@3.0.1: resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /text-table/0.2.0: + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true - /to-fast-properties/2.0.0: + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} - /to-regex-range/5.0.1: + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 dev: true - /ts-node/10.9.1_cbfmry4sbbh4vatmdrsmatfg5a: + /ts-node@10.9.1(@types/node@18.15.3)(typescript@4.9.5): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -1577,11 +1589,11 @@ packages: yn: 3.1.1 dev: true - /tslib/1.14.1: + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tsutils/3.21.0_typescript@4.9.5: + /tsutils@3.21.0(typescript@4.9.5): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: @@ -1591,38 +1603,38 @@ packages: typescript: 4.9.5 dev: true - /type-check/0.4.0: + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.2.1 dev: true - /type-fest/0.20.2: + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} dev: true - /typescript/4.9.5: + /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} hasBin: true - /uri-js/4.4.1: + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.0 dev: true - /util-deprecate/1.0.2: + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /v8-compile-cache-lib/3.0.1: + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /vite/4.1.4_@types+node@18.15.3: + /vite@4.1.4(@types/node@18.15.3): resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -1656,7 +1668,7 @@ packages: fsevents: 2.3.2 dev: true - /vue-eslint-parser/9.1.0_eslint@8.36.0: + /vue-eslint-parser@9.1.0(eslint@8.36.0): resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: @@ -1674,7 +1686,7 @@ packages: - supports-color dev: true - /vue-router/4.1.6_vue@3.2.47: + /vue-router@4.1.6(vue@3.2.47): resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==} peerDependencies: vue: ^3.2.0 @@ -1683,16 +1695,7 @@ packages: vue: 3.2.47 dev: false - /vue/3.2.47: - resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} - dependencies: - '@vue/compiler-dom': 3.2.47 - '@vue/compiler-sfc': 3.2.47 - '@vue/runtime-dom': 3.2.47 - '@vue/server-renderer': 3.2.47_vue@3.2.47 - '@vue/shared': 3.2.47 - - /vue3-apexcharts/1.4.1_w3ch7w2j635wji4x6c2377ue4i: + /vue3-apexcharts@1.4.1(apexcharts@3.37.3)(vue@3.2.47): resolution: {integrity: sha512-96qP8JDqB9vwU7bkG5nVU+E0UGQn7yYQVqUUCLQMYWDuQyu2vE77H/UFZ1yI+hwzlSTBKT9BqnNG8JsFegB3eg==} peerDependencies: apexcharts: '> 3.0.0' @@ -1702,7 +1705,16 @@ packages: vue: 3.2.47 dev: false - /which/2.0.2: + /vue@3.2.47: + resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} + dependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-sfc': 3.2.47 + '@vue/runtime-dom': 3.2.47 + '@vue/server-renderer': 3.2.47(vue@3.2.47) + '@vue/shared': 3.2.47 + + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true @@ -1710,30 +1722,30 @@ packages: isexe: 2.0.0 dev: true - /word-wrap/1.2.3: + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} dev: true - /wrappy/1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /xml-name-validator/4.0.0: + /xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} dev: true - /yallist/4.0.0: + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yn/3.1.1: + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true - /yocto-queue/0.1.0: + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index a6186ece..d34d7c2f 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; using Microsoft.AspNetCore.Mvc; namespace Epsilon.Host.WebApi.Controllers; @@ -12,6 +13,7 @@ public class ComponentController : ControllerBase public ComponentController(IConfiguration configuration, IComponentFetcher competenceProfileManager) { _competenceProfileManager = competenceProfileManager; + _kpiMatrixManager = kpiMatrixManager; } [HttpGet("competence_profile")] @@ -21,4 +23,12 @@ public async Task> GetCompetenceProfile() return competenceProfile; } + + [HttpGet("kpi_matrix")] + public async Task> GetKpiMatrix() + { + var kpiMatrix = await _kpiMatrixManager.Fetch(); + + return kpiMatrix; + } } \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index f8450361..e6930860 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,4 +1,5 @@ using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Spreadsheet; using Epsilon.Abstractions.Component; using Epsilon.Canvas; using Epsilon.Component; @@ -28,6 +29,7 @@ builder.Services.AddComponentFetcher(); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs new file mode 100644 index 00000000..d157702a --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -0,0 +1,83 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Canvas.Abstractions.Service; +using Microsoft.Extensions.Configuration; + +namespace Epsilon.Component; + +public class KpiMatrixComponent : Component +{ + + + private readonly IConfiguration _configuration; + private readonly IGraphQlHttpService _graphQlService; + private readonly IAccountHttpService _accountHttpService; + + public KpiMatrixComponent( + IGraphQlHttpService graphQlService, + IAccountHttpService accountHttpService, + IConfiguration configuration + ) + { + _graphQlService = graphQlService; + _accountHttpService = accountHttpService; + _configuration = configuration; + } + + public async override Task Fetch() + { + var courseId = _configuration["Canvas:courseId"]; + + var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); + var outcomes = await _graphQlService.Query(outcomesQuery); + + var kpiMatrix = ConvertToComponent(outcomes); + + return kpiMatrix; + } + + private GradeStatus GetGradeStatus(double? points) + { + return points switch + { + null => GradeStatus.NotGraded, + >= 4 => GradeStatus.Approved, + _ => GradeStatus.Insufficient + }; + } + + private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + { + var outcomes = new List(); + var assignments = new List(); + + if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) + return new KpiMatrix(outcomes, assignments); + foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + { + var assignmentOutcomes = new List(); + var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() + ?.AssessmentRatings; + if (assessmentRatings == null) continue; + + foreach (var assessmentRating in assessmentRatings) + { + if (assessmentRating.Outcome != null) + { + assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points))); + if (outcomes.All(o => o != assessmentRating.Outcome.Title)) + { + outcomes.Add(assessmentRating.Outcome.Title); + } + } + } + + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + } + + return new KpiMatrix(outcomes ,assignments); + } +} \ No newline at end of file From 064123ed8f5eef0faf3f16f5d2d64a701c9783be Mon Sep 17 00:00:00 2001 From: Jasper123pyah <73039915+Jasper123pyah@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:09:47 +0200 Subject: [PATCH 02/72] Added modules and fixed comments --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ----- .../Model/KpiMatrix/GradeStatus.cs | 8 ++ .../Model/KpiMatrix/KpiMatrix.cs | 9 ++ .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 + .../Model/KpiMatrix/KpiMatrixModule.cs | 6 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 + .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 + .../Model/GraphQl/Outcome.cs | 3 +- Epsilon.Canvas/QueryConstants.cs | 1 + Epsilon.Host.WebApi/Program.cs | 1 + Epsilon/Component/KpiMatrixComponent.cs | 103 +++++++++++------- 11 files changed, 106 insertions(+), 65 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs create mode 100644 Epsilon.Canvas/QueryConstants.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs deleted file mode 100644 index 87ca98e9..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes - ); - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments - ); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs new file mode 100644 index 00000000..d50d2254 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs @@ -0,0 +1,8 @@ +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs new file mode 100644 index 00000000..a86409c5 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs @@ -0,0 +1,9 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs new file mode 100644 index 00000000..6ee6bdcb --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs new file mode 100644 index 00000000..9a874e98 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixModule( + string Name, + KpiMatrix KpiMatrix +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs new file mode 100644 index 00000000..efe15667 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -0,0 +1,5 @@ +namespace Epsilon.Abstractions.Model; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs new file mode 100644 index 00000000..c3aa2732 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs @@ -0,0 +1,4 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixProfile( + IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index 6bdbd704..1a0f6055 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( [property: JsonPropertyName("_id")] int Id, - [property: JsonPropertyName("title")] string Title + [property: JsonPropertyName("title")] string Title, + [property: JsonPropertyName("masteryPoints")] double MasteryPoints ); \ No newline at end of file diff --git a/Epsilon.Canvas/QueryConstants.cs b/Epsilon.Canvas/QueryConstants.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/Epsilon.Canvas/QueryConstants.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index e6930860..424f6555 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,6 +1,7 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Spreadsheet; using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Component; diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index d157702a..b65bd7d7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -1,4 +1,5 @@ -using Epsilon.Abstractions.Component; +using DocumentFormat.OpenXml.Spreadsheet; +using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Canvas.Abstractions.QueryResponse; @@ -7,10 +8,8 @@ namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponent : Component { - - private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; private readonly IAccountHttpService _accountHttpService; @@ -25,59 +24,85 @@ IConfiguration configuration _accountHttpService = accountHttpService; _configuration = configuration; } - - public async override Task Fetch() + + public async override Task Fetch() { var courseId = _configuration["Canvas:courseId"]; var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); var outcomes = await _graphQlService.Query(outcomesQuery); - - var kpiMatrix = ConvertToComponent(outcomes); - return kpiMatrix; + var modules = ConvertToComponent(outcomes); + + return modules; } - private GradeStatus GetGradeStatus(double? points) + private GradeStatus GetGradeStatus(double? points, double mastery) { - return points switch + if (points == null) + { + return GradeStatus.NotGraded; + } + else if (points >= mastery) { - null => GradeStatus.NotGraded, - >= 4 => GradeStatus.Approved, - _ => GradeStatus.Insufficient - }; + return GradeStatus.Approved; + } + else + { + return GradeStatus.Insufficient; + } } - - private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + + + private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) { - var outcomes = new List(); - var assignments = new List(); + var modules = new List(); - if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) - return new KpiMatrix(outcomes, assignments); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - var assignmentOutcomes = new List(); - var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() - ?.AssessmentRatings; - if (assessmentRatings == null) continue; - - foreach (var assessmentRating in assessmentRatings) + // Check if the node has a module + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) continue; + + // Group the assignment outcomes by outcome title + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(n => n?.AssessmentRatings != null) + .SelectMany(n => n.AssessmentRatings) + .Where(r => r.Outcome != null) + .GroupBy(r => r.Outcome.Title); + + var outcomes = outcomeGroups.Select(g => g.Key); + var assignments = outcomeGroups.Select(g => + new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) + )); + + // Find the module in the list of modules or create a new one + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + modules.Add(module); + } + else + { + var moduleIndex = modules.IndexOf(module); + if (moduleIndex == -1) { - if (assessmentRating.Outcome != null) - { - assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points))); - if (outcomes.All(o => o != assessmentRating.Outcome.Title)) - { - outcomes.Add(assessmentRating.Outcome.Title); - } - } + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); } - - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + else + { + var existingModule = modules[moduleIndex]; + var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); + modules[moduleIndex] = newModule; + } + } } - return new KpiMatrix(outcomes ,assignments); + return new KpiMatrixProfile(modules); } } \ No newline at end of file From e304511b95e61f00351b533e3665825c5a6de909 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 25 Apr 2023 12:29:05 +0200 Subject: [PATCH 03/72] Fixed an issue where outcomes where duplicate --- Epsilon/Component/KpiMatrixComponent.cs | 42 ++++++++++++++++++------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index b65bd7d7..75ccd18f 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -60,29 +60,49 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - // Check if the node has a module var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; if (moduleName == null) continue; - // Group the assignment outcomes by outcome title var outcomeGroups = node.RubricAssessmentsConnection.Nodes .Where(n => n?.AssessmentRatings != null) .SelectMany(n => n.AssessmentRatings) .Where(r => r.Outcome != null) .GroupBy(r => r.Outcome.Title); - var outcomes = outcomeGroups.Select(g => g.Key); - var assignments = outcomeGroups.Select(g => - new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + var existingOutcomes = new HashSet(); + var outcomes = new List(); + var assignments = new List(); + + foreach (var outcomeGroup in outcomeGroups) + { + var outcomeTitle = outcomeGroup.Key; + if (existingOutcomes.Contains(outcomeTitle)) continue; + + existingOutcomes.Add(outcomeTitle); + outcomes.Add(outcomeTitle); + + var outcomeAssessments = outcomeGroup.Select(assessmentRating => new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) - )); + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); + + var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); + if (assignment == null) + { + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments)); + } + else + { + assignments.Remove(assignment); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessments).ToList(); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + } + + } - // Find the module in the list of modules or create a new one var module = modules.FirstOrDefault(m => m.Name == moduleName); if (module == null) { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments)); modules.Add(module); } else @@ -90,13 +110,13 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM var moduleIndex = modules.IndexOf(module); if (moduleIndex == -1) { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments))); } else { var existingModule = modules[moduleIndex]; var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes).Distinct(); var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); modules[moduleIndex] = newModule; } From b469a790a18f451921e09859a6c2be3c2f988316 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Mon, 8 May 2023 15:30:23 +0200 Subject: [PATCH 04/72] Created KPIMatrix in word document --- .../KpiMatrixComponentWordConverter.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Epsilon/Component/KpiMatrixComponentWordConverter.cs diff --git a/Epsilon/Component/KpiMatrixComponentWordConverter.cs b/Epsilon/Component/KpiMatrixComponentWordConverter.cs new file mode 100644 index 00000000..e845a2f8 --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponentWordConverter.cs @@ -0,0 +1,122 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using System.Linq; +using System.Threading.Tasks; + +namespace Epsilon.Component +{ + public class KpiMatrixComponentWordConverter : ComponentConverter + { + public override Task Convert(KpiMatrixProfile component) + { + var body = new Body(); + + foreach (var module in component.KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth() { Width = "100%", Type = TableWidthUnitValues.Pct })); + + // Calculate the header row height based on the longest assignment name. + int headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 50; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { Val = (UInt32Value)(uint)headerRowHeight })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders(); + cell.FirstChild.Append(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(outcome))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome); + var cell = CreateTableCellWithBorders(); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF" + }; + cell.FirstChild.Append(new Shading() { Fill = fillColor }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + } + + return Task.FromResult(body); + } + + private TableCell CreateTableCellWithBorders(params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder() { Val = BorderValues.Single }, + new RightBorder() { Val = BorderValues.Single }, + new TopBorder() { Val = BorderValues.Single }, + new BottomBorder() { Val = BorderValues.Single }); + + if (elements != null) + { + foreach (var element in elements) + { + cell.Append(element); + } + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); // Move the PrependChild here + + return cell; + } + + } +} \ No newline at end of file From 522c214e439b2b4760e704bc1a6a484d50d5ab87 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 12:20:46 +0200 Subject: [PATCH 05/72] Fixed all remaining issues. --- .../Model/KpiMatrix/KpiMatrix.cs | 4 +- .../Model/KpiMatrix/KpiMatrixAssignment.cs | 2 +- .../Model/KpiMatrix/KpiMatrixOutcome.cs | 1 + Epsilon/Component/KpiMatrixComponent.cs | 58 +++++++++---------- .../KpiMatrixComponentWordConverter.cs | 28 +++++---- 5 files changed, 47 insertions(+), 46 deletions(-) diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs index a86409c5..5ba95040 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs @@ -3,7 +3,7 @@ namespace Epsilon.Abstractions.Model; public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments + List Outcomes, + List Assignments ); diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs index 6ee6bdcb..f4cdb0a7 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs @@ -2,5 +2,5 @@ namespace Epsilon.Abstractions.Model; public record KpiMatrixAssignment( string Name, - IEnumerable Outcomes + List Outcomes ); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs index efe15667..0d616204 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -1,5 +1,6 @@ namespace Epsilon.Abstractions.Model; public record KpiMatrixOutcome( + int Id, string Title, GradeStatus GradeStatus ); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index 75ccd18f..3a24dcb7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -67,28 +67,42 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM .Where(n => n?.AssessmentRatings != null) .SelectMany(n => n.AssessmentRatings) .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Title); + .GroupBy(r => r.Outcome.Id); - var existingOutcomes = new HashSet(); - var outcomes = new List(); var assignments = new List(); + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, + new KpiMatrix(new List(), new List())); + modules.Add(module); + } + + var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(o => o.Id); + foreach (var outcomeGroup in outcomeGroups) { - var outcomeTitle = outcomeGroup.Key; - if (existingOutcomes.Contains(outcomeTitle)) continue; + var outcomeId = outcomeGroup.Key; - existingOutcomes.Add(outcomeTitle); - outcomes.Add(outcomeTitle); + // Add outcome to moduleOutcomes if it doesn't exist + if (!moduleOutcomes.ContainsKey(outcomeId)) + { + var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; + var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, + GetGradeStatus(0, outcome.MasteryPoints)); + moduleOutcomes[outcomeId] = kpiMatrixOutcome; + module.KpiMatrix.Outcomes.Add(kpiMatrixOutcome); + } var outcomeAssessments = outcomeGroup.Select(assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Title, + new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); if (assignment == null) { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments)); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments.ToList())); } else { @@ -96,31 +110,11 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessments).ToList(); assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); } - } - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments)); - modules.Add(module); - } - else - { - var moduleIndex = modules.IndexOf(module); - if (moduleIndex == -1) - { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments))); - } - else - { - var existingModule = modules[moduleIndex]; - var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes).Distinct(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); - modules[moduleIndex] = newModule; - } - } + var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(module.KpiMatrix.Outcomes, newAssignments)); + modules[modules.IndexOf(module)] = newModule; } return new KpiMatrixProfile(modules); diff --git a/Epsilon/Component/KpiMatrixComponentWordConverter.cs b/Epsilon/Component/KpiMatrixComponentWordConverter.cs index e845a2f8..b64dc1ae 100644 --- a/Epsilon/Component/KpiMatrixComponentWordConverter.cs +++ b/Epsilon/Component/KpiMatrixComponentWordConverter.cs @@ -25,13 +25,13 @@ public override Task Convert(KpiMatrixProfile component) // Set table properties for formatting. table.AppendChild(new TableProperties( - new TableWidth() { Width = "100%", Type = TableWidthUnitValues.Pct })); + new TableWidth() { Width = "0", Type = TableWidthUnitValues.Auto })); // Calculate the header row height based on the longest assignment name. int headerRowHeight = 0; if (module.KpiMatrix.Assignments.Any()) { - headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 50; + headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 111; } // Create the table header row. @@ -40,11 +40,11 @@ public override Task Convert(KpiMatrixProfile component) { Val = (UInt32Value)(uint)headerRowHeight })); // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(""))))); + headerRow.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(""))))); foreach (var assignment in module.KpiMatrix.Assignments) { - var cell = CreateTableCellWithBorders(); + var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight }); cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); @@ -54,18 +54,18 @@ public override Task Convert(KpiMatrixProfile component) table.AppendChild(headerRow); // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes) + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(o => o.Id)) { var row = new TableRow(); // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(outcome))))); + row.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(outcome.Title))))); // Add the assignment cells. foreach (var assignment in module.KpiMatrix.Assignments) { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome); - var cell = CreateTableCellWithBorders(); + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); // Set cell color based on GradeStatus. if (outcomeAssignment != null) @@ -89,12 +89,14 @@ public override Task Convert(KpiMatrixProfile component) } body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break() { Type = BreakValues.Page }))); + } return Task.FromResult(body); } - private TableCell CreateTableCellWithBorders(params OpenXmlElement[] elements) + private TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) { var cell = new TableCell(); var cellProperties = new TableCellProperties(); @@ -111,9 +113,13 @@ private TableCell CreateTableCellWithBorders(params OpenXmlElement[] elements) cell.Append(element); } } - + + if (width != null) + { + cellProperties.Append(new TableCellWidth { Type = TableWidthUnitValues.Dxa, Width = width }); + } cellProperties.Append(borders); - cell.PrependChild(cellProperties); // Move the PrependChild here + cell.PrependChild(cellProperties); return cell; } From 0f2ed396bfd65bedfdbd6412d168abd5e9104dca Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 13:01:40 +0200 Subject: [PATCH 06/72] Merge conflicts --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 + .../Component/KpiMatrixAssignment.cs | 6 + .../Component/KpiMatrixCollection.cs | 148 ++++++++++++++++++ .../KpiMatrixModule.cs | 2 +- .../KpiMatrixOutcome.cs | 4 +- .../Model/KpiMatrix/KpiMatrix.cs | 9 -- .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 - .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 - .../Model}/GradeStatus.cs | 4 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 -- .../Controllers/ComponentController.cs | 20 +-- Epsilon.Host.WebApi/Program.cs | 4 +- ...ponent.cs => KpiMatrixComponentFetcher.cs} | 107 ++++++++----- .../KpiMatrixComponentWordConverter.cs | 128 --------------- 14 files changed, 242 insertions(+), 219 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixModule.cs (64%) rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixOutcome.cs (52%) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs rename {Epsilon.Abstractions/Model/KpiMatrix => Epsilon.Canvas.Abstractions/Model}/GradeStatus.cs (50%) delete mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs rename Epsilon/Component/{KpiMatrixComponent.cs => KpiMatrixComponentFetcher.cs} (52%) delete mode 100644 Epsilon/Component/KpiMatrixComponentWordConverter.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs new file mode 100644 index 00000000..8f5b15a5 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrix.cs @@ -0,0 +1,7 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs new file mode 100644 index 00000000..625815fa --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs new file mode 100644 index 00000000..d1daafec --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs @@ -0,0 +1,148 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +{ + public OpenXmlElement ToWord() + { + var body = new Body(); + + foreach (var module in KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth + { + Width = "0", Type = TableWidthUnitValues.Auto, + })); + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.BottomToTopLeftToRight, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF", + }; + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break + { + Type = BreakValues.Page, + }))); + } + + return body; + } + + private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder + { + Val = BorderValues.Single, + }, + new RightBorder + { + Val = BorderValues.Single, + }, + new TopBorder + { + Val = BorderValues.Single, + }, + new BottomBorder + { + Val = BorderValues.Single, + }); + + foreach (var element in elements) + { + cell.Append(element); + } + + if (width != null) + { + cellProperties.Append(new TableCellWidth + { + Type = TableWidthUnitValues.Dxa, Width = width, + }); + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); + + return cell; + } +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixModule.cs similarity index 64% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixModule.cs index 9a874e98..659c932e 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Abstractions.Component; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs similarity index 52% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs rename to Epsilon.Abstractions/Component/KpiMatrixOutcome.cs index 0d616204..1c6c125c 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs @@ -1,4 +1,6 @@ -namespace Epsilon.Abstractions.Model; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; public record KpiMatrixOutcome( int Id, string Title, diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs deleted file mode 100644 index 5ba95040..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public record KpiMatrix( - List Outcomes, - List Assignments -); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs deleted file mode 100644 index f4cdb0a7..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixAssignment( - string Name, - List Outcomes -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs deleted file mode 100644 index c3aa2732..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixProfile( - IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs similarity index 50% rename from Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs rename to Epsilon.Canvas.Abstractions/Model/GradeStatus.cs index d50d2254..307d4dfc 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs +++ b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs @@ -1,8 +1,8 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Canvas.Abstractions.Model; public enum GradeStatus { Approved, Insufficient, - NotGraded + NotGraded, } \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs deleted file mode 100644 index ca2dc6ad..00000000 --- a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using Epsilon.Canvas.Abstractions.Model.GraphQl; - -namespace Epsilon.Canvas.Abstractions.QueryResponse; -public record GetUserKpiMatrixOutcomes( - [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data -) -{ - public record CourseData( - [property: JsonPropertyName("course")] Course Course - ); -}; \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index d34d7c2f..773f0896 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -1,5 +1,4 @@ using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; using Microsoft.AspNetCore.Mvc; namespace Epsilon.Host.WebApi.Controllers; @@ -9,8 +8,13 @@ namespace Epsilon.Host.WebApi.Controllers; public class ComponentController : ControllerBase { private readonly IComponentFetcher _competenceProfileManager; + private readonly IComponentFetcher _kpiMatrixManager; - public ComponentController(IConfiguration configuration, IComponentFetcher competenceProfileManager) + public ComponentController( + IConfiguration configuration, + IComponentFetcher competenceProfileManager, + IComponentFetcher kpiMatrixManager + ) { _competenceProfileManager = competenceProfileManager; _kpiMatrixManager = kpiMatrixManager; @@ -19,16 +23,12 @@ public ComponentController(IConfiguration configuration, IComponentFetcher> GetCompetenceProfile() { - var competenceProfile = await _competenceProfileManager.Fetch(); - - return competenceProfile; + return await _competenceProfileManager.Fetch(); } - + [HttpGet("kpi_matrix")] - public async Task> GetKpiMatrix() + public async Task> GetKpiMatrix() { - var kpiMatrix = await _kpiMatrixManager.Fetch(); - - return kpiMatrix; + return await _kpiMatrixManager.Fetch(); } } \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index 424f6555..ba4345a1 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,7 +1,4 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Spreadsheet; using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Component; @@ -29,6 +26,7 @@ builder.Services.AddScoped(); builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); var app = builder.Build(); diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs similarity index 52% rename from Epsilon/Component/KpiMatrixComponent.cs rename to Epsilon/Component/KpiMatrixComponentFetcher.cs index 3a24dcb7..960d0340 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -1,73 +1,91 @@ -using DocumentFormat.OpenXml.Spreadsheet; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using Epsilon.Canvas; -using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Abstractions.Component; +using Epsilon.Canvas.Abstractions.Model; +using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponentFetcher : ComponentFetcher { + private const string GetUserKpiMatrixOutcomes = @" + query GetUserKpiMatrixOutcomes { + course(id: $courseId) { + submissionsConnection { + nodes { + assignment { + name + modules { + name + } + } + rubricAssessmentsConnection { + nodes { + assessmentRatings { + points + outcome { + _id + title + masteryPoints + } + } + } + } + } + } + } + } + "; + private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; - private readonly IAccountHttpService _accountHttpService; - public KpiMatrixComponent( + public KpiMatrixComponentFetcher( IGraphQlHttpService graphQlService, - IAccountHttpService accountHttpService, IConfiguration configuration ) { _graphQlService = graphQlService; - _accountHttpService = accountHttpService; _configuration = configuration; } - public async override Task Fetch() + public override async Task Fetch() { - var courseId = _configuration["Canvas:courseId"]; - - var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); - var outcomes = await _graphQlService.Query(outcomesQuery); + var courseId = _configuration["Canvas:CourseId"]; - var modules = ConvertToComponent(outcomes); + var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}", StringComparison.InvariantCultureIgnoreCase); + var outcomes = await _graphQlService.Query(outcomesQuery); - return modules; + return ConvertToComponent(outcomes); } - private GradeStatus GetGradeStatus(double? points, double mastery) + private static GradeStatus GetGradeStatus(double? points, double mastery) { - if (points == null) - { - return GradeStatus.NotGraded; - } - else if (points >= mastery) - { - return GradeStatus.Approved; - } - else - { - return GradeStatus.Insufficient; - } + return points != null + ? points >= mastery + ? GradeStatus.Approved + : GradeStatus.Insufficient + : GradeStatus.NotGraded; } - private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse getUserKpiMatrixOutcomes) { var modules = new List(); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) continue; + if (moduleName == null) + { + continue; + } var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(n => n?.AssessmentRatings != null) - .SelectMany(n => n.AssessmentRatings) - .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Id); + .Where(static n => n.AssessmentRatings != null) + .SelectMany(static n => n.AssessmentRatings) + .Where(static r => r.Outcome != null) + .GroupBy(static r => r.Outcome.Id); var assignments = new List(); @@ -79,7 +97,8 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM modules.Add(module); } - var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(o => o.Id); + var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); + var outcomes = module.KpiMatrix.Outcomes.ToList(); foreach (var outcomeGroup in outcomeGroups) { @@ -92,31 +111,33 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, GetGradeStatus(0, outcome.MasteryPoints)); moduleOutcomes[outcomeId] = kpiMatrixOutcome; - module.KpiMatrix.Outcomes.Add(kpiMatrixOutcome); + + outcomes.Add(kpiMatrixOutcome); } - var outcomeAssessments = outcomeGroup.Select(assessmentRating => + var outcomeAssessments = outcomeGroup.Select(static assessmentRating => new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); + var outcomeAssementList = outcomeAssessments.ToList(); if (assignment == null) { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments.ToList())); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssementList)); } else { assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessments).ToList(); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssementList).ToList(); assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); } } var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(module.KpiMatrix.Outcomes, newAssignments)); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); modules[modules.IndexOf(module)] = newModule; } - return new KpiMatrixProfile(modules); + return new KpiMatrixCollection(modules); } } \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponentWordConverter.cs b/Epsilon/Component/KpiMatrixComponentWordConverter.cs deleted file mode 100644 index b64dc1ae..00000000 --- a/Epsilon/Component/KpiMatrixComponentWordConverter.cs +++ /dev/null @@ -1,128 +0,0 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using System.Linq; -using System.Threading.Tasks; - -namespace Epsilon.Component -{ - public class KpiMatrixComponentWordConverter : ComponentConverter - { - public override Task Convert(KpiMatrixProfile component) - { - var body = new Body(); - - foreach (var module in component.KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth() { Width = "0", Type = TableWidthUnitValues.Auto })); - - // Calculate the header row height based on the longest assignment name. - int headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) - { - headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight - { Val = (UInt32Value)(uint)headerRowHeight })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) - { - var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(o => o.Id)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(outcome.Title))))); - - // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - if (outcomeAssignment != null) - { - var fillColor = outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF" - }; - cell.FirstChild.Append(new Shading() { Fill = fillColor }); - } - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); - } - - body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break() { Type = BreakValues.Page }))); - - } - - return Task.FromResult(body); - } - - private TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) - { - var cell = new TableCell(); - var cellProperties = new TableCellProperties(); - var borders = new TableCellBorders( - new LeftBorder() { Val = BorderValues.Single }, - new RightBorder() { Val = BorderValues.Single }, - new TopBorder() { Val = BorderValues.Single }, - new BottomBorder() { Val = BorderValues.Single }); - - if (elements != null) - { - foreach (var element in elements) - { - cell.Append(element); - } - } - - if (width != null) - { - cellProperties.Append(new TableCellWidth { Type = TableWidthUnitValues.Dxa, Width = width }); - } - cellProperties.Append(borders); - cell.PrependChild(cellProperties); - - return cell; - } - - } -} \ No newline at end of file From 230ffb9fd42abbed8cdd1db343d883508940adf0 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Thu, 11 May 2023 11:07:55 +0200 Subject: [PATCH 07/72] Fixed MR requests --- Epsilon/Component/KpiMatrixComponentFetcher.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 960d0340..cbe43651 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -10,8 +10,8 @@ public class KpiMatrixComponentFetcher : ComponentFetcher { private const string GetUserKpiMatrixOutcomes = @" query GetUserKpiMatrixOutcomes { - course(id: $courseId) { - submissionsConnection { + allCourses { + submissionsConnection(studentIds: $studentIds) { nodes { assignment { name @@ -51,9 +51,8 @@ IConfiguration configuration public override async Task Fetch() { - var courseId = _configuration["Canvas:CourseId"]; - - var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}", StringComparison.InvariantCultureIgnoreCase); + var studentId = _configuration["Canvas:StudentId"]; + var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCultureIgnoreCase); var outcomes = await _graphQlService.Query(outcomesQuery); return ConvertToComponent(outcomes); @@ -120,15 +119,15 @@ private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); - var outcomeAssementList = outcomeAssessments.ToList(); + var outcomeAssessmentsList = outcomeAssessments.ToList(); if (assignment == null) { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssementList)); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); } else { assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssementList).ToList(); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); } } From c5a8eb2427d5a0f5f65b5412b73ac9e3c7857a00 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Mon, 15 May 2023 13:53:23 +0200 Subject: [PATCH 08/72] Changed course to allcourses --- .../Component/KpiMatrixComponentFetcher.cs | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index cbe43651..6acf52ff 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -72,69 +72,72 @@ private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse { var modules = new List(); - foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + foreach (var course in getUserKpiMatrixOutcomes.Data?.Courses) { - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) + foreach (var node in course.SubmissionsConnection.Nodes) { - continue; - } - - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(static n => n.AssessmentRatings != null) - .SelectMany(static n => n.AssessmentRatings) - .Where(static r => r.Outcome != null) - .GroupBy(static r => r.Outcome.Id); + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) + { + continue; + } - var assignments = new List(); + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(static n => n.AssessmentRatings != null) + .SelectMany(static n => n.AssessmentRatings) + .Where(static r => r.Outcome != null) + .GroupBy(static r => r.Outcome.Id); - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, - new KpiMatrix(new List(), new List())); - modules.Add(module); - } + var assignments = new List(); - var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); - var outcomes = module.KpiMatrix.Outcomes.ToList(); + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, + new KpiMatrix(new List(), new List())); + modules.Add(module); + } - foreach (var outcomeGroup in outcomeGroups) - { - var outcomeId = outcomeGroup.Key; + var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); + var outcomes = module.KpiMatrix.Outcomes.ToList(); - // Add outcome to moduleOutcomes if it doesn't exist - if (!moduleOutcomes.ContainsKey(outcomeId)) + foreach (var outcomeGroup in outcomeGroups) { - var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; - var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, - GetGradeStatus(0, outcome.MasteryPoints)); - moduleOutcomes[outcomeId] = kpiMatrixOutcome; + var outcomeId = outcomeGroup.Key; - outcomes.Add(kpiMatrixOutcome); - } + // Add outcome to moduleOutcomes if it doesn't exist + if (!moduleOutcomes.ContainsKey(outcomeId)) + { + var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; + var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, + GetGradeStatus(0, outcome.MasteryPoints)); + moduleOutcomes[outcomeId] = kpiMatrixOutcome; - var outcomeAssessments = outcomeGroup.Select(static assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); + outcomes.Add(kpiMatrixOutcome); + } - var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); - var outcomeAssessmentsList = outcomeAssessments.ToList(); - if (assignment == null) - { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); - } - else - { - assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + var outcomeAssessments = outcomeGroup.Select(static assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); + + var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); + var outcomeAssessmentsList = outcomeAssessments.ToList(); + if (assignment == null) + { + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); + } + else + { + assignments.Remove(assignment); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + } } - } - var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); - modules[modules.IndexOf(module)] = newModule; + var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); + modules[modules.IndexOf(module)] = newModule; + } } return new KpiMatrixCollection(modules); From 789536a8f1d2eb61c90b0353c8804f4778a74ad7 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 16 May 2023 22:32:38 +0200 Subject: [PATCH 09/72] File restructure --- .../Component/{ => KpiMatrixComponent}/KpiMatrix.cs | 2 +- .../Component/{ => KpiMatrixComponent}/KpiMatrixAssignment.cs | 2 +- .../Component/{ => KpiMatrixComponent}/KpiMatrixCollection.cs | 4 ++-- .../Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs | 2 +- .../Component/{ => KpiMatrixComponent}/KpiMatrixOutcome.cs | 2 +- Epsilon.Host.WebApi/Controllers/ComponentController.cs | 1 + Epsilon.Host.WebApi/Program.cs | 1 + Epsilon/Component/KpiMatrixComponentFetcher.cs | 3 ++- 8 files changed, 10 insertions(+), 7 deletions(-) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrix.cs (65%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixAssignment.cs (61%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixCollection.cs (97%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs (55%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixOutcome.cs (68%) diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrix.cs similarity index 65% rename from Epsilon.Abstractions/Component/KpiMatrix.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrix.cs index 8f5b15a5..cb9b8bdc 100644 --- a/Epsilon.Abstractions/Component/KpiMatrix.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrix.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrix( IEnumerable Outcomes, diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixAssignment.cs similarity index 61% rename from Epsilon.Abstractions/Component/KpiMatrixAssignment.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixAssignment.cs index 625815fa..0364a2be 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixAssignment.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixAssignment( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs similarity index 97% rename from Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index d1daafec..24ef9435 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -2,7 +2,7 @@ using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Canvas.Abstractions.Model; -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent { @@ -48,7 +48,7 @@ public OpenXmlElement ToWord() var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection { - Val = TextDirectionValues.BottomToTopLeftToRight, + Val = TextDirectionValues.TopToBottomRightToLeft, }); cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); diff --git a/Epsilon.Abstractions/Component/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs similarity index 55% rename from Epsilon.Abstractions/Component/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs index 659c932e..38f92501 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs similarity index 68% rename from Epsilon.Abstractions/Component/KpiMatrixOutcome.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs index 1c6c125c..75e3de72 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs @@ -1,6 +1,6 @@ using Epsilon.Canvas.Abstractions.Model; -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixOutcome( int Id, string Title, diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index 773f0896..31ab792e 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Microsoft.AspNetCore.Mvc; namespace Epsilon.Host.WebApi.Controllers; diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index ba4345a1..2297f167 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Canvas; using Epsilon.Component; diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 6acf52ff..4084abe8 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; @@ -83,7 +84,7 @@ private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse } var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(static n => n.AssessmentRatings != null) + .Where(static n => n.AssessmentRatings != null && n.AssessmentRatings.Count > 0) .SelectMany(static n => n.AssessmentRatings) .Where(static r => r.Outcome != null) .GroupBy(static r => r.Outcome.Id); From c0f1b3d042706e0bda3d8d5ca8ce288f10ffe1ca Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:46:41 +0200 Subject: [PATCH 10/72] Revised coding structure. Assignments that are not yest graded are also now included --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 73 ++++---- .../KpiMatrixComponent/KpiMatrixModule.cs | 6 - .../Model/GraphQl/Assignment.cs | 3 +- .../Model/GraphQl/Criteria.cs | 7 + .../Model/GraphQl/Rubric.cs | 8 + .../SubmissionsHistoriesConnectionNode.cs | 2 + .../CompetenceProfileComponentFetcher.cs | 67 ++++---- .../Component/KpiMatrixComponentFetcher.cs | 157 +++++++++--------- 8 files changed, 169 insertions(+), 154 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs create mode 100644 Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs create mode 100644 Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index 24ef9435..e3f41a50 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -2,75 +2,79 @@ using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Canvas.Abstractions.Model; +// using Epsilon.Canvas.Abstractions.Model; + namespace Epsilon.Abstractions.Component.KpiMatrixComponent; -public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : IEpsilonWordComponent { public OpenXmlElement ToWord() { var body = new Body(); - - foreach (var module in KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. + // Create a table, with rows for the outcomes and columns for the assignments. var table = new Table(); - + // Set table properties for formatting. table.AppendChild(new TableProperties( new TableWidth { Width = "0", Type = TableWidthUnitValues.Auto, })); - + // Calculate the header row height based on the longest assignment name. var headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) + if (KpiMatrixAssignments.Any()) { - headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + headerRowHeight = KpiMatrixAssignments.Max(static a => a.Name.Length) * 111; } - + // Create the table header row. var headerRow = new TableRow(); headerRow.AppendChild(new TableRowProperties(new TableRowHeight { Val = (UInt32Value)(uint)headerRowHeight, })); - + // Empty top-left cell. headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) + + foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) { var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection { Val = TextDirectionValues.TopToBottomRightToLeft, }); - + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); headerRow.AppendChild(cell); } - + table.AppendChild(headerRow); - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + var listOfOutcomes = new Dictionary(); + foreach (var assignment in KpiMatrixAssignments) + { + foreach (var outcome in assignment.Outcomes) + { + listOfOutcomes.TryAdd(outcome.Id, outcome); + } + } + + // Add the outcome rows. + foreach (var outcome in listOfOutcomes.OrderByDescending(static o => o.Value.Title)) { var row = new TableRow(); - + // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); - + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); + // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) + foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); var cell = CreateTableCellWithBorders("100"); - + // Set cell color based on GradeStatus. if (outcomeAssignment != null) { @@ -86,21 +90,20 @@ public OpenXmlElement ToWord() Fill = fillColor, }); } - + // Add an empty text element since we're using color instead of text. cell.Append(new Paragraph(new Run(new Text("")))); row.AppendChild(cell); } - + table.AppendChild(row); } - + body.AppendChild(table); body.AppendChild(new Paragraph(new Run(new Break { Type = BreakValues.Page, }))); - } return body; } @@ -126,12 +129,12 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm { Val = BorderValues.Single, }); - + foreach (var element in elements) { cell.Append(element); } - + if (width != null) { cellProperties.Append(new TableCellWidth @@ -139,10 +142,10 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm Type = TableWidthUnitValues.Dxa, Width = width, }); } - + cellProperties.Append(borders); cell.PrependChild(cellProperties); - + return cell; } } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs deleted file mode 100644 index 38f92501..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - -public record KpiMatrixModule( - string Name, - KpiMatrix KpiMatrix -); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs index 266d23da..ebc20383 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Assignment( [property: JsonPropertyName("name")] string? Name, - [property: JsonPropertyName("modules")] IEnumerable? Modules + [property: JsonPropertyName("modules")] IEnumerable? Modules , + [property: JsonPropertyName("rubric")] Rubric? Rubric ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs new file mode 100644 index 00000000..38497037 --- /dev/null +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; + +namespace Epsilon.Canvas.Abstractions.Model.GraphQl; + +public record Criteria( + [property: JsonPropertyName("outcome")] Outcome? Outcome + ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs new file mode 100644 index 00000000..5517c745 --- /dev/null +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Epsilon.Canvas.Abstractions.Model.GraphQl; + +public record Rubric( + [property: JsonPropertyName("criteria")] IEnumerable? Criteria + + ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs index 7c6ed535..bf875031 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs @@ -4,5 +4,7 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record SubmissionsHistoriesConnectionNode( [property: JsonPropertyName("attempt")] int? Attempt, + [property: JsonPropertyName("submittedAt")] DateTime? SubmittedAt, + [property: JsonPropertyName("assignment")] Assignment? Assignment, [property: JsonPropertyName("rubricAssessmentsConnection")] RubricAssessmentsConnection? RubricAssessments ); \ No newline at end of file diff --git a/Epsilon/Component/CompetenceProfileComponentFetcher.cs b/Epsilon/Component/CompetenceProfileComponentFetcher.cs index 36efabdd..99269579 100644 --- a/Epsilon/Component/CompetenceProfileComponentFetcher.cs +++ b/Epsilon/Component/CompetenceProfileComponentFetcher.cs @@ -30,13 +30,13 @@ query MyQuery { } } attempt + submittedAt } } postedAt } } } - } "; private readonly IConfiguration _configuration; @@ -76,43 +76,46 @@ IEnumerable enrollmentTerms var taskResults = new List(); var professionalResults = new List(); - foreach (var course in queryResponse.Data.Courses) + if (queryResponse.Data != null) { - foreach (var submissionsConnection in course.SubmissionsConnection.Nodes.Where(static s => s.PostedAt != null)) + foreach (var course in queryResponse.Data.Courses) { - var submission = submissionsConnection.SubmissionsHistories.Nodes - .Where(static h => h.RubricAssessments.Nodes.Any()) - .MaxBy(static h => h.Attempt); - - if (submission != null) + foreach (var submissionsConnection in course.SubmissionsConnection.Nodes) { - var rubricAssessments = submission.RubricAssessments.Nodes; + var submission = submissionsConnection.SubmissionsHistories.Nodes + .Where(static h => h.RubricAssessments.Nodes.Any()) + .MaxBy(static h => h.Attempt); - foreach (var assessmentRating in rubricAssessments.SelectMany(static rubricAssessment => rubricAssessment.AssessmentRatings.Where(static ar => - ar is { Points: not null, Criterion.MasteryPoints: not null, Criterion.Outcome: not null, } && ar.Points >= ar.Criterion.MasteryPoints))) + if (submission != null) { - if (FhictConstants.ProfessionalTasks.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalTask)) - { - taskResults.Add( - new ProfessionalTaskResult( - professionalTask.Layer, - professionalTask.Activity, - professionalTask.MasteryLevel, - assessmentRating.Points!.Value, - submissionsConnection.PostedAt!.Value - ) - ); - } - else if (FhictConstants.ProfessionalSkills.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalSkill)) + var rubricAssessments = submission.RubricAssessments.Nodes; + + foreach (var assessmentRating in rubricAssessments.SelectMany(static rubricAssessment => rubricAssessment.AssessmentRatings.Where(static ar => + ar is { Points: not null, Criterion.MasteryPoints: not null, Criterion.Outcome: not null, } && ar.Points >= ar.Criterion.MasteryPoints))) { - professionalResults.Add( - new ProfessionalSkillResult( - professionalSkill.Skill, - professionalSkill.MasteryLevel, - assessmentRating.Points!.Value, - submissionsConnection.PostedAt!.Value - ) - ); + if (FhictConstants.ProfessionalTasks.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalTask)) + { + taskResults.Add( + new ProfessionalTaskResult( + professionalTask.Layer, + professionalTask.Activity, + professionalTask.MasteryLevel, + assessmentRating.Points!.Value, + new DateTime() + ) + ); + } + else if (FhictConstants.ProfessionalSkills.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalSkill)) + { + professionalResults.Add( + new ProfessionalSkillResult( + professionalSkill.Skill, + professionalSkill.MasteryLevel, + assessmentRating.Points!.Value, + new DateTime() + ) + ); + } } } } diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 4084abe8..59ce77a6 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -10,32 +10,47 @@ namespace Epsilon.Component; public class KpiMatrixComponentFetcher : ComponentFetcher { private const string GetUserKpiMatrixOutcomes = @" - query GetUserKpiMatrixOutcomes { +query GetUserKpiMatrixOutcomes { allCourses { submissionsConnection(studentIds: $studentIds) { + nodes { + submissionHistoriesConnection { + nodes { + rubricAssessmentsConnection { nodes { - assignment { - name - modules { - name + assessmentRatings { + criterion { + outcome { + _id + title + } + masteryPoints } + points } - rubricAssessmentsConnection { - nodes { - assessmentRatings { - points - outcome { - _id - title - masteryPoints - } - } + } + } + attempt + submittedAt + assignment { + name + rubric { + criteria { + outcome { + title + _id +masteryPoints } } } } } } + postedAt + } + } + } +} "; private readonly IConfiguration _configuration; @@ -55,11 +70,10 @@ public override async Task Fetch() var studentId = _configuration["Canvas:StudentId"]; var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCultureIgnoreCase); var outcomes = await _graphQlService.Query(outcomesQuery); - - return ConvertToComponent(outcomes); + return ConvertToComponent(outcomes, new DateTime(2023, 2, 1), DateTime.Now); } - private static GradeStatus GetGradeStatus(double? points, double mastery) + private static GradeStatus GetGradeStatus(double? points, double? mastery) { return points != null ? points >= mastery @@ -69,78 +83,61 @@ private static GradeStatus GetGradeStatus(double? points, double mastery) } - private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse getUserKpiMatrixOutcomes) + private static KpiMatrixCollection ConvertToComponent( + CanvasGraphQlQueryResponse queryResponse, + DateTime startAt, + DateTime endAt + ) { - var modules = new List(); - - foreach (var course in getUserKpiMatrixOutcomes.Data?.Courses) + var assignments = new List(); + foreach (var course in queryResponse.Data!.Courses!) { - foreach (var node in course.SubmissionsConnection.Nodes) + foreach (var submissionsConnection in course.SubmissionsConnection!.Nodes) { - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) - { - continue; - } - - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(static n => n.AssessmentRatings != null && n.AssessmentRatings.Count > 0) - .SelectMany(static n => n.AssessmentRatings) - .Where(static r => r.Outcome != null) - .GroupBy(static r => r.Outcome.Id); - - var assignments = new List(); + var submission = submissionsConnection.SubmissionsHistories.Nodes + .Where(sub => sub.SubmittedAt > startAt && sub.SubmittedAt < endAt) + .MaxBy(static h => h.Attempt); - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) + if (submission != null) { - module = new KpiMatrixModule(moduleName, - new KpiMatrix(new List(), new List())); - modules.Add(module); - } - - var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); - var outcomes = module.KpiMatrix.Outcomes.ToList(); - - foreach (var outcomeGroup in outcomeGroups) - { - var outcomeId = outcomeGroup.Key; - - // Add outcome to moduleOutcomes if it doesn't exist - if (!moduleOutcomes.ContainsKey(outcomeId)) + if (submission.Assignment?.Rubric != null) { - var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; - var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, - GetGradeStatus(0, outcome.MasteryPoints)); - moduleOutcomes[outcomeId] = kpiMatrixOutcome; - - outcomes.Add(kpiMatrixOutcome); - } - - var outcomeAssessments = outcomeGroup.Select(static assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); - - var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); - var outcomeAssessmentsList = outcomeAssessments.ToList(); - if (assignment == null) - { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); - } - else - { - assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + var rubricAssessments = submission.Assignment.Rubric.Criteria; + var kpiMatrixOutcomes = new List(); + if (submission.Assignment.Rubric.Criteria != null) + { + foreach (var criteria in submission.Assignment.Rubric.Criteria) + { + if (criteria.Outcome != null) + { + if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask)) + { + kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, + GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + ?.Points + , criteria.Outcome.MasteryPoints))); + } + else if (FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) + { + kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, + GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + ?.Points + , criteria.Outcome.MasteryPoints))); + } + } + } + } + + if (kpiMatrixOutcomes.Count > 0 && submission.Assignment.Name != null) + { + var assignment = new KpiMatrixAssignment(submission.Assignment.Name, kpiMatrixOutcomes); + assignments.Add(assignment); + } } } - - var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); - modules[modules.IndexOf(module)] = newModule; } } - return new KpiMatrixCollection(modules); + return new KpiMatrixCollection(assignments); } } \ No newline at end of file From 48e5038300efdff58b060e78362fb603994f2624 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 21:12:24 +0200 Subject: [PATCH 11/72] Merged files in new structure --- .../Component/KpiMatrixComponent/KpiMatrixCollection.cs | 2 +- Epsilon.Host.WebApi/Program.cs | 2 ++ Epsilon/Component/KpiMatrixComponentFetcher.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index e3f41a50..d69ab1a2 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -6,7 +6,7 @@ namespace Epsilon.Abstractions.Component.KpiMatrixComponent; -public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : IEpsilonWordComponent +public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent { public OpenXmlElement ToWord() { diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index cb8c9bb3..551098cc 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Abstractions.Service; using Epsilon.Canvas; using Epsilon.Component; @@ -30,6 +31,7 @@ builder.Services.AddScoped(); builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); var app = builder.Build(); diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 59ce77a6..1a3638ae 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -7,7 +7,7 @@ namespace Epsilon.Component; -public class KpiMatrixComponentFetcher : ComponentFetcher +public class KpiMatrixComponentFetcher : CompetenceComponentFetcher { private const string GetUserKpiMatrixOutcomes = @" query GetUserKpiMatrixOutcomes { From d5f3eb71a02381b7d440575e5dd749ff3ac54bb4 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 22:21:45 +0200 Subject: [PATCH 12/72] Colored rows --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 45 ++++++++++++------- .../Component/KpiMatrixComponentFetcher.cs | 4 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index d69ab1a2..bf1dd8ca 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,6 +1,7 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Canvas.Abstractions.Model; +using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; // using Epsilon.Canvas.Abstractions.Model; @@ -13,6 +14,8 @@ public OpenXmlElement ToWord() var body = new Body(); // Create a table, with rows for the outcomes and columns for the assignments. var table = new Table(); + + var assignments = KpiMatrixAssignments.OrderBy(static ass => ass.Name).ToList(); // Set table properties for formatting. table.AppendChild(new TableProperties( @@ -25,7 +28,7 @@ public OpenXmlElement ToWord() var headerRowHeight = 0; if (KpiMatrixAssignments.Any()) { - headerRowHeight = KpiMatrixAssignments.Max(static a => a.Name.Length) * 111; + headerRowHeight = assignments.Max(static a => a.Name.Length) * 111; } // Create the table header row. @@ -38,15 +41,21 @@ public OpenXmlElement ToWord() // Empty top-left cell. headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) + foreach (var assignment in assignments) { var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection { - Val = TextDirectionValues.TopToBottomRightToLeft, + Val = TextDirectionValues.TopToBottomLeftToRightRotated, }); cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + cell.FirstChild.Append(new Shading + { + Fill = assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3", + }); headerRow.AppendChild(cell); } @@ -70,26 +79,34 @@ public OpenXmlElement ToWord() row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); // Add the assignment cells. - foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) + foreach (var assignment in assignments) { var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); var cell = CreateTableCellWithBorders("100"); - + // Set cell color based on GradeStatus. + var fillColor = ""; if (outcomeAssignment != null) { - var fillColor = outcomeAssignment.GradeStatus switch + fillColor = outcomeAssignment.GradeStatus switch { GradeStatus.Approved => "44F656", GradeStatus.Insufficient => "FA1818", GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF", + _ => null, }; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); + + } + else + { + fillColor = assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3"; } + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); // Add an empty text element since we're using color instead of text. cell.Append(new Paragraph(new Run(new Text("")))); @@ -100,12 +117,8 @@ public OpenXmlElement ToWord() } body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break - { - Type = BreakValues.Page, - }))); - return body; + return body; } private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 1a3638ae..9f689504 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -104,9 +104,9 @@ DateTime endAt { var rubricAssessments = submission.Assignment.Rubric.Criteria; var kpiMatrixOutcomes = new List(); - if (submission.Assignment.Rubric.Criteria != null) + if (rubricAssessments != null) { - foreach (var criteria in submission.Assignment.Rubric.Criteria) + foreach (var criteria in rubricAssessments) { if (criteria.Outcome != null) { From 071c312ab16de547a6547eafb48f7dce828b02d5 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 22:23:53 +0200 Subject: [PATCH 13/72] "Simplified" --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index bf1dd8ca..04ab85e7 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -85,25 +85,18 @@ public OpenXmlElement ToWord() var cell = CreateTableCellWithBorders("100"); // Set cell color based on GradeStatus. - var fillColor = ""; - if (outcomeAssignment != null) - { - fillColor = outcomeAssignment.GradeStatus switch + var fillColor = outcomeAssignment != null + ? outcomeAssignment.GradeStatus switch { GradeStatus.Approved => "44F656", GradeStatus.Insufficient => "FA1818", GradeStatus.NotGraded => "FAFF00", _ => null, - }; - - } - else - { - fillColor = assignments.IndexOf(assignment) % 2 == 0 + } + : assignments.IndexOf(assignment) % 2 == 0 ? "FFFFFF" : "d3d3d3"; - } - cell.FirstChild.Append(new Shading + cell.FirstChild.Append(new Shading { Fill = fillColor, }); From 8680552a02b6e2781cb3134ceae438c968146513 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Mon, 22 May 2023 14:49:03 +0200 Subject: [PATCH 14/72] Move HttpService class to Canvas project --- Epsilon.Canvas/Epsilon.Canvas.csproj | 1 - {Epsilon.Abstractions => Epsilon.Canvas}/Http/HttpService.cs | 2 +- Epsilon.Canvas/Service/AccountHttpService.cs | 2 +- Epsilon.Canvas/Service/AssignmentHttpService.cs | 2 +- Epsilon.Canvas/Service/FileHttpService.cs | 4 ++-- Epsilon.Canvas/Service/GraphQlHttpService.cs | 2 +- Epsilon.Canvas/Service/ModuleHttpService.cs | 2 +- Epsilon.Canvas/Service/OutcomeHttpService.cs | 2 +- Epsilon.Canvas/Service/PageHttpService.cs | 2 +- Epsilon.Canvas/Service/PaginatorHttpService.cs | 2 +- Epsilon.Canvas/Service/SubmissionHttpService.cs | 2 +- 11 files changed, 11 insertions(+), 12 deletions(-) rename {Epsilon.Abstractions => Epsilon.Canvas}/Http/HttpService.cs (80%) diff --git a/Epsilon.Canvas/Epsilon.Canvas.csproj b/Epsilon.Canvas/Epsilon.Canvas.csproj index 573ba1ce..1914a099 100644 --- a/Epsilon.Canvas/Epsilon.Canvas.csproj +++ b/Epsilon.Canvas/Epsilon.Canvas.csproj @@ -10,7 +10,6 @@ - diff --git a/Epsilon.Abstractions/Http/HttpService.cs b/Epsilon.Canvas/Http/HttpService.cs similarity index 80% rename from Epsilon.Abstractions/Http/HttpService.cs rename to Epsilon.Canvas/Http/HttpService.cs index fc445bd1..07ef9665 100644 --- a/Epsilon.Abstractions/Http/HttpService.cs +++ b/Epsilon.Canvas/Http/HttpService.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Http; +namespace Epsilon.Canvas.Http; public abstract class HttpService { diff --git a/Epsilon.Canvas/Service/AccountHttpService.cs b/Epsilon.Canvas/Service/AccountHttpService.cs index 4fc1315c..c568c1bb 100644 --- a/Epsilon.Canvas/Service/AccountHttpService.cs +++ b/Epsilon.Canvas/Service/AccountHttpService.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/AssignmentHttpService.cs b/Epsilon.Canvas/Service/AssignmentHttpService.cs index 9edf3843..8468c100 100644 --- a/Epsilon.Canvas/Service/AssignmentHttpService.cs +++ b/Epsilon.Canvas/Service/AssignmentHttpService.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs index 98cfca94..5bbbc8e2 100644 --- a/Epsilon.Canvas/Service/FileHttpService.cs +++ b/Epsilon.Canvas/Service/FileHttpService.cs @@ -1,5 +1,5 @@ -using Epsilon.Abstractions.Http; -using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/GraphQlHttpService.cs b/Epsilon.Canvas/Service/GraphQlHttpService.cs index daf0f400..1e791318 100644 --- a/Epsilon.Canvas/Service/GraphQlHttpService.cs +++ b/Epsilon.Canvas/Service/GraphQlHttpService.cs @@ -1,6 +1,6 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/ModuleHttpService.cs b/Epsilon.Canvas/Service/ModuleHttpService.cs index 0e39e84e..7253ba77 100644 --- a/Epsilon.Canvas/Service/ModuleHttpService.cs +++ b/Epsilon.Canvas/Service/ModuleHttpService.cs @@ -1,8 +1,8 @@ using System.Net.Http.Json; using System.Text; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/OutcomeHttpService.cs b/Epsilon.Canvas/Service/OutcomeHttpService.cs index 6ca33a1a..86eef8e5 100644 --- a/Epsilon.Canvas/Service/OutcomeHttpService.cs +++ b/Epsilon.Canvas/Service/OutcomeHttpService.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs index df99ab98..00e2aebe 100644 --- a/Epsilon.Canvas/Service/PageHttpService.cs +++ b/Epsilon.Canvas/Service/PageHttpService.cs @@ -1,8 +1,8 @@ using System.Net; using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/PaginatorHttpService.cs b/Epsilon.Canvas/Service/PaginatorHttpService.cs index bda57eb9..85082257 100644 --- a/Epsilon.Canvas/Service/PaginatorHttpService.cs +++ b/Epsilon.Canvas/Service/PaginatorHttpService.cs @@ -1,8 +1,8 @@ using System.Net.Http.Json; using System.Web; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Converter; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/SubmissionHttpService.cs b/Epsilon.Canvas/Service/SubmissionHttpService.cs index fa35f7c7..39ebf11e 100644 --- a/Epsilon.Canvas/Service/SubmissionHttpService.cs +++ b/Epsilon.Canvas/Service/SubmissionHttpService.cs @@ -1,7 +1,7 @@ using System.Text; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; From e60b1d2def96291326e7ab3198b225184885ad6b Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 10:02:51 +0200 Subject: [PATCH 15/72] Added legend and changed "GradeStatus" structure --- .../KpiMatrixComponent/GradeStatus.cs | 6 + .../KpiMatrixComponent/KpiMatrixCollection.cs | 204 ++++++++++-------- .../KpiMatrixComponent/KpiMatrixConstants.cs | 18 ++ .../KpiMatrixComponent/KpiMatrixOutcome.cs | 1 - .../Model/GradeStatus.cs | 8 - .../Component/KpiMatrixComponentFetcher.cs | 7 +- 6 files changed, 139 insertions(+), 105 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs delete mode 100644 Epsilon.Canvas.Abstractions/Model/GradeStatus.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs new file mode 100644 index 00000000..0e12f48f --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; + +public record GradeStatus( + string Status, + string Color +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index 04ab85e7..ef272696 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,6 +1,5 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Canvas.Abstractions.Model; using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; // using Epsilon.Canvas.Abstractions.Model; @@ -9,109 +8,130 @@ namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent { + + private static OpenXmlElement GetLegend() + { + var table = new Table(); + foreach (var status in KpiMatrixConstants.GradeStatus) + { + var row = new TableRow(); + var cellName = CreateTableCellWithBorders("200"); + cellName.Append(new Paragraph(new Run(new Text(status.Value.Status)))); + + var cellValue = CreateTableCellWithBorders("200"); + cellValue.Append(new Paragraph(new Run(new Text("")))); + cellValue.FirstChild.Append(new Shading + { + Fill = status.Value.Color, + }); + row.AppendChild(cellName); + row.AppendChild(cellValue); + table.AppendChild(row); + } + + return table; + } + public OpenXmlElement ToWord() { var body = new Body(); // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); + var table = new Table(); - var assignments = KpiMatrixAssignments.OrderBy(static ass => ass.Name).ToList(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth - { - Width = "0", Type = TableWidthUnitValues.Auto, - })); - - // Calculate the header row height based on the longest assignment name. - var headerRowHeight = 0; - if (KpiMatrixAssignments.Any()) - { - headerRowHeight = assignments.Max(static a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight + var assignments = KpiMatrixAssignments.OrderBy(static ass => ass.Name).ToList(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth { - Val = (UInt32Value)(uint)headerRowHeight, + Width = "0", Type = TableWidthUnitValues.Auto, })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (KpiMatrixAssignments.Any()) + { + headerRowHeight = assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.TopToBottomLeftToRightRotated, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + cell.FirstChild.Append(new Shading + { + Fill = assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3", + }); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + var listOfOutcomes = new Dictionary(); + foreach (var assignment in KpiMatrixAssignments) + { + foreach (var outcome in assignment.Outcomes) + { + listOfOutcomes.TryAdd(outcome.Id, outcome); + } + } + + // Add the outcome rows. + foreach (var outcome in listOfOutcomes.OrderByDescending(static o => o.Value.Title)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); + + // Add the assignment cells. foreach (var assignment in assignments) { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection - { - Val = TextDirectionValues.TopToBottomLeftToRightRotated, - }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + + // Set cell color based on GradeStatus. + var fillColor = outcomeAssignment != null + ? outcomeAssignment.GradeStatus.Color + //When no item is present give the cell alternating background color + : assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3"; cell.FirstChild.Append(new Shading { - Fill = assignments.IndexOf(assignment) % 2 == 0 - ? "FFFFFF" - : "d3d3d3", + Fill = fillColor, }); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - var listOfOutcomes = new Dictionary(); - foreach (var assignment in KpiMatrixAssignments) - { - foreach (var outcome in assignment.Outcomes) - { - listOfOutcomes.TryAdd(outcome.Id, outcome); - } - } - - // Add the outcome rows. - foreach (var outcome in listOfOutcomes.OrderByDescending(static o => o.Value.Title)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); - - // Add the assignment cells. - foreach (var assignment in assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - var fillColor = outcomeAssignment != null - ? outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => null, - } - : assignments.IndexOf(assignment) % 2 == 0 - ? "FFFFFF" - : "d3d3d3"; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); } - - body.AppendChild(table); - return body; + table.AppendChild(row); + } + + body.AppendChild(GetLegend()); + body.Append(new Paragraph(new Run(new Text("")))); + body.AppendChild(table); + + return body; } private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) @@ -135,12 +155,12 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm { Val = BorderValues.Single, }); - + foreach (var element in elements) { cell.Append(element); } - + if (width != null) { cellProperties.Append(new TableCellWidth @@ -148,10 +168,10 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm Type = TableWidthUnitValues.Dxa, Width = width, }); } - + cellProperties.Append(borders); cell.PrependChild(cellProperties); - + return cell; } } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs new file mode 100644 index 00000000..13a54cb1 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs @@ -0,0 +1,18 @@ +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; + +public static class KpiMatrixConstants +{ + + public static readonly IDictionary GradeStatus = new Dictionary + { + { + "Approved", new GradeStatus("Approved", "44F656") + }, + { + "Insufficient", new GradeStatus("Insufficient", "FA1818") + }, + { + "NotGraded", new GradeStatus("NotGraded", "FAFF00") + }, + }; +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs index 75e3de72..5a4d2894 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs @@ -1,4 +1,3 @@ -using Epsilon.Canvas.Abstractions.Model; namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixOutcome( diff --git a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs deleted file mode 100644 index 307d4dfc..00000000 --- a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Epsilon.Canvas.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded, -} \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 9f689504..bc6eb47f 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -1,6 +1,5 @@ using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Component.KpiMatrixComponent; -using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; @@ -77,9 +76,9 @@ private static GradeStatus GetGradeStatus(double? points, double? mastery) { return points != null ? points >= mastery - ? GradeStatus.Approved - : GradeStatus.Insufficient - : GradeStatus.NotGraded; + ? KpiMatrixConstants.GradeStatus["Approved"] + : KpiMatrixConstants.GradeStatus["Insufficient"] + : KpiMatrixConstants.GradeStatus["NotGraded"]; } From 0d21121f67911011d3d06706a8cff7c7e04e3c74 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Tue, 23 May 2023 11:25:16 +0200 Subject: [PATCH 16/72] Remove unused constants --- Epsilon/Constants.cs | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Epsilon/Constants.cs diff --git a/Epsilon/Constants.cs b/Epsilon/Constants.cs deleted file mode 100644 index 8998b4c2..00000000 --- a/Epsilon/Constants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Epsilon; - -public static class Constants -{ - public const string ProjectName = "Epsilon"; - public static readonly Uri RepositoryUri = new Uri("https://github.com/Typiqally/epsilon"); -} \ No newline at end of file From 6d0b42d7f3b1bde5085563d362b3f412c8d188f2 Mon Sep 17 00:00:00 2001 From: Koen Janssen <6256259+koen253janssen@users.noreply.github.com> Date: Tue, 23 May 2023 12:20:35 +0200 Subject: [PATCH 17/72] Modify persona page to accommodate new component architecture (#91) * Created PersonaPage component for the new architecture. * fix feedback sven * Cleanup code * Modify architecture to force adding Word components instead of creating them * Add using statement to ensure stream is disposed --------- Co-authored-by: Jelle Maas --- .../Component/CompetenceProfile.cs | 6 +- .../Component/ICompetenceWordComponent.cs | 4 +- .../Component/IWordDocumentBuilder.cs | 8 --- Epsilon.Abstractions/Component/PersonaPage.cs | 25 +++++++ Epsilon.Host.WebApi/Program.cs | 2 +- .../Component/PersonaPageComponentFetcher.cs | 66 +++++++++++++++++++ Epsilon/Component/WordDocumentBuilder.cs | 20 ------ Epsilon/Service/CompetenceDocumentService.cs | 15 +++-- 8 files changed, 105 insertions(+), 41 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/IWordDocumentBuilder.cs create mode 100644 Epsilon.Abstractions/Component/PersonaPage.cs create mode 100644 Epsilon/Component/PersonaPageComponentFetcher.cs delete mode 100644 Epsilon/Component/WordDocumentBuilder.cs diff --git a/Epsilon.Abstractions/Component/CompetenceProfile.cs b/Epsilon.Abstractions/Component/CompetenceProfile.cs index bd86014c..f62c2615 100644 --- a/Epsilon.Abstractions/Component/CompetenceProfile.cs +++ b/Epsilon.Abstractions/Component/CompetenceProfile.cs @@ -1,4 +1,4 @@ -using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Abstractions.Model; using Epsilon.Canvas.Abstractions.Model; @@ -15,7 +15,7 @@ public record CompetenceProfile( IEnumerable DecayingAveragesPerSkill ) : ICompetenceWordComponent { - public OpenXmlElement ToWord() + public void AddToWordDocument(MainDocumentPart mainDocumentPart) { // TODO: This is simply an example to show the capability of the component architecture var body = new Body(); @@ -31,6 +31,6 @@ public OpenXmlElement ToWord() ); } - return body; + mainDocumentPart.Document.AppendChild(body); } } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs b/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs index 6967cf26..29cfca03 100644 --- a/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs +++ b/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs @@ -1,8 +1,8 @@ -using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; namespace Epsilon.Abstractions.Component; public interface ICompetenceWordComponent : ICompetenceComponent { - public OpenXmlElement ToWord(); + public void AddToWordDocument(MainDocumentPart mainDocumentPart); } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/IWordDocumentBuilder.cs b/Epsilon.Abstractions/Component/IWordDocumentBuilder.cs deleted file mode 100644 index fe01dd1b..00000000 --- a/Epsilon.Abstractions/Component/IWordDocumentBuilder.cs +++ /dev/null @@ -1,8 +0,0 @@ -using DocumentFormat.OpenXml.Wordprocessing; - -namespace Epsilon.Abstractions.Component; - -public interface IWordDocumentBuilder -{ - public Document Build(IEnumerable components); -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/PersonaPage.cs b/Epsilon.Abstractions/Component/PersonaPage.cs new file mode 100644 index 00000000..a936bf6f --- /dev/null +++ b/Epsilon.Abstractions/Component/PersonaPage.cs @@ -0,0 +1,25 @@ +using System.Text; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; + +namespace Epsilon.Abstractions.Component; + +[CompetenceComponentName("persona")] +public record PersonaPage(string PersonaHtml) : ICompetenceWordComponent +{ + public void AddToWordDocument(MainDocumentPart mainDocumentPart) + { + var personaHtmlBuffer = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes($"{PersonaHtml}")).ToArray(); + using var personaHtmlStream = new MemoryStream(personaHtmlBuffer); + + var formatImportPart = mainDocumentPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.Html); + formatImportPart.FeedData(personaHtmlStream); + + mainDocumentPart.Document.AppendChild(new Body( + new AltChunk + { + Id = mainDocumentPart.GetIdOfPart(formatImportPart), + } + )); + } +} \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index cb8c9bb3..f27fec07 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -27,9 +27,9 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); var app = builder.Build(); diff --git a/Epsilon/Component/PersonaPageComponentFetcher.cs b/Epsilon/Component/PersonaPageComponentFetcher.cs new file mode 100644 index 00000000..87352755 --- /dev/null +++ b/Epsilon/Component/PersonaPageComponentFetcher.cs @@ -0,0 +1,66 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.Service; +using HtmlAgilityPack; +using Microsoft.Extensions.Options; + +namespace Epsilon.Component; + +public class PersonaPageComponentFetcher : CompetenceComponentFetcher +{ + private readonly IPageHttpService _pageHttpService; + private readonly IFileHttpService _fileHttpService; + private readonly CanvasSettings _canvasSettings; + + public PersonaPageComponentFetcher( + IPageHttpService pageHttpService, + IFileHttpService fileHttpService, + IOptions canvasSettings + ) + { + _pageHttpService = pageHttpService; + _fileHttpService = fileHttpService; + _canvasSettings = canvasSettings.Value; + } + + public override async Task Fetch() + { + var courseId = _canvasSettings.CourseId; + var personaHtml = await _pageHttpService.GetPageByName(courseId, "front_page"); + + var updatedPersonaHtml = await GetPersonaHtmlDocument(personaHtml); + + var personaPage = new PersonaPage(updatedPersonaHtml.Text); + + return personaPage; + } + + private async Task GetPersonaHtmlDocument(string htmlString) + { + var htmlDoc = new HtmlDocument(); + htmlDoc.LoadHtml(htmlString); + if (htmlDoc.DocumentNode.SelectNodes("//img") == null) + { + return htmlDoc; + } + + foreach (var node in htmlDoc.DocumentNode.SelectNodes("//img")) + { + var imageSrc = node + .SelectNodes("//img") + .First() + .Attributes["src"].Value; + + if (imageSrc != null) + { + var imageBytes = await _fileHttpService.GetFileByteArray(new Uri(imageSrc)); + var imageBase64 = Convert.ToBase64String(imageBytes.ToArray()); + + node.SetAttributeValue("src", $"data:image/jpeg;base64,{imageBase64}"); + } + } + + return htmlDoc; + } + +} \ No newline at end of file diff --git a/Epsilon/Component/WordDocumentBuilder.cs b/Epsilon/Component/WordDocumentBuilder.cs deleted file mode 100644 index b31a0a45..00000000 --- a/Epsilon/Component/WordDocumentBuilder.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Abstractions.Component; - -namespace Epsilon.Component; - -public class WordDocumentBuilder : IWordDocumentBuilder -{ - public Document Build(IEnumerable components) - { - var document = new Document(new Body()); - - foreach (var component in components) - { - var element = component.ToWord(); - document.Append(element); - } - - return document; - } -} \ No newline at end of file diff --git a/Epsilon/Service/CompetenceDocumentService.cs b/Epsilon/Service/CompetenceDocumentService.cs index 3ddfcd23..b724736c 100644 --- a/Epsilon/Service/CompetenceDocumentService.cs +++ b/Epsilon/Service/CompetenceDocumentService.cs @@ -1,5 +1,6 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Service; @@ -7,15 +8,10 @@ namespace Epsilon.Service; public class CompetenceDocumentService : ICompetenceDocumentService { - private readonly IWordDocumentBuilder _wordDocumentBuilder; private readonly ICompetenceComponentService _competenceComponentService; - public CompetenceDocumentService( - IWordDocumentBuilder wordDocumentBuilder, - ICompetenceComponentService competenceComponentService - ) + public CompetenceDocumentService(ICompetenceComponentService competenceComponentService) { - _wordDocumentBuilder = wordDocumentBuilder; _competenceComponentService = competenceComponentService; } @@ -27,7 +23,12 @@ public async Task WriteDocument(Stream stream) using var document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document); document.AddMainDocumentPart(); - document.MainDocumentPart!.Document = _wordDocumentBuilder.Build(components); + document.MainDocumentPart!.Document = new Document(); + + foreach (var competenceWordComponent in components) + { + competenceWordComponent.AddToWordDocument(document.MainDocumentPart); + } document.Save(); document.Close(); From 881e0356e731cbf29ce0dac5b04796cb208fc64a Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Mon, 22 May 2023 14:49:03 +0200 Subject: [PATCH 18/72] Move HttpService class to Canvas project --- Epsilon.Canvas/Epsilon.Canvas.csproj | 1 - {Epsilon.Abstractions => Epsilon.Canvas}/Http/HttpService.cs | 2 +- Epsilon.Canvas/Service/AccountHttpService.cs | 2 +- Epsilon.Canvas/Service/AssignmentHttpService.cs | 2 +- Epsilon.Canvas/Service/FileHttpService.cs | 4 ++-- Epsilon.Canvas/Service/GraphQlHttpService.cs | 2 +- Epsilon.Canvas/Service/ModuleHttpService.cs | 2 +- Epsilon.Canvas/Service/OutcomeHttpService.cs | 2 +- Epsilon.Canvas/Service/PageHttpService.cs | 2 +- Epsilon.Canvas/Service/PaginatorHttpService.cs | 2 +- Epsilon.Canvas/Service/SubmissionHttpService.cs | 2 +- 11 files changed, 11 insertions(+), 12 deletions(-) rename {Epsilon.Abstractions => Epsilon.Canvas}/Http/HttpService.cs (80%) diff --git a/Epsilon.Canvas/Epsilon.Canvas.csproj b/Epsilon.Canvas/Epsilon.Canvas.csproj index 573ba1ce..1914a099 100644 --- a/Epsilon.Canvas/Epsilon.Canvas.csproj +++ b/Epsilon.Canvas/Epsilon.Canvas.csproj @@ -10,7 +10,6 @@ - diff --git a/Epsilon.Abstractions/Http/HttpService.cs b/Epsilon.Canvas/Http/HttpService.cs similarity index 80% rename from Epsilon.Abstractions/Http/HttpService.cs rename to Epsilon.Canvas/Http/HttpService.cs index fc445bd1..07ef9665 100644 --- a/Epsilon.Abstractions/Http/HttpService.cs +++ b/Epsilon.Canvas/Http/HttpService.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Http; +namespace Epsilon.Canvas.Http; public abstract class HttpService { diff --git a/Epsilon.Canvas/Service/AccountHttpService.cs b/Epsilon.Canvas/Service/AccountHttpService.cs index 4fc1315c..c568c1bb 100644 --- a/Epsilon.Canvas/Service/AccountHttpService.cs +++ b/Epsilon.Canvas/Service/AccountHttpService.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/AssignmentHttpService.cs b/Epsilon.Canvas/Service/AssignmentHttpService.cs index 9edf3843..8468c100 100644 --- a/Epsilon.Canvas/Service/AssignmentHttpService.cs +++ b/Epsilon.Canvas/Service/AssignmentHttpService.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/FileHttpService.cs b/Epsilon.Canvas/Service/FileHttpService.cs index 98cfca94..5bbbc8e2 100644 --- a/Epsilon.Canvas/Service/FileHttpService.cs +++ b/Epsilon.Canvas/Service/FileHttpService.cs @@ -1,5 +1,5 @@ -using Epsilon.Abstractions.Http; -using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/GraphQlHttpService.cs b/Epsilon.Canvas/Service/GraphQlHttpService.cs index daf0f400..1e791318 100644 --- a/Epsilon.Canvas/Service/GraphQlHttpService.cs +++ b/Epsilon.Canvas/Service/GraphQlHttpService.cs @@ -1,6 +1,6 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/ModuleHttpService.cs b/Epsilon.Canvas/Service/ModuleHttpService.cs index 0e39e84e..7253ba77 100644 --- a/Epsilon.Canvas/Service/ModuleHttpService.cs +++ b/Epsilon.Canvas/Service/ModuleHttpService.cs @@ -1,8 +1,8 @@ using System.Net.Http.Json; using System.Text; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/OutcomeHttpService.cs b/Epsilon.Canvas/Service/OutcomeHttpService.cs index 6ca33a1a..86eef8e5 100644 --- a/Epsilon.Canvas/Service/OutcomeHttpService.cs +++ b/Epsilon.Canvas/Service/OutcomeHttpService.cs @@ -1,7 +1,7 @@ using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/PageHttpService.cs b/Epsilon.Canvas/Service/PageHttpService.cs index df99ab98..00e2aebe 100644 --- a/Epsilon.Canvas/Service/PageHttpService.cs +++ b/Epsilon.Canvas/Service/PageHttpService.cs @@ -1,8 +1,8 @@ using System.Net; using System.Net.Http.Json; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/PaginatorHttpService.cs b/Epsilon.Canvas/Service/PaginatorHttpService.cs index bda57eb9..85082257 100644 --- a/Epsilon.Canvas/Service/PaginatorHttpService.cs +++ b/Epsilon.Canvas/Service/PaginatorHttpService.cs @@ -1,8 +1,8 @@ using System.Net.Http.Json; using System.Web; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Converter; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; diff --git a/Epsilon.Canvas/Service/SubmissionHttpService.cs b/Epsilon.Canvas/Service/SubmissionHttpService.cs index fa35f7c7..39ebf11e 100644 --- a/Epsilon.Canvas/Service/SubmissionHttpService.cs +++ b/Epsilon.Canvas/Service/SubmissionHttpService.cs @@ -1,7 +1,7 @@ using System.Text; -using Epsilon.Abstractions.Http; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Service; +using Epsilon.Canvas.Http; namespace Epsilon.Canvas.Service; From 81bf1e1190668279c86d892f354c4e2434220740 Mon Sep 17 00:00:00 2001 From: Jelle Maas Date: Tue, 23 May 2023 11:25:16 +0200 Subject: [PATCH 19/72] Remove unused constants --- Epsilon/Constants.cs | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Epsilon/Constants.cs diff --git a/Epsilon/Constants.cs b/Epsilon/Constants.cs deleted file mode 100644 index 8998b4c2..00000000 --- a/Epsilon/Constants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Epsilon; - -public static class Constants -{ - public const string ProjectName = "Epsilon"; - public static readonly Uri RepositoryUri = new Uri("https://github.com/Typiqally/epsilon"); -} \ No newline at end of file From a6b30638fc9ba40d41263e74f73211a65590b737 Mon Sep 17 00:00:00 2001 From: Koen Janssen <6256259+koen253janssen@users.noreply.github.com> Date: Tue, 23 May 2023 12:20:35 +0200 Subject: [PATCH 20/72] Modify persona page to accommodate new component architecture (#91) * Created PersonaPage component for the new architecture. * fix feedback sven * Cleanup code * Modify architecture to force adding Word components instead of creating them * Add using statement to ensure stream is disposed --------- Co-authored-by: Jelle Maas --- .../Component/CompetenceProfile.cs | 6 +- .../Component/ICompetenceWordComponent.cs | 4 +- .../Component/IWordDocumentBuilder.cs | 8 --- Epsilon.Abstractions/Component/PersonaPage.cs | 25 +++++++ Epsilon.Host.WebApi/Program.cs | 4 +- .../Component/PersonaPageComponentFetcher.cs | 66 +++++++++++++++++++ Epsilon/Component/WordDocumentBuilder.cs | 20 ------ Epsilon/Service/CompetenceDocumentService.cs | 15 +++-- 8 files changed, 105 insertions(+), 43 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/IWordDocumentBuilder.cs create mode 100644 Epsilon.Abstractions/Component/PersonaPage.cs create mode 100644 Epsilon/Component/PersonaPageComponentFetcher.cs delete mode 100644 Epsilon/Component/WordDocumentBuilder.cs diff --git a/Epsilon.Abstractions/Component/CompetenceProfile.cs b/Epsilon.Abstractions/Component/CompetenceProfile.cs index bd86014c..f62c2615 100644 --- a/Epsilon.Abstractions/Component/CompetenceProfile.cs +++ b/Epsilon.Abstractions/Component/CompetenceProfile.cs @@ -1,4 +1,4 @@ -using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Abstractions.Model; using Epsilon.Canvas.Abstractions.Model; @@ -15,7 +15,7 @@ public record CompetenceProfile( IEnumerable DecayingAveragesPerSkill ) : ICompetenceWordComponent { - public OpenXmlElement ToWord() + public void AddToWordDocument(MainDocumentPart mainDocumentPart) { // TODO: This is simply an example to show the capability of the component architecture var body = new Body(); @@ -31,6 +31,6 @@ public OpenXmlElement ToWord() ); } - return body; + mainDocumentPart.Document.AppendChild(body); } } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs b/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs index 6967cf26..29cfca03 100644 --- a/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs +++ b/Epsilon.Abstractions/Component/ICompetenceWordComponent.cs @@ -1,8 +1,8 @@ -using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; namespace Epsilon.Abstractions.Component; public interface ICompetenceWordComponent : ICompetenceComponent { - public OpenXmlElement ToWord(); + public void AddToWordDocument(MainDocumentPart mainDocumentPart); } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/IWordDocumentBuilder.cs b/Epsilon.Abstractions/Component/IWordDocumentBuilder.cs deleted file mode 100644 index fe01dd1b..00000000 --- a/Epsilon.Abstractions/Component/IWordDocumentBuilder.cs +++ /dev/null @@ -1,8 +0,0 @@ -using DocumentFormat.OpenXml.Wordprocessing; - -namespace Epsilon.Abstractions.Component; - -public interface IWordDocumentBuilder -{ - public Document Build(IEnumerable components); -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/PersonaPage.cs b/Epsilon.Abstractions/Component/PersonaPage.cs new file mode 100644 index 00000000..a936bf6f --- /dev/null +++ b/Epsilon.Abstractions/Component/PersonaPage.cs @@ -0,0 +1,25 @@ +using System.Text; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; + +namespace Epsilon.Abstractions.Component; + +[CompetenceComponentName("persona")] +public record PersonaPage(string PersonaHtml) : ICompetenceWordComponent +{ + public void AddToWordDocument(MainDocumentPart mainDocumentPart) + { + var personaHtmlBuffer = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes($"{PersonaHtml}")).ToArray(); + using var personaHtmlStream = new MemoryStream(personaHtmlBuffer); + + var formatImportPart = mainDocumentPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.Html); + formatImportPart.FeedData(personaHtmlStream); + + mainDocumentPart.Document.AppendChild(new Body( + new AltChunk + { + Id = mainDocumentPart.GetIdOfPart(formatImportPart), + } + )); + } +} \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index 551098cc..f27fec07 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,5 +1,4 @@ using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Abstractions.Service; using Epsilon.Canvas; using Epsilon.Component; @@ -28,10 +27,9 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); builder.Services.AddComponentFetcher(); -builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); var app = builder.Build(); diff --git a/Epsilon/Component/PersonaPageComponentFetcher.cs b/Epsilon/Component/PersonaPageComponentFetcher.cs new file mode 100644 index 00000000..87352755 --- /dev/null +++ b/Epsilon/Component/PersonaPageComponentFetcher.cs @@ -0,0 +1,66 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.Service; +using HtmlAgilityPack; +using Microsoft.Extensions.Options; + +namespace Epsilon.Component; + +public class PersonaPageComponentFetcher : CompetenceComponentFetcher +{ + private readonly IPageHttpService _pageHttpService; + private readonly IFileHttpService _fileHttpService; + private readonly CanvasSettings _canvasSettings; + + public PersonaPageComponentFetcher( + IPageHttpService pageHttpService, + IFileHttpService fileHttpService, + IOptions canvasSettings + ) + { + _pageHttpService = pageHttpService; + _fileHttpService = fileHttpService; + _canvasSettings = canvasSettings.Value; + } + + public override async Task Fetch() + { + var courseId = _canvasSettings.CourseId; + var personaHtml = await _pageHttpService.GetPageByName(courseId, "front_page"); + + var updatedPersonaHtml = await GetPersonaHtmlDocument(personaHtml); + + var personaPage = new PersonaPage(updatedPersonaHtml.Text); + + return personaPage; + } + + private async Task GetPersonaHtmlDocument(string htmlString) + { + var htmlDoc = new HtmlDocument(); + htmlDoc.LoadHtml(htmlString); + if (htmlDoc.DocumentNode.SelectNodes("//img") == null) + { + return htmlDoc; + } + + foreach (var node in htmlDoc.DocumentNode.SelectNodes("//img")) + { + var imageSrc = node + .SelectNodes("//img") + .First() + .Attributes["src"].Value; + + if (imageSrc != null) + { + var imageBytes = await _fileHttpService.GetFileByteArray(new Uri(imageSrc)); + var imageBase64 = Convert.ToBase64String(imageBytes.ToArray()); + + node.SetAttributeValue("src", $"data:image/jpeg;base64,{imageBase64}"); + } + } + + return htmlDoc; + } + +} \ No newline at end of file diff --git a/Epsilon/Component/WordDocumentBuilder.cs b/Epsilon/Component/WordDocumentBuilder.cs deleted file mode 100644 index b31a0a45..00000000 --- a/Epsilon/Component/WordDocumentBuilder.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Abstractions.Component; - -namespace Epsilon.Component; - -public class WordDocumentBuilder : IWordDocumentBuilder -{ - public Document Build(IEnumerable components) - { - var document = new Document(new Body()); - - foreach (var component in components) - { - var element = component.ToWord(); - document.Append(element); - } - - return document; - } -} \ No newline at end of file diff --git a/Epsilon/Service/CompetenceDocumentService.cs b/Epsilon/Service/CompetenceDocumentService.cs index 3ddfcd23..b724736c 100644 --- a/Epsilon/Service/CompetenceDocumentService.cs +++ b/Epsilon/Service/CompetenceDocumentService.cs @@ -1,5 +1,6 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Service; @@ -7,15 +8,10 @@ namespace Epsilon.Service; public class CompetenceDocumentService : ICompetenceDocumentService { - private readonly IWordDocumentBuilder _wordDocumentBuilder; private readonly ICompetenceComponentService _competenceComponentService; - public CompetenceDocumentService( - IWordDocumentBuilder wordDocumentBuilder, - ICompetenceComponentService competenceComponentService - ) + public CompetenceDocumentService(ICompetenceComponentService competenceComponentService) { - _wordDocumentBuilder = wordDocumentBuilder; _competenceComponentService = competenceComponentService; } @@ -27,7 +23,12 @@ public async Task WriteDocument(Stream stream) using var document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document); document.AddMainDocumentPart(); - document.MainDocumentPart!.Document = _wordDocumentBuilder.Build(components); + document.MainDocumentPart!.Document = new Document(); + + foreach (var competenceWordComponent in components) + { + competenceWordComponent.AddToWordDocument(document.MainDocumentPart); + } document.Save(); document.Close(); From 92f0d979ffe2efec08cd3051147a5d792f145adf Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Fri, 21 Apr 2023 12:53:09 +0200 Subject: [PATCH 21/72] Created KpiMatrix component --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ++++++ .../Model/GraphQl/Outcome.cs | 3 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 +++ Epsilon/Component/KpiMatrixComponent.cs | 83 +++++++++++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs create mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs new file mode 100644 index 00000000..87ca98e9 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix.cs @@ -0,0 +1,25 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes + ); + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments + ); + diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index 1a0f6055..6bdbd704 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -4,6 +4,5 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( [property: JsonPropertyName("_id")] int Id, - [property: JsonPropertyName("title")] string Title, - [property: JsonPropertyName("masteryPoints")] double MasteryPoints + [property: JsonPropertyName("title")] string Title ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs new file mode 100644 index 00000000..ca2dc6ad --- /dev/null +++ b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Epsilon.Canvas.Abstractions.Model.GraphQl; + +namespace Epsilon.Canvas.Abstractions.QueryResponse; +public record GetUserKpiMatrixOutcomes( + [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data +) +{ + public record CourseData( + [property: JsonPropertyName("course")] Course Course + ); +}; \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs new file mode 100644 index 00000000..d157702a --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -0,0 +1,83 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Canvas.Abstractions.Service; +using Microsoft.Extensions.Configuration; + +namespace Epsilon.Component; + +public class KpiMatrixComponent : Component +{ + + + private readonly IConfiguration _configuration; + private readonly IGraphQlHttpService _graphQlService; + private readonly IAccountHttpService _accountHttpService; + + public KpiMatrixComponent( + IGraphQlHttpService graphQlService, + IAccountHttpService accountHttpService, + IConfiguration configuration + ) + { + _graphQlService = graphQlService; + _accountHttpService = accountHttpService; + _configuration = configuration; + } + + public async override Task Fetch() + { + var courseId = _configuration["Canvas:courseId"]; + + var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); + var outcomes = await _graphQlService.Query(outcomesQuery); + + var kpiMatrix = ConvertToComponent(outcomes); + + return kpiMatrix; + } + + private GradeStatus GetGradeStatus(double? points) + { + return points switch + { + null => GradeStatus.NotGraded, + >= 4 => GradeStatus.Approved, + _ => GradeStatus.Insufficient + }; + } + + private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + { + var outcomes = new List(); + var assignments = new List(); + + if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) + return new KpiMatrix(outcomes, assignments); + foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + { + var assignmentOutcomes = new List(); + var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() + ?.AssessmentRatings; + if (assessmentRatings == null) continue; + + foreach (var assessmentRating in assessmentRatings) + { + if (assessmentRating.Outcome != null) + { + assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points))); + if (outcomes.All(o => o != assessmentRating.Outcome.Title)) + { + outcomes.Add(assessmentRating.Outcome.Title); + } + } + } + + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + } + + return new KpiMatrix(outcomes ,assignments); + } +} \ No newline at end of file From fcd42196f5a9ae0081cc97a68a14b8f71954e13a Mon Sep 17 00:00:00 2001 From: Jasper123pyah <73039915+Jasper123pyah@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:09:47 +0200 Subject: [PATCH 22/72] Added modules and fixed comments --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ----- .../Model/KpiMatrix/GradeStatus.cs | 8 ++ .../Model/KpiMatrix/KpiMatrix.cs | 9 ++ .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 + .../Model/KpiMatrix/KpiMatrixModule.cs | 6 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 + .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 + .../Model/GraphQl/Outcome.cs | 3 +- Epsilon/Component/KpiMatrixComponent.cs | 103 +++++++++++------- 9 files changed, 104 insertions(+), 65 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs deleted file mode 100644 index 87ca98e9..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes - ); - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments - ); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs new file mode 100644 index 00000000..d50d2254 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs @@ -0,0 +1,8 @@ +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs new file mode 100644 index 00000000..a86409c5 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs @@ -0,0 +1,9 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs new file mode 100644 index 00000000..6ee6bdcb --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs new file mode 100644 index 00000000..9a874e98 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixModule( + string Name, + KpiMatrix KpiMatrix +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs new file mode 100644 index 00000000..efe15667 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -0,0 +1,5 @@ +namespace Epsilon.Abstractions.Model; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs new file mode 100644 index 00000000..c3aa2732 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs @@ -0,0 +1,4 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixProfile( + IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index 6bdbd704..1a0f6055 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( [property: JsonPropertyName("_id")] int Id, - [property: JsonPropertyName("title")] string Title + [property: JsonPropertyName("title")] string Title, + [property: JsonPropertyName("masteryPoints")] double MasteryPoints ); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index d157702a..b65bd7d7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -1,4 +1,5 @@ -using Epsilon.Abstractions.Component; +using DocumentFormat.OpenXml.Spreadsheet; +using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Canvas.Abstractions.QueryResponse; @@ -7,10 +8,8 @@ namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponent : Component { - - private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; private readonly IAccountHttpService _accountHttpService; @@ -25,59 +24,85 @@ IConfiguration configuration _accountHttpService = accountHttpService; _configuration = configuration; } - - public async override Task Fetch() + + public async override Task Fetch() { var courseId = _configuration["Canvas:courseId"]; var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); var outcomes = await _graphQlService.Query(outcomesQuery); - - var kpiMatrix = ConvertToComponent(outcomes); - return kpiMatrix; + var modules = ConvertToComponent(outcomes); + + return modules; } - private GradeStatus GetGradeStatus(double? points) + private GradeStatus GetGradeStatus(double? points, double mastery) { - return points switch + if (points == null) + { + return GradeStatus.NotGraded; + } + else if (points >= mastery) { - null => GradeStatus.NotGraded, - >= 4 => GradeStatus.Approved, - _ => GradeStatus.Insufficient - }; + return GradeStatus.Approved; + } + else + { + return GradeStatus.Insufficient; + } } - - private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + + + private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) { - var outcomes = new List(); - var assignments = new List(); + var modules = new List(); - if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) - return new KpiMatrix(outcomes, assignments); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - var assignmentOutcomes = new List(); - var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() - ?.AssessmentRatings; - if (assessmentRatings == null) continue; - - foreach (var assessmentRating in assessmentRatings) + // Check if the node has a module + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) continue; + + // Group the assignment outcomes by outcome title + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(n => n?.AssessmentRatings != null) + .SelectMany(n => n.AssessmentRatings) + .Where(r => r.Outcome != null) + .GroupBy(r => r.Outcome.Title); + + var outcomes = outcomeGroups.Select(g => g.Key); + var assignments = outcomeGroups.Select(g => + new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) + )); + + // Find the module in the list of modules or create a new one + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + modules.Add(module); + } + else + { + var moduleIndex = modules.IndexOf(module); + if (moduleIndex == -1) { - if (assessmentRating.Outcome != null) - { - assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points))); - if (outcomes.All(o => o != assessmentRating.Outcome.Title)) - { - outcomes.Add(assessmentRating.Outcome.Title); - } - } + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); } - - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + else + { + var existingModule = modules[moduleIndex]; + var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); + modules[moduleIndex] = newModule; + } + } } - return new KpiMatrix(outcomes ,assignments); + return new KpiMatrixProfile(modules); } } \ No newline at end of file From e521d08754480dc1cbe5a915480a5eb283b562f6 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 13:01:40 +0200 Subject: [PATCH 23/72] Merge conflicts --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 + .../Component/KpiMatrixAssignment.cs | 6 + .../Component/KpiMatrixCollection.cs | 148 ++++++++++++++++++ .../KpiMatrixModule.cs | 2 +- .../Component/KpiMatrixOutcome.cs | 7 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 - .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 - .../Model}/GradeStatus.cs | 4 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 -- Epsilon.Host.WebApi/Program.cs | 4 +- Epsilon/Component/KpiMatrixComponent.cs | 108 ------------- 11 files changed, 174 insertions(+), 133 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixModule.cs (64%) create mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs rename {Epsilon.Abstractions/Model/KpiMatrix => Epsilon.Canvas.Abstractions/Model}/GradeStatus.cs (50%) delete mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs delete mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs new file mode 100644 index 00000000..8f5b15a5 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrix.cs @@ -0,0 +1,7 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs new file mode 100644 index 00000000..625815fa --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs new file mode 100644 index 00000000..d1daafec --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs @@ -0,0 +1,148 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +{ + public OpenXmlElement ToWord() + { + var body = new Body(); + + foreach (var module in KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth + { + Width = "0", Type = TableWidthUnitValues.Auto, + })); + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.BottomToTopLeftToRight, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF", + }; + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break + { + Type = BreakValues.Page, + }))); + } + + return body; + } + + private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder + { + Val = BorderValues.Single, + }, + new RightBorder + { + Val = BorderValues.Single, + }, + new TopBorder + { + Val = BorderValues.Single, + }, + new BottomBorder + { + Val = BorderValues.Single, + }); + + foreach (var element in elements) + { + cell.Append(element); + } + + if (width != null) + { + cellProperties.Append(new TableCellWidth + { + Type = TableWidthUnitValues.Dxa, Width = width, + }); + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); + + return cell; + } +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixModule.cs similarity index 64% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixModule.cs index 9a874e98..659c932e 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Abstractions.Component; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs new file mode 100644 index 00000000..43140a0d --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs @@ -0,0 +1,7 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs deleted file mode 100644 index efe15667..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Epsilon.Abstractions.Model; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs deleted file mode 100644 index c3aa2732..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixProfile( - IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs similarity index 50% rename from Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs rename to Epsilon.Canvas.Abstractions/Model/GradeStatus.cs index d50d2254..307d4dfc 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs +++ b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs @@ -1,8 +1,8 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Canvas.Abstractions.Model; public enum GradeStatus { Approved, Insufficient, - NotGraded + NotGraded, } \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs deleted file mode 100644 index ca2dc6ad..00000000 --- a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using Epsilon.Canvas.Abstractions.Model.GraphQl; - -namespace Epsilon.Canvas.Abstractions.QueryResponse; -public record GetUserKpiMatrixOutcomes( - [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data -) -{ - public record CourseData( - [property: JsonPropertyName("course")] Course Course - ); -}; \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index f27fec07..7097ff1e 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Abstractions.Service; using Epsilon.Canvas; using Epsilon.Component; @@ -28,7 +29,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); builder.Services.AddComponentFetcher(); var app = builder.Build(); diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs deleted file mode 100644 index b65bd7d7..00000000 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ /dev/null @@ -1,108 +0,0 @@ -using DocumentFormat.OpenXml.Spreadsheet; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using Epsilon.Canvas; -using Epsilon.Canvas.Abstractions.QueryResponse; -using Epsilon.Canvas.Abstractions.Service; -using Microsoft.Extensions.Configuration; - -namespace Epsilon.Component; - -public class KpiMatrixComponent : Component -{ - private readonly IConfiguration _configuration; - private readonly IGraphQlHttpService _graphQlService; - private readonly IAccountHttpService _accountHttpService; - - public KpiMatrixComponent( - IGraphQlHttpService graphQlService, - IAccountHttpService accountHttpService, - IConfiguration configuration - ) - { - _graphQlService = graphQlService; - _accountHttpService = accountHttpService; - _configuration = configuration; - } - - public async override Task Fetch() - { - var courseId = _configuration["Canvas:courseId"]; - - var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); - var outcomes = await _graphQlService.Query(outcomesQuery); - - var modules = ConvertToComponent(outcomes); - - return modules; - } - - private GradeStatus GetGradeStatus(double? points, double mastery) - { - if (points == null) - { - return GradeStatus.NotGraded; - } - else if (points >= mastery) - { - return GradeStatus.Approved; - } - else - { - return GradeStatus.Insufficient; - } - } - - - private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) - { - var modules = new List(); - - foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) - { - // Check if the node has a module - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) continue; - - // Group the assignment outcomes by outcome title - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(n => n?.AssessmentRatings != null) - .SelectMany(n => n.AssessmentRatings) - .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Title); - - var outcomes = outcomeGroups.Select(g => g.Key); - var assignments = outcomeGroups.Select(g => - new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) - )); - - // Find the module in the list of modules or create a new one - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); - modules.Add(module); - } - else - { - var moduleIndex = modules.IndexOf(module); - if (moduleIndex == -1) - { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); - } - else - { - var existingModule = modules[moduleIndex]; - var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); - modules[moduleIndex] = newModule; - } - } - } - - return new KpiMatrixProfile(modules); - } -} \ No newline at end of file From a7b95021c21f23d0a7f6883a954ef85adfaf15a5 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 16 May 2023 22:32:38 +0200 Subject: [PATCH 24/72] File restructure --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 - .../Component/KpiMatrixAssignment.cs | 6 - .../Component/KpiMatrixCollection.cs | 148 ------------------ .../KpiMatrixModule.cs | 2 +- .../Component/KpiMatrixOutcome.cs | 7 - 5 files changed, 1 insertion(+), 169 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs (55%) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs deleted file mode 100644 index 8f5b15a5..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrix.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments -); - diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs deleted file mode 100644 index 625815fa..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs deleted file mode 100644 index d1daafec..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs +++ /dev/null @@ -1,148 +0,0 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent -{ - public OpenXmlElement ToWord() - { - var body = new Body(); - - foreach (var module in KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth - { - Width = "0", Type = TableWidthUnitValues.Auto, - })); - - // Calculate the header row height based on the longest assignment name. - var headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) - { - headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight - { - Val = (UInt32Value)(uint)headerRowHeight, - })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) - { - var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection - { - Val = TextDirectionValues.BottomToTopLeftToRight, - }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); - - // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - if (outcomeAssignment != null) - { - var fillColor = outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF", - }; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); - } - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); - } - - body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break - { - Type = BreakValues.Page, - }))); - } - - return body; - } - - private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) - { - var cell = new TableCell(); - var cellProperties = new TableCellProperties(); - var borders = new TableCellBorders( - new LeftBorder - { - Val = BorderValues.Single, - }, - new RightBorder - { - Val = BorderValues.Single, - }, - new TopBorder - { - Val = BorderValues.Single, - }, - new BottomBorder - { - Val = BorderValues.Single, - }); - - foreach (var element in elements) - { - cell.Append(element); - } - - if (width != null) - { - cellProperties.Append(new TableCellWidth - { - Type = TableWidthUnitValues.Dxa, Width = width, - }); - } - - cellProperties.Append(borders); - cell.PrependChild(cellProperties); - - return cell; - } -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs similarity index 55% rename from Epsilon.Abstractions/Component/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs index 659c932e..38f92501 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs deleted file mode 100644 index 43140a0d..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file From 5270f6d4d947088e2fba54c1eea81e2a1a9cf8a7 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:46:41 +0200 Subject: [PATCH 25/72] Revised coding structure. Assignments that are not yest graded are also now included --- .../Component/KpiMatrixComponent/KpiMatrixModule.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs deleted file mode 100644 index 38f92501..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - -public record KpiMatrixModule( - string Name, - KpiMatrix KpiMatrix -); \ No newline at end of file From bc884bafda3b650848f7d446536017070d741f38 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Fri, 21 Apr 2023 12:53:09 +0200 Subject: [PATCH 26/72] Created KpiMatrix component --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ++++++ .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 +++ Epsilon/Component/KpiMatrixComponent.cs | 83 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs create mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs new file mode 100644 index 00000000..87ca98e9 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix.cs @@ -0,0 +1,25 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes + ); + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments + ); + diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs new file mode 100644 index 00000000..ca2dc6ad --- /dev/null +++ b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Epsilon.Canvas.Abstractions.Model.GraphQl; + +namespace Epsilon.Canvas.Abstractions.QueryResponse; +public record GetUserKpiMatrixOutcomes( + [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data +) +{ + public record CourseData( + [property: JsonPropertyName("course")] Course Course + ); +}; \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs new file mode 100644 index 00000000..d157702a --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -0,0 +1,83 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Canvas.Abstractions.Service; +using Microsoft.Extensions.Configuration; + +namespace Epsilon.Component; + +public class KpiMatrixComponent : Component +{ + + + private readonly IConfiguration _configuration; + private readonly IGraphQlHttpService _graphQlService; + private readonly IAccountHttpService _accountHttpService; + + public KpiMatrixComponent( + IGraphQlHttpService graphQlService, + IAccountHttpService accountHttpService, + IConfiguration configuration + ) + { + _graphQlService = graphQlService; + _accountHttpService = accountHttpService; + _configuration = configuration; + } + + public async override Task Fetch() + { + var courseId = _configuration["Canvas:courseId"]; + + var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); + var outcomes = await _graphQlService.Query(outcomesQuery); + + var kpiMatrix = ConvertToComponent(outcomes); + + return kpiMatrix; + } + + private GradeStatus GetGradeStatus(double? points) + { + return points switch + { + null => GradeStatus.NotGraded, + >= 4 => GradeStatus.Approved, + _ => GradeStatus.Insufficient + }; + } + + private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + { + var outcomes = new List(); + var assignments = new List(); + + if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) + return new KpiMatrix(outcomes, assignments); + foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + { + var assignmentOutcomes = new List(); + var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() + ?.AssessmentRatings; + if (assessmentRatings == null) continue; + + foreach (var assessmentRating in assessmentRatings) + { + if (assessmentRating.Outcome != null) + { + assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points))); + if (outcomes.All(o => o != assessmentRating.Outcome.Title)) + { + outcomes.Add(assessmentRating.Outcome.Title); + } + } + } + + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + } + + return new KpiMatrix(outcomes ,assignments); + } +} \ No newline at end of file From 325d314b5cf4b34764db60623415e70cc8feee50 Mon Sep 17 00:00:00 2001 From: Jasper123pyah <73039915+Jasper123pyah@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:09:47 +0200 Subject: [PATCH 27/72] Added modules and fixed comments --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ----- .../Model/KpiMatrix/GradeStatus.cs | 8 ++ .../Model/KpiMatrix/KpiMatrixModule.cs | 6 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 + .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 + Epsilon/Component/KpiMatrixComponent.cs | 103 +++++++++++------- 6 files changed, 87 insertions(+), 64 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs deleted file mode 100644 index 87ca98e9..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes - ); - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments - ); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs new file mode 100644 index 00000000..d50d2254 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs @@ -0,0 +1,8 @@ +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs new file mode 100644 index 00000000..9a874e98 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixModule( + string Name, + KpiMatrix KpiMatrix +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs new file mode 100644 index 00000000..efe15667 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -0,0 +1,5 @@ +namespace Epsilon.Abstractions.Model; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs new file mode 100644 index 00000000..c3aa2732 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs @@ -0,0 +1,4 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixProfile( + IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index d157702a..b65bd7d7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -1,4 +1,5 @@ -using Epsilon.Abstractions.Component; +using DocumentFormat.OpenXml.Spreadsheet; +using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Canvas.Abstractions.QueryResponse; @@ -7,10 +8,8 @@ namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponent : Component { - - private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; private readonly IAccountHttpService _accountHttpService; @@ -25,59 +24,85 @@ IConfiguration configuration _accountHttpService = accountHttpService; _configuration = configuration; } - - public async override Task Fetch() + + public async override Task Fetch() { var courseId = _configuration["Canvas:courseId"]; var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); var outcomes = await _graphQlService.Query(outcomesQuery); - - var kpiMatrix = ConvertToComponent(outcomes); - return kpiMatrix; + var modules = ConvertToComponent(outcomes); + + return modules; } - private GradeStatus GetGradeStatus(double? points) + private GradeStatus GetGradeStatus(double? points, double mastery) { - return points switch + if (points == null) + { + return GradeStatus.NotGraded; + } + else if (points >= mastery) { - null => GradeStatus.NotGraded, - >= 4 => GradeStatus.Approved, - _ => GradeStatus.Insufficient - }; + return GradeStatus.Approved; + } + else + { + return GradeStatus.Insufficient; + } } - - private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + + + private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) { - var outcomes = new List(); - var assignments = new List(); + var modules = new List(); - if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) - return new KpiMatrix(outcomes, assignments); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - var assignmentOutcomes = new List(); - var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() - ?.AssessmentRatings; - if (assessmentRatings == null) continue; - - foreach (var assessmentRating in assessmentRatings) + // Check if the node has a module + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) continue; + + // Group the assignment outcomes by outcome title + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(n => n?.AssessmentRatings != null) + .SelectMany(n => n.AssessmentRatings) + .Where(r => r.Outcome != null) + .GroupBy(r => r.Outcome.Title); + + var outcomes = outcomeGroups.Select(g => g.Key); + var assignments = outcomeGroups.Select(g => + new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) + )); + + // Find the module in the list of modules or create a new one + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + modules.Add(module); + } + else + { + var moduleIndex = modules.IndexOf(module); + if (moduleIndex == -1) { - if (assessmentRating.Outcome != null) - { - assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points))); - if (outcomes.All(o => o != assessmentRating.Outcome.Title)) - { - outcomes.Add(assessmentRating.Outcome.Title); - } - } + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); } - - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + else + { + var existingModule = modules[moduleIndex]; + var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); + modules[moduleIndex] = newModule; + } + } } - return new KpiMatrix(outcomes ,assignments); + return new KpiMatrixProfile(modules); } } \ No newline at end of file From ab68843feab50ea94a4e1e21faf02ec9b702e249 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 13:01:40 +0200 Subject: [PATCH 28/72] Merge conflicts --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 + .../Component/KpiMatrixAssignment.cs | 6 + .../Component/KpiMatrixCollection.cs | 148 ++++++++++++++++++ .../KpiMatrixModule.cs | 2 +- .../Component/KpiMatrixOutcome.cs | 7 + .../Model/KpiMatrix/GradeStatus.cs | 8 - .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 - .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 - .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 -- 9 files changed, 169 insertions(+), 30 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixModule.cs (64%) create mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs delete mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs new file mode 100644 index 00000000..8f5b15a5 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrix.cs @@ -0,0 +1,7 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs new file mode 100644 index 00000000..625815fa --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs new file mode 100644 index 00000000..d1daafec --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs @@ -0,0 +1,148 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +{ + public OpenXmlElement ToWord() + { + var body = new Body(); + + foreach (var module in KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth + { + Width = "0", Type = TableWidthUnitValues.Auto, + })); + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.BottomToTopLeftToRight, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF", + }; + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break + { + Type = BreakValues.Page, + }))); + } + + return body; + } + + private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder + { + Val = BorderValues.Single, + }, + new RightBorder + { + Val = BorderValues.Single, + }, + new TopBorder + { + Val = BorderValues.Single, + }, + new BottomBorder + { + Val = BorderValues.Single, + }); + + foreach (var element in elements) + { + cell.Append(element); + } + + if (width != null) + { + cellProperties.Append(new TableCellWidth + { + Type = TableWidthUnitValues.Dxa, Width = width, + }); + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); + + return cell; + } +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixModule.cs similarity index 64% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixModule.cs index 9a874e98..659c932e 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Abstractions.Component; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs new file mode 100644 index 00000000..43140a0d --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs @@ -0,0 +1,7 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs deleted file mode 100644 index d50d2254..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs deleted file mode 100644 index efe15667..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Epsilon.Abstractions.Model; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs deleted file mode 100644 index c3aa2732..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixProfile( - IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs deleted file mode 100644 index ca2dc6ad..00000000 --- a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using Epsilon.Canvas.Abstractions.Model.GraphQl; - -namespace Epsilon.Canvas.Abstractions.QueryResponse; -public record GetUserKpiMatrixOutcomes( - [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data -) -{ - public record CourseData( - [property: JsonPropertyName("course")] Course Course - ); -}; \ No newline at end of file From a167198d19fcfe8e7f6bcacb5bbbf23fa6c45a67 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 16 May 2023 22:32:38 +0200 Subject: [PATCH 29/72] File restructure --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 - .../Component/KpiMatrixAssignment.cs | 6 - .../Component/KpiMatrixCollection.cs | 148 ------------------ .../KpiMatrixModule.cs | 2 +- 4 files changed, 1 insertion(+), 162 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs (55%) diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs deleted file mode 100644 index 8f5b15a5..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrix.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments -); - diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs deleted file mode 100644 index 625815fa..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs deleted file mode 100644 index d1daafec..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs +++ /dev/null @@ -1,148 +0,0 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent -{ - public OpenXmlElement ToWord() - { - var body = new Body(); - - foreach (var module in KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth - { - Width = "0", Type = TableWidthUnitValues.Auto, - })); - - // Calculate the header row height based on the longest assignment name. - var headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) - { - headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight - { - Val = (UInt32Value)(uint)headerRowHeight, - })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) - { - var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection - { - Val = TextDirectionValues.BottomToTopLeftToRight, - }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); - - // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - if (outcomeAssignment != null) - { - var fillColor = outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF", - }; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); - } - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); - } - - body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break - { - Type = BreakValues.Page, - }))); - } - - return body; - } - - private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) - { - var cell = new TableCell(); - var cellProperties = new TableCellProperties(); - var borders = new TableCellBorders( - new LeftBorder - { - Val = BorderValues.Single, - }, - new RightBorder - { - Val = BorderValues.Single, - }, - new TopBorder - { - Val = BorderValues.Single, - }, - new BottomBorder - { - Val = BorderValues.Single, - }); - - foreach (var element in elements) - { - cell.Append(element); - } - - if (width != null) - { - cellProperties.Append(new TableCellWidth - { - Type = TableWidthUnitValues.Dxa, Width = width, - }); - } - - cellProperties.Append(borders); - cell.PrependChild(cellProperties); - - return cell; - } -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs similarity index 55% rename from Epsilon.Abstractions/Component/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs index 659c932e..38f92501 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixModule( string Name, From 6cba853cd0c35b00542f1377196ca406098bc35e Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:46:41 +0200 Subject: [PATCH 30/72] Revised coding structure. Assignments that are not yest graded are also now included --- .../Component/KpiMatrixComponent/KpiMatrixModule.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs deleted file mode 100644 index 38f92501..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - -public record KpiMatrixModule( - string Name, - KpiMatrix KpiMatrix -); \ No newline at end of file From b04c3f83e0eb9c2edc7c74a5a403ba09e36ffdca Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 10:02:51 +0200 Subject: [PATCH 31/72] Added legend and changed "GradeStatus" structure --- Epsilon.Canvas.Abstractions/Model/GradeStatus.cs | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Epsilon.Canvas.Abstractions/Model/GradeStatus.cs diff --git a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs deleted file mode 100644 index 307d4dfc..00000000 --- a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Epsilon.Canvas.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded, -} \ No newline at end of file From 779114fedcc30b9e8b46cee0ef3cb92b6fba05e7 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 13:03:27 +0200 Subject: [PATCH 32/72] Added extra grade type --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 7 +- .../KpiMatrixComponent/KpiMatrixConstants.cs | 7 +- .../Component/KpiMatrixOutcome.cs | 7 -- .../Model/KpiMatrix/KpiMatrix.cs | 9 -- .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 - Epsilon/Component/KpiMatrixComponent.cs | 108 ------------------ .../Component/KpiMatrixComponentFetcher.cs | 28 +++-- 7 files changed, 22 insertions(+), 150 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs delete mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index ef272696..1c2ae122 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,11 +1,12 @@ using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; // using Epsilon.Canvas.Abstractions.Model; namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - +[CompetenceComponentName("kpi_matrix")] public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent { @@ -32,7 +33,7 @@ private static OpenXmlElement GetLegend() return table; } - public OpenXmlElement ToWord() + public void AddToWordDocument(MainDocumentPart mainDocumentPart) { var body = new Body(); // Create a table, with rows for the outcomes and columns for the assignments. @@ -131,7 +132,7 @@ public OpenXmlElement ToWord() body.Append(new Paragraph(new Run(new Text("")))); body.AppendChild(table); - return body; + mainDocumentPart.Document.AppendChild(body); } private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs index 13a54cb1..3e0d3ccf 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs @@ -6,13 +6,16 @@ public static class KpiMatrixConstants public static readonly IDictionary GradeStatus = new Dictionary { { - "Approved", new GradeStatus("Approved", "44F656") + "Mastered", new GradeStatus("Mastered", "44F656") }, { "Insufficient", new GradeStatus("Insufficient", "FA1818") }, { - "NotGraded", new GradeStatus("NotGraded", "FAFF00") + "NotGradedAssessed", new GradeStatus("Not graded, Assignment assessed", "FAFF00") + }, + { + "NotGradedNotAssessed", new GradeStatus("Not graded, Assignment not assessed", "9F2B68") }, }; } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs deleted file mode 100644 index 43140a0d..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs deleted file mode 100644 index a86409c5..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments -); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs deleted file mode 100644 index 6ee6bdcb..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes -); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs deleted file mode 100644 index b65bd7d7..00000000 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ /dev/null @@ -1,108 +0,0 @@ -using DocumentFormat.OpenXml.Spreadsheet; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using Epsilon.Canvas; -using Epsilon.Canvas.Abstractions.QueryResponse; -using Epsilon.Canvas.Abstractions.Service; -using Microsoft.Extensions.Configuration; - -namespace Epsilon.Component; - -public class KpiMatrixComponent : Component -{ - private readonly IConfiguration _configuration; - private readonly IGraphQlHttpService _graphQlService; - private readonly IAccountHttpService _accountHttpService; - - public KpiMatrixComponent( - IGraphQlHttpService graphQlService, - IAccountHttpService accountHttpService, - IConfiguration configuration - ) - { - _graphQlService = graphQlService; - _accountHttpService = accountHttpService; - _configuration = configuration; - } - - public async override Task Fetch() - { - var courseId = _configuration["Canvas:courseId"]; - - var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); - var outcomes = await _graphQlService.Query(outcomesQuery); - - var modules = ConvertToComponent(outcomes); - - return modules; - } - - private GradeStatus GetGradeStatus(double? points, double mastery) - { - if (points == null) - { - return GradeStatus.NotGraded; - } - else if (points >= mastery) - { - return GradeStatus.Approved; - } - else - { - return GradeStatus.Insufficient; - } - } - - - private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) - { - var modules = new List(); - - foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) - { - // Check if the node has a module - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) continue; - - // Group the assignment outcomes by outcome title - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(n => n?.AssessmentRatings != null) - .SelectMany(n => n.AssessmentRatings) - .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Title); - - var outcomes = outcomeGroups.Select(g => g.Key); - var assignments = outcomeGroups.Select(g => - new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) - )); - - // Find the module in the list of modules or create a new one - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); - modules.Add(module); - } - else - { - var moduleIndex = modules.IndexOf(module); - if (moduleIndex == -1) - { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); - } - else - { - var existingModule = modules[moduleIndex]; - var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); - modules[moduleIndex] = newModule; - } - } - } - - return new KpiMatrixProfile(modules); - } -} \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index bc6eb47f..461d52ed 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -3,6 +3,7 @@ using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; +using KpiMatrixOutcome = Epsilon.Abstractions.Component.KpiMatrixComponent.KpiMatrixOutcome; namespace Epsilon.Component; @@ -72,13 +73,15 @@ public override async Task Fetch() return ConvertToComponent(outcomes, new DateTime(2023, 2, 1), DateTime.Now); } - private static GradeStatus GetGradeStatus(double? points, double? mastery) + private static GradeStatus GetGradeStatus(bool isSubmitted, double? points, double? mastery) { - return points != null - ? points >= mastery - ? KpiMatrixConstants.GradeStatus["Approved"] - : KpiMatrixConstants.GradeStatus["Insufficient"] - : KpiMatrixConstants.GradeStatus["NotGraded"]; + return isSubmitted + ? points != null + ? points >= mastery + ? KpiMatrixConstants.GradeStatus["Mastered"] + : KpiMatrixConstants.GradeStatus["Insufficient"] + : KpiMatrixConstants.GradeStatus["NotGradedAssessed"] + : KpiMatrixConstants.GradeStatus["NotGradedNotAssessed"]; } @@ -109,17 +112,12 @@ DateTime endAt { if (criteria.Outcome != null) { - if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask)) + //Validate that outcome is a HboI KPI + if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask) || FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) { + var assessmentRatings = submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings; kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, - GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) - ?.Points - , criteria.Outcome.MasteryPoints))); - } - else if (FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) - { - kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, - GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + GetGradeStatus(assessmentRatings != null, assessmentRatings?.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) ?.Points , criteria.Outcome.MasteryPoints))); } From 93848089d4eebffe0fc7bda34e1f1754d2f28d47 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 13:14:43 +0200 Subject: [PATCH 33/72] Added legend to json result --- .../Component/KpiMatrixComponent/KpiMatrixCollection.cs | 9 +++------ Epsilon/Component/KpiMatrixComponentFetcher.cs | 3 +-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index 1c2ae122..d1670f71 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,19 +1,16 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; -using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; - -// using Epsilon.Canvas.Abstractions.Model; namespace Epsilon.Abstractions.Component.KpiMatrixComponent; [CompetenceComponentName("kpi_matrix")] -public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent +public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments, IDictionary GradeStatus) : ICompetenceWordComponent { - private static OpenXmlElement GetLegend() + private OpenXmlElement GetLegend() { var table = new Table(); - foreach (var status in KpiMatrixConstants.GradeStatus) + foreach (var status in GradeStatus) { var row = new TableRow(); var cellName = CreateTableCellWithBorders("200"); diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 461d52ed..69edb152 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -3,7 +3,6 @@ using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; -using KpiMatrixOutcome = Epsilon.Abstractions.Component.KpiMatrixComponent.KpiMatrixOutcome; namespace Epsilon.Component; @@ -135,6 +134,6 @@ DateTime endAt } } - return new KpiMatrixCollection(assignments); + return new KpiMatrixCollection(assignments, KpiMatrixConstants.GradeStatus); } } \ No newline at end of file From 2d29c6c3378f42ccab2afda993175f73091abafc Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 21:21:42 +0200 Subject: [PATCH 34/72] Working order --- .../Component/CompetenceComponentFetcher.cs | 4 ++-- .../Component/ICompetenceComponentFetcher.cs | 4 ++-- .../Service/ICompetenceComponentService.cs | 8 ++++---- .../Model/GraphQl/RubricAssessmentNode.cs | 2 +- .../Controllers/ComponentController.cs | 19 +++++++++++++----- ...etenceProfileCompetenceComponentFetcher.cs | 2 +- .../Component/KpiMatrixComponentFetcher.cs | 20 +++++++++++-------- .../Component/PersonaPageComponentFetcher.cs | 2 +- Epsilon/Service/CompetenceComponentService.cs | 18 +++++++++-------- 9 files changed, 47 insertions(+), 32 deletions(-) diff --git a/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs b/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs index d84e9bf6..b3acc779 100644 --- a/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs +++ b/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs @@ -2,7 +2,7 @@ namespace Epsilon.Abstractions.Component; public abstract class CompetenceComponentFetcher : ICompetenceComponentFetcher where TComponent : ICompetenceComponent { - public async Task FetchUnknown() => await Fetch(); + public async Task FetchUnknown(DateTime? startDate = null, DateTime? endDate = null) => await Fetch(startDate, endDate); - public abstract Task Fetch(); + public abstract Task Fetch(DateTime? startDate = null, DateTime? endDate = null); } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs b/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs index cac2873a..fa028746 100644 --- a/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs +++ b/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs @@ -2,11 +2,11 @@ public interface ICompetenceComponentFetcher { - public Task FetchUnknown(); + public Task FetchUnknown(DateTime? startDate = null, DateTime? endDate = null); } public interface ICompetenceComponentFetcher : ICompetenceComponentFetcher where TComponent : ICompetenceComponent { - public Task Fetch(); + public Task Fetch(DateTime? startDate = null, DateTime? endDate = null); } \ No newline at end of file diff --git a/Epsilon.Abstractions/Service/ICompetenceComponentService.cs b/Epsilon.Abstractions/Service/ICompetenceComponentService.cs index 7b9c252c..f06d916a 100644 --- a/Epsilon.Abstractions/Service/ICompetenceComponentService.cs +++ b/Epsilon.Abstractions/Service/ICompetenceComponentService.cs @@ -4,11 +4,11 @@ namespace Epsilon.Abstractions.Service; public interface ICompetenceComponentService { - IAsyncEnumerable GetComponents(); + IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null); - IAsyncEnumerable GetComponents() where TComponent : ICompetenceComponent; + IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null) where TComponent : ICompetenceComponent; - Task GetComponent(string name); + Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null); - Task GetComponent(string name) where TComponent : class, ICompetenceComponent; + Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null) where TComponent : class, ICompetenceComponent; } \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs index 901f2b2a..e2f0ef08 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs @@ -3,5 +3,5 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record RubricAssessmentNode( - [property: JsonPropertyName("assessmentRatings")] IReadOnlyList AssessmentRatings + [property: JsonPropertyName("assessmentRatings")] IReadOnlyList? AssessmentRatings ); \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index 563823ff..b8860a88 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -1,3 +1,4 @@ +using System.Globalization; using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Service; using Microsoft.AspNetCore.Mvc; @@ -15,16 +16,24 @@ public ComponentController(ICompetenceComponentService competenceComponentServic _competenceComponentService = competenceComponentService; } + // + // DateTime.ParseExact(startDate ?? "", "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("en-US")), + // DateTime.Parse(endDate ?? "", CultureInfo.CreateSpecificCulture("en-US")) + [HttpGet("{componentName}")] [Produces(typeof(CompetenceProfile))] - public async Task> GetCompetenceProfile(string componentName) + public async Task> GetCompetenceProfile(string componentName, DateTime? startDate, DateTime? endDate) { - var component = await _competenceComponentService.GetComponent(componentName); - if (component == null) { - return NotFound(); + var component = await _competenceComponentService.GetComponent(componentName, startDate, endDate); + if (component == null) + { + return NotFound(); + } + + return Ok(component); } - return Ok(component); + return NotFound(); } } \ No newline at end of file diff --git a/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs b/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs index 3266f96e..8783a24d 100644 --- a/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs +++ b/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs @@ -54,7 +54,7 @@ IConfiguration configuration _configuration = configuration; } - public override async Task Fetch() + public override async Task Fetch(DateTime ?startDate = null, DateTime ?endDate = null) { var studentId = _configuration["Canvas:StudentId"]; var outcomesQuery = GetAllUserCoursesSubmissionOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCulture); diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 69edb152..fa772ea9 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -64,12 +64,12 @@ IConfiguration configuration _configuration = configuration; } - public override async Task Fetch() + public override async Task Fetch(DateTime? startDate = null, DateTime? endDate = null) { var studentId = _configuration["Canvas:StudentId"]; var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCultureIgnoreCase); var outcomes = await _graphQlService.Query(outcomesQuery); - return ConvertToComponent(outcomes, new DateTime(2023, 2, 1), DateTime.Now); + return ConvertToComponent(outcomes, startDate ?? DateTime.Now, endDate ?? DateTime.Now); } private static GradeStatus GetGradeStatus(bool isSubmitted, double? points, double? mastery) @@ -85,9 +85,9 @@ private static GradeStatus GetGradeStatus(bool isSubmitted, double? points, doub private static KpiMatrixCollection ConvertToComponent( - CanvasGraphQlQueryResponse queryResponse, - DateTime startAt, - DateTime endAt + CanvasGraphQlQueryResponse? queryResponse, + DateTime? startAt, + DateTime? endAt ) { var assignments = new List(); @@ -101,7 +101,7 @@ DateTime endAt if (submission != null) { - if (submission.Assignment?.Rubric != null) + if (submission.Assignment?.Rubric != null && submission.RubricAssessments?.Nodes != null) { var rubricAssessments = submission.Assignment.Rubric.Criteria; var kpiMatrixOutcomes = new List(); @@ -112,11 +112,15 @@ DateTime endAt if (criteria.Outcome != null) { //Validate that outcome is a HboI KPI - if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask) || FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) + if ((FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask) || + FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) && + rubricAssessments.Any()) { var assessmentRatings = submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings; + var test = assessmentRatings != null; + var test2 = assessmentRatings?.FirstOrDefault(o => o?.Criterion?.Outcome?.Id == criteria?.Outcome?.Id); kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, - GetGradeStatus(assessmentRatings != null, assessmentRatings?.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + GetGradeStatus(assessmentRatings != null, assessmentRatings?.FirstOrDefault(o => o?.Criterion?.Outcome?.Id == criteria?.Outcome?.Id) ?.Points , criteria.Outcome.MasteryPoints))); } diff --git a/Epsilon/Component/PersonaPageComponentFetcher.cs b/Epsilon/Component/PersonaPageComponentFetcher.cs index 87352755..e3da6b18 100644 --- a/Epsilon/Component/PersonaPageComponentFetcher.cs +++ b/Epsilon/Component/PersonaPageComponentFetcher.cs @@ -23,7 +23,7 @@ IOptions canvasSettings _canvasSettings = canvasSettings.Value; } - public override async Task Fetch() + public override async Task Fetch(DateTime ?startDate = null, DateTime ?endDate = null) { var courseId = _canvasSettings.CourseId; var personaHtml = await _pageHttpService.GetPageByName(courseId, "front_page"); diff --git a/Epsilon/Service/CompetenceComponentService.cs b/Epsilon/Service/CompetenceComponentService.cs index e2ddb435..55fe6bbf 100644 --- a/Epsilon/Service/CompetenceComponentService.cs +++ b/Epsilon/Service/CompetenceComponentService.cs @@ -13,17 +13,17 @@ public CompetenceComponentService(IEnumerable compo _componentFetchers = componentFetchers; } - public async IAsyncEnumerable GetComponents() + public async IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null) { foreach (var componentFetcher in _componentFetchers) { - yield return await componentFetcher.FetchUnknown(); + yield return await componentFetcher.FetchUnknown(startDate, endDate); } } - public async IAsyncEnumerable GetComponents() where TComponent : ICompetenceComponent + public async IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null) where TComponent : ICompetenceComponent { - await foreach (var component in GetComponents()) + await foreach (var component in GetComponents(startDate, endDate)) { if (component is TComponent componentOfT) { @@ -32,7 +32,7 @@ public async IAsyncEnumerable GetComponents() where TCom } } - public async Task GetComponent(string name) + public async Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null) { var fetcher = _componentFetchers.SingleOrDefault(f => { @@ -45,11 +45,13 @@ public async IAsyncEnumerable GetComponents() where TCom return false; }); - return fetcher == null ? null : await fetcher.FetchUnknown(); + return fetcher == null + ? null + : await fetcher.FetchUnknown(startDate, endDate); } - public async Task GetComponent(string name) where TComponent : class, ICompetenceComponent + public async Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null) where TComponent : class, ICompetenceComponent { - return await GetComponent(name) as TComponent; + return await GetComponent(name, startDate, endDate) as TComponent; } } \ No newline at end of file From 668cf8f783f5c88d3a0d2897cbb5d5ce30935f83 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 21:26:09 +0200 Subject: [PATCH 35/72] startDate & endDate implementation for document generator --- Epsilon.Abstractions/Service/ICompetenceDocumentService.cs | 2 +- Epsilon.Host.WebApi/Controllers/DocumentController.cs | 4 ++-- Epsilon/Service/CompetenceDocumentService.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs b/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs index 7ff220e3..0d65e92b 100644 --- a/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs +++ b/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs @@ -2,5 +2,5 @@ namespace Epsilon.Abstractions.Service; public interface ICompetenceDocumentService { - Task WriteDocument(Stream stream); + Task WriteDocument(Stream stream, DateTime? startDate = null, DateTime? endDate = null); } \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Controllers/DocumentController.cs b/Epsilon.Host.WebApi/Controllers/DocumentController.cs index e3259bfb..6fb22ed8 100644 --- a/Epsilon.Host.WebApi/Controllers/DocumentController.cs +++ b/Epsilon.Host.WebApi/Controllers/DocumentController.cs @@ -15,10 +15,10 @@ public DocumentController(ICompetenceDocumentService competenceDocumentService) } [HttpGet("word")] - public async Task DownloadWordDocument() + public async Task DownloadWordDocument(DateTime? startDate = null, DateTime? endDate = null) { var stream = new MemoryStream(); - await _competenceDocumentService.WriteDocument(stream); + await _competenceDocumentService.WriteDocument(stream, startDate, endDate); return File( stream, diff --git a/Epsilon/Service/CompetenceDocumentService.cs b/Epsilon/Service/CompetenceDocumentService.cs index b724736c..f7994436 100644 --- a/Epsilon/Service/CompetenceDocumentService.cs +++ b/Epsilon/Service/CompetenceDocumentService.cs @@ -15,11 +15,11 @@ public CompetenceDocumentService(ICompetenceComponentService competenceComponent _competenceComponentService = competenceComponentService; } - public async Task WriteDocument(Stream stream) + public async Task WriteDocument(Stream stream, DateTime? startDate = null, DateTime? endDate = null) { var startPosition = stream.Position; - var components = await _competenceComponentService.GetComponents().ToListAsync(); + var components = await _competenceComponentService.GetComponents(startDate, endDate).ToListAsync(); using var document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document); document.AddMainDocumentPart(); From a511a5a828aa16a7834ab849b57e23d4be1ac21a Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Wed, 24 May 2023 00:24:29 +0200 Subject: [PATCH 36/72] POC working --- .../src/components/Competance/KPIMatrix.vue | 68 ++ .../src/components/Competance/KpiMatrix.vue | 100 --- Epsilon.Host.Frontend/src/logic/Api.ts | 782 +++++++++--------- .../src/views/PerformanceDashboard.vue | 40 +- .../Controllers/ComponentController.cs | 4 - 5 files changed, 456 insertions(+), 538 deletions(-) create mode 100644 Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue delete mode 100644 Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue diff --git a/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue b/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue new file mode 100644 index 00000000..9b20586c --- /dev/null +++ b/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue b/Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue deleted file mode 100644 index d7222343..00000000 --- a/Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - diff --git a/Epsilon.Host.Frontend/src/logic/Api.ts b/Epsilon.Host.Frontend/src/logic/Api.ts index f35e2098..c04b7680 100644 --- a/Epsilon.Host.Frontend/src/logic/Api.ts +++ b/Epsilon.Host.Frontend/src/logic/Api.ts @@ -10,469 +10,439 @@ */ export interface Activity { - name?: string | null; - color?: string | null; + /** @format int32 */ + id?: number; + name?: string | null; + color?: string | null; } export interface ArchitectureLayer { - name?: string | null; - shortName?: string | null; - color?: string | null; -} - -export interface AssessmentRating { - /** @format double */ - points?: number | null; - outcome?: Outcome; -} - -export interface Assignment { - name?: string | null; - modules?: Module[] | null; + /** @format int32 */ + id?: number; + name?: string | null; + shortName?: string | null; + color?: string | null; } export interface CompetenceProfile { - hboIDomain?: HboIDomain; - professionalTaskOutcomes?: ProfessionalTaskOutcome[] | null; - professionalSkillOutcomes?: ProfessionalSkillOutcome[] | null; -} - -export interface Course { - name?: string | null; - submissionsConnection?: SubmissionsConnection; + hboIDomain?: IHboIDomain; + professionalTaskOutcomes?: ProfessionalTaskResult[] | null; + professionalSkillOutcomes?: ProfessionalSkillResult[] | null; + terms?: EnrollmentTerm[] | null; + decayingAveragesPerTask?: DecayingAveragePerLayer[] | null; + decayingAveragesPerSkill?: DecayingAveragePerSkill[] | null; } -export interface CourseData { - course?: Course; +export interface DecayingAveragePerActivity { + /** @format int32 */ + activity?: number; + /** @format double */ + decayingAverage?: number; } -export interface GetUserSubmissionOutcomes { - data?: CourseData; +export interface DecayingAveragePerLayer { + /** @format int32 */ + architectureLayer?: number; + layerActivities?: DecayingAveragePerActivity[] | null; } -export interface HboIDomain { - architectureLayers?: Record; - activities?: Record; - professionalSkills?: Record; - masteryLevels?: Record; +export interface DecayingAveragePerSkill { + /** @format int32 */ + skill?: number; + /** @format double */ + decayingAverage?: number; } -export interface LtiOpenIdConnectCallback { - authenticityToken?: string | null; - idToken?: string | null; - state?: string | null; - ltiStorageTarget?: string | null; +export interface EnrollmentTerm { + name?: string | null; + /** @format date-time */ + start_at?: string | null; + /** @format date-time */ + end_at?: 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; +export interface IHboIDomain { + architectureLayers?: ArchitectureLayer[] | null; + activities?: Activity[] | null; + professionalSkills?: ProfessionalSkill[] | null; + masteryLevels?: MasteryLevel[] | null; } export interface MasteryLevel { - /** @format int32 */ - level?: number; - color?: string | null; -} - -export interface Module { - name?: string | null; -} - -export interface Node { - assignment?: Assignment; - rubricAssessmentsConnection?: RubricAssessmentsConnection; -} - -export interface Outcome { - title?: string | null; + /** @format int32 */ + id?: number; + /** @format int32 */ + level?: number; + color?: string | null; } export interface ProfessionalSkill { - name?: string | null; - shortName?: string | null; - color?: string | null; + /** @format int32 */ + id?: number; + 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; +export interface ProfessionalSkillResult { + /** @format int32 */ + skill?: number; + /** @format int32 */ + masteryLevel?: number; + /** @format double */ + 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; -} - -export interface RubricAssessmentNode { - assessmentRatings?: AssessmentRating[] | null; - user?: User; -} - -export interface RubricAssessmentsConnection { - nodes?: RubricAssessmentNode[] | null; -} - -export interface SubmissionsConnection { - nodes?: Node[] | null; -} - -export interface User { - name?: string | null; +export interface ProfessionalTaskResult { + /** @format int32 */ + architectureLayer?: number; + /** @format int32 */ + activity?: number; + /** @format int32 */ + masteryLevel?: number; + /** @format double */ + grade?: number; + /** @format date-time */ + assessedAt?: string; } 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 type RequestParams = Omit< + FullRequestParams, + "body" | "method" | "query" | "path" +>; 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; +export interface HttpResponse + extends Response { + 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); + } + + 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("&"); } - const abortController = new AbortController(); - this.abortControllers.set(cancelToken, abortController); - return abortController.signal; - }; + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ""; + } - public abortRequest = (cancelToken: CancelToken) => { - const abortController = this.abortControllers.get(cancelToken); + 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), + }; - if (abortController) { - abortController.abort(); - this.abortControllers.delete(cancelToken); + protected mergeRequestParams( + params1: RequestParams, + params2?: RequestParams + ): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; } - }; - - 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 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; + }); + }; } /** * @title Epsilon.Host.WebApi * @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, - }), - }; +export class Api< + SecurityDataType extends unknown +> 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 ComponentDetail + * @request GET:/Component/{componentName} + */ + componentDetail: ( + componentName: string, + query?: { + /** @format date-time */ + startDate?: string; + /** @format date-time */ + endDate?: string; + }, + params: RequestParams = {} + ) => + this.request({ + path: `/Component/${componentName}`, + method: "GET", + query: query, + format: "json", + ...params, + }), + }; + document = { + /** + * No description + * + * @tags Document + * @name WordList + * @request GET:/Document/word + */ + wordList: ( + query?: { + /** @format date-time */ + startDate?: string; + /** @format date-time */ + endDate?: string; + }, + params: RequestParams = {} + ) => + this.request({ + path: `/Document/word`, + method: "GET", + query: query, + ...params, + }), + }; } diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index 87f187e3..75a02032 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -1,28 +1,12 @@ diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index b8860a88..30fc722d 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -16,10 +16,6 @@ public ComponentController(ICompetenceComponentService competenceComponentServic _competenceComponentService = competenceComponentService; } - // - // DateTime.ParseExact(startDate ?? "", "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("en-US")), - // DateTime.Parse(endDate ?? "", CultureInfo.CreateSpecificCulture("en-US")) - [HttpGet("{componentName}")] [Produces(typeof(CompetenceProfile))] public async Task> GetCompetenceProfile(string componentName, DateTime? startDate, DateTime? endDate) From 5f5e44d5eb0fbc3ca513d5c209c5fe583913e508 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Fri, 21 Apr 2023 12:53:09 +0200 Subject: [PATCH 37/72] Created KpiMatrix component --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 + .../Model/GraphQl/AssessmentRating.cs | 3 +- .../Model/GraphQl/Outcome.cs | 3 +- .../GraphQl/SubmissionsConnectionNode.cs | 4 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 + Epsilon.Host.Frontend/pnpm-lock.yaml | 604 +++++++++--------- Epsilon/Component/KpiMatrixComponent.cs | 83 +++ 7 files changed, 435 insertions(+), 299 deletions(-) create mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs create mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs new file mode 100644 index 00000000..87ca98e9 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix.cs @@ -0,0 +1,25 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes + ); + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments + ); + diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs index 5354ed79..d4be774b 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/AssessmentRating.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record AssessmentRating( [property: JsonPropertyName("criterion")] Criterion Criterion, - [property: JsonPropertyName("points")] double? Points + [property: JsonPropertyName("points")] double? Points, + [property: JsonPropertyName("outcome")] Outcome? Outcome ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index d9caddc1..6bdbd704 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -3,5 +3,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( - [property: JsonPropertyName("_id")] int Id + [property: JsonPropertyName("_id")] int Id, + [property: JsonPropertyName("title")] string Title ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs index b5aded9c..013fdbb2 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsConnectionNode.cs @@ -4,5 +4,7 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record SubmissionsConnectionNode( [property: JsonPropertyName("postedAt")] DateTime? PostedAt, - [property: JsonPropertyName("submissionHistoriesConnection")] SubmissionsHistoriesConnection SubmissionsHistories + [property: JsonPropertyName("assignment")] Assignment? Assignment, + [property: JsonPropertyName("submissionHistoriesConnection")] SubmissionsHistoriesConnection SubmissionsHistories, + [property: JsonPropertyName("rubricAssessmentsConnection")] RubricAssessmentsConnection RubricAssessmentsConnection ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs new file mode 100644 index 00000000..ca2dc6ad --- /dev/null +++ b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Epsilon.Canvas.Abstractions.Model.GraphQl; + +namespace Epsilon.Canvas.Abstractions.QueryResponse; +public record GetUserKpiMatrixOutcomes( + [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data +) +{ + public record CourseData( + [property: JsonPropertyName("course")] Course Course + ); +}; \ No newline at end of file diff --git a/Epsilon.Host.Frontend/pnpm-lock.yaml b/Epsilon.Host.Frontend/pnpm-lock.yaml index e59ead27..7cf285cd 100644 --- a/Epsilon.Host.Frontend/pnpm-lock.yaml +++ b/Epsilon.Host.Frontend/pnpm-lock.yaml @@ -1,57 +1,69 @@ -lockfileVersion: 5.4 - -specifiers: - '@types/node': ^18.15.3 - '@typescript-eslint/eslint-plugin': ^5.55.0 - '@typescript-eslint/parser': ^5.55.0 - '@vitejs/plugin-vue': ^4.0.0 - '@vue/eslint-config-typescript': ^11.0.2 - apexcharts: ^3.37.3 - eslint: ^8.36.0 - eslint-plugin-vue: ^9.9.0 - ts-node: ^10.9.1 - typescript: ^4.9.5 - vite: ^4.1.0 - vue: ^3.2.45 - vue-router: ^4.1.6 - vue3-apexcharts: ^1.4.1 +lockfileVersion: '6.0' dependencies: - apexcharts: 3.37.3 - typescript: 4.9.5 - vue: 3.2.47 - vue-router: 4.1.6_vue@3.2.47 - vue3-apexcharts: 1.4.1_w3ch7w2j635wji4x6c2377ue4i + apexcharts: + specifier: ^3.37.3 + version: 3.37.3 + typescript: + specifier: ^4.9.5 + version: 4.9.5 + vue: + specifier: ^3.2.45 + version: 3.2.47 + vue-router: + specifier: ^4.1.6 + version: 4.1.6(vue@3.2.47) + vue3-apexcharts: + specifier: ^1.4.1 + version: 1.4.1(apexcharts@3.37.3)(vue@3.2.47) devDependencies: - '@types/node': 18.15.3 - '@typescript-eslint/eslint-plugin': 5.55.0_342y7v4tc7ytrrysmit6jo4wri - '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m - '@vitejs/plugin-vue': 4.0.0_vite@4.1.4+vue@3.2.47 - '@vue/eslint-config-typescript': 11.0.2_75cttubc7yphoaabg6k3yrjnke - eslint: 8.36.0 - eslint-plugin-vue: 9.9.0_eslint@8.36.0 - ts-node: 10.9.1_cbfmry4sbbh4vatmdrsmatfg5a - vite: 4.1.4_@types+node@18.15.3 + '@types/node': + specifier: ^18.15.3 + version: 18.15.3 + '@typescript-eslint/eslint-plugin': + specifier: ^5.55.0 + version: 5.55.0(@typescript-eslint/parser@5.55.0)(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.55.0 + version: 5.55.0(eslint@8.36.0)(typescript@4.9.5) + '@vitejs/plugin-vue': + specifier: ^4.0.0 + version: 4.0.0(vite@4.1.4)(vue@3.2.47) + '@vue/eslint-config-typescript': + specifier: ^11.0.2 + version: 11.0.2(eslint-plugin-vue@9.9.0)(eslint@8.36.0)(typescript@4.9.5) + eslint: + specifier: ^8.36.0 + version: 8.36.0 + eslint-plugin-vue: + specifier: ^9.9.0 + version: 9.9.0(eslint@8.36.0) + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.15.3)(typescript@4.9.5) + vite: + specifier: ^4.1.0 + version: 4.1.4(@types/node@18.15.3) packages: - /@babel/helper-string-parser/7.19.4: + /@babel/helper-string-parser@7.19.4: resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier/7.19.1: + /@babel/helper-validator-identifier@7.19.1: resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} engines: {node: '>=6.9.0'} - /@babel/parser/7.21.3: + /@babel/parser@7.21.3: resolution: {integrity: sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==} engines: {node: '>=6.0.0'} hasBin: true dependencies: '@babel/types': 7.21.3 - /@babel/types/7.21.3: + /@babel/types@7.21.3: resolution: {integrity: sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==} engines: {node: '>=6.9.0'} dependencies: @@ -59,32 +71,32 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - /@cspotcode/source-map-support/0.8.1: + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} dependencies: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@esbuild/android-arm/0.16.17: - resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} + /@esbuild/android-arm64@0.16.17: + resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-arm64/0.16.17: - resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} + /@esbuild/android-arm@0.16.17: + resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} engines: {node: '>=12'} - cpu: [arm64] + cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-x64/0.16.17: + /@esbuild/android-x64@0.16.17: resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} engines: {node: '>=12'} cpu: [x64] @@ -93,7 +105,7 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64/0.16.17: + /@esbuild/darwin-arm64@0.16.17: resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} engines: {node: '>=12'} cpu: [arm64] @@ -102,7 +114,7 @@ packages: dev: true optional: true - /@esbuild/darwin-x64/0.16.17: + /@esbuild/darwin-x64@0.16.17: resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} engines: {node: '>=12'} cpu: [x64] @@ -111,7 +123,7 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64/0.16.17: + /@esbuild/freebsd-arm64@0.16.17: resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} engines: {node: '>=12'} cpu: [arm64] @@ -120,7 +132,7 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64/0.16.17: + /@esbuild/freebsd-x64@0.16.17: resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} engines: {node: '>=12'} cpu: [x64] @@ -129,25 +141,25 @@ packages: dev: true optional: true - /@esbuild/linux-arm/0.16.17: - resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + /@esbuild/linux-arm64@0.16.17: + resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} engines: {node: '>=12'} - cpu: [arm] + cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-arm64/0.16.17: - resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + /@esbuild/linux-arm@0.16.17: + resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} engines: {node: '>=12'} - cpu: [arm64] + cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-ia32/0.16.17: + /@esbuild/linux-ia32@0.16.17: resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} engines: {node: '>=12'} cpu: [ia32] @@ -156,7 +168,7 @@ packages: dev: true optional: true - /@esbuild/linux-loong64/0.16.17: + /@esbuild/linux-loong64@0.16.17: resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} engines: {node: '>=12'} cpu: [loong64] @@ -165,7 +177,7 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el/0.16.17: + /@esbuild/linux-mips64el@0.16.17: resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} engines: {node: '>=12'} cpu: [mips64el] @@ -174,7 +186,7 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64/0.16.17: + /@esbuild/linux-ppc64@0.16.17: resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} engines: {node: '>=12'} cpu: [ppc64] @@ -183,7 +195,7 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64/0.16.17: + /@esbuild/linux-riscv64@0.16.17: resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} engines: {node: '>=12'} cpu: [riscv64] @@ -192,7 +204,7 @@ packages: dev: true optional: true - /@esbuild/linux-s390x/0.16.17: + /@esbuild/linux-s390x@0.16.17: resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} engines: {node: '>=12'} cpu: [s390x] @@ -201,7 +213,7 @@ packages: dev: true optional: true - /@esbuild/linux-x64/0.16.17: + /@esbuild/linux-x64@0.16.17: resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} engines: {node: '>=12'} cpu: [x64] @@ -210,7 +222,7 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64/0.16.17: + /@esbuild/netbsd-x64@0.16.17: resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} engines: {node: '>=12'} cpu: [x64] @@ -219,7 +231,7 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64/0.16.17: + /@esbuild/openbsd-x64@0.16.17: resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} engines: {node: '>=12'} cpu: [x64] @@ -228,7 +240,7 @@ packages: dev: true optional: true - /@esbuild/sunos-x64/0.16.17: + /@esbuild/sunos-x64@0.16.17: resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} engines: {node: '>=12'} cpu: [x64] @@ -237,7 +249,7 @@ packages: dev: true optional: true - /@esbuild/win32-arm64/0.16.17: + /@esbuild/win32-arm64@0.16.17: resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} engines: {node: '>=12'} cpu: [arm64] @@ -246,7 +258,7 @@ packages: dev: true optional: true - /@esbuild/win32-ia32/0.16.17: + /@esbuild/win32-ia32@0.16.17: resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} engines: {node: '>=12'} cpu: [ia32] @@ -255,7 +267,7 @@ packages: dev: true optional: true - /@esbuild/win32-x64/0.16.17: + /@esbuild/win32-x64@0.16.17: resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} engines: {node: '>=12'} cpu: [x64] @@ -264,7 +276,7 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils/4.2.0_eslint@8.36.0: + /@eslint-community/eslint-utils@4.2.0(eslint@8.36.0): resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -274,12 +286,12 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@eslint-community/regexpp/4.4.0: + /@eslint-community/regexpp@4.4.0: resolution: {integrity: sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc/2.0.1: + /@eslint/eslintrc@2.0.1: resolution: {integrity: sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -296,12 +308,12 @@ packages: - supports-color dev: true - /@eslint/js/8.36.0: + /@eslint/js@8.36.0: resolution: {integrity: sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@humanwhocodes/config-array/0.11.8: + /@humanwhocodes/config-array@0.11.8: resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} engines: {node: '>=10.10.0'} dependencies: @@ -312,32 +324,32 @@ packages: - supports-color dev: true - /@humanwhocodes/module-importer/1.0.1: + /@humanwhocodes/module-importer@1.0.1: resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema/1.2.1: + /@humanwhocodes/object-schema@1.2.1: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@jridgewell/resolve-uri/3.1.0: + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/sourcemap-codec/1.4.14: + /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true - /@jridgewell/trace-mapping/0.3.9: + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 dev: true - /@nodelib/fs.scandir/2.1.5: + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} dependencies: @@ -345,12 +357,12 @@ packages: run-parallel: 1.2.0 dev: true - /@nodelib/fs.stat/2.0.5: + /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} dev: true - /@nodelib/fs.walk/1.2.8: + /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} dependencies: @@ -358,35 +370,35 @@ packages: fastq: 1.15.0 dev: true - /@tsconfig/node10/1.0.9: + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true - /@tsconfig/node12/1.0.11: + /@tsconfig/node12@1.0.11: resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} dev: true - /@tsconfig/node14/1.0.3: + /@tsconfig/node14@1.0.3: resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} dev: true - /@tsconfig/node16/1.0.3: + /@tsconfig/node16@1.0.3: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@types/json-schema/7.0.11: + /@types/json-schema@7.0.11: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true - /@types/node/18.15.3: + /@types/node@18.15.3: resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} dev: true - /@types/semver/7.3.13: + /@types/semver@7.3.13: resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} dev: true - /@typescript-eslint/eslint-plugin/5.55.0_342y7v4tc7ytrrysmit6jo4wri: + /@typescript-eslint/eslint-plugin@5.55.0(@typescript-eslint/parser@5.55.0)(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -398,23 +410,23 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.4.0 - '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/parser': 5.55.0(eslint@8.36.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 5.55.0 - '@typescript-eslint/type-utils': 5.55.0_vgl77cfdswitgr47lm5swmv43m - '@typescript-eslint/utils': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/type-utils': 5.55.0(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.55.0(eslint@8.36.0)(typescript@4.9.5) debug: 4.3.4 eslint: 8.36.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.9.5 + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/5.55.0_vgl77cfdswitgr47lm5swmv43m: + /@typescript-eslint/parser@5.55.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -426,7 +438,7 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.55.0 '@typescript-eslint/types': 5.55.0 - '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5 + '@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5) debug: 4.3.4 eslint: 8.36.0 typescript: 4.9.5 @@ -434,7 +446,7 @@ packages: - supports-color dev: true - /@typescript-eslint/scope-manager/5.55.0: + /@typescript-eslint/scope-manager@5.55.0: resolution: {integrity: sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -442,7 +454,7 @@ packages: '@typescript-eslint/visitor-keys': 5.55.0 dev: true - /@typescript-eslint/type-utils/5.55.0_vgl77cfdswitgr47lm5swmv43m: + /@typescript-eslint/type-utils@5.55.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -452,22 +464,22 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5 - '@typescript-eslint/utils': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5) + '@typescript-eslint/utils': 5.55.0(eslint@8.36.0)(typescript@4.9.5) debug: 4.3.4 eslint: 8.36.0 - tsutils: 3.21.0_typescript@4.9.5 + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/types/5.55.0: + /@typescript-eslint/types@5.55.0: resolution: {integrity: sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.55.0_typescript@4.9.5: + /@typescript-eslint/typescript-estree@5.55.0(typescript@4.9.5): resolution: {integrity: sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -482,24 +494,24 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.8 - tsutils: 3.21.0_typescript@4.9.5 + tsutils: 3.21.0(typescript@4.9.5) typescript: 4.9.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.55.0_vgl77cfdswitgr47lm5swmv43m: + /@typescript-eslint/utils@5.55.0(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@eslint-community/eslint-utils': 4.2.0(eslint@8.36.0) '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 '@typescript-eslint/scope-manager': 5.55.0 '@typescript-eslint/types': 5.55.0 - '@typescript-eslint/typescript-estree': 5.55.0_typescript@4.9.5 + '@typescript-eslint/typescript-estree': 5.55.0(typescript@4.9.5) eslint: 8.36.0 eslint-scope: 5.1.1 semver: 7.3.8 @@ -508,7 +520,7 @@ packages: - typescript dev: true - /@typescript-eslint/visitor-keys/5.55.0: + /@typescript-eslint/visitor-keys@5.55.0: resolution: {integrity: sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -516,18 +528,18 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@vitejs/plugin-vue/4.0.0_vite@4.1.4+vue@3.2.47: + /@vitejs/plugin-vue@4.0.0(vite@4.1.4)(vue@3.2.47): resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.0.0 vue: ^3.2.25 dependencies: - vite: 4.1.4_@types+node@18.15.3 + vite: 4.1.4(@types/node@18.15.3) vue: 3.2.47 dev: true - /@vue/compiler-core/3.2.47: + /@vue/compiler-core@3.2.47: resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} dependencies: '@babel/parser': 7.21.3 @@ -535,13 +547,13 @@ packages: estree-walker: 2.0.2 source-map: 0.6.1 - /@vue/compiler-dom/3.2.47: + /@vue/compiler-dom@3.2.47: resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} dependencies: '@vue/compiler-core': 3.2.47 '@vue/shared': 3.2.47 - /@vue/compiler-sfc/3.2.47: + /@vue/compiler-sfc@3.2.47: resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} dependencies: '@babel/parser': 7.21.3 @@ -555,17 +567,17 @@ packages: postcss: 8.4.21 source-map: 0.6.1 - /@vue/compiler-ssr/3.2.47: + /@vue/compiler-ssr@3.2.47: resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} dependencies: '@vue/compiler-dom': 3.2.47 '@vue/shared': 3.2.47 - /@vue/devtools-api/6.5.0: + /@vue/devtools-api@6.5.0: resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} dev: false - /@vue/eslint-config-typescript/11.0.2_75cttubc7yphoaabg6k3yrjnke: + /@vue/eslint-config-typescript@11.0.2(eslint-plugin-vue@9.9.0)(eslint@8.36.0)(typescript@4.9.5): resolution: {integrity: sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: @@ -576,17 +588,17 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 5.55.0_342y7v4tc7ytrrysmit6jo4wri - '@typescript-eslint/parser': 5.55.0_vgl77cfdswitgr47lm5swmv43m + '@typescript-eslint/eslint-plugin': 5.55.0(@typescript-eslint/parser@5.55.0)(eslint@8.36.0)(typescript@4.9.5) + '@typescript-eslint/parser': 5.55.0(eslint@8.36.0)(typescript@4.9.5) eslint: 8.36.0 - eslint-plugin-vue: 9.9.0_eslint@8.36.0 + eslint-plugin-vue: 9.9.0(eslint@8.36.0) typescript: 4.9.5 - vue-eslint-parser: 9.1.0_eslint@8.36.0 + vue-eslint-parser: 9.1.0(eslint@8.36.0) transitivePeerDependencies: - supports-color dev: true - /@vue/reactivity-transform/3.2.47: + /@vue/reactivity-transform@3.2.47: resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} dependencies: '@babel/parser': 7.21.3 @@ -595,25 +607,25 @@ packages: estree-walker: 2.0.2 magic-string: 0.25.9 - /@vue/reactivity/3.2.47: + /@vue/reactivity@3.2.47: resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==} dependencies: '@vue/shared': 3.2.47 - /@vue/runtime-core/3.2.47: + /@vue/runtime-core@3.2.47: resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==} dependencies: '@vue/reactivity': 3.2.47 '@vue/shared': 3.2.47 - /@vue/runtime-dom/3.2.47: + /@vue/runtime-dom@3.2.47: resolution: {integrity: sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==} dependencies: '@vue/runtime-core': 3.2.47 '@vue/shared': 3.2.47 csstype: 2.6.21 - /@vue/server-renderer/3.2.47_vue@3.2.47: + /@vue/server-renderer@3.2.47(vue@3.2.47): resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==} peerDependencies: vue: 3.2.47 @@ -622,10 +634,10 @@ packages: '@vue/shared': 3.2.47 vue: 3.2.47 - /@vue/shared/3.2.47: + /@vue/shared@3.2.47: resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} - /acorn-jsx/5.3.2_acorn@8.8.2: + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -633,18 +645,18 @@ packages: acorn: 8.8.2 dev: true - /acorn-walk/8.2.0: + /acorn-walk@8.2.0: resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} engines: {node: '>=0.4.0'} dev: true - /acorn/8.8.2: + /acorn@8.8.2: resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} engines: {node: '>=0.4.0'} hasBin: true dev: true - /ajv/6.12.6: + /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} dependencies: fast-deep-equal: 3.1.3 @@ -653,19 +665,19 @@ packages: uri-js: 4.4.1 dev: true - /ansi-regex/5.0.1: + /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true - /ansi-styles/4.3.0: + /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} dependencies: color-convert: 2.0.1 dev: true - /apexcharts/3.37.3: + /apexcharts@3.37.3: resolution: {integrity: sha512-+rnUui9uC3Mvh9qbQxUfqBnuJ0nAJOYTp+yKnA5bVmmndKXj5X/Q+OVIxkq0Jr5ysiK200Dsg1Tuz/OUG+DEpw==} dependencies: svg.draggable.js: 2.2.2 @@ -676,47 +688,47 @@ packages: svg.select.js: 3.0.1 dev: false - /arg/4.1.3: + /arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} dev: true - /argparse/2.0.1: + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true - /array-union/2.1.0: + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} dev: true - /balanced-match/1.0.2: + /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /boolbase/1.0.0: + /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true - /brace-expansion/1.1.11: + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 dev: true - /braces/3.0.2: + /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} dependencies: fill-range: 7.0.1 dev: true - /callsites/3.1.0: + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} dev: true - /chalk/4.1.2: + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} dependencies: @@ -724,26 +736,26 @@ packages: supports-color: 7.2.0 dev: true - /color-convert/2.0.1: + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 dev: true - /color-name/1.1.4: + /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true - /concat-map/0.0.1: + /concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /create-require/1.1.1: + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true - /cross-spawn/7.0.3: + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} dependencies: @@ -752,16 +764,16 @@ packages: which: 2.0.2 dev: true - /cssesc/3.0.0: + /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true dev: true - /csstype/2.6.21: + /csstype@2.6.21: resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} - /debug/4.3.4: + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -773,30 +785,30 @@ packages: ms: 2.1.2 dev: true - /deep-is/0.1.4: + /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /diff/4.0.2: + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} dev: true - /dir-glob/3.0.1: + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dependencies: path-type: 4.0.0 dev: true - /doctrine/3.0.0: + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} dependencies: esutils: 2.0.3 dev: true - /esbuild/0.16.17: + /esbuild@0.16.17: resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} engines: {node: '>=12'} hasBin: true @@ -826,30 +838,30 @@ packages: '@esbuild/win32-x64': 0.16.17 dev: true - /escape-string-regexp/4.0.0: + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} dev: true - /eslint-plugin-vue/9.9.0_eslint@8.36.0: + /eslint-plugin-vue@9.9.0(eslint@8.36.0): resolution: {integrity: sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: eslint: 8.36.0 - eslint-utils: 3.0.0_eslint@8.36.0 + eslint-utils: 3.0.0(eslint@8.36.0) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.11 semver: 7.3.8 - vue-eslint-parser: 9.1.0_eslint@8.36.0 + vue-eslint-parser: 9.1.0(eslint@8.36.0) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color dev: true - /eslint-scope/5.1.1: + /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} dependencies: @@ -857,7 +869,7 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope/7.1.1: + /eslint-scope@7.1.1: resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: @@ -865,7 +877,7 @@ packages: estraverse: 5.3.0 dev: true - /eslint-utils/3.0.0_eslint@8.36.0: + /eslint-utils@3.0.0(eslint@8.36.0): resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} peerDependencies: @@ -875,22 +887,22 @@ packages: eslint-visitor-keys: 2.1.0 dev: true - /eslint-visitor-keys/2.1.0: + /eslint-visitor-keys@2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} dev: true - /eslint-visitor-keys/3.3.0: + /eslint-visitor-keys@3.3.0: resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint/8.36.0: + /eslint@8.36.0: resolution: {integrity: sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@eslint-community/eslint-utils': 4.2.0(eslint@8.36.0) '@eslint-community/regexpp': 4.4.0 '@eslint/eslintrc': 2.0.1 '@eslint/js': 8.36.0 @@ -934,52 +946,52 @@ packages: - supports-color dev: true - /espree/9.5.0: + /espree@9.5.0: resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: acorn: 8.8.2 - acorn-jsx: 5.3.2_acorn@8.8.2 + acorn-jsx: 5.3.2(acorn@8.8.2) eslint-visitor-keys: 3.3.0 dev: true - /esquery/1.5.0: + /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} dependencies: estraverse: 5.3.0 dev: true - /esrecurse/4.3.0: + /esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} dependencies: estraverse: 5.3.0 dev: true - /estraverse/4.3.0: + /estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} dev: true - /estraverse/5.3.0: + /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} dev: true - /estree-walker/2.0.2: + /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - /esutils/2.0.3: + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} dev: true - /fast-deep-equal/3.1.3: + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true - /fast-glob/3.2.12: + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} dependencies: @@ -990,35 +1002,35 @@ packages: micromatch: 4.0.5 dev: true - /fast-json-stable-stringify/2.1.0: + /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} dev: true - /fast-levenshtein/2.0.6: + /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fastq/1.15.0: + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 dev: true - /file-entry-cache/6.0.1: + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: flat-cache: 3.0.4 dev: true - /fill-range/7.0.1: + /fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 dev: true - /find-up/5.0.0: + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} dependencies: @@ -1026,7 +1038,7 @@ packages: path-exists: 4.0.0 dev: true - /flat-cache/3.0.4: + /flat-cache@3.0.4: resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} engines: {node: ^10.12.0 || >=12.0.0} dependencies: @@ -1034,15 +1046,15 @@ packages: rimraf: 3.0.2 dev: true - /flatted/3.2.7: + /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true - /fs.realpath/1.0.0: + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} dev: true - /fsevents/2.3.2: + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] @@ -1050,25 +1062,25 @@ packages: dev: true optional: true - /function-bind/1.1.1: + /function-bind@1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /glob-parent/5.1.2: + /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 dev: true - /glob-parent/6.0.2: + /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} dependencies: is-glob: 4.0.3 dev: true - /glob/7.2.3: + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 @@ -1079,14 +1091,14 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals/13.20.0: + /globals@13.20.0: resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} engines: {node: '>=8'} dependencies: type-fest: 0.20.2 dev: true - /globby/11.1.0: + /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} dependencies: @@ -1098,28 +1110,28 @@ packages: slash: 3.0.0 dev: true - /grapheme-splitter/1.0.4: + /grapheme-splitter@1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true - /has-flag/4.0.0: + /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} dev: true - /has/1.0.3: + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} dependencies: function-bind: 1.1.1 dev: true - /ignore/5.2.4: + /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} dev: true - /import-fresh/3.3.0: + /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} dependencies: @@ -1127,74 +1139,74 @@ packages: resolve-from: 4.0.0 dev: true - /imurmurhash/0.1.4: + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} dev: true - /inflight/1.0.6: + /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 dev: true - /inherits/2.0.4: + /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} dev: true - /is-core-module/2.11.0: + /is-core-module@2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: has: 1.0.3 dev: true - /is-extglob/2.1.1: + /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} dev: true - /is-glob/4.0.3: + /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 dev: true - /is-number/7.0.0: + /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} dev: true - /is-path-inside/3.0.3: + /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} dev: true - /isexe/2.0.0: + /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /js-sdsl/4.3.0: + /js-sdsl@4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} dev: true - /js-yaml/4.1.0: + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true dependencies: argparse: 2.0.1 dev: true - /json-schema-traverse/0.4.1: + /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true - /json-stable-stringify-without-jsonify/1.0.1: + /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /levn/0.4.1: + /levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} dependencies: @@ -1202,43 +1214,43 @@ packages: type-check: 0.4.0 dev: true - /locate-path/6.0.0: + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} dependencies: p-locate: 5.0.0 dev: true - /lodash.merge/4.6.2: + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /lodash/4.17.21: + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: true - /lru-cache/6.0.0: + /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} dependencies: yallist: 4.0.0 dev: true - /magic-string/0.25.9: + /magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} dependencies: sourcemap-codec: 1.4.8 - /make-error/1.3.6: + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /merge2/1.4.1: + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} dev: true - /micromatch/4.0.5: + /micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} dependencies: @@ -1246,42 +1258,42 @@ packages: picomatch: 2.3.1 dev: true - /minimatch/3.1.2: + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 dev: true - /ms/2.1.2: + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true - /nanoid/3.3.4: + /nanoid@3.3.4: resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - /natural-compare-lite/1.4.0: + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true - /natural-compare/1.4.0: + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /nth-check/2.1.1: + /nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} dependencies: boolbase: 1.0.0 dev: true - /once/1.4.0: + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true - /optionator/0.9.1: + /optionator@0.9.1: resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} engines: {node: '>= 0.8.0'} dependencies: @@ -1293,60 +1305,60 @@ packages: word-wrap: 1.2.3 dev: true - /p-limit/3.1.0: + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 dev: true - /p-locate/5.0.0: + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} dependencies: p-limit: 3.1.0 dev: true - /parent-module/1.0.1: + /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} dependencies: callsites: 3.1.0 dev: true - /path-exists/4.0.0: + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} dev: true - /path-is-absolute/1.0.1: + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} dev: true - /path-key/3.1.1: + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} dev: true - /path-parse/1.0.7: + /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true - /path-type/4.0.0: + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} dev: true - /picocolors/1.0.0: + /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - /picomatch/2.3.1: + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: true - /postcss-selector-parser/6.0.11: + /postcss-selector-parser@6.0.11: resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} engines: {node: '>=4'} dependencies: @@ -1354,7 +1366,7 @@ packages: util-deprecate: 1.0.2 dev: true - /postcss/8.4.21: + /postcss@8.4.21: resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} engines: {node: ^10 || ^12 || >=14} dependencies: @@ -1362,26 +1374,26 @@ packages: picocolors: 1.0.0 source-map-js: 1.0.2 - /prelude-ls/1.2.1: + /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} dev: true - /punycode/2.3.0: + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} dev: true - /queue-microtask/1.2.3: + /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /resolve-from/4.0.0: + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} dev: true - /resolve/1.22.1: + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true dependencies: @@ -1390,19 +1402,19 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /reusify/1.0.4: + /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} dev: true - /rimraf/3.0.2: + /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true dependencies: glob: 7.2.3 dev: true - /rollup/3.19.1: + /rollup@3.19.1: resolution: {integrity: sha512-lAbrdN7neYCg/8WaoWn/ckzCtz+jr70GFfYdlf50OF7387HTg+wiuiqJRFYawwSPpqfqDNYqK7smY/ks2iAudg==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true @@ -1410,13 +1422,13 @@ packages: fsevents: 2.3.2 dev: true - /run-parallel/1.2.0: + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 dev: true - /semver/7.3.8: + /semver@7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} hasBin: true @@ -1424,92 +1436,92 @@ packages: lru-cache: 6.0.0 dev: true - /shebang-command/2.0.0: + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 dev: true - /shebang-regex/3.0.0: + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} dev: true - /slash/3.0.0: + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} dev: true - /source-map-js/1.0.2: + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - /source-map/0.6.1: + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - /sourcemap-codec/1.4.8: + /sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead - /strip-ansi/6.0.1: + /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 dev: true - /strip-json-comments/3.1.1: + /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true - /supports-color/7.2.0: + /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} dependencies: has-flag: 4.0.0 dev: true - /supports-preserve-symlinks-flag/1.0.0: + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true - /svg.draggable.js/2.2.2: + /svg.draggable.js@2.2.2: resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.easing.js/2.0.0: + /svg.easing.js@2.0.0: resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.filter.js/2.0.2: + /svg.filter.js@2.0.2: resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.js/2.7.1: + /svg.js@2.7.1: resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==} dev: false - /svg.pathmorphing.js/0.1.3: + /svg.pathmorphing.js@0.1.3: resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.resize.js/1.4.3: + /svg.resize.js@1.4.3: resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==} engines: {node: '>= 0.8.0'} dependencies: @@ -1517,36 +1529,36 @@ packages: svg.select.js: 2.1.2 dev: false - /svg.select.js/2.1.2: + /svg.select.js@2.1.2: resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /svg.select.js/3.0.1: + /svg.select.js@3.0.1: resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==} engines: {node: '>= 0.8.0'} dependencies: svg.js: 2.7.1 dev: false - /text-table/0.2.0: + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true - /to-fast-properties/2.0.0: + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} - /to-regex-range/5.0.1: + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 dev: true - /ts-node/10.9.1_cbfmry4sbbh4vatmdrsmatfg5a: + /ts-node@10.9.1(@types/node@18.15.3)(typescript@4.9.5): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -1577,11 +1589,11 @@ packages: yn: 3.1.1 dev: true - /tslib/1.14.1: + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true - /tsutils/3.21.0_typescript@4.9.5: + /tsutils@3.21.0(typescript@4.9.5): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: @@ -1591,38 +1603,38 @@ packages: typescript: 4.9.5 dev: true - /type-check/0.4.0: + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} dependencies: prelude-ls: 1.2.1 dev: true - /type-fest/0.20.2: + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} dev: true - /typescript/4.9.5: + /typescript@4.9.5: resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} engines: {node: '>=4.2.0'} hasBin: true - /uri-js/4.4.1: + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.0 dev: true - /util-deprecate/1.0.2: + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true - /v8-compile-cache-lib/3.0.1: + /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /vite/4.1.4_@types+node@18.15.3: + /vite@4.1.4(@types/node@18.15.3): resolution: {integrity: sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -1656,7 +1668,7 @@ packages: fsevents: 2.3.2 dev: true - /vue-eslint-parser/9.1.0_eslint@8.36.0: + /vue-eslint-parser@9.1.0(eslint@8.36.0): resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: @@ -1674,7 +1686,7 @@ packages: - supports-color dev: true - /vue-router/4.1.6_vue@3.2.47: + /vue-router@4.1.6(vue@3.2.47): resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==} peerDependencies: vue: ^3.2.0 @@ -1683,16 +1695,7 @@ packages: vue: 3.2.47 dev: false - /vue/3.2.47: - resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} - dependencies: - '@vue/compiler-dom': 3.2.47 - '@vue/compiler-sfc': 3.2.47 - '@vue/runtime-dom': 3.2.47 - '@vue/server-renderer': 3.2.47_vue@3.2.47 - '@vue/shared': 3.2.47 - - /vue3-apexcharts/1.4.1_w3ch7w2j635wji4x6c2377ue4i: + /vue3-apexcharts@1.4.1(apexcharts@3.37.3)(vue@3.2.47): resolution: {integrity: sha512-96qP8JDqB9vwU7bkG5nVU+E0UGQn7yYQVqUUCLQMYWDuQyu2vE77H/UFZ1yI+hwzlSTBKT9BqnNG8JsFegB3eg==} peerDependencies: apexcharts: '> 3.0.0' @@ -1702,7 +1705,16 @@ packages: vue: 3.2.47 dev: false - /which/2.0.2: + /vue@3.2.47: + resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} + dependencies: + '@vue/compiler-dom': 3.2.47 + '@vue/compiler-sfc': 3.2.47 + '@vue/runtime-dom': 3.2.47 + '@vue/server-renderer': 3.2.47(vue@3.2.47) + '@vue/shared': 3.2.47 + + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true @@ -1710,30 +1722,30 @@ packages: isexe: 2.0.0 dev: true - /word-wrap/1.2.3: + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} dev: true - /wrappy/1.0.2: + /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /xml-name-validator/4.0.0: + /xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} dev: true - /yallist/4.0.0: + /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yn/3.1.1: + /yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} dev: true - /yocto-queue/0.1.0: + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs new file mode 100644 index 00000000..d157702a --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -0,0 +1,83 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Canvas.Abstractions.Service; +using Microsoft.Extensions.Configuration; + +namespace Epsilon.Component; + +public class KpiMatrixComponent : Component +{ + + + private readonly IConfiguration _configuration; + private readonly IGraphQlHttpService _graphQlService; + private readonly IAccountHttpService _accountHttpService; + + public KpiMatrixComponent( + IGraphQlHttpService graphQlService, + IAccountHttpService accountHttpService, + IConfiguration configuration + ) + { + _graphQlService = graphQlService; + _accountHttpService = accountHttpService; + _configuration = configuration; + } + + public async override Task Fetch() + { + var courseId = _configuration["Canvas:courseId"]; + + var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); + var outcomes = await _graphQlService.Query(outcomesQuery); + + var kpiMatrix = ConvertToComponent(outcomes); + + return kpiMatrix; + } + + private GradeStatus GetGradeStatus(double? points) + { + return points switch + { + null => GradeStatus.NotGraded, + >= 4 => GradeStatus.Approved, + _ => GradeStatus.Insufficient + }; + } + + private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + { + var outcomes = new List(); + var assignments = new List(); + + if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) + return new KpiMatrix(outcomes, assignments); + foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + { + var assignmentOutcomes = new List(); + var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() + ?.AssessmentRatings; + if (assessmentRatings == null) continue; + + foreach (var assessmentRating in assessmentRatings) + { + if (assessmentRating.Outcome != null) + { + assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points))); + if (outcomes.All(o => o != assessmentRating.Outcome.Title)) + { + outcomes.Add(assessmentRating.Outcome.Title); + } + } + } + + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + } + + return new KpiMatrix(outcomes ,assignments); + } +} \ No newline at end of file From 16223e53583a67918639cf4d86c6efe8332b2bc9 Mon Sep 17 00:00:00 2001 From: Jasper123pyah <73039915+Jasper123pyah@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:09:47 +0200 Subject: [PATCH 38/72] Added modules and fixed comments --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ----- .../Model/KpiMatrix/GradeStatus.cs | 8 ++ .../Model/KpiMatrix/KpiMatrix.cs | 9 ++ .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 + .../Model/KpiMatrix/KpiMatrixModule.cs | 6 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 + .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 + .../Model/GraphQl/Outcome.cs | 3 +- Epsilon.Canvas/QueryConstants.cs | 1 + Epsilon/Component/KpiMatrixComponent.cs | 103 +++++++++++------- 10 files changed, 105 insertions(+), 65 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs create mode 100644 Epsilon.Canvas/QueryConstants.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs deleted file mode 100644 index 87ca98e9..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes - ); - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments - ); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs new file mode 100644 index 00000000..d50d2254 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs @@ -0,0 +1,8 @@ +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs new file mode 100644 index 00000000..a86409c5 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs @@ -0,0 +1,9 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs new file mode 100644 index 00000000..6ee6bdcb --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs new file mode 100644 index 00000000..9a874e98 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixModule( + string Name, + KpiMatrix KpiMatrix +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs new file mode 100644 index 00000000..efe15667 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -0,0 +1,5 @@ +namespace Epsilon.Abstractions.Model; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs new file mode 100644 index 00000000..c3aa2732 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs @@ -0,0 +1,4 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixProfile( + IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index 6bdbd704..1a0f6055 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( [property: JsonPropertyName("_id")] int Id, - [property: JsonPropertyName("title")] string Title + [property: JsonPropertyName("title")] string Title, + [property: JsonPropertyName("masteryPoints")] double MasteryPoints ); \ No newline at end of file diff --git a/Epsilon.Canvas/QueryConstants.cs b/Epsilon.Canvas/QueryConstants.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/Epsilon.Canvas/QueryConstants.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index d157702a..b65bd7d7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -1,4 +1,5 @@ -using Epsilon.Abstractions.Component; +using DocumentFormat.OpenXml.Spreadsheet; +using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Canvas.Abstractions.QueryResponse; @@ -7,10 +8,8 @@ namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponent : Component { - - private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; private readonly IAccountHttpService _accountHttpService; @@ -25,59 +24,85 @@ IConfiguration configuration _accountHttpService = accountHttpService; _configuration = configuration; } - - public async override Task Fetch() + + public async override Task Fetch() { var courseId = _configuration["Canvas:courseId"]; var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); var outcomes = await _graphQlService.Query(outcomesQuery); - - var kpiMatrix = ConvertToComponent(outcomes); - return kpiMatrix; + var modules = ConvertToComponent(outcomes); + + return modules; } - private GradeStatus GetGradeStatus(double? points) + private GradeStatus GetGradeStatus(double? points, double mastery) { - return points switch + if (points == null) + { + return GradeStatus.NotGraded; + } + else if (points >= mastery) { - null => GradeStatus.NotGraded, - >= 4 => GradeStatus.Approved, - _ => GradeStatus.Insufficient - }; + return GradeStatus.Approved; + } + else + { + return GradeStatus.Insufficient; + } } - - private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + + + private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) { - var outcomes = new List(); - var assignments = new List(); + var modules = new List(); - if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) - return new KpiMatrix(outcomes, assignments); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - var assignmentOutcomes = new List(); - var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() - ?.AssessmentRatings; - if (assessmentRatings == null) continue; - - foreach (var assessmentRating in assessmentRatings) + // Check if the node has a module + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) continue; + + // Group the assignment outcomes by outcome title + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(n => n?.AssessmentRatings != null) + .SelectMany(n => n.AssessmentRatings) + .Where(r => r.Outcome != null) + .GroupBy(r => r.Outcome.Title); + + var outcomes = outcomeGroups.Select(g => g.Key); + var assignments = outcomeGroups.Select(g => + new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) + )); + + // Find the module in the list of modules or create a new one + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + modules.Add(module); + } + else + { + var moduleIndex = modules.IndexOf(module); + if (moduleIndex == -1) { - if (assessmentRating.Outcome != null) - { - assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points))); - if (outcomes.All(o => o != assessmentRating.Outcome.Title)) - { - outcomes.Add(assessmentRating.Outcome.Title); - } - } + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); } - - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + else + { + var existingModule = modules[moduleIndex]; + var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); + modules[moduleIndex] = newModule; + } + } } - return new KpiMatrix(outcomes ,assignments); + return new KpiMatrixProfile(modules); } } \ No newline at end of file From bad4f0ed2bac3b8cfc9c515c0657879c71946b51 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 25 Apr 2023 12:29:05 +0200 Subject: [PATCH 39/72] Fixed an issue where outcomes where duplicate --- Epsilon/Component/KpiMatrixComponent.cs | 42 ++++++++++++++++++------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index b65bd7d7..75ccd18f 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -60,29 +60,49 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - // Check if the node has a module var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; if (moduleName == null) continue; - // Group the assignment outcomes by outcome title var outcomeGroups = node.RubricAssessmentsConnection.Nodes .Where(n => n?.AssessmentRatings != null) .SelectMany(n => n.AssessmentRatings) .Where(r => r.Outcome != null) .GroupBy(r => r.Outcome.Title); - var outcomes = outcomeGroups.Select(g => g.Key); - var assignments = outcomeGroups.Select(g => - new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + var existingOutcomes = new HashSet(); + var outcomes = new List(); + var assignments = new List(); + + foreach (var outcomeGroup in outcomeGroups) + { + var outcomeTitle = outcomeGroup.Key; + if (existingOutcomes.Contains(outcomeTitle)) continue; + + existingOutcomes.Add(outcomeTitle); + outcomes.Add(outcomeTitle); + + var outcomeAssessments = outcomeGroup.Select(assessmentRating => new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) - )); + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); + + var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); + if (assignment == null) + { + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments)); + } + else + { + assignments.Remove(assignment); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessments).ToList(); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + } + + } - // Find the module in the list of modules or create a new one var module = modules.FirstOrDefault(m => m.Name == moduleName); if (module == null) { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments)); modules.Add(module); } else @@ -90,13 +110,13 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM var moduleIndex = modules.IndexOf(module); if (moduleIndex == -1) { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments))); } else { var existingModule = modules[moduleIndex]; var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes).Distinct(); var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); modules[moduleIndex] = newModule; } From 3fdcc820f60c06f43f93ab234b958d794f7edaf1 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Mon, 8 May 2023 15:30:23 +0200 Subject: [PATCH 40/72] Created KPIMatrix in word document --- .../KpiMatrixComponentWordConverter.cs | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 Epsilon/Component/KpiMatrixComponentWordConverter.cs diff --git a/Epsilon/Component/KpiMatrixComponentWordConverter.cs b/Epsilon/Component/KpiMatrixComponentWordConverter.cs new file mode 100644 index 00000000..e845a2f8 --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponentWordConverter.cs @@ -0,0 +1,122 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using System.Linq; +using System.Threading.Tasks; + +namespace Epsilon.Component +{ + public class KpiMatrixComponentWordConverter : ComponentConverter + { + public override Task Convert(KpiMatrixProfile component) + { + var body = new Body(); + + foreach (var module in component.KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth() { Width = "100%", Type = TableWidthUnitValues.Pct })); + + // Calculate the header row height based on the longest assignment name. + int headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 50; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { Val = (UInt32Value)(uint)headerRowHeight })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders(); + cell.FirstChild.Append(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(outcome))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome); + var cell = CreateTableCellWithBorders(); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF" + }; + cell.FirstChild.Append(new Shading() { Fill = fillColor }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + } + + return Task.FromResult(body); + } + + private TableCell CreateTableCellWithBorders(params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder() { Val = BorderValues.Single }, + new RightBorder() { Val = BorderValues.Single }, + new TopBorder() { Val = BorderValues.Single }, + new BottomBorder() { Val = BorderValues.Single }); + + if (elements != null) + { + foreach (var element in elements) + { + cell.Append(element); + } + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); // Move the PrependChild here + + return cell; + } + + } +} \ No newline at end of file From b0715fa5a5e3ac52f244fd51bbb66b0e0dd8f585 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 12:20:46 +0200 Subject: [PATCH 41/72] Fixed all remaining issues. --- .../Model/KpiMatrix/KpiMatrix.cs | 4 +- .../Model/KpiMatrix/KpiMatrixAssignment.cs | 2 +- .../Model/KpiMatrix/KpiMatrixOutcome.cs | 1 + Epsilon/Component/KpiMatrixComponent.cs | 58 +++++++++---------- .../KpiMatrixComponentWordConverter.cs | 28 +++++---- 5 files changed, 47 insertions(+), 46 deletions(-) diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs index a86409c5..5ba95040 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs @@ -3,7 +3,7 @@ namespace Epsilon.Abstractions.Model; public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments + List Outcomes, + List Assignments ); diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs index 6ee6bdcb..f4cdb0a7 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs @@ -2,5 +2,5 @@ namespace Epsilon.Abstractions.Model; public record KpiMatrixAssignment( string Name, - IEnumerable Outcomes + List Outcomes ); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs index efe15667..0d616204 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -1,5 +1,6 @@ namespace Epsilon.Abstractions.Model; public record KpiMatrixOutcome( + int Id, string Title, GradeStatus GradeStatus ); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index 75ccd18f..3a24dcb7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -67,28 +67,42 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM .Where(n => n?.AssessmentRatings != null) .SelectMany(n => n.AssessmentRatings) .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Title); + .GroupBy(r => r.Outcome.Id); - var existingOutcomes = new HashSet(); - var outcomes = new List(); var assignments = new List(); + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, + new KpiMatrix(new List(), new List())); + modules.Add(module); + } + + var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(o => o.Id); + foreach (var outcomeGroup in outcomeGroups) { - var outcomeTitle = outcomeGroup.Key; - if (existingOutcomes.Contains(outcomeTitle)) continue; + var outcomeId = outcomeGroup.Key; - existingOutcomes.Add(outcomeTitle); - outcomes.Add(outcomeTitle); + // Add outcome to moduleOutcomes if it doesn't exist + if (!moduleOutcomes.ContainsKey(outcomeId)) + { + var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; + var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, + GetGradeStatus(0, outcome.MasteryPoints)); + moduleOutcomes[outcomeId] = kpiMatrixOutcome; + module.KpiMatrix.Outcomes.Add(kpiMatrixOutcome); + } var outcomeAssessments = outcomeGroup.Select(assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Title, + new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); if (assignment == null) { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments)); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments.ToList())); } else { @@ -96,31 +110,11 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessments).ToList(); assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); } - } - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments)); - modules.Add(module); - } - else - { - var moduleIndex = modules.IndexOf(module); - if (moduleIndex == -1) - { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes.Distinct(), assignments))); - } - else - { - var existingModule = modules[moduleIndex]; - var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes).Distinct(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); - modules[moduleIndex] = newModule; - } - } + var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(module.KpiMatrix.Outcomes, newAssignments)); + modules[modules.IndexOf(module)] = newModule; } return new KpiMatrixProfile(modules); diff --git a/Epsilon/Component/KpiMatrixComponentWordConverter.cs b/Epsilon/Component/KpiMatrixComponentWordConverter.cs index e845a2f8..b64dc1ae 100644 --- a/Epsilon/Component/KpiMatrixComponentWordConverter.cs +++ b/Epsilon/Component/KpiMatrixComponentWordConverter.cs @@ -25,13 +25,13 @@ public override Task Convert(KpiMatrixProfile component) // Set table properties for formatting. table.AppendChild(new TableProperties( - new TableWidth() { Width = "100%", Type = TableWidthUnitValues.Pct })); + new TableWidth() { Width = "0", Type = TableWidthUnitValues.Auto })); // Calculate the header row height based on the longest assignment name. int headerRowHeight = 0; if (module.KpiMatrix.Assignments.Any()) { - headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 50; + headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 111; } // Create the table header row. @@ -40,11 +40,11 @@ public override Task Convert(KpiMatrixProfile component) { Val = (UInt32Value)(uint)headerRowHeight })); // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(""))))); + headerRow.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(""))))); foreach (var assignment in module.KpiMatrix.Assignments) { - var cell = CreateTableCellWithBorders(); + var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight }); cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); @@ -54,18 +54,18 @@ public override Task Convert(KpiMatrixProfile component) table.AppendChild(headerRow); // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes) + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(o => o.Id)) { var row = new TableRow(); // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders(new Paragraph(new Run(new Text(outcome))))); + row.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(outcome.Title))))); // Add the assignment cells. foreach (var assignment in module.KpiMatrix.Assignments) { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome); - var cell = CreateTableCellWithBorders(); + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); // Set cell color based on GradeStatus. if (outcomeAssignment != null) @@ -89,12 +89,14 @@ public override Task Convert(KpiMatrixProfile component) } body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break() { Type = BreakValues.Page }))); + } return Task.FromResult(body); } - private TableCell CreateTableCellWithBorders(params OpenXmlElement[] elements) + private TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) { var cell = new TableCell(); var cellProperties = new TableCellProperties(); @@ -111,9 +113,13 @@ private TableCell CreateTableCellWithBorders(params OpenXmlElement[] elements) cell.Append(element); } } - + + if (width != null) + { + cellProperties.Append(new TableCellWidth { Type = TableWidthUnitValues.Dxa, Width = width }); + } cellProperties.Append(borders); - cell.PrependChild(cellProperties); // Move the PrependChild here + cell.PrependChild(cellProperties); return cell; } From 29dc2f692b89f0018ea132b616f3f71982fd6723 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 13:01:40 +0200 Subject: [PATCH 42/72] Merge conflicts --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 + .../Component/KpiMatrixAssignment.cs | 6 + .../Component/KpiMatrixCollection.cs | 148 ++++++++++++++++++ .../KpiMatrixModule.cs | 2 +- .../KpiMatrixOutcome.cs | 4 +- .../Model/KpiMatrix/KpiMatrix.cs | 9 -- .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 - .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 - .../Model}/GradeStatus.cs | 4 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 -- ...ponent.cs => KpiMatrixComponentFetcher.cs} | 107 ++++++++----- .../KpiMatrixComponentWordConverter.cs | 128 --------------- 12 files changed, 231 insertions(+), 206 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixModule.cs (64%) rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixOutcome.cs (52%) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs rename {Epsilon.Abstractions/Model/KpiMatrix => Epsilon.Canvas.Abstractions/Model}/GradeStatus.cs (50%) delete mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs rename Epsilon/Component/{KpiMatrixComponent.cs => KpiMatrixComponentFetcher.cs} (52%) delete mode 100644 Epsilon/Component/KpiMatrixComponentWordConverter.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs new file mode 100644 index 00000000..8f5b15a5 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrix.cs @@ -0,0 +1,7 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs new file mode 100644 index 00000000..625815fa --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs new file mode 100644 index 00000000..d1daafec --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs @@ -0,0 +1,148 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +{ + public OpenXmlElement ToWord() + { + var body = new Body(); + + foreach (var module in KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth + { + Width = "0", Type = TableWidthUnitValues.Auto, + })); + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.BottomToTopLeftToRight, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF", + }; + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break + { + Type = BreakValues.Page, + }))); + } + + return body; + } + + private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder + { + Val = BorderValues.Single, + }, + new RightBorder + { + Val = BorderValues.Single, + }, + new TopBorder + { + Val = BorderValues.Single, + }, + new BottomBorder + { + Val = BorderValues.Single, + }); + + foreach (var element in elements) + { + cell.Append(element); + } + + if (width != null) + { + cellProperties.Append(new TableCellWidth + { + Type = TableWidthUnitValues.Dxa, Width = width, + }); + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); + + return cell; + } +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixModule.cs similarity index 64% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixModule.cs index 9a874e98..659c932e 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Abstractions.Component; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs similarity index 52% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs rename to Epsilon.Abstractions/Component/KpiMatrixOutcome.cs index 0d616204..1c6c125c 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs @@ -1,4 +1,6 @@ -namespace Epsilon.Abstractions.Model; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; public record KpiMatrixOutcome( int Id, string Title, diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs deleted file mode 100644 index 5ba95040..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public record KpiMatrix( - List Outcomes, - List Assignments -); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs deleted file mode 100644 index f4cdb0a7..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixAssignment( - string Name, - List Outcomes -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs deleted file mode 100644 index c3aa2732..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixProfile( - IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs similarity index 50% rename from Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs rename to Epsilon.Canvas.Abstractions/Model/GradeStatus.cs index d50d2254..307d4dfc 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs +++ b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs @@ -1,8 +1,8 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Canvas.Abstractions.Model; public enum GradeStatus { Approved, Insufficient, - NotGraded + NotGraded, } \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs deleted file mode 100644 index ca2dc6ad..00000000 --- a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using Epsilon.Canvas.Abstractions.Model.GraphQl; - -namespace Epsilon.Canvas.Abstractions.QueryResponse; -public record GetUserKpiMatrixOutcomes( - [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data -) -{ - public record CourseData( - [property: JsonPropertyName("course")] Course Course - ); -}; \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs similarity index 52% rename from Epsilon/Component/KpiMatrixComponent.cs rename to Epsilon/Component/KpiMatrixComponentFetcher.cs index 3a24dcb7..960d0340 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -1,73 +1,91 @@ -using DocumentFormat.OpenXml.Spreadsheet; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using Epsilon.Canvas; -using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Abstractions.Component; +using Epsilon.Canvas.Abstractions.Model; +using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponentFetcher : ComponentFetcher { + private const string GetUserKpiMatrixOutcomes = @" + query GetUserKpiMatrixOutcomes { + course(id: $courseId) { + submissionsConnection { + nodes { + assignment { + name + modules { + name + } + } + rubricAssessmentsConnection { + nodes { + assessmentRatings { + points + outcome { + _id + title + masteryPoints + } + } + } + } + } + } + } + } + "; + private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; - private readonly IAccountHttpService _accountHttpService; - public KpiMatrixComponent( + public KpiMatrixComponentFetcher( IGraphQlHttpService graphQlService, - IAccountHttpService accountHttpService, IConfiguration configuration ) { _graphQlService = graphQlService; - _accountHttpService = accountHttpService; _configuration = configuration; } - public async override Task Fetch() + public override async Task Fetch() { - var courseId = _configuration["Canvas:courseId"]; - - var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); - var outcomes = await _graphQlService.Query(outcomesQuery); + var courseId = _configuration["Canvas:CourseId"]; - var modules = ConvertToComponent(outcomes); + var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}", StringComparison.InvariantCultureIgnoreCase); + var outcomes = await _graphQlService.Query(outcomesQuery); - return modules; + return ConvertToComponent(outcomes); } - private GradeStatus GetGradeStatus(double? points, double mastery) + private static GradeStatus GetGradeStatus(double? points, double mastery) { - if (points == null) - { - return GradeStatus.NotGraded; - } - else if (points >= mastery) - { - return GradeStatus.Approved; - } - else - { - return GradeStatus.Insufficient; - } + return points != null + ? points >= mastery + ? GradeStatus.Approved + : GradeStatus.Insufficient + : GradeStatus.NotGraded; } - private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse getUserKpiMatrixOutcomes) { var modules = new List(); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) continue; + if (moduleName == null) + { + continue; + } var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(n => n?.AssessmentRatings != null) - .SelectMany(n => n.AssessmentRatings) - .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Id); + .Where(static n => n.AssessmentRatings != null) + .SelectMany(static n => n.AssessmentRatings) + .Where(static r => r.Outcome != null) + .GroupBy(static r => r.Outcome.Id); var assignments = new List(); @@ -79,7 +97,8 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM modules.Add(module); } - var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(o => o.Id); + var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); + var outcomes = module.KpiMatrix.Outcomes.ToList(); foreach (var outcomeGroup in outcomeGroups) { @@ -92,31 +111,33 @@ private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiM var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, GetGradeStatus(0, outcome.MasteryPoints)); moduleOutcomes[outcomeId] = kpiMatrixOutcome; - module.KpiMatrix.Outcomes.Add(kpiMatrixOutcome); + + outcomes.Add(kpiMatrixOutcome); } - var outcomeAssessments = outcomeGroup.Select(assessmentRating => + var outcomeAssessments = outcomeGroup.Select(static assessmentRating => new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); + var outcomeAssementList = outcomeAssessments.ToList(); if (assignment == null) { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessments.ToList())); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssementList)); } else { assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessments).ToList(); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssementList).ToList(); assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); } } var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(module.KpiMatrix.Outcomes, newAssignments)); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); modules[modules.IndexOf(module)] = newModule; } - return new KpiMatrixProfile(modules); + return new KpiMatrixCollection(modules); } } \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponentWordConverter.cs b/Epsilon/Component/KpiMatrixComponentWordConverter.cs deleted file mode 100644 index b64dc1ae..00000000 --- a/Epsilon/Component/KpiMatrixComponentWordConverter.cs +++ /dev/null @@ -1,128 +0,0 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using System.Linq; -using System.Threading.Tasks; - -namespace Epsilon.Component -{ - public class KpiMatrixComponentWordConverter : ComponentConverter - { - public override Task Convert(KpiMatrixProfile component) - { - var body = new Body(); - - foreach (var module in component.KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth() { Width = "0", Type = TableWidthUnitValues.Auto })); - - // Calculate the header row height based on the longest assignment name. - int headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) - { - headerRowHeight = module.KpiMatrix.Assignments.Max(a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight - { Val = (UInt32Value)(uint)headerRowHeight })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) - { - var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection() { Val = TextDirectionValues.BottomToTopLeftToRight }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(o => o.Id)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500",new Paragraph(new Run(new Text(outcome.Title))))); - - // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - if (outcomeAssignment != null) - { - var fillColor = outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF" - }; - cell.FirstChild.Append(new Shading() { Fill = fillColor }); - } - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); - } - - body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break() { Type = BreakValues.Page }))); - - } - - return Task.FromResult(body); - } - - private TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) - { - var cell = new TableCell(); - var cellProperties = new TableCellProperties(); - var borders = new TableCellBorders( - new LeftBorder() { Val = BorderValues.Single }, - new RightBorder() { Val = BorderValues.Single }, - new TopBorder() { Val = BorderValues.Single }, - new BottomBorder() { Val = BorderValues.Single }); - - if (elements != null) - { - foreach (var element in elements) - { - cell.Append(element); - } - } - - if (width != null) - { - cellProperties.Append(new TableCellWidth { Type = TableWidthUnitValues.Dxa, Width = width }); - } - cellProperties.Append(borders); - cell.PrependChild(cellProperties); - - return cell; - } - - } -} \ No newline at end of file From 12b58ff29785b0a605c109c652c8eb7fd504592b Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Thu, 11 May 2023 11:07:55 +0200 Subject: [PATCH 43/72] Fixed MR requests --- Epsilon/Component/KpiMatrixComponentFetcher.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 960d0340..cbe43651 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -10,8 +10,8 @@ public class KpiMatrixComponentFetcher : ComponentFetcher { private const string GetUserKpiMatrixOutcomes = @" query GetUserKpiMatrixOutcomes { - course(id: $courseId) { - submissionsConnection { + allCourses { + submissionsConnection(studentIds: $studentIds) { nodes { assignment { name @@ -51,9 +51,8 @@ IConfiguration configuration public override async Task Fetch() { - var courseId = _configuration["Canvas:CourseId"]; - - var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}", StringComparison.InvariantCultureIgnoreCase); + var studentId = _configuration["Canvas:StudentId"]; + var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCultureIgnoreCase); var outcomes = await _graphQlService.Query(outcomesQuery); return ConvertToComponent(outcomes); @@ -120,15 +119,15 @@ private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); - var outcomeAssementList = outcomeAssessments.ToList(); + var outcomeAssessmentsList = outcomeAssessments.ToList(); if (assignment == null) { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssementList)); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); } else { assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssementList).ToList(); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); } } From 088521852a45226503ef8426661e54d02aeb9d5c Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Mon, 15 May 2023 13:53:23 +0200 Subject: [PATCH 44/72] Changed course to allcourses --- .../Component/KpiMatrixComponentFetcher.cs | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index cbe43651..6acf52ff 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -72,69 +72,72 @@ private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse { var modules = new List(); - foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + foreach (var course in getUserKpiMatrixOutcomes.Data?.Courses) { - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) + foreach (var node in course.SubmissionsConnection.Nodes) { - continue; - } - - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(static n => n.AssessmentRatings != null) - .SelectMany(static n => n.AssessmentRatings) - .Where(static r => r.Outcome != null) - .GroupBy(static r => r.Outcome.Id); + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) + { + continue; + } - var assignments = new List(); + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(static n => n.AssessmentRatings != null) + .SelectMany(static n => n.AssessmentRatings) + .Where(static r => r.Outcome != null) + .GroupBy(static r => r.Outcome.Id); - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, - new KpiMatrix(new List(), new List())); - modules.Add(module); - } + var assignments = new List(); - var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); - var outcomes = module.KpiMatrix.Outcomes.ToList(); + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, + new KpiMatrix(new List(), new List())); + modules.Add(module); + } - foreach (var outcomeGroup in outcomeGroups) - { - var outcomeId = outcomeGroup.Key; + var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); + var outcomes = module.KpiMatrix.Outcomes.ToList(); - // Add outcome to moduleOutcomes if it doesn't exist - if (!moduleOutcomes.ContainsKey(outcomeId)) + foreach (var outcomeGroup in outcomeGroups) { - var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; - var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, - GetGradeStatus(0, outcome.MasteryPoints)); - moduleOutcomes[outcomeId] = kpiMatrixOutcome; + var outcomeId = outcomeGroup.Key; - outcomes.Add(kpiMatrixOutcome); - } + // Add outcome to moduleOutcomes if it doesn't exist + if (!moduleOutcomes.ContainsKey(outcomeId)) + { + var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; + var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, + GetGradeStatus(0, outcome.MasteryPoints)); + moduleOutcomes[outcomeId] = kpiMatrixOutcome; - var outcomeAssessments = outcomeGroup.Select(static assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); + outcomes.Add(kpiMatrixOutcome); + } - var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); - var outcomeAssessmentsList = outcomeAssessments.ToList(); - if (assignment == null) - { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); - } - else - { - assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + var outcomeAssessments = outcomeGroup.Select(static assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); + + var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); + var outcomeAssessmentsList = outcomeAssessments.ToList(); + if (assignment == null) + { + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); + } + else + { + assignments.Remove(assignment); + var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + } } - } - var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); - modules[modules.IndexOf(module)] = newModule; + var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); + modules[modules.IndexOf(module)] = newModule; + } } return new KpiMatrixCollection(modules); From cb11da14e05d4732d7372271e38f3ccbe8d9424f Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 16 May 2023 22:32:38 +0200 Subject: [PATCH 45/72] File restructure --- .../Component/{ => KpiMatrixComponent}/KpiMatrix.cs | 2 +- .../Component/{ => KpiMatrixComponent}/KpiMatrixAssignment.cs | 2 +- .../Component/{ => KpiMatrixComponent}/KpiMatrixCollection.cs | 4 ++-- .../Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs | 2 +- .../Component/{ => KpiMatrixComponent}/KpiMatrixOutcome.cs | 2 +- Epsilon/Component/KpiMatrixComponentFetcher.cs | 3 ++- 6 files changed, 8 insertions(+), 7 deletions(-) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrix.cs (65%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixAssignment.cs (61%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixCollection.cs (97%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs (55%) rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixOutcome.cs (68%) diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrix.cs similarity index 65% rename from Epsilon.Abstractions/Component/KpiMatrix.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrix.cs index 8f5b15a5..cb9b8bdc 100644 --- a/Epsilon.Abstractions/Component/KpiMatrix.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrix.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrix( IEnumerable Outcomes, diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixAssignment.cs similarity index 61% rename from Epsilon.Abstractions/Component/KpiMatrixAssignment.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixAssignment.cs index 625815fa..0364a2be 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixAssignment.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixAssignment( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs similarity index 97% rename from Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index d1daafec..24ef9435 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -2,7 +2,7 @@ using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Canvas.Abstractions.Model; -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent { @@ -48,7 +48,7 @@ public OpenXmlElement ToWord() var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection { - Val = TextDirectionValues.BottomToTopLeftToRight, + Val = TextDirectionValues.TopToBottomRightToLeft, }); cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); diff --git a/Epsilon.Abstractions/Component/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs similarity index 55% rename from Epsilon.Abstractions/Component/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs index 659c932e..38f92501 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs similarity index 68% rename from Epsilon.Abstractions/Component/KpiMatrixOutcome.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs index 1c6c125c..75e3de72 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs @@ -1,6 +1,6 @@ using Epsilon.Canvas.Abstractions.Model; -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixOutcome( int Id, string Title, diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 6acf52ff..4084abe8 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; @@ -83,7 +84,7 @@ private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse } var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(static n => n.AssessmentRatings != null) + .Where(static n => n.AssessmentRatings != null && n.AssessmentRatings.Count > 0) .SelectMany(static n => n.AssessmentRatings) .Where(static r => r.Outcome != null) .GroupBy(static r => r.Outcome.Id); From 94284e8527c0ad61dc9e7d6baa16f1fb046354e5 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:46:41 +0200 Subject: [PATCH 46/72] Revised coding structure. Assignments that are not yest graded are also now included --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 73 ++++---- .../KpiMatrixComponent/KpiMatrixModule.cs | 6 - .../Model/GraphQl/Assignment.cs | 3 +- .../Model/GraphQl/Criteria.cs | 7 + .../Model/GraphQl/Rubric.cs | 8 + .../SubmissionsHistoriesConnectionNode.cs | 2 + ...etenceProfileCompetenceComponentFetcher.cs | 67 ++++---- .../Component/KpiMatrixComponentFetcher.cs | 157 +++++++++--------- 8 files changed, 169 insertions(+), 154 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs create mode 100644 Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs create mode 100644 Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index 24ef9435..e3f41a50 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -2,75 +2,79 @@ using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Canvas.Abstractions.Model; +// using Epsilon.Canvas.Abstractions.Model; + namespace Epsilon.Abstractions.Component.KpiMatrixComponent; -public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : IEpsilonWordComponent { public OpenXmlElement ToWord() { var body = new Body(); - - foreach (var module in KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. + // Create a table, with rows for the outcomes and columns for the assignments. var table = new Table(); - + // Set table properties for formatting. table.AppendChild(new TableProperties( new TableWidth { Width = "0", Type = TableWidthUnitValues.Auto, })); - + // Calculate the header row height based on the longest assignment name. var headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) + if (KpiMatrixAssignments.Any()) { - headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + headerRowHeight = KpiMatrixAssignments.Max(static a => a.Name.Length) * 111; } - + // Create the table header row. var headerRow = new TableRow(); headerRow.AppendChild(new TableRowProperties(new TableRowHeight { Val = (UInt32Value)(uint)headerRowHeight, })); - + // Empty top-left cell. headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) + + foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) { var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection { Val = TextDirectionValues.TopToBottomRightToLeft, }); - + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); headerRow.AppendChild(cell); } - + table.AppendChild(headerRow); - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + var listOfOutcomes = new Dictionary(); + foreach (var assignment in KpiMatrixAssignments) + { + foreach (var outcome in assignment.Outcomes) + { + listOfOutcomes.TryAdd(outcome.Id, outcome); + } + } + + // Add the outcome rows. + foreach (var outcome in listOfOutcomes.OrderByDescending(static o => o.Value.Title)) { var row = new TableRow(); - + // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); - + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); + // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) + foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); var cell = CreateTableCellWithBorders("100"); - + // Set cell color based on GradeStatus. if (outcomeAssignment != null) { @@ -86,21 +90,20 @@ public OpenXmlElement ToWord() Fill = fillColor, }); } - + // Add an empty text element since we're using color instead of text. cell.Append(new Paragraph(new Run(new Text("")))); row.AppendChild(cell); } - + table.AppendChild(row); } - + body.AppendChild(table); body.AppendChild(new Paragraph(new Run(new Break { Type = BreakValues.Page, }))); - } return body; } @@ -126,12 +129,12 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm { Val = BorderValues.Single, }); - + foreach (var element in elements) { cell.Append(element); } - + if (width != null) { cellProperties.Append(new TableCellWidth @@ -139,10 +142,10 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm Type = TableWidthUnitValues.Dxa, Width = width, }); } - + cellProperties.Append(borders); cell.PrependChild(cellProperties); - + return cell; } } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs deleted file mode 100644 index 38f92501..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - -public record KpiMatrixModule( - string Name, - KpiMatrix KpiMatrix -); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs index 266d23da..ebc20383 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Assignment.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Assignment( [property: JsonPropertyName("name")] string? Name, - [property: JsonPropertyName("modules")] IEnumerable? Modules + [property: JsonPropertyName("modules")] IEnumerable? Modules , + [property: JsonPropertyName("rubric")] Rubric? Rubric ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs new file mode 100644 index 00000000..38497037 --- /dev/null +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Criteria.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; + +namespace Epsilon.Canvas.Abstractions.Model.GraphQl; + +public record Criteria( + [property: JsonPropertyName("outcome")] Outcome? Outcome + ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs new file mode 100644 index 00000000..5517c745 --- /dev/null +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Rubric.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace Epsilon.Canvas.Abstractions.Model.GraphQl; + +public record Rubric( + [property: JsonPropertyName("criteria")] IEnumerable? Criteria + + ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs index 7c6ed535..bf875031 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/SubmissionsHistoriesConnectionNode.cs @@ -4,5 +4,7 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record SubmissionsHistoriesConnectionNode( [property: JsonPropertyName("attempt")] int? Attempt, + [property: JsonPropertyName("submittedAt")] DateTime? SubmittedAt, + [property: JsonPropertyName("assignment")] Assignment? Assignment, [property: JsonPropertyName("rubricAssessmentsConnection")] RubricAssessmentsConnection? RubricAssessments ); \ No newline at end of file diff --git a/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs b/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs index ec815f86..3266f96e 100644 --- a/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs +++ b/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs @@ -30,13 +30,13 @@ query MyQuery { } } attempt + submittedAt } } postedAt } } } - } "; private readonly IConfiguration _configuration; @@ -76,43 +76,46 @@ IEnumerable enrollmentTerms var taskResults = new List(); var professionalResults = new List(); - foreach (var course in queryResponse.Data.Courses) + if (queryResponse.Data != null) { - foreach (var submissionsConnection in course.SubmissionsConnection.Nodes.Where(static s => s.PostedAt != null)) + foreach (var course in queryResponse.Data.Courses) { - var submission = submissionsConnection.SubmissionsHistories.Nodes - .Where(static h => h.RubricAssessments.Nodes.Any()) - .MaxBy(static h => h.Attempt); - - if (submission != null) + foreach (var submissionsConnection in course.SubmissionsConnection.Nodes) { - var rubricAssessments = submission.RubricAssessments.Nodes; + var submission = submissionsConnection.SubmissionsHistories.Nodes + .Where(static h => h.RubricAssessments.Nodes.Any()) + .MaxBy(static h => h.Attempt); - foreach (var assessmentRating in rubricAssessments.SelectMany(static rubricAssessment => rubricAssessment.AssessmentRatings.Where(static ar => - ar is { Points: not null, Criterion.MasteryPoints: not null, Criterion.Outcome: not null, } && ar.Points >= ar.Criterion.MasteryPoints))) + if (submission != null) { - if (FhictConstants.ProfessionalTasks.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalTask)) - { - taskResults.Add( - new ProfessionalTaskResult( - professionalTask.Layer, - professionalTask.Activity, - professionalTask.MasteryLevel, - assessmentRating.Points!.Value, - submissionsConnection.PostedAt!.Value - ) - ); - } - else if (FhictConstants.ProfessionalSkills.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalSkill)) + var rubricAssessments = submission.RubricAssessments.Nodes; + + foreach (var assessmentRating in rubricAssessments.SelectMany(static rubricAssessment => rubricAssessment.AssessmentRatings.Where(static ar => + ar is { Points: not null, Criterion.MasteryPoints: not null, Criterion.Outcome: not null, } && ar.Points >= ar.Criterion.MasteryPoints))) { - professionalResults.Add( - new ProfessionalSkillResult( - professionalSkill.Skill, - professionalSkill.MasteryLevel, - assessmentRating.Points!.Value, - submissionsConnection.PostedAt!.Value - ) - ); + if (FhictConstants.ProfessionalTasks.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalTask)) + { + taskResults.Add( + new ProfessionalTaskResult( + professionalTask.Layer, + professionalTask.Activity, + professionalTask.MasteryLevel, + assessmentRating.Points!.Value, + new DateTime() + ) + ); + } + else if (FhictConstants.ProfessionalSkills.TryGetValue(assessmentRating.Criterion.Outcome.Id, out var professionalSkill)) + { + professionalResults.Add( + new ProfessionalSkillResult( + professionalSkill.Skill, + professionalSkill.MasteryLevel, + assessmentRating.Points!.Value, + new DateTime() + ) + ); + } } } } diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 4084abe8..59ce77a6 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -10,32 +10,47 @@ namespace Epsilon.Component; public class KpiMatrixComponentFetcher : ComponentFetcher { private const string GetUserKpiMatrixOutcomes = @" - query GetUserKpiMatrixOutcomes { +query GetUserKpiMatrixOutcomes { allCourses { submissionsConnection(studentIds: $studentIds) { + nodes { + submissionHistoriesConnection { + nodes { + rubricAssessmentsConnection { nodes { - assignment { - name - modules { - name + assessmentRatings { + criterion { + outcome { + _id + title + } + masteryPoints } + points } - rubricAssessmentsConnection { - nodes { - assessmentRatings { - points - outcome { - _id - title - masteryPoints - } - } + } + } + attempt + submittedAt + assignment { + name + rubric { + criteria { + outcome { + title + _id +masteryPoints } } } } } } + postedAt + } + } + } +} "; private readonly IConfiguration _configuration; @@ -55,11 +70,10 @@ public override async Task Fetch() var studentId = _configuration["Canvas:StudentId"]; var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCultureIgnoreCase); var outcomes = await _graphQlService.Query(outcomesQuery); - - return ConvertToComponent(outcomes); + return ConvertToComponent(outcomes, new DateTime(2023, 2, 1), DateTime.Now); } - private static GradeStatus GetGradeStatus(double? points, double mastery) + private static GradeStatus GetGradeStatus(double? points, double? mastery) { return points != null ? points >= mastery @@ -69,78 +83,61 @@ private static GradeStatus GetGradeStatus(double? points, double mastery) } - private static KpiMatrixCollection ConvertToComponent(CanvasGraphQlQueryResponse getUserKpiMatrixOutcomes) + private static KpiMatrixCollection ConvertToComponent( + CanvasGraphQlQueryResponse queryResponse, + DateTime startAt, + DateTime endAt + ) { - var modules = new List(); - - foreach (var course in getUserKpiMatrixOutcomes.Data?.Courses) + var assignments = new List(); + foreach (var course in queryResponse.Data!.Courses!) { - foreach (var node in course.SubmissionsConnection.Nodes) + foreach (var submissionsConnection in course.SubmissionsConnection!.Nodes) { - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) - { - continue; - } - - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(static n => n.AssessmentRatings != null && n.AssessmentRatings.Count > 0) - .SelectMany(static n => n.AssessmentRatings) - .Where(static r => r.Outcome != null) - .GroupBy(static r => r.Outcome.Id); - - var assignments = new List(); + var submission = submissionsConnection.SubmissionsHistories.Nodes + .Where(sub => sub.SubmittedAt > startAt && sub.SubmittedAt < endAt) + .MaxBy(static h => h.Attempt); - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) + if (submission != null) { - module = new KpiMatrixModule(moduleName, - new KpiMatrix(new List(), new List())); - modules.Add(module); - } - - var moduleOutcomes = module.KpiMatrix.Outcomes.ToDictionary(static o => o.Id); - var outcomes = module.KpiMatrix.Outcomes.ToList(); - - foreach (var outcomeGroup in outcomeGroups) - { - var outcomeId = outcomeGroup.Key; - - // Add outcome to moduleOutcomes if it doesn't exist - if (!moduleOutcomes.ContainsKey(outcomeId)) + if (submission.Assignment?.Rubric != null) { - var outcome = outcomeGroup.FirstOrDefault(x => x.Outcome.Id == outcomeId).Outcome; - var kpiMatrixOutcome = new KpiMatrixOutcome(outcome.Id, outcome.Title, - GetGradeStatus(0, outcome.MasteryPoints)); - moduleOutcomes[outcomeId] = kpiMatrixOutcome; - - outcomes.Add(kpiMatrixOutcome); - } - - var outcomeAssessments = outcomeGroup.Select(static assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Id, assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))); - - var assignment = assignments.FirstOrDefault(a => a.Name == node.Assignment.Name); - var outcomeAssessmentsList = outcomeAssessments.ToList(); - if (assignment == null) - { - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, outcomeAssessmentsList)); - } - else - { - assignments.Remove(assignment); - var combinedOutcomes = assignment.Outcomes.Concat(outcomeAssessmentsList).ToList(); - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, combinedOutcomes)); + var rubricAssessments = submission.Assignment.Rubric.Criteria; + var kpiMatrixOutcomes = new List(); + if (submission.Assignment.Rubric.Criteria != null) + { + foreach (var criteria in submission.Assignment.Rubric.Criteria) + { + if (criteria.Outcome != null) + { + if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask)) + { + kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, + GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + ?.Points + , criteria.Outcome.MasteryPoints))); + } + else if (FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) + { + kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, + GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + ?.Points + , criteria.Outcome.MasteryPoints))); + } + } + } + } + + if (kpiMatrixOutcomes.Count > 0 && submission.Assignment.Name != null) + { + var assignment = new KpiMatrixAssignment(submission.Assignment.Name, kpiMatrixOutcomes); + assignments.Add(assignment); + } } } - - var newAssignments = module.KpiMatrix.Assignments.Concat(assignments).ToList(); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, newAssignments)); - modules[modules.IndexOf(module)] = newModule; } } - return new KpiMatrixCollection(modules); + return new KpiMatrixCollection(assignments); } } \ No newline at end of file From be86b43c7bd70fc4212263e99c98022a4b6fc8ed Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 21:12:24 +0200 Subject: [PATCH 47/72] Merged files in new structure --- .../Component/KpiMatrixComponent/KpiMatrixCollection.cs | 2 +- Epsilon/Component/KpiMatrixComponentFetcher.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index e3f41a50..d69ab1a2 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -6,7 +6,7 @@ namespace Epsilon.Abstractions.Component.KpiMatrixComponent; -public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : IEpsilonWordComponent +public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent { public OpenXmlElement ToWord() { diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 59ce77a6..1a3638ae 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -7,7 +7,7 @@ namespace Epsilon.Component; -public class KpiMatrixComponentFetcher : ComponentFetcher +public class KpiMatrixComponentFetcher : CompetenceComponentFetcher { private const string GetUserKpiMatrixOutcomes = @" query GetUserKpiMatrixOutcomes { From aa02adc66286c52a371374ea777c6d1c4d1ce142 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 22:21:45 +0200 Subject: [PATCH 48/72] Colored rows --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 45 ++++++++++++------- .../Component/KpiMatrixComponentFetcher.cs | 4 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index d69ab1a2..bf1dd8ca 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,6 +1,7 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Wordprocessing; using Epsilon.Canvas.Abstractions.Model; +using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; // using Epsilon.Canvas.Abstractions.Model; @@ -13,6 +14,8 @@ public OpenXmlElement ToWord() var body = new Body(); // Create a table, with rows for the outcomes and columns for the assignments. var table = new Table(); + + var assignments = KpiMatrixAssignments.OrderBy(static ass => ass.Name).ToList(); // Set table properties for formatting. table.AppendChild(new TableProperties( @@ -25,7 +28,7 @@ public OpenXmlElement ToWord() var headerRowHeight = 0; if (KpiMatrixAssignments.Any()) { - headerRowHeight = KpiMatrixAssignments.Max(static a => a.Name.Length) * 111; + headerRowHeight = assignments.Max(static a => a.Name.Length) * 111; } // Create the table header row. @@ -38,15 +41,21 @@ public OpenXmlElement ToWord() // Empty top-left cell. headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) + foreach (var assignment in assignments) { var cell = CreateTableCellWithBorders("100"); cell.FirstChild.Append(new TextDirection { - Val = TextDirectionValues.TopToBottomRightToLeft, + Val = TextDirectionValues.TopToBottomLeftToRightRotated, }); cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + cell.FirstChild.Append(new Shading + { + Fill = assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3", + }); headerRow.AppendChild(cell); } @@ -70,26 +79,34 @@ public OpenXmlElement ToWord() row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); // Add the assignment cells. - foreach (var assignment in KpiMatrixAssignments.OrderBy(static ass => ass.Name)) + foreach (var assignment in assignments) { var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); var cell = CreateTableCellWithBorders("100"); - + // Set cell color based on GradeStatus. + var fillColor = ""; if (outcomeAssignment != null) { - var fillColor = outcomeAssignment.GradeStatus switch + fillColor = outcomeAssignment.GradeStatus switch { GradeStatus.Approved => "44F656", GradeStatus.Insufficient => "FA1818", GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF", + _ => null, }; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); + + } + else + { + fillColor = assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3"; } + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); // Add an empty text element since we're using color instead of text. cell.Append(new Paragraph(new Run(new Text("")))); @@ -100,12 +117,8 @@ public OpenXmlElement ToWord() } body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break - { - Type = BreakValues.Page, - }))); - return body; + return body; } private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 1a3638ae..9f689504 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -104,9 +104,9 @@ DateTime endAt { var rubricAssessments = submission.Assignment.Rubric.Criteria; var kpiMatrixOutcomes = new List(); - if (submission.Assignment.Rubric.Criteria != null) + if (rubricAssessments != null) { - foreach (var criteria in submission.Assignment.Rubric.Criteria) + foreach (var criteria in rubricAssessments) { if (criteria.Outcome != null) { From 1f4436e1f31620f5ad281f0f340372b02147a2a4 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 22:23:53 +0200 Subject: [PATCH 49/72] "Simplified" --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index bf1dd8ca..04ab85e7 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -85,25 +85,18 @@ public OpenXmlElement ToWord() var cell = CreateTableCellWithBorders("100"); // Set cell color based on GradeStatus. - var fillColor = ""; - if (outcomeAssignment != null) - { - fillColor = outcomeAssignment.GradeStatus switch + var fillColor = outcomeAssignment != null + ? outcomeAssignment.GradeStatus switch { GradeStatus.Approved => "44F656", GradeStatus.Insufficient => "FA1818", GradeStatus.NotGraded => "FAFF00", _ => null, - }; - - } - else - { - fillColor = assignments.IndexOf(assignment) % 2 == 0 + } + : assignments.IndexOf(assignment) % 2 == 0 ? "FFFFFF" : "d3d3d3"; - } - cell.FirstChild.Append(new Shading + cell.FirstChild.Append(new Shading { Fill = fillColor, }); From 89c837002f3155f21ce266947cdb3518831eed89 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 10:02:51 +0200 Subject: [PATCH 50/72] Added legend and changed "GradeStatus" structure --- .../KpiMatrixComponent/GradeStatus.cs | 6 + .../KpiMatrixComponent/KpiMatrixCollection.cs | 204 ++++++++++-------- .../KpiMatrixComponent/KpiMatrixConstants.cs | 18 ++ .../KpiMatrixComponent/KpiMatrixOutcome.cs | 1 - .../Model/GradeStatus.cs | 8 - .../Component/KpiMatrixComponentFetcher.cs | 7 +- 6 files changed, 139 insertions(+), 105 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs delete mode 100644 Epsilon.Canvas.Abstractions/Model/GradeStatus.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs new file mode 100644 index 00000000..0e12f48f --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/GradeStatus.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; + +public record GradeStatus( + string Status, + string Color +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index 04ab85e7..ef272696 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,6 +1,5 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Canvas.Abstractions.Model; using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; // using Epsilon.Canvas.Abstractions.Model; @@ -9,109 +8,130 @@ namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent { + + private static OpenXmlElement GetLegend() + { + var table = new Table(); + foreach (var status in KpiMatrixConstants.GradeStatus) + { + var row = new TableRow(); + var cellName = CreateTableCellWithBorders("200"); + cellName.Append(new Paragraph(new Run(new Text(status.Value.Status)))); + + var cellValue = CreateTableCellWithBorders("200"); + cellValue.Append(new Paragraph(new Run(new Text("")))); + cellValue.FirstChild.Append(new Shading + { + Fill = status.Value.Color, + }); + row.AppendChild(cellName); + row.AppendChild(cellValue); + table.AppendChild(row); + } + + return table; + } + public OpenXmlElement ToWord() { var body = new Body(); // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); + var table = new Table(); - var assignments = KpiMatrixAssignments.OrderBy(static ass => ass.Name).ToList(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth - { - Width = "0", Type = TableWidthUnitValues.Auto, - })); - - // Calculate the header row height based on the longest assignment name. - var headerRowHeight = 0; - if (KpiMatrixAssignments.Any()) - { - headerRowHeight = assignments.Max(static a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight + var assignments = KpiMatrixAssignments.OrderBy(static ass => ass.Name).ToList(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth { - Val = (UInt32Value)(uint)headerRowHeight, + Width = "0", Type = TableWidthUnitValues.Auto, })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (KpiMatrixAssignments.Any()) + { + headerRowHeight = assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.TopToBottomLeftToRightRotated, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + cell.FirstChild.Append(new Shading + { + Fill = assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3", + }); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + var listOfOutcomes = new Dictionary(); + foreach (var assignment in KpiMatrixAssignments) + { + foreach (var outcome in assignment.Outcomes) + { + listOfOutcomes.TryAdd(outcome.Id, outcome); + } + } + + // Add the outcome rows. + foreach (var outcome in listOfOutcomes.OrderByDescending(static o => o.Value.Title)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); + + // Add the assignment cells. foreach (var assignment in assignments) { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection - { - Val = TextDirectionValues.TopToBottomLeftToRightRotated, - }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + + // Set cell color based on GradeStatus. + var fillColor = outcomeAssignment != null + ? outcomeAssignment.GradeStatus.Color + //When no item is present give the cell alternating background color + : assignments.IndexOf(assignment) % 2 == 0 + ? "FFFFFF" + : "d3d3d3"; cell.FirstChild.Append(new Shading { - Fill = assignments.IndexOf(assignment) % 2 == 0 - ? "FFFFFF" - : "d3d3d3", + Fill = fillColor, }); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - var listOfOutcomes = new Dictionary(); - foreach (var assignment in KpiMatrixAssignments) - { - foreach (var outcome in assignment.Outcomes) - { - listOfOutcomes.TryAdd(outcome.Id, outcome); - } - } - - // Add the outcome rows. - foreach (var outcome in listOfOutcomes.OrderByDescending(static o => o.Value.Title)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Value.Title))))); - - // Add the assignment cells. - foreach (var assignment in assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Id == outcome.Key); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - var fillColor = outcomeAssignment != null - ? outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => null, - } - : assignments.IndexOf(assignment) % 2 == 0 - ? "FFFFFF" - : "d3d3d3"; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); } - - body.AppendChild(table); - return body; + table.AppendChild(row); + } + + body.AppendChild(GetLegend()); + body.Append(new Paragraph(new Run(new Text("")))); + body.AppendChild(table); + + return body; } private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) @@ -135,12 +155,12 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm { Val = BorderValues.Single, }); - + foreach (var element in elements) { cell.Append(element); } - + if (width != null) { cellProperties.Append(new TableCellWidth @@ -148,10 +168,10 @@ private static TableCell CreateTableCellWithBorders(string? width, params OpenXm Type = TableWidthUnitValues.Dxa, Width = width, }); } - + cellProperties.Append(borders); cell.PrependChild(cellProperties); - + return cell; } } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs new file mode 100644 index 00000000..13a54cb1 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs @@ -0,0 +1,18 @@ +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; + +public static class KpiMatrixConstants +{ + + public static readonly IDictionary GradeStatus = new Dictionary + { + { + "Approved", new GradeStatus("Approved", "44F656") + }, + { + "Insufficient", new GradeStatus("Insufficient", "FA1818") + }, + { + "NotGraded", new GradeStatus("NotGraded", "FAFF00") + }, + }; +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs index 75e3de72..5a4d2894 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixOutcome.cs @@ -1,4 +1,3 @@ -using Epsilon.Canvas.Abstractions.Model; namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixOutcome( diff --git a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs deleted file mode 100644 index 307d4dfc..00000000 --- a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Epsilon.Canvas.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded, -} \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 9f689504..bc6eb47f 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -1,6 +1,5 @@ using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Component.KpiMatrixComponent; -using Epsilon.Canvas.Abstractions.Model; using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; @@ -77,9 +76,9 @@ private static GradeStatus GetGradeStatus(double? points, double? mastery) { return points != null ? points >= mastery - ? GradeStatus.Approved - : GradeStatus.Insufficient - : GradeStatus.NotGraded; + ? KpiMatrixConstants.GradeStatus["Approved"] + : KpiMatrixConstants.GradeStatus["Insufficient"] + : KpiMatrixConstants.GradeStatus["NotGraded"]; } From 7403cde83b29af9503702df15d59bdb3130c384f Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Fri, 21 Apr 2023 12:53:09 +0200 Subject: [PATCH 51/72] Created KpiMatrix component --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ++++++ .../Model/GraphQl/Outcome.cs | 3 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 +++ Epsilon/Component/KpiMatrixComponent.cs | 83 +++++++++++++++++++ 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs create mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs new file mode 100644 index 00000000..87ca98e9 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix.cs @@ -0,0 +1,25 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes + ); + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments + ); + diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index 1a0f6055..6bdbd704 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -4,6 +4,5 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( [property: JsonPropertyName("_id")] int Id, - [property: JsonPropertyName("title")] string Title, - [property: JsonPropertyName("masteryPoints")] double MasteryPoints + [property: JsonPropertyName("title")] string Title ); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs new file mode 100644 index 00000000..ca2dc6ad --- /dev/null +++ b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Epsilon.Canvas.Abstractions.Model.GraphQl; + +namespace Epsilon.Canvas.Abstractions.QueryResponse; +public record GetUserKpiMatrixOutcomes( + [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data +) +{ + public record CourseData( + [property: JsonPropertyName("course")] Course Course + ); +}; \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs new file mode 100644 index 00000000..d157702a --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -0,0 +1,83 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Canvas.Abstractions.Service; +using Microsoft.Extensions.Configuration; + +namespace Epsilon.Component; + +public class KpiMatrixComponent : Component +{ + + + private readonly IConfiguration _configuration; + private readonly IGraphQlHttpService _graphQlService; + private readonly IAccountHttpService _accountHttpService; + + public KpiMatrixComponent( + IGraphQlHttpService graphQlService, + IAccountHttpService accountHttpService, + IConfiguration configuration + ) + { + _graphQlService = graphQlService; + _accountHttpService = accountHttpService; + _configuration = configuration; + } + + public async override Task Fetch() + { + var courseId = _configuration["Canvas:courseId"]; + + var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); + var outcomes = await _graphQlService.Query(outcomesQuery); + + var kpiMatrix = ConvertToComponent(outcomes); + + return kpiMatrix; + } + + private GradeStatus GetGradeStatus(double? points) + { + return points switch + { + null => GradeStatus.NotGraded, + >= 4 => GradeStatus.Approved, + _ => GradeStatus.Insufficient + }; + } + + private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + { + var outcomes = new List(); + var assignments = new List(); + + if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) + return new KpiMatrix(outcomes, assignments); + foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + { + var assignmentOutcomes = new List(); + var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() + ?.AssessmentRatings; + if (assessmentRatings == null) continue; + + foreach (var assessmentRating in assessmentRatings) + { + if (assessmentRating.Outcome != null) + { + assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points))); + if (outcomes.All(o => o != assessmentRating.Outcome.Title)) + { + outcomes.Add(assessmentRating.Outcome.Title); + } + } + } + + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + } + + return new KpiMatrix(outcomes ,assignments); + } +} \ No newline at end of file From 60af6e1ea0f965e18f0d193da1a043d669d055bd Mon Sep 17 00:00:00 2001 From: Jasper123pyah <73039915+Jasper123pyah@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:09:47 +0200 Subject: [PATCH 52/72] Added modules and fixed comments --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ----- .../Model/KpiMatrix/GradeStatus.cs | 8 ++ .../Model/KpiMatrix/KpiMatrix.cs | 9 ++ .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 + .../Model/KpiMatrix/KpiMatrixModule.cs | 6 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 + .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 + .../Model/GraphQl/Outcome.cs | 3 +- Epsilon/Component/KpiMatrixComponent.cs | 103 +++++++++++------- 9 files changed, 104 insertions(+), 65 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs deleted file mode 100644 index 87ca98e9..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes - ); - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments - ); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs new file mode 100644 index 00000000..d50d2254 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs @@ -0,0 +1,8 @@ +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs new file mode 100644 index 00000000..a86409c5 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs @@ -0,0 +1,9 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs new file mode 100644 index 00000000..6ee6bdcb --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs new file mode 100644 index 00000000..9a874e98 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixModule( + string Name, + KpiMatrix KpiMatrix +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs new file mode 100644 index 00000000..efe15667 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -0,0 +1,5 @@ +namespace Epsilon.Abstractions.Model; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs new file mode 100644 index 00000000..c3aa2732 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs @@ -0,0 +1,4 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixProfile( + IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs index 6bdbd704..1a0f6055 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/Outcome.cs @@ -4,5 +4,6 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record Outcome( [property: JsonPropertyName("_id")] int Id, - [property: JsonPropertyName("title")] string Title + [property: JsonPropertyName("title")] string Title, + [property: JsonPropertyName("masteryPoints")] double MasteryPoints ); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index d157702a..b65bd7d7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -1,4 +1,5 @@ -using Epsilon.Abstractions.Component; +using DocumentFormat.OpenXml.Spreadsheet; +using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Canvas.Abstractions.QueryResponse; @@ -7,10 +8,8 @@ namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponent : Component { - - private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; private readonly IAccountHttpService _accountHttpService; @@ -25,59 +24,85 @@ IConfiguration configuration _accountHttpService = accountHttpService; _configuration = configuration; } - - public async override Task Fetch() + + public async override Task Fetch() { var courseId = _configuration["Canvas:courseId"]; var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); var outcomes = await _graphQlService.Query(outcomesQuery); - - var kpiMatrix = ConvertToComponent(outcomes); - return kpiMatrix; + var modules = ConvertToComponent(outcomes); + + return modules; } - private GradeStatus GetGradeStatus(double? points) + private GradeStatus GetGradeStatus(double? points, double mastery) { - return points switch + if (points == null) + { + return GradeStatus.NotGraded; + } + else if (points >= mastery) { - null => GradeStatus.NotGraded, - >= 4 => GradeStatus.Approved, - _ => GradeStatus.Insufficient - }; + return GradeStatus.Approved; + } + else + { + return GradeStatus.Insufficient; + } } - - private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + + + private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) { - var outcomes = new List(); - var assignments = new List(); + var modules = new List(); - if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) - return new KpiMatrix(outcomes, assignments); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - var assignmentOutcomes = new List(); - var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() - ?.AssessmentRatings; - if (assessmentRatings == null) continue; - - foreach (var assessmentRating in assessmentRatings) + // Check if the node has a module + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) continue; + + // Group the assignment outcomes by outcome title + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(n => n?.AssessmentRatings != null) + .SelectMany(n => n.AssessmentRatings) + .Where(r => r.Outcome != null) + .GroupBy(r => r.Outcome.Title); + + var outcomes = outcomeGroups.Select(g => g.Key); + var assignments = outcomeGroups.Select(g => + new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) + )); + + // Find the module in the list of modules or create a new one + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + modules.Add(module); + } + else + { + var moduleIndex = modules.IndexOf(module); + if (moduleIndex == -1) { - if (assessmentRating.Outcome != null) - { - assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points))); - if (outcomes.All(o => o != assessmentRating.Outcome.Title)) - { - outcomes.Add(assessmentRating.Outcome.Title); - } - } + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); } - - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + else + { + var existingModule = modules[moduleIndex]; + var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); + modules[moduleIndex] = newModule; + } + } } - return new KpiMatrix(outcomes ,assignments); + return new KpiMatrixProfile(modules); } } \ No newline at end of file From 0ff2c3cc3cff4bbaa3c951e725b9dad9c4732b4e Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 13:01:40 +0200 Subject: [PATCH 53/72] Merge conflicts --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 + .../Component/KpiMatrixAssignment.cs | 6 + .../Component/KpiMatrixCollection.cs | 148 ++++++++++++++++++ .../KpiMatrixModule.cs | 2 +- .../Component/KpiMatrixOutcome.cs | 7 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 - .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 - .../Model}/GradeStatus.cs | 4 +- .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 -- Epsilon.Host.WebApi/Program.cs | 4 +- Epsilon/Component/KpiMatrixComponent.cs | 108 ------------- 11 files changed, 174 insertions(+), 133 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixModule.cs (64%) create mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs rename {Epsilon.Abstractions/Model/KpiMatrix => Epsilon.Canvas.Abstractions/Model}/GradeStatus.cs (50%) delete mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs delete mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs new file mode 100644 index 00000000..8f5b15a5 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrix.cs @@ -0,0 +1,7 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs new file mode 100644 index 00000000..625815fa --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs new file mode 100644 index 00000000..d1daafec --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs @@ -0,0 +1,148 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +{ + public OpenXmlElement ToWord() + { + var body = new Body(); + + foreach (var module in KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth + { + Width = "0", Type = TableWidthUnitValues.Auto, + })); + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.BottomToTopLeftToRight, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF", + }; + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break + { + Type = BreakValues.Page, + }))); + } + + return body; + } + + private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder + { + Val = BorderValues.Single, + }, + new RightBorder + { + Val = BorderValues.Single, + }, + new TopBorder + { + Val = BorderValues.Single, + }, + new BottomBorder + { + Val = BorderValues.Single, + }); + + foreach (var element in elements) + { + cell.Append(element); + } + + if (width != null) + { + cellProperties.Append(new TableCellWidth + { + Type = TableWidthUnitValues.Dxa, Width = width, + }); + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); + + return cell; + } +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixModule.cs similarity index 64% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixModule.cs index 9a874e98..659c932e 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Abstractions.Component; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs new file mode 100644 index 00000000..43140a0d --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs @@ -0,0 +1,7 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs deleted file mode 100644 index efe15667..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Epsilon.Abstractions.Model; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs deleted file mode 100644 index c3aa2732..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixProfile( - IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs similarity index 50% rename from Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs rename to Epsilon.Canvas.Abstractions/Model/GradeStatus.cs index d50d2254..307d4dfc 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs +++ b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs @@ -1,8 +1,8 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Canvas.Abstractions.Model; public enum GradeStatus { Approved, Insufficient, - NotGraded + NotGraded, } \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs deleted file mode 100644 index ca2dc6ad..00000000 --- a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using Epsilon.Canvas.Abstractions.Model.GraphQl; - -namespace Epsilon.Canvas.Abstractions.QueryResponse; -public record GetUserKpiMatrixOutcomes( - [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data -) -{ - public record CourseData( - [property: JsonPropertyName("course")] Course Course - ); -}; \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Program.cs b/Epsilon.Host.WebApi/Program.cs index f27fec07..7097ff1e 100644 --- a/Epsilon.Host.WebApi/Program.cs +++ b/Epsilon.Host.WebApi/Program.cs @@ -1,4 +1,5 @@ using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Component.KpiMatrixComponent; using Epsilon.Abstractions.Service; using Epsilon.Canvas; using Epsilon.Component; @@ -28,7 +29,8 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); +builder.Services.AddComponentFetcher(); builder.Services.AddComponentFetcher(); var app = builder.Build(); diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs deleted file mode 100644 index b65bd7d7..00000000 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ /dev/null @@ -1,108 +0,0 @@ -using DocumentFormat.OpenXml.Spreadsheet; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using Epsilon.Canvas; -using Epsilon.Canvas.Abstractions.QueryResponse; -using Epsilon.Canvas.Abstractions.Service; -using Microsoft.Extensions.Configuration; - -namespace Epsilon.Component; - -public class KpiMatrixComponent : Component -{ - private readonly IConfiguration _configuration; - private readonly IGraphQlHttpService _graphQlService; - private readonly IAccountHttpService _accountHttpService; - - public KpiMatrixComponent( - IGraphQlHttpService graphQlService, - IAccountHttpService accountHttpService, - IConfiguration configuration - ) - { - _graphQlService = graphQlService; - _accountHttpService = accountHttpService; - _configuration = configuration; - } - - public async override Task Fetch() - { - var courseId = _configuration["Canvas:courseId"]; - - var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); - var outcomes = await _graphQlService.Query(outcomesQuery); - - var modules = ConvertToComponent(outcomes); - - return modules; - } - - private GradeStatus GetGradeStatus(double? points, double mastery) - { - if (points == null) - { - return GradeStatus.NotGraded; - } - else if (points >= mastery) - { - return GradeStatus.Approved; - } - else - { - return GradeStatus.Insufficient; - } - } - - - private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) - { - var modules = new List(); - - foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) - { - // Check if the node has a module - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) continue; - - // Group the assignment outcomes by outcome title - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(n => n?.AssessmentRatings != null) - .SelectMany(n => n.AssessmentRatings) - .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Title); - - var outcomes = outcomeGroups.Select(g => g.Key); - var assignments = outcomeGroups.Select(g => - new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) - )); - - // Find the module in the list of modules or create a new one - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); - modules.Add(module); - } - else - { - var moduleIndex = modules.IndexOf(module); - if (moduleIndex == -1) - { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); - } - else - { - var existingModule = modules[moduleIndex]; - var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); - modules[moduleIndex] = newModule; - } - } - } - - return new KpiMatrixProfile(modules); - } -} \ No newline at end of file From 7f76d8a0f00018c6379e4c11d23a23c780cc3351 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 16 May 2023 22:32:38 +0200 Subject: [PATCH 54/72] File restructure --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 - .../Component/KpiMatrixAssignment.cs | 6 - .../Component/KpiMatrixCollection.cs | 148 ------------------ .../KpiMatrixModule.cs | 2 +- .../Component/KpiMatrixOutcome.cs | 7 - 5 files changed, 1 insertion(+), 169 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs (55%) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs deleted file mode 100644 index 8f5b15a5..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrix.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments -); - diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs deleted file mode 100644 index 625815fa..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs deleted file mode 100644 index d1daafec..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs +++ /dev/null @@ -1,148 +0,0 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent -{ - public OpenXmlElement ToWord() - { - var body = new Body(); - - foreach (var module in KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth - { - Width = "0", Type = TableWidthUnitValues.Auto, - })); - - // Calculate the header row height based on the longest assignment name. - var headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) - { - headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight - { - Val = (UInt32Value)(uint)headerRowHeight, - })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) - { - var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection - { - Val = TextDirectionValues.BottomToTopLeftToRight, - }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); - - // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - if (outcomeAssignment != null) - { - var fillColor = outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF", - }; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); - } - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); - } - - body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break - { - Type = BreakValues.Page, - }))); - } - - return body; - } - - private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) - { - var cell = new TableCell(); - var cellProperties = new TableCellProperties(); - var borders = new TableCellBorders( - new LeftBorder - { - Val = BorderValues.Single, - }, - new RightBorder - { - Val = BorderValues.Single, - }, - new TopBorder - { - Val = BorderValues.Single, - }, - new BottomBorder - { - Val = BorderValues.Single, - }); - - foreach (var element in elements) - { - cell.Append(element); - } - - if (width != null) - { - cellProperties.Append(new TableCellWidth - { - Type = TableWidthUnitValues.Dxa, Width = width, - }); - } - - cellProperties.Append(borders); - cell.PrependChild(cellProperties); - - return cell; - } -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs similarity index 55% rename from Epsilon.Abstractions/Component/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs index 659c932e..38f92501 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs deleted file mode 100644 index 43140a0d..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file From a07c9b315685cd2df61eb603016c9bb05a32a5d3 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:46:41 +0200 Subject: [PATCH 55/72] Revised coding structure. Assignments that are not yest graded are also now included --- .../Component/KpiMatrixComponent/KpiMatrixModule.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs deleted file mode 100644 index 38f92501..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - -public record KpiMatrixModule( - string Name, - KpiMatrix KpiMatrix -); \ No newline at end of file From 7b3f3503a747eb63c71601407dfa1620ef2b57fc Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Fri, 21 Apr 2023 12:53:09 +0200 Subject: [PATCH 56/72] Created KpiMatrix component --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ++++++ .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 +++ Epsilon/Component/KpiMatrixComponent.cs | 83 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs create mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs new file mode 100644 index 00000000..87ca98e9 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix.cs @@ -0,0 +1,25 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes + ); + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments + ); + diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs new file mode 100644 index 00000000..ca2dc6ad --- /dev/null +++ b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; +using Epsilon.Canvas.Abstractions.Model.GraphQl; + +namespace Epsilon.Canvas.Abstractions.QueryResponse; +public record GetUserKpiMatrixOutcomes( + [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data +) +{ + public record CourseData( + [property: JsonPropertyName("course")] Course Course + ); +}; \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs new file mode 100644 index 00000000..d157702a --- /dev/null +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -0,0 +1,83 @@ +using Epsilon.Abstractions.Component; +using Epsilon.Abstractions.Model; +using Epsilon.Canvas; +using Epsilon.Canvas.Abstractions.QueryResponse; +using Epsilon.Canvas.Abstractions.Service; +using Microsoft.Extensions.Configuration; + +namespace Epsilon.Component; + +public class KpiMatrixComponent : Component +{ + + + private readonly IConfiguration _configuration; + private readonly IGraphQlHttpService _graphQlService; + private readonly IAccountHttpService _accountHttpService; + + public KpiMatrixComponent( + IGraphQlHttpService graphQlService, + IAccountHttpService accountHttpService, + IConfiguration configuration + ) + { + _graphQlService = graphQlService; + _accountHttpService = accountHttpService; + _configuration = configuration; + } + + public async override Task Fetch() + { + var courseId = _configuration["Canvas:courseId"]; + + var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); + var outcomes = await _graphQlService.Query(outcomesQuery); + + var kpiMatrix = ConvertToComponent(outcomes); + + return kpiMatrix; + } + + private GradeStatus GetGradeStatus(double? points) + { + return points switch + { + null => GradeStatus.NotGraded, + >= 4 => GradeStatus.Approved, + _ => GradeStatus.Insufficient + }; + } + + private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + { + var outcomes = new List(); + var assignments = new List(); + + if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) + return new KpiMatrix(outcomes, assignments); + foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) + { + var assignmentOutcomes = new List(); + var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() + ?.AssessmentRatings; + if (assessmentRatings == null) continue; + + foreach (var assessmentRating in assessmentRatings) + { + if (assessmentRating.Outcome != null) + { + assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points))); + if (outcomes.All(o => o != assessmentRating.Outcome.Title)) + { + outcomes.Add(assessmentRating.Outcome.Title); + } + } + } + + assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + } + + return new KpiMatrix(outcomes ,assignments); + } +} \ No newline at end of file From 5df36d5661950bd42674727c1c086520e3ed0a0c Mon Sep 17 00:00:00 2001 From: Jasper123pyah <73039915+Jasper123pyah@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:09:47 +0200 Subject: [PATCH 57/72] Added modules and fixed comments --- Epsilon.Abstractions/Model/KpiMatrix.cs | 25 ----- .../Model/KpiMatrix/GradeStatus.cs | 8 ++ .../Model/KpiMatrix/KpiMatrixModule.cs | 6 + .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 + .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 + Epsilon/Component/KpiMatrixComponent.cs | 103 +++++++++++------- 6 files changed, 87 insertions(+), 64 deletions(-) delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs create mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs diff --git a/Epsilon.Abstractions/Model/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix.cs deleted file mode 100644 index 87ca98e9..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes - ); - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments - ); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs new file mode 100644 index 00000000..d50d2254 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs @@ -0,0 +1,8 @@ +namespace Epsilon.Abstractions.Model; + +public enum GradeStatus +{ + Approved, + Insufficient, + NotGraded +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs new file mode 100644 index 00000000..9a874e98 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixModule( + string Name, + KpiMatrix KpiMatrix +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs new file mode 100644 index 00000000..efe15667 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs @@ -0,0 +1,5 @@ +namespace Epsilon.Abstractions.Model; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs new file mode 100644 index 00000000..c3aa2732 --- /dev/null +++ b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs @@ -0,0 +1,4 @@ +namespace Epsilon.Abstractions.Model; + +public record KpiMatrixProfile( + IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs index d157702a..b65bd7d7 100644 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ b/Epsilon/Component/KpiMatrixComponent.cs @@ -1,4 +1,5 @@ -using Epsilon.Abstractions.Component; +using DocumentFormat.OpenXml.Spreadsheet; +using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Model; using Epsilon.Canvas; using Epsilon.Canvas.Abstractions.QueryResponse; @@ -7,10 +8,8 @@ namespace Epsilon.Component; -public class KpiMatrixComponent : Component +public class KpiMatrixComponent : Component { - - private readonly IConfiguration _configuration; private readonly IGraphQlHttpService _graphQlService; private readonly IAccountHttpService _accountHttpService; @@ -25,59 +24,85 @@ IConfiguration configuration _accountHttpService = accountHttpService; _configuration = configuration; } - - public async override Task Fetch() + + public async override Task Fetch() { var courseId = _configuration["Canvas:courseId"]; var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); var outcomes = await _graphQlService.Query(outcomesQuery); - - var kpiMatrix = ConvertToComponent(outcomes); - return kpiMatrix; + var modules = ConvertToComponent(outcomes); + + return modules; } - private GradeStatus GetGradeStatus(double? points) + private GradeStatus GetGradeStatus(double? points, double mastery) { - return points switch + if (points == null) + { + return GradeStatus.NotGraded; + } + else if (points >= mastery) { - null => GradeStatus.NotGraded, - >= 4 => GradeStatus.Approved, - _ => GradeStatus.Insufficient - }; + return GradeStatus.Approved; + } + else + { + return GradeStatus.Insufficient; + } } - - private KpiMatrix ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) + + + private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) { - var outcomes = new List(); - var assignments = new List(); + var modules = new List(); - if (getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes == null) - return new KpiMatrix(outcomes, assignments); foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) { - var assignmentOutcomes = new List(); - var assessmentRatings = node.RubricAssessmentsConnection.Nodes.FirstOrDefault() - ?.AssessmentRatings; - if (assessmentRatings == null) continue; - - foreach (var assessmentRating in assessmentRatings) + // Check if the node has a module + var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; + if (moduleName == null) continue; + + // Group the assignment outcomes by outcome title + var outcomeGroups = node.RubricAssessmentsConnection.Nodes + .Where(n => n?.AssessmentRatings != null) + .SelectMany(n => n.AssessmentRatings) + .Where(r => r.Outcome != null) + .GroupBy(r => r.Outcome.Title); + + var outcomes = outcomeGroups.Select(g => g.Key); + var assignments = outcomeGroups.Select(g => + new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => + new KpiMatrixOutcome(assessmentRating.Outcome.Title, + GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) + )); + + // Find the module in the list of modules or create a new one + var module = modules.FirstOrDefault(m => m.Name == moduleName); + if (module == null) + { + module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); + modules.Add(module); + } + else + { + var moduleIndex = modules.IndexOf(module); + if (moduleIndex == -1) { - if (assessmentRating.Outcome != null) - { - assignmentOutcomes.Add(new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points))); - if (outcomes.All(o => o != assessmentRating.Outcome.Title)) - { - outcomes.Add(assessmentRating.Outcome.Title); - } - } + modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); } - - assignments.Add(new KpiMatrixAssignment(node.Assignment.Name, assignmentOutcomes)); + else + { + var existingModule = modules[moduleIndex]; + var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); + var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); + var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); + modules[moduleIndex] = newModule; + } + } } - return new KpiMatrix(outcomes ,assignments); + return new KpiMatrixProfile(modules); } } \ No newline at end of file From 5528d2569fb3f95440eabea27556917ec64592c1 Mon Sep 17 00:00:00 2001 From: jasper123pyah Date: Tue, 9 May 2023 13:01:40 +0200 Subject: [PATCH 58/72] Merge conflicts --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 + .../Component/KpiMatrixAssignment.cs | 6 + .../Component/KpiMatrixCollection.cs | 148 ++++++++++++++++++ .../KpiMatrixModule.cs | 2 +- .../Component/KpiMatrixOutcome.cs | 7 + .../Model/KpiMatrix/GradeStatus.cs | 8 - .../Model/KpiMatrix/KpiMatrixOutcome.cs | 5 - .../Model/KpiMatrix/KpiMatrixProfile.cs | 4 - .../QueryResponse/GetUserKpiMatrixOutcomes.cs | 12 -- 9 files changed, 169 insertions(+), 30 deletions(-) create mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs create mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/{Model/KpiMatrix => Component}/KpiMatrixModule.cs (64%) create mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs delete mode 100644 Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs new file mode 100644 index 00000000..8f5b15a5 --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrix.cs @@ -0,0 +1,7 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrix( + IEnumerable Outcomes, + IEnumerable Assignments +); + diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs new file mode 100644 index 00000000..625815fa --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs @@ -0,0 +1,6 @@ +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixAssignment( + string Name, + IEnumerable Outcomes +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs new file mode 100644 index 00000000..d1daafec --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs @@ -0,0 +1,148 @@ +using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Wordprocessing; +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; + +public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent +{ + public OpenXmlElement ToWord() + { + var body = new Body(); + + foreach (var module in KpiMatrixModules) + { + // Create a header with the Name of the module. + var header = new Paragraph(new Run(new Text(module.Name))); + body.AppendChild(header); + + // Create a table, with rows for the outcomes and columns for the assignments. + var table = new Table(); + + // Set table properties for formatting. + table.AppendChild(new TableProperties( + new TableWidth + { + Width = "0", Type = TableWidthUnitValues.Auto, + })); + + // Calculate the header row height based on the longest assignment name. + var headerRowHeight = 0; + if (module.KpiMatrix.Assignments.Any()) + { + headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; + } + + // Create the table header row. + var headerRow = new TableRow(); + headerRow.AppendChild(new TableRowProperties(new TableRowHeight + { + Val = (UInt32Value)(uint)headerRowHeight, + })); + + // Empty top-left cell. + headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); + + foreach (var assignment in module.KpiMatrix.Assignments) + { + var cell = CreateTableCellWithBorders("100"); + cell.FirstChild.Append(new TextDirection + { + Val = TextDirectionValues.BottomToTopLeftToRight, + }); + + cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); + headerRow.AppendChild(cell); + } + + table.AppendChild(headerRow); + + // Add the outcome rows. + foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) + { + var row = new TableRow(); + + // Add the outcome title cell. + row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); + + // Add the assignment cells. + foreach (var assignment in module.KpiMatrix.Assignments) + { + var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); + var cell = CreateTableCellWithBorders("100"); + + // Set cell color based on GradeStatus. + if (outcomeAssignment != null) + { + var fillColor = outcomeAssignment.GradeStatus switch + { + GradeStatus.Approved => "44F656", + GradeStatus.Insufficient => "FA1818", + GradeStatus.NotGraded => "FAFF00", + _ => "FFFFFF", + }; + cell.FirstChild.Append(new Shading + { + Fill = fillColor, + }); + } + + // Add an empty text element since we're using color instead of text. + cell.Append(new Paragraph(new Run(new Text("")))); + row.AppendChild(cell); + } + + table.AppendChild(row); + } + + body.AppendChild(table); + body.AppendChild(new Paragraph(new Run(new Break + { + Type = BreakValues.Page, + }))); + } + + return body; + } + + private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) + { + var cell = new TableCell(); + var cellProperties = new TableCellProperties(); + var borders = new TableCellBorders( + new LeftBorder + { + Val = BorderValues.Single, + }, + new RightBorder + { + Val = BorderValues.Single, + }, + new TopBorder + { + Val = BorderValues.Single, + }, + new BottomBorder + { + Val = BorderValues.Single, + }); + + foreach (var element in elements) + { + cell.Append(element); + } + + if (width != null) + { + cellProperties.Append(new TableCellWidth + { + Type = TableWidthUnitValues.Dxa, Width = width, + }); + } + + cellProperties.Append(borders); + cell.PrependChild(cellProperties); + + return cell; + } +} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixModule.cs similarity index 64% rename from Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixModule.cs index 9a874e98..659c932e 100644 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Model; +namespace Epsilon.Abstractions.Component; public record KpiMatrixModule( string Name, diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs new file mode 100644 index 00000000..43140a0d --- /dev/null +++ b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs @@ -0,0 +1,7 @@ +using Epsilon.Canvas.Abstractions.Model; + +namespace Epsilon.Abstractions.Component; +public record KpiMatrixOutcome( + string Title, + GradeStatus GradeStatus +); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs b/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs deleted file mode 100644 index d50d2254..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/GradeStatus.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs deleted file mode 100644 index efe15667..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixOutcome.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Epsilon.Abstractions.Model; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs deleted file mode 100644 index c3aa2732..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixProfile.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixProfile( - IEnumerable KpiMatrixModules); \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs b/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs deleted file mode 100644 index ca2dc6ad..00000000 --- a/Epsilon.Canvas.Abstractions/QueryResponse/GetUserKpiMatrixOutcomes.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Text.Json.Serialization; -using Epsilon.Canvas.Abstractions.Model.GraphQl; - -namespace Epsilon.Canvas.Abstractions.QueryResponse; -public record GetUserKpiMatrixOutcomes( - [property: JsonPropertyName("data")] GetUserKpiMatrixOutcomes.CourseData? Data -) -{ - public record CourseData( - [property: JsonPropertyName("course")] Course Course - ); -}; \ No newline at end of file From 836ef47bdf029f7c82fcccc26a4033a4f82615e3 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 16 May 2023 22:32:38 +0200 Subject: [PATCH 59/72] File restructure --- Epsilon.Abstractions/Component/KpiMatrix.cs | 7 - .../Component/KpiMatrixAssignment.cs | 6 - .../Component/KpiMatrixCollection.cs | 148 ------------------ .../KpiMatrixModule.cs | 2 +- 4 files changed, 1 insertion(+), 162 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixAssignment.cs delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixCollection.cs rename Epsilon.Abstractions/Component/{ => KpiMatrixComponent}/KpiMatrixModule.cs (55%) diff --git a/Epsilon.Abstractions/Component/KpiMatrix.cs b/Epsilon.Abstractions/Component/KpiMatrix.cs deleted file mode 100644 index 8f5b15a5..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrix.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments -); - diff --git a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs deleted file mode 100644 index 625815fa..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixCollection.cs deleted file mode 100644 index d1daafec..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixCollection.cs +++ /dev/null @@ -1,148 +0,0 @@ -using DocumentFormat.OpenXml; -using DocumentFormat.OpenXml.Wordprocessing; -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; - -public record KpiMatrixCollection(IEnumerable KpiMatrixModules) : IEpsilonWordComponent -{ - public OpenXmlElement ToWord() - { - var body = new Body(); - - foreach (var module in KpiMatrixModules) - { - // Create a header with the Name of the module. - var header = new Paragraph(new Run(new Text(module.Name))); - body.AppendChild(header); - - // Create a table, with rows for the outcomes and columns for the assignments. - var table = new Table(); - - // Set table properties for formatting. - table.AppendChild(new TableProperties( - new TableWidth - { - Width = "0", Type = TableWidthUnitValues.Auto, - })); - - // Calculate the header row height based on the longest assignment name. - var headerRowHeight = 0; - if (module.KpiMatrix.Assignments.Any()) - { - headerRowHeight = module.KpiMatrix.Assignments.Max(static a => a.Name.Length) * 111; - } - - // Create the table header row. - var headerRow = new TableRow(); - headerRow.AppendChild(new TableRowProperties(new TableRowHeight - { - Val = (UInt32Value)(uint)headerRowHeight, - })); - - // Empty top-left cell. - headerRow.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(""))))); - - foreach (var assignment in module.KpiMatrix.Assignments) - { - var cell = CreateTableCellWithBorders("100"); - cell.FirstChild.Append(new TextDirection - { - Val = TextDirectionValues.BottomToTopLeftToRight, - }); - - cell.Append(new Paragraph(new Run(new Text(assignment.Name)))); - headerRow.AppendChild(cell); - } - - table.AppendChild(headerRow); - - // Add the outcome rows. - foreach (var outcome in module.KpiMatrix.Outcomes.OrderBy(static o => o.Id)) - { - var row = new TableRow(); - - // Add the outcome title cell. - row.AppendChild(CreateTableCellWithBorders("2500", new Paragraph(new Run(new Text(outcome.Title))))); - - // Add the assignment cells. - foreach (var assignment in module.KpiMatrix.Assignments) - { - var outcomeAssignment = assignment.Outcomes.FirstOrDefault(o => o.Title == outcome.Title); - var cell = CreateTableCellWithBorders("100"); - - // Set cell color based on GradeStatus. - if (outcomeAssignment != null) - { - var fillColor = outcomeAssignment.GradeStatus switch - { - GradeStatus.Approved => "44F656", - GradeStatus.Insufficient => "FA1818", - GradeStatus.NotGraded => "FAFF00", - _ => "FFFFFF", - }; - cell.FirstChild.Append(new Shading - { - Fill = fillColor, - }); - } - - // Add an empty text element since we're using color instead of text. - cell.Append(new Paragraph(new Run(new Text("")))); - row.AppendChild(cell); - } - - table.AppendChild(row); - } - - body.AppendChild(table); - body.AppendChild(new Paragraph(new Run(new Break - { - Type = BreakValues.Page, - }))); - } - - return body; - } - - private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) - { - var cell = new TableCell(); - var cellProperties = new TableCellProperties(); - var borders = new TableCellBorders( - new LeftBorder - { - Val = BorderValues.Single, - }, - new RightBorder - { - Val = BorderValues.Single, - }, - new TopBorder - { - Val = BorderValues.Single, - }, - new BottomBorder - { - Val = BorderValues.Single, - }); - - foreach (var element in elements) - { - cell.Append(element); - } - - if (width != null) - { - cellProperties.Append(new TableCellWidth - { - Type = TableWidthUnitValues.Dxa, Width = width, - }); - } - - cellProperties.Append(borders); - cell.PrependChild(cellProperties); - - return cell; - } -} \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs similarity index 55% rename from Epsilon.Abstractions/Component/KpiMatrixModule.cs rename to Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs index 659c932e..38f92501 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixModule.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs @@ -1,4 +1,4 @@ -namespace Epsilon.Abstractions.Component; +namespace Epsilon.Abstractions.Component.KpiMatrixComponent; public record KpiMatrixModule( string Name, From 44927c044f535e32dd7f2c39559f296aebdf211f Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 18 May 2023 20:46:41 +0200 Subject: [PATCH 60/72] Revised coding structure. Assignments that are not yest graded are also now included --- .../Component/KpiMatrixComponent/KpiMatrixModule.cs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs deleted file mode 100644 index 38f92501..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixModule.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - -public record KpiMatrixModule( - string Name, - KpiMatrix KpiMatrix -); \ No newline at end of file From 6d89277b598c1bc20155cdc8d204331d74982b2f Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 10:02:51 +0200 Subject: [PATCH 61/72] Added legend and changed "GradeStatus" structure --- Epsilon.Canvas.Abstractions/Model/GradeStatus.cs | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Epsilon.Canvas.Abstractions/Model/GradeStatus.cs diff --git a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs b/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs deleted file mode 100644 index 307d4dfc..00000000 --- a/Epsilon.Canvas.Abstractions/Model/GradeStatus.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Epsilon.Canvas.Abstractions.Model; - -public enum GradeStatus -{ - Approved, - Insufficient, - NotGraded, -} \ No newline at end of file From 0dd2e3ca10534390299e651f087fb52e86271d39 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 13:03:27 +0200 Subject: [PATCH 62/72] Added extra grade type --- .../KpiMatrixComponent/KpiMatrixCollection.cs | 7 +- .../KpiMatrixComponent/KpiMatrixConstants.cs | 7 +- .../Component/KpiMatrixOutcome.cs | 7 -- .../Model/KpiMatrix/KpiMatrix.cs | 9 -- .../Model/KpiMatrix/KpiMatrixAssignment.cs | 6 - Epsilon/Component/KpiMatrixComponent.cs | 108 ------------------ .../Component/KpiMatrixComponentFetcher.cs | 28 +++-- 7 files changed, 22 insertions(+), 150 deletions(-) delete mode 100644 Epsilon.Abstractions/Component/KpiMatrixOutcome.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs delete mode 100644 Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs delete mode 100644 Epsilon/Component/KpiMatrixComponent.cs diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index ef272696..1c2ae122 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,11 +1,12 @@ using DocumentFormat.OpenXml; +using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; // using Epsilon.Canvas.Abstractions.Model; namespace Epsilon.Abstractions.Component.KpiMatrixComponent; - +[CompetenceComponentName("kpi_matrix")] public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent { @@ -32,7 +33,7 @@ private static OpenXmlElement GetLegend() return table; } - public OpenXmlElement ToWord() + public void AddToWordDocument(MainDocumentPart mainDocumentPart) { var body = new Body(); // Create a table, with rows for the outcomes and columns for the assignments. @@ -131,7 +132,7 @@ public OpenXmlElement ToWord() body.Append(new Paragraph(new Run(new Text("")))); body.AppendChild(table); - return body; + mainDocumentPart.Document.AppendChild(body); } private static TableCell CreateTableCellWithBorders(string? width, params OpenXmlElement[] elements) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs index 13a54cb1..3e0d3ccf 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixConstants.cs @@ -6,13 +6,16 @@ public static class KpiMatrixConstants public static readonly IDictionary GradeStatus = new Dictionary { { - "Approved", new GradeStatus("Approved", "44F656") + "Mastered", new GradeStatus("Mastered", "44F656") }, { "Insufficient", new GradeStatus("Insufficient", "FA1818") }, { - "NotGraded", new GradeStatus("NotGraded", "FAFF00") + "NotGradedAssessed", new GradeStatus("Not graded, Assignment assessed", "FAFF00") + }, + { + "NotGradedNotAssessed", new GradeStatus("Not graded, Assignment not assessed", "9F2B68") }, }; } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs b/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs deleted file mode 100644 index 43140a0d..00000000 --- a/Epsilon.Abstractions/Component/KpiMatrixOutcome.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Component; -public record KpiMatrixOutcome( - string Title, - GradeStatus GradeStatus -); \ No newline at end of file diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs deleted file mode 100644 index a86409c5..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrix.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Epsilon.Canvas.Abstractions.Model; - -namespace Epsilon.Abstractions.Model; - -public record KpiMatrix( - IEnumerable Outcomes, - IEnumerable Assignments -); - diff --git a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs b/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs deleted file mode 100644 index 6ee6bdcb..00000000 --- a/Epsilon.Abstractions/Model/KpiMatrix/KpiMatrixAssignment.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Epsilon.Abstractions.Model; - -public record KpiMatrixAssignment( - string Name, - IEnumerable Outcomes -); \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponent.cs b/Epsilon/Component/KpiMatrixComponent.cs deleted file mode 100644 index b65bd7d7..00000000 --- a/Epsilon/Component/KpiMatrixComponent.cs +++ /dev/null @@ -1,108 +0,0 @@ -using DocumentFormat.OpenXml.Spreadsheet; -using Epsilon.Abstractions.Component; -using Epsilon.Abstractions.Model; -using Epsilon.Canvas; -using Epsilon.Canvas.Abstractions.QueryResponse; -using Epsilon.Canvas.Abstractions.Service; -using Microsoft.Extensions.Configuration; - -namespace Epsilon.Component; - -public class KpiMatrixComponent : Component -{ - private readonly IConfiguration _configuration; - private readonly IGraphQlHttpService _graphQlService; - private readonly IAccountHttpService _accountHttpService; - - public KpiMatrixComponent( - IGraphQlHttpService graphQlService, - IAccountHttpService accountHttpService, - IConfiguration configuration - ) - { - _graphQlService = graphQlService; - _accountHttpService = accountHttpService; - _configuration = configuration; - } - - public async override Task Fetch() - { - var courseId = _configuration["Canvas:courseId"]; - - var outcomesQuery = QueryConstants.GetUserKpiMatrixOutcomes.Replace("$courseId", $"{courseId}"); - var outcomes = await _graphQlService.Query(outcomesQuery); - - var modules = ConvertToComponent(outcomes); - - return modules; - } - - private GradeStatus GetGradeStatus(double? points, double mastery) - { - if (points == null) - { - return GradeStatus.NotGraded; - } - else if (points >= mastery) - { - return GradeStatus.Approved; - } - else - { - return GradeStatus.Insufficient; - } - } - - - private KpiMatrixProfile ConvertToComponent(GetUserKpiMatrixOutcomes getUserKpiMatrixOutcomes) - { - var modules = new List(); - - foreach (var node in getUserKpiMatrixOutcomes.Data?.Course.SubmissionsConnection?.Nodes) - { - // Check if the node has a module - var moduleName = node.Assignment.Modules.FirstOrDefault()?.Name; - if (moduleName == null) continue; - - // Group the assignment outcomes by outcome title - var outcomeGroups = node.RubricAssessmentsConnection.Nodes - .Where(n => n?.AssessmentRatings != null) - .SelectMany(n => n.AssessmentRatings) - .Where(r => r.Outcome != null) - .GroupBy(r => r.Outcome.Title); - - var outcomes = outcomeGroups.Select(g => g.Key); - var assignments = outcomeGroups.Select(g => - new KpiMatrixAssignment(node.Assignment.Name, g.Select(assessmentRating => - new KpiMatrixOutcome(assessmentRating.Outcome.Title, - GetGradeStatus(assessmentRating.Points, assessmentRating.Outcome.MasteryPoints))) - )); - - // Find the module in the list of modules or create a new one - var module = modules.FirstOrDefault(m => m.Name == moduleName); - if (module == null) - { - module = new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments)); - modules.Add(module); - } - else - { - var moduleIndex = modules.IndexOf(module); - if (moduleIndex == -1) - { - modules.Add(new KpiMatrixModule(moduleName, new KpiMatrix(outcomes, assignments))); - } - else - { - var existingModule = modules[moduleIndex]; - var newAssignments = existingModule.KpiMatrix.Assignments.Concat(assignments); - var newOutcomes = existingModule.KpiMatrix.Outcomes.Concat(outcomes); - var newModule = new KpiMatrixModule(moduleName, new KpiMatrix(newOutcomes, newAssignments)); - modules[moduleIndex] = newModule; - } - } - } - - return new KpiMatrixProfile(modules); - } -} \ No newline at end of file diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index bc6eb47f..461d52ed 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -3,6 +3,7 @@ using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; +using KpiMatrixOutcome = Epsilon.Abstractions.Component.KpiMatrixComponent.KpiMatrixOutcome; namespace Epsilon.Component; @@ -72,13 +73,15 @@ public override async Task Fetch() return ConvertToComponent(outcomes, new DateTime(2023, 2, 1), DateTime.Now); } - private static GradeStatus GetGradeStatus(double? points, double? mastery) + private static GradeStatus GetGradeStatus(bool isSubmitted, double? points, double? mastery) { - return points != null - ? points >= mastery - ? KpiMatrixConstants.GradeStatus["Approved"] - : KpiMatrixConstants.GradeStatus["Insufficient"] - : KpiMatrixConstants.GradeStatus["NotGraded"]; + return isSubmitted + ? points != null + ? points >= mastery + ? KpiMatrixConstants.GradeStatus["Mastered"] + : KpiMatrixConstants.GradeStatus["Insufficient"] + : KpiMatrixConstants.GradeStatus["NotGradedAssessed"] + : KpiMatrixConstants.GradeStatus["NotGradedNotAssessed"]; } @@ -109,17 +112,12 @@ DateTime endAt { if (criteria.Outcome != null) { - if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask)) + //Validate that outcome is a HboI KPI + if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask) || FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) { + var assessmentRatings = submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings; kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, - GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) - ?.Points - , criteria.Outcome.MasteryPoints))); - } - else if (FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) - { - kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, - GetGradeStatus(submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + GetGradeStatus(assessmentRatings != null, assessmentRatings?.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) ?.Points , criteria.Outcome.MasteryPoints))); } From 6b0e126889223f1fa4058a1cdb56062d016bc7d4 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 13:14:43 +0200 Subject: [PATCH 63/72] Added legend to json result --- .../Component/KpiMatrixComponent/KpiMatrixCollection.cs | 9 +++------ Epsilon/Component/KpiMatrixComponentFetcher.cs | 3 +-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs index 1c2ae122..d1670f71 100644 --- a/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs +++ b/Epsilon.Abstractions/Component/KpiMatrixComponent/KpiMatrixCollection.cs @@ -1,19 +1,16 @@ using DocumentFormat.OpenXml; using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Wordprocessing; -using TextDirectionValues = DocumentFormat.OpenXml.Wordprocessing.TextDirectionValues; - -// using Epsilon.Canvas.Abstractions.Model; namespace Epsilon.Abstractions.Component.KpiMatrixComponent; [CompetenceComponentName("kpi_matrix")] -public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments) : ICompetenceWordComponent +public record KpiMatrixCollection(IEnumerable KpiMatrixAssignments, IDictionary GradeStatus) : ICompetenceWordComponent { - private static OpenXmlElement GetLegend() + private OpenXmlElement GetLegend() { var table = new Table(); - foreach (var status in KpiMatrixConstants.GradeStatus) + foreach (var status in GradeStatus) { var row = new TableRow(); var cellName = CreateTableCellWithBorders("200"); diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 461d52ed..69edb152 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -3,7 +3,6 @@ using Epsilon.Canvas.Abstractions.Model.GraphQl; using Epsilon.Canvas.Abstractions.Service; using Microsoft.Extensions.Configuration; -using KpiMatrixOutcome = Epsilon.Abstractions.Component.KpiMatrixComponent.KpiMatrixOutcome; namespace Epsilon.Component; @@ -135,6 +134,6 @@ DateTime endAt } } - return new KpiMatrixCollection(assignments); + return new KpiMatrixCollection(assignments, KpiMatrixConstants.GradeStatus); } } \ No newline at end of file From 1dd5d3f2ced329dd245a9c35cfff54a0b22a68c2 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 21:21:42 +0200 Subject: [PATCH 64/72] Working order --- .../Component/CompetenceComponentFetcher.cs | 4 ++-- .../Component/ICompetenceComponentFetcher.cs | 4 ++-- .../Service/ICompetenceComponentService.cs | 8 ++++---- .../Model/GraphQl/RubricAssessmentNode.cs | 2 +- .../Controllers/ComponentController.cs | 19 +++++++++++++----- ...etenceProfileCompetenceComponentFetcher.cs | 2 +- .../Component/KpiMatrixComponentFetcher.cs | 20 +++++++++++-------- .../Component/PersonaPageComponentFetcher.cs | 2 +- Epsilon/Service/CompetenceComponentService.cs | 18 +++++++++-------- 9 files changed, 47 insertions(+), 32 deletions(-) diff --git a/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs b/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs index d84e9bf6..b3acc779 100644 --- a/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs +++ b/Epsilon.Abstractions/Component/CompetenceComponentFetcher.cs @@ -2,7 +2,7 @@ namespace Epsilon.Abstractions.Component; public abstract class CompetenceComponentFetcher : ICompetenceComponentFetcher where TComponent : ICompetenceComponent { - public async Task FetchUnknown() => await Fetch(); + public async Task FetchUnknown(DateTime? startDate = null, DateTime? endDate = null) => await Fetch(startDate, endDate); - public abstract Task Fetch(); + public abstract Task Fetch(DateTime? startDate = null, DateTime? endDate = null); } \ No newline at end of file diff --git a/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs b/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs index cac2873a..fa028746 100644 --- a/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs +++ b/Epsilon.Abstractions/Component/ICompetenceComponentFetcher.cs @@ -2,11 +2,11 @@ public interface ICompetenceComponentFetcher { - public Task FetchUnknown(); + public Task FetchUnknown(DateTime? startDate = null, DateTime? endDate = null); } public interface ICompetenceComponentFetcher : ICompetenceComponentFetcher where TComponent : ICompetenceComponent { - public Task Fetch(); + public Task Fetch(DateTime? startDate = null, DateTime? endDate = null); } \ No newline at end of file diff --git a/Epsilon.Abstractions/Service/ICompetenceComponentService.cs b/Epsilon.Abstractions/Service/ICompetenceComponentService.cs index 7b9c252c..f06d916a 100644 --- a/Epsilon.Abstractions/Service/ICompetenceComponentService.cs +++ b/Epsilon.Abstractions/Service/ICompetenceComponentService.cs @@ -4,11 +4,11 @@ namespace Epsilon.Abstractions.Service; public interface ICompetenceComponentService { - IAsyncEnumerable GetComponents(); + IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null); - IAsyncEnumerable GetComponents() where TComponent : ICompetenceComponent; + IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null) where TComponent : ICompetenceComponent; - Task GetComponent(string name); + Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null); - Task GetComponent(string name) where TComponent : class, ICompetenceComponent; + Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null) where TComponent : class, ICompetenceComponent; } \ No newline at end of file diff --git a/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs b/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs index 901f2b2a..e2f0ef08 100644 --- a/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs +++ b/Epsilon.Canvas.Abstractions/Model/GraphQl/RubricAssessmentNode.cs @@ -3,5 +3,5 @@ namespace Epsilon.Canvas.Abstractions.Model.GraphQl; public record RubricAssessmentNode( - [property: JsonPropertyName("assessmentRatings")] IReadOnlyList AssessmentRatings + [property: JsonPropertyName("assessmentRatings")] IReadOnlyList? AssessmentRatings ); \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index 563823ff..b8860a88 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -1,3 +1,4 @@ +using System.Globalization; using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Service; using Microsoft.AspNetCore.Mvc; @@ -15,16 +16,24 @@ public ComponentController(ICompetenceComponentService competenceComponentServic _competenceComponentService = competenceComponentService; } + // + // DateTime.ParseExact(startDate ?? "", "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("en-US")), + // DateTime.Parse(endDate ?? "", CultureInfo.CreateSpecificCulture("en-US")) + [HttpGet("{componentName}")] [Produces(typeof(CompetenceProfile))] - public async Task> GetCompetenceProfile(string componentName) + public async Task> GetCompetenceProfile(string componentName, DateTime? startDate, DateTime? endDate) { - var component = await _competenceComponentService.GetComponent(componentName); - if (component == null) { - return NotFound(); + var component = await _competenceComponentService.GetComponent(componentName, startDate, endDate); + if (component == null) + { + return NotFound(); + } + + return Ok(component); } - return Ok(component); + return NotFound(); } } \ No newline at end of file diff --git a/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs b/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs index 3266f96e..8783a24d 100644 --- a/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs +++ b/Epsilon/Component/CompetenceProfileCompetenceComponentFetcher.cs @@ -54,7 +54,7 @@ IConfiguration configuration _configuration = configuration; } - public override async Task Fetch() + public override async Task Fetch(DateTime ?startDate = null, DateTime ?endDate = null) { var studentId = _configuration["Canvas:StudentId"]; var outcomesQuery = GetAllUserCoursesSubmissionOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCulture); diff --git a/Epsilon/Component/KpiMatrixComponentFetcher.cs b/Epsilon/Component/KpiMatrixComponentFetcher.cs index 69edb152..fa772ea9 100644 --- a/Epsilon/Component/KpiMatrixComponentFetcher.cs +++ b/Epsilon/Component/KpiMatrixComponentFetcher.cs @@ -64,12 +64,12 @@ IConfiguration configuration _configuration = configuration; } - public override async Task Fetch() + public override async Task Fetch(DateTime? startDate = null, DateTime? endDate = null) { var studentId = _configuration["Canvas:StudentId"]; var outcomesQuery = GetUserKpiMatrixOutcomes.Replace("$studentIds", $"{studentId}", StringComparison.InvariantCultureIgnoreCase); var outcomes = await _graphQlService.Query(outcomesQuery); - return ConvertToComponent(outcomes, new DateTime(2023, 2, 1), DateTime.Now); + return ConvertToComponent(outcomes, startDate ?? DateTime.Now, endDate ?? DateTime.Now); } private static GradeStatus GetGradeStatus(bool isSubmitted, double? points, double? mastery) @@ -85,9 +85,9 @@ private static GradeStatus GetGradeStatus(bool isSubmitted, double? points, doub private static KpiMatrixCollection ConvertToComponent( - CanvasGraphQlQueryResponse queryResponse, - DateTime startAt, - DateTime endAt + CanvasGraphQlQueryResponse? queryResponse, + DateTime? startAt, + DateTime? endAt ) { var assignments = new List(); @@ -101,7 +101,7 @@ DateTime endAt if (submission != null) { - if (submission.Assignment?.Rubric != null) + if (submission.Assignment?.Rubric != null && submission.RubricAssessments?.Nodes != null) { var rubricAssessments = submission.Assignment.Rubric.Criteria; var kpiMatrixOutcomes = new List(); @@ -112,11 +112,15 @@ DateTime endAt if (criteria.Outcome != null) { //Validate that outcome is a HboI KPI - if (FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask) || FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) + if ((FhictConstants.ProfessionalTasks.TryGetValue(criteria.Outcome.Id, out var professionalTask) || + FhictConstants.ProfessionalSkills.TryGetValue(criteria.Outcome.Id, out var professionalSkill)) && + rubricAssessments.Any()) { var assessmentRatings = submission.RubricAssessments?.Nodes?.FirstOrDefault()?.AssessmentRatings; + var test = assessmentRatings != null; + var test2 = assessmentRatings?.FirstOrDefault(o => o?.Criterion?.Outcome?.Id == criteria?.Outcome?.Id); kpiMatrixOutcomes.Add(new KpiMatrixOutcome(criteria.Outcome.Id, criteria.Outcome.Title, - GetGradeStatus(assessmentRatings != null, assessmentRatings?.FirstOrDefault(o => o.Criterion.Outcome?.Id == criteria.Outcome.Id) + GetGradeStatus(assessmentRatings != null, assessmentRatings?.FirstOrDefault(o => o?.Criterion?.Outcome?.Id == criteria?.Outcome?.Id) ?.Points , criteria.Outcome.MasteryPoints))); } diff --git a/Epsilon/Component/PersonaPageComponentFetcher.cs b/Epsilon/Component/PersonaPageComponentFetcher.cs index 87352755..e3da6b18 100644 --- a/Epsilon/Component/PersonaPageComponentFetcher.cs +++ b/Epsilon/Component/PersonaPageComponentFetcher.cs @@ -23,7 +23,7 @@ IOptions canvasSettings _canvasSettings = canvasSettings.Value; } - public override async Task Fetch() + public override async Task Fetch(DateTime ?startDate = null, DateTime ?endDate = null) { var courseId = _canvasSettings.CourseId; var personaHtml = await _pageHttpService.GetPageByName(courseId, "front_page"); diff --git a/Epsilon/Service/CompetenceComponentService.cs b/Epsilon/Service/CompetenceComponentService.cs index e2ddb435..55fe6bbf 100644 --- a/Epsilon/Service/CompetenceComponentService.cs +++ b/Epsilon/Service/CompetenceComponentService.cs @@ -13,17 +13,17 @@ public CompetenceComponentService(IEnumerable compo _componentFetchers = componentFetchers; } - public async IAsyncEnumerable GetComponents() + public async IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null) { foreach (var componentFetcher in _componentFetchers) { - yield return await componentFetcher.FetchUnknown(); + yield return await componentFetcher.FetchUnknown(startDate, endDate); } } - public async IAsyncEnumerable GetComponents() where TComponent : ICompetenceComponent + public async IAsyncEnumerable GetComponents(DateTime? startDate = null, DateTime? endDate = null) where TComponent : ICompetenceComponent { - await foreach (var component in GetComponents()) + await foreach (var component in GetComponents(startDate, endDate)) { if (component is TComponent componentOfT) { @@ -32,7 +32,7 @@ public async IAsyncEnumerable GetComponents() where TCom } } - public async Task GetComponent(string name) + public async Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null) { var fetcher = _componentFetchers.SingleOrDefault(f => { @@ -45,11 +45,13 @@ public async IAsyncEnumerable GetComponents() where TCom return false; }); - return fetcher == null ? null : await fetcher.FetchUnknown(); + return fetcher == null + ? null + : await fetcher.FetchUnknown(startDate, endDate); } - public async Task GetComponent(string name) where TComponent : class, ICompetenceComponent + public async Task GetComponent(string name, DateTime? startDate = null, DateTime? endDate = null) where TComponent : class, ICompetenceComponent { - return await GetComponent(name) as TComponent; + return await GetComponent(name, startDate, endDate) as TComponent; } } \ No newline at end of file From 9c225696867e55068be2bddd795c643c70612788 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Tue, 23 May 2023 21:26:09 +0200 Subject: [PATCH 65/72] startDate & endDate implementation for document generator --- Epsilon.Abstractions/Service/ICompetenceDocumentService.cs | 2 +- Epsilon.Host.WebApi/Controllers/DocumentController.cs | 4 ++-- Epsilon/Service/CompetenceDocumentService.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs b/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs index 7ff220e3..0d65e92b 100644 --- a/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs +++ b/Epsilon.Abstractions/Service/ICompetenceDocumentService.cs @@ -2,5 +2,5 @@ namespace Epsilon.Abstractions.Service; public interface ICompetenceDocumentService { - Task WriteDocument(Stream stream); + Task WriteDocument(Stream stream, DateTime? startDate = null, DateTime? endDate = null); } \ No newline at end of file diff --git a/Epsilon.Host.WebApi/Controllers/DocumentController.cs b/Epsilon.Host.WebApi/Controllers/DocumentController.cs index e3259bfb..6fb22ed8 100644 --- a/Epsilon.Host.WebApi/Controllers/DocumentController.cs +++ b/Epsilon.Host.WebApi/Controllers/DocumentController.cs @@ -15,10 +15,10 @@ public DocumentController(ICompetenceDocumentService competenceDocumentService) } [HttpGet("word")] - public async Task DownloadWordDocument() + public async Task DownloadWordDocument(DateTime? startDate = null, DateTime? endDate = null) { var stream = new MemoryStream(); - await _competenceDocumentService.WriteDocument(stream); + await _competenceDocumentService.WriteDocument(stream, startDate, endDate); return File( stream, diff --git a/Epsilon/Service/CompetenceDocumentService.cs b/Epsilon/Service/CompetenceDocumentService.cs index b724736c..f7994436 100644 --- a/Epsilon/Service/CompetenceDocumentService.cs +++ b/Epsilon/Service/CompetenceDocumentService.cs @@ -15,11 +15,11 @@ public CompetenceDocumentService(ICompetenceComponentService competenceComponent _competenceComponentService = competenceComponentService; } - public async Task WriteDocument(Stream stream) + public async Task WriteDocument(Stream stream, DateTime? startDate = null, DateTime? endDate = null) { var startPosition = stream.Position; - var components = await _competenceComponentService.GetComponents().ToListAsync(); + var components = await _competenceComponentService.GetComponents(startDate, endDate).ToListAsync(); using var document = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document); document.AddMainDocumentPart(); From 5a5e3121569c8b487bf9a0ad402663f043fdd671 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Wed, 24 May 2023 00:24:29 +0200 Subject: [PATCH 66/72] POC working --- .../src/components/Competance/KPIMatrix.vue | 68 ++ .../src/components/Competance/KpiMatrix.vue | 100 --- Epsilon.Host.Frontend/src/logic/Api.ts | 782 +++++++++--------- .../src/views/PerformanceDashboard.vue | 40 +- .../Controllers/ComponentController.cs | 4 - 5 files changed, 456 insertions(+), 538 deletions(-) create mode 100644 Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue delete mode 100644 Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue diff --git a/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue b/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue new file mode 100644 index 00000000..9b20586c --- /dev/null +++ b/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue b/Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue deleted file mode 100644 index d7222343..00000000 --- a/Epsilon.Host.Frontend/src/components/Competance/KpiMatrix.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - - - diff --git a/Epsilon.Host.Frontend/src/logic/Api.ts b/Epsilon.Host.Frontend/src/logic/Api.ts index f35e2098..c04b7680 100644 --- a/Epsilon.Host.Frontend/src/logic/Api.ts +++ b/Epsilon.Host.Frontend/src/logic/Api.ts @@ -10,469 +10,439 @@ */ export interface Activity { - name?: string | null; - color?: string | null; + /** @format int32 */ + id?: number; + name?: string | null; + color?: string | null; } export interface ArchitectureLayer { - name?: string | null; - shortName?: string | null; - color?: string | null; -} - -export interface AssessmentRating { - /** @format double */ - points?: number | null; - outcome?: Outcome; -} - -export interface Assignment { - name?: string | null; - modules?: Module[] | null; + /** @format int32 */ + id?: number; + name?: string | null; + shortName?: string | null; + color?: string | null; } export interface CompetenceProfile { - hboIDomain?: HboIDomain; - professionalTaskOutcomes?: ProfessionalTaskOutcome[] | null; - professionalSkillOutcomes?: ProfessionalSkillOutcome[] | null; -} - -export interface Course { - name?: string | null; - submissionsConnection?: SubmissionsConnection; + hboIDomain?: IHboIDomain; + professionalTaskOutcomes?: ProfessionalTaskResult[] | null; + professionalSkillOutcomes?: ProfessionalSkillResult[] | null; + terms?: EnrollmentTerm[] | null; + decayingAveragesPerTask?: DecayingAveragePerLayer[] | null; + decayingAveragesPerSkill?: DecayingAveragePerSkill[] | null; } -export interface CourseData { - course?: Course; +export interface DecayingAveragePerActivity { + /** @format int32 */ + activity?: number; + /** @format double */ + decayingAverage?: number; } -export interface GetUserSubmissionOutcomes { - data?: CourseData; +export interface DecayingAveragePerLayer { + /** @format int32 */ + architectureLayer?: number; + layerActivities?: DecayingAveragePerActivity[] | null; } -export interface HboIDomain { - architectureLayers?: Record; - activities?: Record; - professionalSkills?: Record; - masteryLevels?: Record; +export interface DecayingAveragePerSkill { + /** @format int32 */ + skill?: number; + /** @format double */ + decayingAverage?: number; } -export interface LtiOpenIdConnectCallback { - authenticityToken?: string | null; - idToken?: string | null; - state?: string | null; - ltiStorageTarget?: string | null; +export interface EnrollmentTerm { + name?: string | null; + /** @format date-time */ + start_at?: string | null; + /** @format date-time */ + end_at?: 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; +export interface IHboIDomain { + architectureLayers?: ArchitectureLayer[] | null; + activities?: Activity[] | null; + professionalSkills?: ProfessionalSkill[] | null; + masteryLevels?: MasteryLevel[] | null; } export interface MasteryLevel { - /** @format int32 */ - level?: number; - color?: string | null; -} - -export interface Module { - name?: string | null; -} - -export interface Node { - assignment?: Assignment; - rubricAssessmentsConnection?: RubricAssessmentsConnection; -} - -export interface Outcome { - title?: string | null; + /** @format int32 */ + id?: number; + /** @format int32 */ + level?: number; + color?: string | null; } export interface ProfessionalSkill { - name?: string | null; - shortName?: string | null; - color?: string | null; + /** @format int32 */ + id?: number; + 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; +export interface ProfessionalSkillResult { + /** @format int32 */ + skill?: number; + /** @format int32 */ + masteryLevel?: number; + /** @format double */ + 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; -} - -export interface RubricAssessmentNode { - assessmentRatings?: AssessmentRating[] | null; - user?: User; -} - -export interface RubricAssessmentsConnection { - nodes?: RubricAssessmentNode[] | null; -} - -export interface SubmissionsConnection { - nodes?: Node[] | null; -} - -export interface User { - name?: string | null; +export interface ProfessionalTaskResult { + /** @format int32 */ + architectureLayer?: number; + /** @format int32 */ + activity?: number; + /** @format int32 */ + masteryLevel?: number; + /** @format double */ + grade?: number; + /** @format date-time */ + assessedAt?: string; } 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 type RequestParams = Omit< + FullRequestParams, + "body" | "method" | "query" | "path" +>; 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; +export interface HttpResponse + extends Response { + 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); + } + + 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("&"); } - const abortController = new AbortController(); - this.abortControllers.set(cancelToken, abortController); - return abortController.signal; - }; + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ""; + } - public abortRequest = (cancelToken: CancelToken) => { - const abortController = this.abortControllers.get(cancelToken); + 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), + }; - if (abortController) { - abortController.abort(); - this.abortControllers.delete(cancelToken); + protected mergeRequestParams( + params1: RequestParams, + params2?: RequestParams + ): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; } - }; - - 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 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; + }); + }; } /** * @title Epsilon.Host.WebApi * @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, - }), - }; +export class Api< + SecurityDataType extends unknown +> 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 ComponentDetail + * @request GET:/Component/{componentName} + */ + componentDetail: ( + componentName: string, + query?: { + /** @format date-time */ + startDate?: string; + /** @format date-time */ + endDate?: string; + }, + params: RequestParams = {} + ) => + this.request({ + path: `/Component/${componentName}`, + method: "GET", + query: query, + format: "json", + ...params, + }), + }; + document = { + /** + * No description + * + * @tags Document + * @name WordList + * @request GET:/Document/word + */ + wordList: ( + query?: { + /** @format date-time */ + startDate?: string; + /** @format date-time */ + endDate?: string; + }, + params: RequestParams = {} + ) => + this.request({ + path: `/Document/word`, + method: "GET", + query: query, + ...params, + }), + }; } diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index 87f187e3..75a02032 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -1,28 +1,12 @@ diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index b8860a88..30fc722d 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -16,10 +16,6 @@ public ComponentController(ICompetenceComponentService competenceComponentServic _competenceComponentService = competenceComponentService; } - // - // DateTime.ParseExact(startDate ?? "", "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("en-US")), - // DateTime.Parse(endDate ?? "", CultureInfo.CreateSpecificCulture("en-US")) - [HttpGet("{componentName}")] [Produces(typeof(CompetenceProfile))] public async Task> GetCompetenceProfile(string componentName, DateTime? startDate, DateTime? endDate) From 103e9028389194c8544bd011386a31f9764b0d04 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Wed, 24 May 2023 00:38:03 +0200 Subject: [PATCH 67/72] changes --- Epsilon.Host.WebApi/Controllers/ComponentController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Epsilon.Host.WebApi/Controllers/ComponentController.cs b/Epsilon.Host.WebApi/Controllers/ComponentController.cs index 30fc722d..bb880723 100644 --- a/Epsilon.Host.WebApi/Controllers/ComponentController.cs +++ b/Epsilon.Host.WebApi/Controllers/ComponentController.cs @@ -1,4 +1,3 @@ -using System.Globalization; using Epsilon.Abstractions.Component; using Epsilon.Abstractions.Service; using Microsoft.AspNetCore.Mvc; From 737e9c62197c0306603bf0d2948931df4498b9c9 Mon Sep 17 00:00:00 2001 From: Neal Geilen Date: Thu, 25 May 2023 23:32:47 +0200 Subject: [PATCH 68/72] Lint fixes --- .../src/components/Competance/KPIMatrix.vue | 85 ++++++++++--------- .../src/components/Competance/KpiTable.vue | 84 ------------------ .../src/components/KpiLegend/KpiLegend.vue | 67 --------------- .../src/views/PerformanceDashboard.vue | 24 +----- 4 files changed, 45 insertions(+), 215 deletions(-) delete mode 100644 Epsilon.Host.Frontend/src/components/Competance/KpiTable.vue delete mode 100644 Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue diff --git a/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue b/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue index 9b20586c..83096717 100644 --- a/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue +++ b/Epsilon.Host.Frontend/src/components/Competance/KPIMatrix.vue @@ -1,19 +1,13 @@ diff --git a/Epsilon.Host.Frontend/src/components/Competance/KpiTable.vue b/Epsilon.Host.Frontend/src/components/Competance/KpiTable.vue deleted file mode 100644 index 889f0d1a..00000000 --- a/Epsilon.Host.Frontend/src/components/Competance/KpiTable.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - diff --git a/Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue b/Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue deleted file mode 100644 index c47cb156..00000000 --- a/Epsilon.Host.Frontend/src/components/KpiLegend/KpiLegend.vue +++ /dev/null @@ -1,67 +0,0 @@ - - - - - diff --git a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue index 75a02032..61dda17a 100644 --- a/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue +++ b/Epsilon.Host.Frontend/src/views/PerformanceDashboard.vue @@ -1,29 +1,9 @@