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';
15 export const VANDELAY_EXPORT_PATH = '/exporter';
16 export const VANDELAY_UPLOAD_PATH = '/vandelay-upload';
18 export class VandelayImportSelection {
21 importQueue: boolean; // import the whole queue
22 overlayMap: {[qrId: number]: /* breId */ number};
31 export class VandelayService {
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[];
43 // Used for tracking records between the queue page and
44 // the import page. Fields managed externally.
45 importSelection: VandelayImportSelection;
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;
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
65 this.importSelection = null;
66 this.queuePageOffset = 0;
69 getAttrDefs(dtype: string): Promise<IdlObject[]> {
70 if (this.attrDefs[dtype]) {
71 return Promise.resolve(this.attrDefs[dtype]);
73 const cls = (dtype === 'bib') ? 'vqbrad' : 'vqarad';
76 return this.pcrud.retrieveAll(cls,
77 {order_by: orderBy}, {atomic: true}).toPromise()
79 this.attrDefs[dtype] = list;
84 getMergeProfiles(): Promise<IdlObject[]> {
85 if (this.mergeProfiles) {
86 return Promise.resolve(this.mergeProfiles);
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;
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]);
103 this.allQueues[qtype] = [];
106 // could be a big list, invoke in streaming mode
107 return this.net.request(
109 `open-ils.vandelay.${qtype}_queue.owner.retrieve`,
112 queue => this.allQueues[qtype].push(queue)
113 )).toPromise().then(() => this.allQueues[qtype]);
116 getBibSources(): Promise<IdlObject[]> {
117 if (this.bibSources) {
118 return Promise.resolve(this.bibSources);
121 return this.pcrud.retrieveAll('cbs',
122 {order_by: {cbs: 'id'}},
124 ).toPromise().then(sources => {
125 this.bibSources = sources;
130 getItemImportDefs(): Promise<IdlObject[]> {
131 if (this.importItemAttrDefs) {
132 return Promise.resolve(this.importItemAttrDefs);
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;
143 // todo: differentiate between biblio and authority a la queue api
144 getMatchSets(mtype: string): Promise<IdlObject[]> {
146 const mstype = mtype.match(/bib/) ? 'biblio' : 'authority';
148 if (this.matchSets[mtype]) {
149 return Promise.resolve(this.matchSets[mtype]);
151 this.matchSets[mtype] = [];
154 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
156 return this.pcrud.search('vms',
157 {owner: owners, mtype: mstype}, {}, {atomic: true})
158 .toPromise().then(sets => {
159 this.matchSets[mtype] = sets;
164 getBibBuckets(): Promise<IdlObject[]> {
165 if (this.bibBuckets) {
166 return Promise.resolve(this.bibBuckets);
169 return this.net.request(
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;
179 getCopyStatuses(): Promise<any> {
180 if (this.copyStatuses) {
181 return Promise.resolve(this.copyStatuses);
183 return this.pcrud.retrieveAll('ccs', {}, {atomic: true})
184 .toPromise().then(stats => {
185 this.copyStatuses = stats;
190 getBibTrashGroups(): Promise<any> {
191 if (this.bibTrashGroups) {
192 return Promise.resolve(this.bibTrashGroups);
195 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
197 return this.pcrud.search('vibtg',
198 {always_apply : 'f', owner: owners},
201 ).toPromise().then(groups => {
202 this.bibTrashGroups = groups;
208 // Create a queue and return the ID of the new queue via promise.
214 matchBucket: number): Promise<number> {
216 const method = `open-ils.vandelay.${recordType}_queue.create`;
218 let qType = recordType;
219 if (recordType.match(/bib_acq/)) {
223 return new Promise((resolve, reject) => {
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);
233 // createQueue is always called after queues have
234 // been fetched and cached.
235 this.allQueues[qType].push(queue);
242 getQueuedRecords(queueId: number, queueType: string,
243 options?: any, limitToMatches?: boolean): Observable<any> {
245 const qtype = queueType.match(/bib/) ? 'bib' : 'auth';
248 `open-ils.vandelay.${qtype}_queue.records.retrieve`;
250 if (limitToMatches) {
252 `open-ils.vandelay.${qtype}_queue.records.matches.retrieve`;
255 return this.net.request('open-ils.vandelay',
256 method, this.auth.token(), queueId, options);
259 // Download a queue as a MARC file.
260 exportQueue(queue: IdlObject, nonImported?: boolean) {
262 const etype = queue.queue_type().match(/auth/) ? 'auth' : 'bib';
265 `${VANDELAY_EXPORT_PATH}?type=${etype}&queueid=${queue.id()}`;
267 let saveName = queue.name();
270 url += '&nonimported=1';
271 saveName += '_nonimported';
276 this.http.get(url, {responseType: 'text'}).subscribe(
279 new Blob([data], {type: 'application/octet-stream'}),
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);
298 getNextSessionTracker(id: number, observer: any) {
300 // No need for this to be an authoritative call.
301 // It will complete eventually regardless.
302 this.pcrud.retrieve('vst', id).subscribe(
304 if (tracker && tracker.state() === 'active') {
305 observer.next(tracker);
307 this.getNextSessionTracker(id, observer), 2000);
310 `Vandelay session tracker ${id} is ${tracker.state()}`);