]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/booking/schedule-grid.service.ts
LP1884787 ng lint and unit test repairs
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / booking / schedule-grid.service.ts
1 import {Injectable} from '@angular/core';
2 import {Observable, of} from 'rxjs';
3 import {switchMap} from 'rxjs/operators';
4 import {NgbTimeStruct} from '@ng-bootstrap/ng-bootstrap';
5 import {AuthService} from '@eg/core/auth.service';
6 import {IdlObject} from '@eg/core/idl.service';
7 import {PcrudService} from '@eg/core/pcrud.service';
8 import {GridRowFlairEntry} from '@eg/share/grid/grid';
9 import {DateRange} from '@eg/share/daterange-select/daterange-select.component';
10
11 import * as moment from 'moment-timezone';
12
13 export interface ReservationPatron {
14   patronId: number;
15   patronLabel: string;
16   reservationId: number;
17 }
18
19 interface ScheduleRowPatrons {
20     [key: string]: ReservationPatron[];
21 }
22
23 export interface ScheduleRow {
24     time: moment.Moment;
25     patrons: ScheduleRowPatrons;
26 }
27
28 // Various methods that fetch data for and process the schedule of reservations
29
30 @Injectable({providedIn: 'root'})
31 export class ScheduleGridService {
32
33     constructor(
34         private auth: AuthService,
35         private pcrud: PcrudService,
36     ) {
37     }
38     hoursOfOperation = (date: Date): Observable<{startOfDay: NgbTimeStruct, endOfDay: NgbTimeStruct}> => {
39         const defaultStartHour = 9;
40         const defaultEndHour = 17;
41         return this.pcrud.retrieve('aouhoo', this.auth.user().ws_ou())
42             .pipe(switchMap((hours) => {
43                 const startArray = hours[this.evergreenStyleDow(date) + '_open']().split(':');
44                 const endArray = hours[this.evergreenStyleDow(date) + '_close']().split(':');
45                 return of({
46                     startOfDay: {
47                         hour: ('00' === startArray[0]) ? defaultStartHour : +startArray[0],
48                         minute: +startArray[1],
49                         second: 0},
50                     endOfDay: {
51                         hour: ('00' === endArray[0]) ? defaultEndHour : +endArray[0],
52                         minute: +endArray[1],
53                         second: 0}
54                 });
55             }));
56     }
57
58     resourceAvailabilityIcon = (row: ScheduleRow, numResources: number): GridRowFlairEntry => {
59         let icon = {icon: 'event_busy', title: 'All resources are reserved at this time'};
60         let busyColumns = 0;
61         for (const key in row.patrons) {
62             if (row.patrons[key] instanceof Array && row.patrons[key].length) {
63                 busyColumns += 1;
64             }
65         }
66         if (busyColumns < numResources) {
67             icon = {icon: 'event_available', title: 'Resources are available at this time'};
68         }
69         return icon;
70     }
71
72     fetchRelevantResources = (resourceTypeId: number, owningLibraries: number[], selectedAttributes: number[]): Observable<IdlObject> => {
73         const where = {
74             type: resourceTypeId,
75             owner: owningLibraries,
76         };
77
78         if (selectedAttributes.length) {
79             where['id'] = {'in':
80                 {'from': 'bram', 'select': {'bram': ['resource']},
81                 'where': {'value':  selectedAttributes}}};
82         }
83         return this.pcrud.search('brsrc', where, {
84             order_by: 'barcode ASC',
85             flesh: 1,
86             flesh_fields: {'brsrc': ['attr_maps']},
87         });
88     }
89
90     momentizeDateRange = (range: DateRange, timezone: string): {startTime: moment.Moment, endTime: moment.Moment} => {
91         return {
92             startTime: moment.tz([
93                 range.fromDate.year,
94                 range.fromDate.month - 1,
95                 range.fromDate.day],
96                 timezone),
97             endTime: moment.tz([
98                 range.toDate.year,
99                 range.toDate.month - 1,
100                 range.toDate.day + 1],
101                 timezone)
102         };
103     }
104     momentizeDay = (date: Date, start: NgbTimeStruct, end: NgbTimeStruct, timezone: string):
105         {startTime: moment.Moment, endTime: moment.Moment} => {
106         return {
107             startTime: moment.tz([
108                 date.getFullYear(),
109                 date.getMonth(),
110                 date.getDate(),
111                 start.hour,
112                 start.minute],
113                 timezone),
114             endTime: moment.tz([
115                 date.getFullYear(),
116                 date.getMonth(),
117                 date.getDate(),
118                 end.hour,
119                 end.minute],
120                 timezone)
121         };
122     }
123
124     createBasicSchedule = (range: {startTime: moment.Moment, endTime: moment.Moment}, granularity: number): ScheduleRow[] => {
125         const currentTime = range.startTime.clone();
126         const schedule = [];
127         while (currentTime < range.endTime) {
128             schedule.push({'time': currentTime.clone()});
129             currentTime.add(granularity, 'minutes');
130         }
131         return schedule;
132     }
133
134     fetchReservations = (range: {startTime: moment.Moment, endTime: moment.Moment}, resourceIds: number[]): Observable<IdlObject> => {
135         return this.pcrud.search('bresv', {
136             '-or': {'target_resource': resourceIds, 'current_resource': resourceIds},
137             'end_time': {'>': range.startTime.toISOString()},
138             'start_time': {'<': range.endTime.toISOString()},
139             'return_time': null,
140             'cancel_time': null },
141             {'flesh': 1, 'flesh_fields': {'bresv': ['current_resource', 'usr']}});
142     }
143
144     addReservationToSchedule = (reservation: IdlObject, schedule: ScheduleRow[], granularity: number, timezone: string): ScheduleRow[] => {
145         for (let index = 0; index < schedule.length; index++) {
146             const start = schedule[index].time;
147             const end = (index + 1 < schedule.length) ?
148                 schedule[index + 1].time :
149                 schedule[index].time.clone().add(granularity, 'minutes');
150             if ((moment.tz(reservation.start_time(), timezone).isBefore(end)) &&
151                 (moment.tz(reservation.end_time(), timezone).isAfter(start))) {
152                 if (!schedule[index]['patrons']) { schedule[index].patrons = {}; }
153                 if (!schedule[index].patrons[reservation.current_resource().barcode()]) {
154                     schedule[index].patrons[reservation.current_resource().barcode()] = [];
155                 }
156                 if (schedule[index].patrons[reservation.current_resource().barcode()]
157                     .findIndex(patron => patron.patronId === reservation.usr().id()) === -1) {
158                     schedule[index].patrons[reservation.current_resource().barcode()].push(
159                         {'patronLabel': reservation.usr().usrname(),
160                         'patronId': reservation.usr().id(),
161                         'reservationId': reservation.id()});
162                 }
163             }
164
165         }
166         return schedule;
167
168     }
169
170     // Evergreen uses its own day of week style, where dow_0 = Monday and dow_6 = Sunday
171     private evergreenStyleDow = (original: Date): string => {
172         const daysInAWeek = 7;
173         const offset = 6;
174         return 'dow_' + (original.getDay() + offset) % daysInAWeek;
175     }
176
177
178 }
179