From b7a17a3bb5e232578496d9fba3ac28410478fd3b Mon Sep 17 00:00:00 2001 From: Jane Sandberg Date: Fri, 23 Jul 2021 10:15:13 -0700 Subject: [PATCH] LP1906058: Course-term map interface only allow reasonable mappings Different libraries will use different sets of course terms. In an academic library context, for example, a consortium may include libraries with very different start and end dates to their terms/semesters, different numbers of terms/year, etc. This commit changes the interface to only allow users to associate a course with a term from the same library or one of its ancestors. To test: 1. Create many courses and course terms and many different OUs. 2. On the course list, click "Terms taught". Associate some courses and course terms. Make sure that you aren't able to associate your course with any course terms that would not be reasonable for the course's library. 3. Edit a course, and choose the Course terms tab. Continue to associate courses and terms, and make sure the mappings are reasonable. Signed-off-by: Jane Sandberg Signed-off-by: Beth Willis Signed-off-by: Galen Charlton --- .../course-reserves/course-page.component.html | 5 +- .../local/course-reserves/course-page.component.ts | 27 +----- .../course-reserves/course-reserves.module.ts | 4 +- .../course-term-map-grid.component.html | 24 ++++++ .../course-term-map-grid.component.ts | 98 ++++++++++++++++++++++ .../course-reserves/course-term-map.component.ts | 11 ++- 6 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.ts diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.html index 9d57bc0fbf..4936a97440 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.html +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.html @@ -66,10 +66,7 @@
  • Course terms - - +
  • diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.ts index 07d5cbf7fa..1bbc487d7e 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-page.component.ts @@ -1,14 +1,12 @@ import {Component, ViewChild, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {PcrudService} from '@eg/core/pcrud.service'; -import {IdlObject, IdlService} from '@eg/core/idl.service'; -import {GridDataSource} from '@eg/share/grid/grid'; +import {IdlObject} from '@eg/core/idl.service'; import {StringComponent} from '@eg/share/string/string.component'; import {ToastService} from '@eg/share/toast/toast.service'; import {CourseService} from '@eg/staff/share/course.service'; import {CourseAssociateUsersComponent} from './course-associate-users.component'; import {CourseAssociateMaterialComponent} from './course-associate-material.component'; -import {Pager} from '@eg/share/util/pager'; @Component({ selector: 'eg-course-page', @@ -32,13 +30,8 @@ export class CoursePageComponent implements OnInit { @ViewChild('archiveSuccessString', { static: true }) archiveSuccessString: StringComponent; - // Course Tab - termsDataSource: GridDataSource = new GridDataSource(); - defaultNewAcmtcm: IdlObject; - constructor( private course: CourseService, - private idl: IdlService, private pcrud: PcrudService, private route: ActivatedRoute, private toast: ToastService @@ -50,24 +43,6 @@ export class CoursePageComponent implements OnInit { this.course.getCourses([this.courseId]).then(course => { this.currentCourse = course[0]; }); - - this.defaultNewAcmtcm = this.idl.create('acmtcm'); - this.defaultNewAcmtcm.course(this.courseId); - - this.termsDataSource.getRows = (pager: Pager, sort: any[]) => { - const orderBy: any = {}; - if (sort.length) { - orderBy.acmtcm = sort[0].name + ' ' + sort[0].dir; - } - const searchOps = { - offset: pager.offset, - limit: pager.limit, - order_by: orderBy - }; - - return this.pcrud.search('acmtcm', {course: this.courseId}, - searchOps, {fleshSelectors: true}); - }; } // Edit Tab diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts index 32a44a5c1d..149a39aa53 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-reserves.module.ts @@ -11,6 +11,7 @@ import {ItemLocationSelectModule} from '@eg/share/item-location-select/item-loca import {MarcSimplifiedEditorModule} from '@eg/staff/share/marc-edit/simplified-editor/simplified-editor.module'; import {PatronModule} from '@eg/staff/share/patron/patron.module'; import {CourseTermMapComponent} from './course-term-map.component'; +import {CourseTermMapGridComponent} from './course-term-map-grid.component'; @NgModule({ declarations: [ @@ -18,7 +19,8 @@ import {CourseTermMapComponent} from './course-term-map.component'; CoursePageComponent, CourseAssociateMaterialComponent, CourseAssociateUsersComponent, - CourseTermMapComponent + CourseTermMapComponent, + CourseTermMapGridComponent ], imports: [ StaffCommonModule, diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.html b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.html new file mode 100644 index 0000000000..0b9adf5e60 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.ts new file mode 100644 index 0000000000..8d3545378b --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map-grid.component.ts @@ -0,0 +1,98 @@ +import {Component, Input, OnInit, ViewChild} from '@angular/core'; +import {Observable} from 'rxjs'; +import {map, switchMap} from 'rxjs/operators'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {Pager} from '@eg/share/util/pager'; +import {GridDataSource} from '@eg/share/grid/grid'; +import {GridComponent} from '@eg/share/grid/grid.component'; +import {ComboboxEntry} from '@eg/share/combobox/combobox.component'; +import {IdlObject, IdlService} from '@eg/core/idl.service'; +import {OrgService} from '@eg/core/org.service'; +import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component'; + +@Component({ + selector: 'eg-course-term-map-grid', + templateUrl: './course-term-map-grid.component.html', +}) +export class CourseTermMapGridComponent implements OnInit { + @Input() courseId: number; + @ViewChild('grid') private grid: GridComponent; + @ViewChild('editDialog') private editDialog: FmRecordEditorComponent; + + readonlyFields: string; + + defaultNewAcmtcm: IdlObject; + gridDataSource: GridDataSource; + createNew: () => void; + deleteSelected: (rows: IdlObject[]) => void; + termEntryGenerator: (course: number) => (query: string) => Observable; + termEntries: (query: string) => Observable; + + constructor( + private idl: IdlService, + private org: OrgService, + private pcrud: PcrudService, + ) { + this.gridDataSource = new GridDataSource(); + this.defaultNewAcmtcm = this.idl.create('acmtcm'); + } + + ngOnInit() { + + if (this.courseId) { + this.defaultNewAcmtcm.course(this.courseId); + this.readonlyFields = 'course'; + } + + this.gridDataSource.getRows = (pager: Pager, sort: any[]) => { + const orderBy: any = {}; + + const searchOps = { + offset: pager.offset, + limit: pager.limit, + order_by: orderBy + }; + + const criteria = this.courseId ? {course: this.courseId} : {}; + + return this.pcrud.search('acmtcm', + criteria, searchOps, {fleshSelectors: true}); + }; + + // Produce a bespoke callback for the combobox search, which + // limits the results to course terms that make sense for the + // selected course. This prevents users from associating a + // course at their library to a term from a completely different + // academic calendar. + this.termEntryGenerator = (courseId: number) => { + return (query: string) => { + return this.pcrud.retrieve('acmc', courseId).pipe(switchMap(fullCourseObject => { + return this.pcrud.search( + 'acmt', { + name: {'ilike': `%${query}`}, + owning_lib: this.org.ancestors(fullCourseObject.owning_lib(), true) + }, + {order_by: {'acmt': 'name'}} + ); + }), map(courseTerm => { + return {id: courseTerm.id(), label: courseTerm.name()}; + })); + }; + }; + + this.createNew = () => { + this.editDialog.mode = 'create'; + this.editDialog.open({size: 'lg'}) + .subscribe(() => this.grid.reload()); + }; + + this.deleteSelected = (termMaps: IdlObject[]) => { + termMaps.forEach(termMap => termMap.isdeleted(true)); + this.pcrud.autoApply(termMaps).subscribe( + val => console.debug('deleted: ' + val), + err => {}, + () => this.grid.reload() + ); + }; + } +} diff --git a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map.component.ts b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map.component.ts index bfca8a7270..ecbd3c1b2f 100644 --- a/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/admin/local/course-reserves/course-term-map.component.ts @@ -1,4 +1,5 @@ import {Component} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; /** * Very basic page for editing course/term map @@ -18,11 +19,17 @@ import {Component} from '@angular/core'; - + ` }) export class CourseTermMapComponent { + public courseId: number; + + constructor(private route: ActivatedRoute) { + const filters = this.route.snapshot.queryParamMap.get('gridFilters'); + this.courseId = JSON.parse(filters)['course'] || 1; + } + } -- 2.11.0