-import {Component, OnInit} from '@angular/core';
+import {Component, OnInit, AfterViewInit, ViewChild} from '@angular/core';
import {Router, ActivatedRoute, NavigationEnd} from "@angular/router";
import {take} from 'rxjs/operators/take';
+import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
+import {VandelayService} from './vandelay.service';
+import {IdlObject} from '@eg/core/idl.service';
@Component({
templateUrl: 'vandelay.component.html'
})
-export class VandelayComponent implements OnInit {
+export class VandelayComponent implements OnInit, AfterViewInit {
tab: string;
+ attrDefs = {bib: [], auth: []};
+ allQueues = {bib: [], auth: []};
+ activeQueues = {bib: [], auth: []}; // complete === 'f'
+ bibSources: IdlObject[];
+ bibBuckets: IdlObject[];
+ bibTrashGroups: IdlObject[];
+ copyStatuses: IdlObject[];
+ importItemDefs: IdlObject[];
+ matchSets: {[stype: string]: IdlObject[]};
+
+ @ViewChild('progressDialog')
+ private progressDialog: ProgressDialogComponent;
+
constructor(
private router: Router,
- private route: ActivatedRoute
- ) {
+ private route: ActivatedRoute,
+ private vandelay: VandelayService) {
// As the parent component of the vandelay route tree, our
// activated route never changes. Instead, listen for global
.subscribe(segments => this.tab = segments[0].path);
}
});
+
+ this.attachDataListeners();
}
ngOnInit() {
}
+ // Manage the shared data here so it persists across tabs.
+ // Load the shared data after view init so the page can initialize
+ // first and display the loading message.
+ ngAfterViewInit() {
+
+ // loading startup data causes data in this component to change
+ // inside its own lifecycle hook, which is a no-no. Run after
+ // the hook.
+ setTimeout(() => {
+
+ this.vandelay.loadStartupData(this.progressDialog).then(ok => {
+
+ // Pull data that does not change during a vandelay
+ // UI directly from the service.
+ this.bibSources = this.vandelay.bibSources;
+ this.bibBuckets = this.vandelay.bibBuckets;
+ this.bibTrashGroups = this.vandelay.bibTrashGroups;
+ this.copyStatuses = this.vandelay.copyStatuses;
+ });
+ });
+ }
+
+
+ // Listen for global data changes, store data locally so it
+ // may be propagated to child components.
+ attachDataListeners() {
+
+ this.vandelay.onAttrDefsUpdate$.subscribe(collection => {
+ const dtype = collection.dtype;
+ this.attrDefs[dtype] = collection.defs;
+ });
+
+ // Bib/auth queue list
+ this.vandelay.onQueueListUpdate$.subscribe(collection => {
+ const qtype = collection.qtype;
+ collection.queues.forEach(q => {
+ this.allQueues[qtype] = q;
+ if (q.complete() === 'f') {
+ this.activeQueues[qtype] = q;
+ }
+ });
+ });
+
+ this.vandelay.onItemImportDefsUpdate$.subscribe(
+ defs => this.importItemDefs = defs
+ );
+
+ this.vandelay.onMatchSetsUpdate$.subscribe(sets => {
+ sets.forEach(s => {
+ if (!this.matchSets[s.mtype()]) {
+ this.matchSets[s.mtype()] = [];
+ }
+ this.matchSets[s.mtype()].push(s);
+ });
+ });
+ }
}
--- /dev/null
+import {Injectable, EventEmitter} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+import {tap} from 'rxjs/operators/tap';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {PermService} from '@eg/core/perm.service';
+import {EventService} from '@eg/core/event.service';
+import {ProgressDialogComponent} from '@eg/share/dialog/progress.component';
+
+@Injectable()
+export class VandelayService {
+
+ // Data that may change within the vandelay UI is propagated
+ // via EventEmitters
+ onQueueListUpdate$: EventEmitter<any>;
+ onItemImportDefsUpdate$: EventEmitter<IdlObject[]>;
+ onMatchSetsUpdate$: EventEmitter<IdlObject[]>;
+ onAttrDefsUpdate$: EventEmitter<any>;
+
+ // Data that persists during a vandelay session needs no emitters.
+ bibSources: IdlObject[];
+ bibBuckets: IdlObject[];
+ bibTrashGroups: IdlObject[];
+ copyStatuses: IdlObject[];
+
+ // Settings to retrieve (and cache) at load time.
+ orgSettings = ['vandelay.default_match_set'];
+
+ constructor(
+ private idl: IdlService,
+ private org: OrgService,
+ private evt: EventService,
+ private net: NetService,
+ private auth: AuthService,
+ private pcrud: PcrudService,
+ private perm: PermService
+ ) {
+ this.onQueueListUpdate$ = new EventEmitter<any>();
+ this.onItemImportDefsUpdate$ = new EventEmitter<IdlObject[]>();
+ this.onMatchSetsUpdate$ = new EventEmitter<IdlObject[]>();
+ this.onAttrDefsUpdate$ = new EventEmitter<any>();
+ }
+
+ loadStartupData(dialog: ProgressDialogComponent): Promise<any> {
+
+ dialog.open();
+
+ const promises = [
+ this.getAttrDefs('bib').toPromise(),
+ this.getAttrDefs('auth').toPromise(),
+ this.getQueueList('bib').toPromise(),
+ this.getQueueList('auth').toPromise(),
+ this.getItemImportDefs().toPromise(),
+ this.getMatchSets().toPromise(),
+ this.getBibBuckets(),
+ this.getBibSources(),
+ this.getBibTrashGroups(),
+ this.getCopyStatuses(),
+ this.org.settings(this.orgSettings)
+ ];
+
+ dialog.update({value: 0, max: promises.length});
+ promises.forEach(p => p.then(_ => dialog.increment()));
+
+ return Promise.all(promises).then(ok => dialog.close());
+ }
+
+ getAttrDefs(dtype: string): Observable<IdlObject> {
+ const cls = (dtype === 'bib') ? 'vqbrad' : 'vqarad';
+ const defs = [];
+
+ const orderBy = {};
+ orderBy[cls] = 'id'
+
+ return this.pcrud.retrieveAll(cls, {order_by: orderBy})
+ .pipe(tap(
+ def => defs.push(def),
+ err => {},
+ () => {
+ console.debug(`Fetched ${defs.length} ${dtype} attr def(s)`);
+ this.onAttrDefsUpdate$.emit({dtype: dtype, defs: defs});
+ }
+ ));
+ }
+
+ // Returns a promise resolved with the list of queues.
+ // Also emits the onQueueListUpdate event so listeners
+ // can detect queue content changes.
+ getQueueList(qtype: string, filters?: any): Observable<IdlObject> {
+ const qt = (qtype === 'bib') ? qtype : 'authority';
+ const queues = [];
+
+ return this.net.request(
+ 'open-ils.vandelay',
+ `open-ils.vandelay.${qt}_queue.owner.retrieve`,
+ this.auth.token(), null, filters
+ ).pipe(tap(
+ queue => {
+ const evt = this.evt.parse(queue);
+ if (evt) { return alert(evt); } // should not happen
+ queues.push(queue);
+ },
+ err => {console.error(err)},
+ () => {
+ console.debug(`Fetched ${queues.length} "${qtype}" queue(s)`);
+ this.onQueueListUpdate$.emit({qtype: qtype, queues: queues});
+ }
+ ));
+ }
+
+ getBibSources(): Promise<any> {
+ return this.pcrud.retrieveAll('cbs',
+ {order_by: {cbs: 'id'}},
+ {atomic: true}
+ ).toPromise().then(sources => {
+ console.debug(`Fetched ${sources.length} bib source(s)`);
+ this.bibSources = sources;
+ });
+ }
+
+ getItemImportDefs(): Observable<IdlObject> {
+ const defs = [];
+ const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
+
+ return this.pcrud.search('viiad', {owner: owners})
+ .pipe(tap(
+ def => defs.push(def),
+ err => {},
+ () => {
+ console.debug(`Fetched ${defs.length} import item def(s)`);
+ this.onItemImportDefsUpdate$.emit(defs);
+ }
+ ));
+ }
+
+ getMatchSets(): Observable<IdlObject[]> {
+ const sets = [];
+ const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
+ return this.pcrud.search('vms', {owner: owners})
+ .pipe(tap(
+ s => sets.push(s),
+ err => {},
+ () => {
+ console.debug(`Fetched ${sets.length} match set(s)`);
+ this.onMatchSetsUpdate$.emit(sets);
+ }
+ ));
+ }
+
+ getBibBuckets(): Promise<any> {
+ this.bibBuckets = [];
+
+ return this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.container.retrieve_by_class',
+ this.auth.token(), this.auth.user().id(), 'biblio', 'staff_client'
+ ).pipe(tap(
+ bkt => this.bibBuckets.push(bkt),
+ err => {},
+ () => console.debug(`Fetched ${this.bibBuckets.length} bucket(s)`)
+ )).toPromise();
+ }
+
+ getCopyStatuses(): Promise<any> {
+ return this.pcrud.retrieveAll('ccs', {}, {atomic: true})
+ .toPromise().then(stats => {
+ console.debug(`Fetched ${stats.length} copy status(es)`);
+ this.copyStatuses = stats;
+ });
+ }
+
+ getBibTrashGroups(): Promise<any> {
+ const owners = this.org.ancestors(this.auth.user().ws_ou(), true);
+
+ return this.pcrud.search('vibtg',
+ {always_apply : 'f', owner: owners},
+ {vibtg : ['label']},
+ {atomic: true}
+ ).toPromise().then(groups => {
+ console.debug(`Fetched ${groups.length} bib trash field group(s)`);
+ this.bibTrashGroups = groups;
+ });
+ }
+
+
+}
+