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';
11 import * as moment from 'moment-timezone';
13 export interface ReservationPatron {
16 reservationId: number;
19 interface ScheduleRowPatrons {
20 [key: string]: ReservationPatron[];
23 export interface ScheduleRow {
25 patrons: ScheduleRowPatrons;
28 // Various methods that fetch data for and process the schedule of reservations
30 @Injectable({providedIn: 'root'})
31 export class ScheduleGridService {
34 private auth: AuthService,
35 private pcrud: PcrudService,
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(':');
47 hour: ('00' === startArray[0]) ? defaultStartHour : +startArray[0],
48 minute: +startArray[1],
51 hour: ('00' === endArray[0]) ? defaultEndHour : +endArray[0],
58 resourceAvailabilityIcon = (row: ScheduleRow, numResources: number): GridRowFlairEntry => {
59 let icon = {icon: 'event_busy', title: 'All resources are reserved at this time'};
61 for (const key in row.patrons) {
62 if (row.patrons[key] instanceof Array && row.patrons[key].length) {
66 if (busyColumns < numResources) {
67 icon = {icon: 'event_available', title: 'Resources are available at this time'};
72 fetchRelevantResources = (resourceTypeId: number, owningLibraries: number[], selectedAttributes: number[]): Observable<IdlObject> => {
75 owner: owningLibraries,
78 if (selectedAttributes.length) {
80 {'from': 'bram', 'select': {'bram': ['resource']},
81 'where': {'value': selectedAttributes}}};
83 return this.pcrud.search('brsrc', where, {
84 order_by: 'barcode ASC',
86 flesh_fields: {'brsrc': ['attr_maps']},
90 momentizeDateRange = (range: DateRange, timezone: string): {startTime: moment.Moment, endTime: moment.Moment} => {
92 startTime: moment.tz([
94 range.fromDate.month - 1,
99 range.toDate.month - 1,
100 range.toDate.day + 1],
104 momentizeDay = (date: Date, start: NgbTimeStruct, end: NgbTimeStruct, timezone: string): {startTime: moment.Moment, endTime: moment.Moment} => {
106 startTime: moment.tz([
123 createBasicSchedule = (range: {startTime: moment.Moment, endTime: moment.Moment}, granularity: number): ScheduleRow[] => {
124 const currentTime = range.startTime.clone();
126 while (currentTime < range.endTime) {
127 schedule.push({'time': currentTime.clone()});
128 currentTime.add(granularity, 'minutes');
133 fetchReservations = (range: {startTime: moment.Moment, endTime: moment.Moment}, resourceIds: number[]): Observable<IdlObject> => {
134 return this.pcrud.search('bresv', {
135 '-or': {'target_resource': resourceIds, 'current_resource': resourceIds},
136 'end_time': {'>': range.startTime.toISOString()},
137 'start_time': {'<': range.endTime.toISOString()},
139 'cancel_time': null },
140 {'flesh': 1, 'flesh_fields': {'bresv': ['current_resource', 'usr']}});
143 addReservationToSchedule = (reservation: IdlObject, schedule: ScheduleRow[], granularity: number, timezone: string): ScheduleRow[] => {
144 for (let index = 0; index < schedule.length; index++) {
145 const start = schedule[index].time;
146 const end = (index + 1 < schedule.length) ?
147 schedule[index + 1].time :
148 schedule[index].time.clone().add(granularity, 'minutes');
149 if ((moment.tz(reservation.start_time(), timezone).isBefore(end)) &&
150 (moment.tz(reservation.end_time(), timezone).isAfter(start))) {
151 if (!schedule[index]['patrons']) schedule[index].patrons = {};
152 if (!schedule[index].patrons[reservation.current_resource().barcode()]) {
153 schedule[index].patrons[reservation.current_resource().barcode()] = [];
155 if (schedule[index].patrons[reservation.current_resource().barcode()]
156 .findIndex(patron => patron.patronId === reservation.usr().id()) === -1) {
157 schedule[index].patrons[reservation.current_resource().barcode()].push(
158 {'patronLabel': reservation.usr().usrname(),
159 'patronId': reservation.usr().id(),
160 'reservationId': reservation.id()});
169 // Evergreen uses its own day of week style, where dow_0 = Monday and dow_6 = Sunday
170 private evergreenStyleDow = (original: Date): string => {
171 const daysInAWeek = 7;
173 return 'dow_' + (original.getDay() + offset) % daysInAWeek;