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';
14 export const VANDELAY_EXPORT_PATH = '/exporter';
15 export const VANDELAY_UPLOAD_PATH = '/vandelay-upload';
17 export class VandelayImportSelection {
20 importQueue: boolean; // import the whole queue
21 overlayMap: {[qrId: number]: /* breId */ number};
30 export class VandelayService {
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[];
42 // Used for tracking records between the queue page and
43 // the import page. Fields managed externally.
44 importSelection: VandelayImportSelection;
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;
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
64 this.importSelection = null;
65 this.queuePageOffset = 0;
68 getAttrDefs(dtype: string): Promise<IdlObject[]> {
69 if (this.attrDefs[dtype]) {
70 return Promise.resolve(this.attrDefs[dtype]);
72 const cls = (dtype === 'bib') ? 'vqbrad' : 'vqarad';
75 return this.pcrud.retrieveAll(cls,
76 {order_by: orderBy}, {atomic: true}).toPromise()
78 this.attrDefs[dtype] = list;
83 getMergeProfiles(): Promise<IdlObject[]> {
84 if (this.mergeProfiles) {
85 return Promise.resolve(this.mergeProfiles);
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;
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]);
102 this.allQueues[qtype] = [];
105 // could be a big list, invoke in streaming mode
106 return this.net.request(
108 `open-ils.vandelay.${qtype}_queue.owner.retrieve`,
111 queue => this.allQueues[qtype].push(queue)
112 )).toPromise().then(() => this.allQueues[qtype]);
115 getBibSources(): Promise<IdlObject[]> {
116 if (this.bibSources) {
117 return Promise.resolve(this.bibSources);
120 return this.pcrud.retrieveAll('cbs',
121 {order_by: {cbs: 'id'}},
123 ).toPromise().then(sources => {
124 this.bibSources = sources;
129 getItemImportDefs(): Promise<IdlObject[]> {
130 if (this.importItemAttrDefs) {
131 return Promise.resolve(this.importItemAttrDefs);
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;
142 // todo: differentiate between biblio and authority a la queue api
143 getMatchSets(mtype: string): Promise<IdlObject[]> {
145 const mstype = mtype.match(/bib/) ? 'biblio' : 'authority';
147 if (this.matchSets[mtype]) {
148 return Promise.resolve(this.matchSets[mtype]);
150 this.matchSets[mtype] = [];
153 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
155 return this.pcrud.search('vms',
156 {owner: owners, mtype: mstype}, {}, {atomic: true})
157 .toPromise().then(sets => {
158 this.matchSets[mtype] = sets;
163 getBibBuckets(): Promise<IdlObject[]> {
164 if (this.bibBuckets) {
165 return Promise.resolve(this.bibBuckets);
168 return this.net.request(
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;
178 getCopyStatuses(): Promise<any> {
179 if (this.copyStatuses) {
180 return Promise.resolve(this.copyStatuses);
182 return this.pcrud.retrieveAll('ccs', {}, {atomic: true})
183 .toPromise().then(stats => {
184 this.copyStatuses = stats;
189 getBibTrashGroups(): Promise<any> {
190 if (this.bibTrashGroups) {
191 return Promise.resolve(this.bibTrashGroups);
194 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
196 return this.pcrud.search('vibtg',
197 {always_apply : 'f', owner: owners},
200 ).toPromise().then(groups => {
201 this.bibTrashGroups = groups;
207 // Create a queue and return the ID of the new queue via promise.
213 matchBucket: number): Promise<number> {
215 const method = `open-ils.vandelay.${recordType}_queue.create`;
217 let qType = recordType;
218 if (recordType.match(/bib_acq/)) {
222 return new Promise((resolve, reject) => {
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);
232 // createQueue is always called after queues have
233 // been fetched and cached.
234 this.allQueues[qType].push(queue);
241 getQueuedRecords(queueId: number, queueType: string,
242 options?: any, limitToMatches?: boolean): Observable<any> {
244 const qtype = queueType.match(/bib/) ? 'bib' : 'auth';
247 `open-ils.vandelay.${qtype}_queue.records.retrieve`;
249 if (limitToMatches) {
251 `open-ils.vandelay.${qtype}_queue.records.matches.retrieve`;
254 return this.net.request('open-ils.vandelay',
255 method, this.auth.token(), queueId, options);
258 // Download a queue as a MARC file.
259 exportQueue(queue: IdlObject, nonImported?: boolean) {
261 const etype = queue.queue_type().match(/auth/) ? 'auth' : 'bib';
264 `${VANDELAY_EXPORT_PATH}?type=${etype}&queueid=${queue.id()}`;
266 let saveName = queue.name();
269 url += '&nonimported=1';
270 saveName += '_nonimported';
275 this.http.get(url, {responseType: 'text'}).subscribe(
278 new Blob([data], {type: 'application/octet-stream'}),
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);
297 getNextSessionTracker(id: number, observer: any) {
299 // No need for this to be an authoritative call.
300 // It will complete eventually regardless.
301 this.pcrud.retrieve('vst', id).subscribe(
303 if (tracker && tracker.state() === 'active') {
304 observer.next(tracker);
306 this.getNextSessionTracker(id, observer), 2000);
309 `Vandelay session tracker ${id} is ${tracker.state()}`);