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 export interface ScheduleRow {
21 [key: string]: ReservationPatron[];
24 // Various methods that fetch data for and process the schedule of reservations
26 @Injectable({providedIn: 'root'})
27 export class ScheduleGridService {
30 private auth: AuthService,
31 private pcrud: PcrudService,
34 hoursOfOperation = (date: Date): Observable<{startOfDay: NgbTimeStruct, endOfDay: NgbTimeStruct}> => {
35 const defaultStartHour = 9;
36 const defaultEndHour = 17;
37 return this.pcrud.retrieve('aouhoo', this.auth.user().ws_ou())
38 .pipe(switchMap((hours) => {
39 const startArray = hours[this.evergreenStyleDow(date) + '_open']().split(':');
40 const endArray = hours[this.evergreenStyleDow(date) + '_close']().split(':');
43 hour: ('00' === startArray[0]) ? defaultStartHour : +startArray[0],
44 minute: +startArray[1],
47 hour: ('00' === endArray[0]) ? defaultEndHour : +endArray[0],
54 resourceAvailabilityIcon = (row: ScheduleRow, numResources: number): GridRowFlairEntry => {
55 let icon = {icon: 'event_busy', title: 'All resources are reserved at this time'};
57 for (const key in row) {
58 if (row[key] instanceof Array && row[key].length) {
62 if (busyColumns < numResources) {
63 icon = {icon: 'event_available', title: 'Resources are available at this time'};
68 fetchRelevantResources = (resourceTypeId: number, owningLibraries: number[], selectedAttributes: number[]): Observable<IdlObject> => {
71 owner: owningLibraries,
74 if (selectedAttributes.length) {
76 {'from': 'bram', 'select': {'bram': ['resource']},
77 'where': {'value': selectedAttributes}}};
79 return this.pcrud.search('brsrc', where, {
80 order_by: 'barcode ASC',
82 flesh_fields: {'brsrc': ['attr_maps']},
86 momentizeDateRange = (range: DateRange, timezone: string): {startTime: Moment, endTime: Moment} => {
88 startTime: Moment.tz([
90 range.fromDate.month - 1,
95 range.toDate.month - 1,
96 range.toDate.day + 1],
100 momentizeDay = (date: Date, start: NgbTimeStruct, end: NgbTimeStruct, timezone: string): {startTime: Moment, endTime: Moment} => {
102 startTime: Moment.tz([
119 createBasicSchedule = (range: {startTime: Moment, endTime: Moment}, granularity: number): ScheduleRow[] => {
120 const currentTime = range.startTime.clone();
122 while (currentTime < range.endTime) {
123 schedule.push({'time': currentTime.clone()});
124 currentTime.add(granularity, 'minutes');
129 fetchReservations = (range: {startTime: Moment, endTime: Moment}, resourceIds: number[]): Observable<IdlObject> => {
130 return this.pcrud.search('bresv', {
131 '-or': {'target_resource': resourceIds, 'current_resource': resourceIds},
132 'end_time': {'>': range.startTime.toISOString()},
133 'start_time': {'<': range.endTime.toISOString()},
135 'cancel_time': null },
136 {'flesh': 1, 'flesh_fields': {'bresv': ['current_resource', 'usr']}});
139 addReservationToSchedule = (reservation: IdlObject, schedule: ScheduleRow[], granularity: number, timezone: string): ScheduleRow[] => {
140 for (let index = 0; index < schedule.length; index++) {
141 const start = schedule[index].time;
142 const end = (index + 1 < schedule.length) ?
143 schedule[index + 1].time :
144 schedule[index].time.clone().add(granularity, 'minutes');
145 if ((Moment.tz(reservation.start_time(), timezone).isBefore(end)) &&
146 (Moment.tz(reservation.end_time(), timezone).isAfter(start))) {
147 if (!schedule[index][reservation.current_resource().barcode()]) {
148 schedule[index][reservation.current_resource().barcode()] = [];
150 if (schedule[index][reservation.current_resource().barcode()]
151 .findIndex(patron => patron.patronId === reservation.usr().id()) === -1) {
152 schedule[index][reservation.current_resource().barcode()].push(
153 {'patronLabel': reservation.usr().usrname(),
154 'patronId': reservation.usr().id(),
155 'reservationId': reservation.id()});
164 // Evergreen uses its own day of week style, where dow_0 = Monday and dow_6 = Sunday
165 private evergreenStyleDow = (original: Date): string => {
166 const daysInAWeek = 7;
168 return 'dow_' + (original.getDay() + offset) % daysInAWeek;