1 import {Injectable, EventEmitter} from '@angular/core';
2 import {Observable} from 'rxjs/Observable';
3 import {tap} from 'rxjs/operators/tap';
4 import {map} from 'rxjs/operators/map';
5 import {HttpClient} from '@angular/common/http';
6 import {saveAs} from 'file-saver/FileSaver';
7 import {IdlService, IdlObject} from '@eg/core/idl.service';
8 import {OrgService} from '@eg/core/org.service';
9 import {NetService} from '@eg/core/net.service';
10 import {AuthService} from '@eg/core/auth.service';
11 import {PcrudService} from '@eg/core/pcrud.service';
12 import {PermService} from '@eg/core/perm.service';
13 import {EventService} from '@eg/core/event.service';
14 import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
16 export const VANDELAY_EXPORT_PATH = '/exporter';
17 export const VANDELAY_UPLOAD_PATH = '/vandelay-upload';
19 export class VandelayImportSelection {
22 importQueue: boolean; // import the whole queue
23 overlayMap: {[qrId: number]: /* breId */ number};
32 export class VandelayService {
34 allQueues: {[qtype: string]: IdlObject[]};
35 activeQueues: {[qtype: string]: IdlObject[]};
36 attrDefs: {[atype: string]: IdlObject[]};
37 bibSources: IdlObject[];
38 bibBuckets: IdlObject[];
39 copyStatuses: IdlObject[];
40 matchSets: {[stype: string]: IdlObject[]};
41 importItemAttrDefs: IdlObject[];
42 bibTrashGroups: IdlObject[];
43 mergeProfiles: IdlObject[];
45 // Used for tracking records between the queue page and
46 // the import page. Fields managed externally.
47 importSelection: VandelayImportSelection;
49 // Track the last grid offset in the queue page so we
50 // can return the user to the same page of data after
51 // going to the matches page.
52 queuePageOffset: number;
55 private http: HttpClient,
56 private idl: IdlService,
57 private org: OrgService,
58 private evt: EventService,
59 private net: NetService,
60 private auth: AuthService,
61 private pcrud: PcrudService,
62 private perm: PermService
65 this.activeQueues = {};
68 this.importSelection = null;
69 this.queuePageOffset = 0;
72 getAttrDefs(dtype: string): Promise<IdlObject[]> {
73 if (this.attrDefs[dtype]) {
74 return Promise.resolve(this.attrDefs[dtype]);
76 const cls = (dtype === 'bib') ? 'vqbrad' : 'vqarad';
79 return this.pcrud.retrieveAll(cls,
80 {order_by: orderBy}, {atomic: true}).toPromise()
82 this.attrDefs[dtype] = list;
87 getMergeProfiles(): Promise<IdlObject[]> {
88 if (this.mergeProfiles) {
89 return Promise.resolve(this.mergeProfiles);
92 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
93 return this.pcrud.search('vmp',
94 {owner: owners}, {order_by: {vmp: ['name']}}, {atomic: true})
95 .toPromise().then(profiles => {
96 this.mergeProfiles = profiles;
101 // Returns a promise resolved with the list of queues.
102 // Also emits the onQueueListUpdate event so listeners
103 // can detect queue content changes.
104 getAllQueues(qtype: string): Promise<IdlObject[]> {
105 if (this.allQueues[qtype]) {
106 return Promise.resolve(this.allQueues[qtype]);
108 this.allQueues[qtype] = [];
111 // could be a big list, invoke in streaming mode
112 return this.net.request(
114 `open-ils.vandelay.${qtype}_queue.owner.retrieve`,
117 queue => this.allQueues[qtype].push(queue)
118 )).toPromise().then(() => this.allQueues[qtype]);
122 // Returns a promise resolved with the list of queues.
123 // Also emits the onQueueListUpdate event so listeners
124 // can detect queue content changes.
125 getActiveQueues(qtype: string): Promise<IdlObject[]> {
126 if (this.activeQueues[qtype]) {
127 return Promise.resolve(this.activeQueues[qtype]);
129 this.activeQueues[qtype] = [];
132 // could be a big list, invoke in streaming mode
133 return this.net.request(
135 `open-ils.vandelay.${qtype}_queue.owner.retrieve`,
136 this.auth.token(), null, {complete: 'f'}
138 queue => this.activeQueues[qtype].push(queue)
139 )).toPromise().then(() => this.activeQueues[qtype]);
142 getBibSources(): Promise<IdlObject[]> {
143 if (this.bibSources) {
144 return Promise.resolve(this.bibSources);
147 return this.pcrud.retrieveAll('cbs',
148 {order_by: {cbs: 'id'}},
150 ).toPromise().then(sources => {
151 this.bibSources = sources;
156 getItemImportDefs(): Promise<IdlObject[]> {
157 if (this.importItemAttrDefs) {
158 return Promise.resolve(this.importItemAttrDefs);
161 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
162 return this.pcrud.search('viiad', {owner: owners}, {}, {atomic: true})
163 .toPromise().then(defs => {
164 this.importItemAttrDefs = defs;
169 // todo: differentiate between biblio and authority a la queue api
170 getMatchSets(mtype: string): Promise<IdlObject[]> {
172 const mstype = mtype.match(/bib/) ? 'biblio' : 'authority';
174 if (this.matchSets[mtype]) {
175 return Promise.resolve(this.matchSets[mtype]);
177 this.matchSets[mtype] = [];
180 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
182 return this.pcrud.search('vms',
183 {owner: owners, mtype: mstype}, {}, {atomic: true})
184 .toPromise().then(sets => {
185 this.matchSets[mtype] = sets;
190 getBibBuckets(): Promise<IdlObject[]> {
191 if (this.bibBuckets) {
192 return Promise.resolve(this.bibBuckets);
196 return this.net.request(
198 'open-ils.actor.container.retrieve_by_class',
199 this.auth.token(), this.auth.user().id(), 'biblio', 'staff_client'
200 //).pipe(tap(bkt => bkts.push(bkt))).toPromise().then(() => bkts);
201 ).toPromise().then(bkts => {
202 this.bibBuckets = bkts;
207 getCopyStatuses(): Promise<any> {
208 if (this.copyStatuses) {
209 return Promise.resolve(this.copyStatuses);
211 return this.pcrud.retrieveAll('ccs', {}, {atomic: true})
212 .toPromise().then(stats => {
213 this.copyStatuses = stats;
218 getBibTrashGroups(): Promise<any> {
219 if (this.bibTrashGroups) {
220 return Promise.resolve(this.bibTrashGroups);
223 const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
225 return this.pcrud.search('vibtg',
226 {always_apply : 'f', owner: owners},
229 ).toPromise().then(groups => {
230 this.bibTrashGroups = groups;
236 // Create a queue and return the ID of the new queue via promise.
242 matchBucket: number): Promise<number> {
244 const method = `open-ils.vandelay.${recordType}_queue.create`;
246 let qType = recordType;
247 if (recordType.match(/acq/)) {
251 return new Promise((resolve, reject) => {
253 'open-ils.vandelay', method,
254 this.auth.token(), queueName, null, qType,
255 matchSet, importDefId, matchBucket
256 ).subscribe(queue => {
257 const e = this.evt.parse(queue);
268 getQueuedRecords(queueId: number, queueType: string,
269 options?: any, limitToMatches?: boolean): Observable<any> {
271 const qtype = queueType.match(/bib/) ? 'bib' : 'auth';
274 `open-ils.vandelay.${qtype}_queue.records.retrieve`;
276 if (limitToMatches) {
278 `open-ils.vandelay.${qtype}_queue.records.matches.retrieve`;
281 return this.net.request('open-ils.vandelay',
282 method, this.auth.token(), queueId, options);
285 // Download a queue as a MARC file.
286 exportQueue(queue: IdlObject, nonImported?: boolean) {
288 const etype = queue.queue_type().match(/auth/) ? 'auth' : 'bib';
291 `${VANDELAY_EXPORT_PATH}?type=${etype}&queueid=${queue.id()}`
293 let saveName = queue.name();
296 url += '&nonimported=1';
297 saveName += '_nonimported';
302 this.http.get(url, {responseType: 'text'}).subscribe(
305 new Blob([data], {type: 'application/octet-stream'}),
315 // Poll every 2 seconds for session tracker updates so long
316 // as the session tracker is active.
317 // Returns an Observable of tracker objects.
318 pollSessionTracker(id: number): Observable<IdlObject> {
319 return new Observable(observer => {
320 this.getNextSessionTracker(id, observer);
324 getNextSessionTracker(id: number, observer: any) {
326 // No need for this to be an authoritative call.
327 // It will complete eventually regardless.
328 this.pcrud.retrieve('vst', id).subscribe(
330 if (tracker && tracker.state() === 'active') {
331 observer.next(tracker);
333 this.getNextSessionTracker(id, observer), 2000);
336 `Vandelay session tracker ${id} is ${tracker.state()}`);