]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/cat/vandelay/vandelay.service.ts
LP2045292 Color contrast for AngularJS patron bills
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / cat / vandelay / vandelay.service.ts
1 import {Injectable} from '@angular/core';
2 import {Observable} from 'rxjs';
3 import {tap} from 'rxjs/operators';
4 import {HttpClient} from '@angular/common/http';
5 import {saveAs} from 'file-saver';
6 import {IdlService, IdlObject} from '@eg/core/idl.service';
7 import {OrgService} from '@eg/core/org.service';
8 import {NetService} from '@eg/core/net.service';
9 import {AuthService} from '@eg/core/auth.service';
10 import {PcrudService} from '@eg/core/pcrud.service';
11 import {PermService} from '@eg/core/perm.service';
12 import {EventService} from '@eg/core/event.service';
13
14 export const VANDELAY_EXPORT_PATH = '/exporter';
15 export const VANDELAY_UPLOAD_PATH = '/vandelay-upload';
16
17 export class VandelayImportSelection {
18     recordIds: number[];
19     queue: IdlObject;
20     importQueue: boolean; // import the whole queue
21     overlayMap: {[qrId: number]: /* breId */ number};
22
23     constructor() {
24         this.recordIds = [];
25         this.overlayMap = {};
26     }
27 }
28
29 @Injectable()
30 export class VandelayService {
31
32     allQueues: {[qtype: string]: IdlObject[]};
33     attrDefs: {[atype: string]: IdlObject[]};
34     bibSources: IdlObject[];
35     bibBuckets: IdlObject[];
36     copyStatuses: IdlObject[];
37     matchSets: {[stype: string]: IdlObject[]};
38     importItemAttrDefs: IdlObject[];
39     bibTrashGroups: IdlObject[];
40     mergeProfiles: IdlObject[];
41
42     // Used for tracking records between the queue page and
43     // the import page.  Fields managed externally.
44     importSelection: VandelayImportSelection;
45
46     // Track the last grid offset in the queue page so we
47     // can return the user to the same page of data after
48     // going to the matches page.
49     queuePageOffset: number;
50
51     constructor(
52         private http: HttpClient,
53         private idl: IdlService,
54         private org: OrgService,
55         private evt: EventService,
56         private net: NetService,
57         private auth: AuthService,
58         private pcrud: PcrudService,
59         private perm: PermService
60     ) {
61         this.attrDefs = {};
62         this.allQueues = {};
63         this.matchSets = {};
64         this.importSelection = null;
65         this.queuePageOffset = 0;
66     }
67
68     getAttrDefs(dtype: string): Promise<IdlObject[]> {
69         if (this.attrDefs[dtype]) {
70             return Promise.resolve(this.attrDefs[dtype]);
71         }
72         const cls = (dtype === 'bib') ? 'vqbrad' : 'vqarad';
73         const orderBy = {};
74         orderBy[cls] = 'id';
75         return this.pcrud.retrieveAll(cls,
76             {order_by: orderBy}, {atomic: true}).toPromise()
77             .then(list => {
78                 this.attrDefs[dtype] = list;
79                 return list;
80             });
81     }
82
83     getMergeProfiles(): Promise<IdlObject[]> {
84         if (this.mergeProfiles) {
85             return Promise.resolve(this.mergeProfiles);
86         }
87
88         const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
89         return this.pcrud.search('vmp',
90             {owner: owners}, {order_by: {vmp: ['name']}}, {atomic: true})
91             .toPromise().then(profiles => {
92                 this.mergeProfiles = profiles;
93                 return profiles;
94             });
95     }
96
97     // Returns a promise resolved with the list of queues.
98     getAllQueues(qtype: string): Promise<IdlObject[]> {
99         if (this.allQueues[qtype]) {
100             return Promise.resolve(this.allQueues[qtype]);
101         } else {
102             this.allQueues[qtype] = [];
103         }
104
105         // could be a big list, invoke in streaming mode
106         return this.net.request(
107             'open-ils.vandelay',
108             `open-ils.vandelay.${qtype}_queue.owner.retrieve`,
109             this.auth.token()
110         ).pipe(tap(
111             queue => this.allQueues[qtype].push(queue)
112         )).toPromise().then(() => this.allQueues[qtype]);
113     }
114
115     getBibSources(): Promise<IdlObject[]> {
116         if (this.bibSources) {
117             return Promise.resolve(this.bibSources);
118         }
119
120         return this.pcrud.retrieveAll('cbs',
121             {order_by: {cbs: 'id'}},
122             {atomic: true}
123         ).toPromise().then(sources => {
124             this.bibSources = sources;
125             return sources;
126         });
127     }
128
129     getItemImportDefs(): Promise<IdlObject[]> {
130         if (this.importItemAttrDefs) {
131             return Promise.resolve(this.importItemAttrDefs);
132         }
133
134         const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
135         return this.pcrud.search('viiad', {owner: owners}, {}, {atomic: true})
136             .toPromise().then(defs => {
137                 this.importItemAttrDefs = defs;
138                 return defs;
139             });
140     }
141
142     // todo: differentiate between biblio and authority a la queue api
143     getMatchSets(mtype: string): Promise<IdlObject[]> {
144
145         const mstype = mtype.match(/bib/) ? 'biblio' : 'authority';
146
147         if (this.matchSets[mtype]) {
148             return Promise.resolve(this.matchSets[mtype]);
149         } else {
150             this.matchSets[mtype] = [];
151         }
152
153         const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
154
155         return this.pcrud.search('vms',
156             {owner: owners, mtype: mstype}, {}, {atomic: true})
157             .toPromise().then(sets => {
158                 this.matchSets[mtype] = sets;
159                 return sets;
160             });
161     }
162
163     getBibBuckets(): Promise<IdlObject[]> {
164         if (this.bibBuckets) {
165             return Promise.resolve(this.bibBuckets);
166         }
167
168         return this.net.request(
169             'open-ils.actor',
170             'open-ils.actor.container.retrieve_by_class',
171             this.auth.token(), this.auth.user().id(), 'biblio', 'staff_client'
172         ).toPromise().then(bkts => {
173             this.bibBuckets = bkts;
174             return bkts;
175         });
176     }
177
178     getCopyStatuses(): Promise<any> {
179         if (this.copyStatuses) {
180             return Promise.resolve(this.copyStatuses);
181         }
182         return this.pcrud.retrieveAll('ccs', {}, {atomic: true})
183             .toPromise().then(stats => {
184                 this.copyStatuses = stats;
185                 return stats;
186             });
187     }
188
189     getBibTrashGroups(): Promise<any> {
190         if (this.bibTrashGroups) {
191             return Promise.resolve(this.bibTrashGroups);
192         }
193
194         const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
195
196         return this.pcrud.search('vibtg',
197             {always_apply : 'f', owner: owners},
198             {vibtg : ['label']},
199             {atomic: true}
200         ).toPromise().then(groups => {
201             this.bibTrashGroups = groups;
202             return groups;
203         });
204     }
205
206
207     // Create a queue and return the ID of the new queue via promise.
208     createQueue(
209         queueName: string,
210         recordType: string,
211         importDefId: number,
212         matchSet: number,
213         matchBucket: number): Promise<number> {
214
215         const method = `open-ils.vandelay.${recordType}_queue.create`;
216
217         let qType = recordType;
218         if (recordType.match(/bib_acq/)) {
219             qType = 'acq';
220         }
221
222         return new Promise((resolve, reject) => {
223             this.net.request(
224                 'open-ils.vandelay', method,
225                 this.auth.token(), queueName, null, qType,
226                 matchSet, importDefId, matchBucket
227             ).subscribe(queue => {
228                 const e = this.evt.parse(queue);
229                 if (e) {
230                     reject(e);
231                 } else {
232                     // createQueue is always called after queues have
233                     // been fetched and cached.
234                     this.allQueues[qType].push(queue);
235                     resolve(queue.id());
236                 }
237             });
238         });
239     }
240
241     getQueuedRecords(queueId: number, queueType: string,
242         options?: any, limitToMatches?: boolean): Observable<any> {
243
244         const qtype = queueType.match(/bib/) ? 'bib' : 'auth';
245
246         let method =
247           `open-ils.vandelay.${qtype}_queue.records.retrieve`;
248
249         if (limitToMatches) {
250             method =
251               `open-ils.vandelay.${qtype}_queue.records.matches.retrieve`;
252         }
253
254         return this.net.request('open-ils.vandelay',
255             method, this.auth.token(), queueId, options);
256     }
257
258     // Download a queue as a MARC file.
259     exportQueue(queue: IdlObject, nonImported?: boolean) {
260
261         const etype = queue.queue_type().match(/auth/) ? 'auth' : 'bib';
262
263         let url =
264           `${VANDELAY_EXPORT_PATH}?type=${etype}&queueid=${queue.id()}`;
265
266         let saveName = queue.name();
267
268         if (nonImported) {
269             url += '&nonimported=1';
270             saveName += '_nonimported';
271         }
272
273         saveName += '.mrc';
274
275         this.http.get(url, {responseType: 'text'}).subscribe(
276             data => {
277                 saveAs(
278                     new Blob([data], {type: 'application/octet-stream'}),
279                     saveName
280                 );
281             },
282             (err: unknown)  => {
283                 console.error(err);
284             }
285         );
286     }
287
288     // Poll every 2 seconds for session tracker updates so long
289     // as the session tracker is active.
290     // Returns an Observable of tracker objects.
291     pollSessionTracker(id: number): Observable<IdlObject> {
292         return new Observable(observer => {
293             this.getNextSessionTracker(id, observer);
294         });
295     }
296
297     getNextSessionTracker(id: number, observer: any) {
298
299         // No need for this to be an authoritative call.
300         // It will complete eventually regardless.
301         this.pcrud.retrieve('vst', id).subscribe(
302             tracker => {
303                 if (tracker && tracker.state() === 'active') {
304                     observer.next(tracker);
305                     setTimeout(() =>
306                         // eslint-disable-next-line no-magic-numbers
307                         this.getNextSessionTracker(id, observer), 2000);
308                 } else {
309                     console.debug(
310                         `Vandelay session tracker ${id} is ${tracker.state()}`);
311                     observer.complete();
312                 }
313             }
314         );
315     }
316 }
317