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