From 6a862730e2b71220eb42c6ae7762a04c00011fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 16 Feb 2022 21:30:05 +0100 Subject: [PATCH] MOBILE-3931 module: Add grades and course to module summary --- scripts/langindex.json | 1 + .../module-summary/module-summary.html | 112 +++++++++++++++++- .../module-summary/module-summary.ts | 69 ++++++++++- .../services/handlers/default-format.ts | 10 +- src/core/features/grades/lang.json | 1 + .../features/grades/services/grades-helper.ts | 32 +++++ .../components/module-index/module-index.ts | 7 +- .../siteplugins/services/siteplugins.ts | 1 + 8 files changed, 221 insertions(+), 12 deletions(-) diff --git a/scripts/langindex.json b/scripts/langindex.json index 07000f48f..59d395a92 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1741,6 +1741,7 @@ "core.grades.fail": "grades", "core.grades.feedback": "grades", "core.grades.grade": "grades", + "core.grades.gradebook": "grades", "core.grades.gradeitem": "grades", "core.grades.gradepass": "grades", "core.grades.grades": "grades", diff --git a/src/core/features/course/components/module-summary/module-summary.html b/src/core/features/course/components/module-summary/module-summary.html index 7830b0baf..e77abac35 100644 --- a/src/core/features/course/components/module-summary/module-summary.html +++ b/src/core/features/course/components/module-summary/module-summary.html @@ -24,6 +24,17 @@ + + + +

{{ 'core.course' | translate}}

+

+ + +

+
+
+ - +

{{ prefetchText }}

{{ downloadTimeReadable }}

@@ -45,7 +56,7 @@
- +

{{ 'addon.storagemanager.totalspaceusage' | translate }}

{{ sizeReadable | coreBytesToSize }} @@ -57,7 +68,102 @@
- + + + + +

{{ 'core.grades.gradebook' | translate }}

+
+
+ + + + +

+ + +

+

+ {{ 'core.grades.grade' | translate}} +

+

+
+ + + + + +
+
+ + +

{{ 'core.grades.weight' | translate}}

+

+
+
+ + +

{{ 'core.grades.range' | translate}}

+

+
+
+ + + +

{{ 'core.grades.percentage' | translate}}

+

+
+
+ + + +

{{ 'core.grades.lettergrade' | translate}}

+

+
+
+ + + +

{{ 'core.grades.rank' | translate}}

+

+
+
+ + + +

{{ 'core.grades.average' | translate}}

+

+
+
+ + + +

{{ 'core.grades.feedback' | translate}}

+

+ + +

+
+
+ + + +

{{ 'core.grades.contributiontocoursetotal' | translate}}

+

+
+
+
+
+
+
+ + {{ 'addon.blog.blog' | translate }} diff --git a/src/core/features/course/components/module-summary/module-summary.ts b/src/core/features/course/components/module-summary/module-summary.ts index 3475ca44f..f66609555 100644 --- a/src/core/features/course/components/module-summary/module-summary.ts +++ b/src/core/features/course/components/module-summary/module-summary.ts @@ -17,8 +17,12 @@ import { AddonBlog } from '@addons/blog/services/blog'; import { AddonBlogMainMenuHandlerService } from '@addons/blog/services/handlers/mainmenu'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Params } from '@angular/router'; +import { CoreCourse } from '@features/course/services/course'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper'; +import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate'; +import { CoreCourses, CoreEnrolledCourseData } from '@features/courses/services/courses'; +import { CoreGradesFormattedRow, CoreGradesFormattedTableRow, CoreGradesHelper } from '@features/grades/services/grades-helper'; import { CoreApp } from '@services/app'; import { CoreFilepool } from '@services/filepool'; import { CoreNavigator } from '@services/navigator'; @@ -61,13 +65,12 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { sizeReadable?: string; downloadTimeReadable?: string; // Last download time in a readable format. size = 0; - + grades?: CoreGradesFormattedRow[]; blog = false; // If blog is available. - isOnline = false; // If the app is online or not. + course?: CoreEnrolledCourseData; protected onlineSubscription: Subscription; // It will observe the status of the network connection. - protected packageStatusObserver?: CoreEventObserver; // Observer of package status. protected fileStatusObserver?: CoreEventObserver; // Observer of file status. protected siteId: string; @@ -103,8 +106,15 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { displayPrefetch: true, displaySize: true, displayBlog: true, + displayGrades: true, }, this.displayOptions); + this.displayOptions.displayGrades = this.displayOptions.displayGrades && + CoreCourseModuleDelegate.supportsFeature(this.module.modname, CoreConstants.FEATURE_GRADE_HAS_GRADE, true); + + this.displayOptions.displayDescription = this.displayOptions.displayDescription && + CoreCourseModuleDelegate.supportsFeature(this.module.modname, CoreConstants.FEATURE_SHOW_DESCRIPTION, true); + this.fetchContent(); if (this.component) { @@ -164,7 +174,11 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { this.blog = await AddonBlog.isPluginEnabled(); - await this.getPackageStatus(); + await Promise.all([ + this.getPackageStatus(), + this.fetchGrades(), + this.fetchCourse(), + ]); this.loaded = true; } @@ -174,7 +188,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { * * @param refresh If prefetch info has to be refreshed. */ - async getPackageStatus(refresh = false): Promise { + protected async getPackageStatus(refresh = false): Promise { if (!this.module) { return; } @@ -214,6 +228,50 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy { await CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params }); } + /** + * Fetch grade module info. + */ + protected async fetchGrades(): Promise { + if (!this.displayOptions.displayGrades) { + return; + } + + this.grades = await CoreGradesHelper.getModuleGrades(this.courseId, this.moduleId); + } + + /** + * Toggle grades expand. + * + * @param grade Row to expand. + */ + toggleGrade(grade: CoreGradesFormattedTableRow): void { + grade.expanded = !grade.expanded; + } + + /** + * Fetch course. + */ + protected async fetchCourse(): Promise { + this.course = await CoreCourses.getUserCourse(this.courseId, true); + } + + /** + * Open course. + */ + openCourse(): void { + if (!this.course) { + return; + } + + CoreCourse.openCourse( + this.course, + { + replace: true, + animationDirection: 'back', + }, + ); + } + /** * Prefetch the module. */ @@ -327,4 +385,5 @@ export type CoreCourseModuleSummaryDisplayOptions = { displayPrefetch?: boolean; displaySize?: boolean; displayBlog?: boolean; + displayGrades?: boolean; }; diff --git a/src/core/features/course/services/handlers/default-format.ts b/src/core/features/course/services/handlers/default-format.ts index 38c68d3df..5a4833f15 100644 --- a/src/core/features/course/services/handlers/default-format.ts +++ b/src/core/features/course/services/handlers/default-format.ts @@ -130,9 +130,13 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler { navOptions.params = navOptions.params || {}; Object.assign(navOptions.params, { course: course }); - // Don't return the .push promise, we don't want to display a loading modal during the page transition. - const currentTab = CoreNavigator.getCurrentMainMenuTab(); - const routeDepth = CoreNavigator.getRouteDepth(`/main/${currentTab}/course/${course.id}`); + // When replace is true, disable route depth. + let routeDepth = 0; + if (!navOptions.replace) { + // Don't return the .push promise, we don't want to display a loading modal during the page transition. + const currentTab = CoreNavigator.getCurrentMainMenuTab(); + routeDepth = CoreNavigator.getRouteDepth(`/main/${currentTab}/course/${course.id}`); + } const deepPath = '/deep'.repeat(routeDepth); CoreNavigator.navigateToSitePath(`course${deepPath}/${course.id}`, navOptions); diff --git a/src/core/features/grades/lang.json b/src/core/features/grades/lang.json index 504a4ae34..456ff43e8 100644 --- a/src/core/features/grades/lang.json +++ b/src/core/features/grades/lang.json @@ -9,6 +9,7 @@ "fail": "Fail", "feedback": "Feedback", "grade": "Grade", + "gradebook": "Gradebook", "gradeitem": "Grade item", "gradepass": "Grade to pass", "grades": "Grades", diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index 942f46895..1a827ce7d 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -457,6 +457,38 @@ export class CoreGradesHelperProvider { }).map((row) => this.formatGradeRow(row))); } + /** + * Get module grades to display. + * + * @param courseId Course Id. + * @param moduleId Module Id. + * @return Formatted table rows. + */ + async getModuleGrades(courseId: number, moduleId: number): Promise { + const table = await CoreGrades.getCourseGradesTable(courseId); + + if (!table.tabledata) { + return []; + } + + // Find href containing "/mod/xxx/xxx.php". + const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/; + + return await Promise.all(table.tabledata.filter((row) => { + if (row.itemname && row.itemname.content) { + const matches = row.itemname.content.match(regex); + + if (matches && matches.length) { + const hrefParams = CoreUrlUtils.extractUrlParams(matches[1]); + + return hrefParams && parseInt(hrefParams.id) === moduleId; + } + } + + return false; + }).map((row) => this.formatGradeRowForTable(row))); + } + /** * Go to view grades. * diff --git a/src/core/features/siteplugins/components/module-index/module-index.ts b/src/core/features/siteplugins/components/module-index/module-index.ts index a87dd2527..2bd8be6b8 100644 --- a/src/core/features/siteplugins/components/module-index/module-index.ts +++ b/src/core/features/siteplugins/components/module-index/module-index.ts @@ -90,6 +90,9 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C displayRefresh = true; displayPrefetch = true; displaySize = true; + displayGrades = false; + // @TODO: // Currently display blogs is not an option since it may change soon adding new summary handlers. + displayBlog = false; ptrEnabled = true; isDestroyed = false; @@ -128,6 +131,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C this.displayRefresh = !CoreUtils.isFalseOrZero(handlerSchema.displayrefresh); this.displayPrefetch = !CoreUtils.isFalseOrZero(handlerSchema.displayprefetch); this.displaySize = !CoreUtils.isFalseOrZero(handlerSchema.displaysize); + this.displayGrades = CoreUtils.isTrueOrOne(handlerSchema.displaygrades); // False by default. this.ptrEnabled = !CoreUtils.isFalseOrZero(handlerSchema.ptrenabled); } @@ -196,7 +200,8 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C displayRefresh: this.displayRefresh, displayPrefetch: this.displayPrefetch, displaySize: this.displaySize, - displayBlog: false, + displayBlog: this.displayBlog, + displayGrades: this.displayGrades, }, }, }); diff --git a/src/core/features/siteplugins/services/siteplugins.ts b/src/core/features/siteplugins/services/siteplugins.ts index eded0cd07..29ba79e0a 100644 --- a/src/core/features/siteplugins/services/siteplugins.ts +++ b/src/core/features/siteplugins/services/siteplugins.ts @@ -871,6 +871,7 @@ export type CoreSitePluginsCourseModuleHandlerData = CoreSitePluginsHandlerCommo displayrefresh?: boolean; displayprefetch?: boolean; displaysize?: boolean; + displaygrades?: boolean; coursepagemethod?: string; ptrenabled?: boolean; supportedfeatures?: Record;