1 import {Injectable} from '@angular/core';
2 import {Observable, from} from 'rxjs';
3 import {map} from 'rxjs/operators';
4 import {OrgService} from '@eg/core/org.service';
5 import {IdlObject} from '@eg/core/idl.service';
6 import {NetService} from '@eg/core/net.service';
7 import {PermService} from '@eg/core/perm.service';
9 export const NAMESPACE_MAPS = {
10 'mods': 'http://www.loc.gov/mods/v3',
11 'biblio': 'http://open-ils.org/spec/biblio/v1',
12 'holdings': 'http://open-ils.org/spec/holdings/v1',
13 'indexing': 'http://open-ils.org/spec/indexing/v1'
16 export const HOLDINGS_XPATH =
17 '/holdings:holdings/holdings:counts/holdings:count';
19 interface EResourceUrl {
25 export interface HoldingsSummary {
34 export class BibRecordSummary {
35 id: number; // == record.id() for convenience
36 metabibId: number; // If present, this is a metabib summary
37 metabibRecords: number[]; // all constituent bib records
38 staffViewMetabibId: number; // to supplement a record summary
39 staffViewMetabibRecords: number[]; // to supplement a record summary
40 staffViewMetabibAttributes: any; // to supplement a record summary
46 holdingsSummary: HoldingsSummary[];
47 prefOuHoldingsSummary: HoldingsSummary[];
49 recordNoteCount: number;
50 bibCallNumber: string;
51 firstCallNumber: string;
53 displayHighlights: {[name: string]: string | string[]} = {};
54 eResourceUrls: EResourceUrl[] = [];
58 constructor(record: IdlObject, orgId: number, orgDepth?: number) {
59 this.id = Number(record.id());
62 this.orgDepth = orgDepth;
65 this.bibCallNumber = null;
66 this.metabibRecords = [];
69 // Get -> Set -> Return bib-level call number
70 getBibCallNumber(): Promise<string> {
72 if (this.bibCallNumber !== null) {
73 return Promise.resolve(this.bibCallNumber);
76 return this.net.request(
78 'open-ils.cat.biblio.record.marc_cn.retrieve',
79 this.id, null, this.orgId
80 ).toPromise().then(cnArray => {
81 if (cnArray && cnArray.length > 0) {
82 const key1 = Object.keys(cnArray[0])[0];
83 this.bibCallNumber = cnArray[0][key1];
85 this.bibCallNumber = '';
87 return this.bibCallNumber;
93 export class BibRecordService {
95 // Cache of bib editor / creator objects
96 // Assumption is this list will be limited in size.
97 userCache: {[id: number]: IdlObject};
98 allowUnfillableHolds: boolean;
101 private net: NetService,
102 private org: OrgService,
103 private perm: PermService
106 this.perm.hasWorkPermHere(['PLACE_UNFILLABLE_HOLD'])
108 this.allowUnfillableHolds = perms.PLACE_UNFILLABLE_HOLD;
112 getBibSummary(id: number,
113 orgId?: number, isStaff?: boolean): Observable<BibRecordSummary> {
114 return this.getBibSummaries([id], orgId, isStaff);
117 getBibSummaries(bibIds: number[], orgId?: number,
118 isStaff?: boolean, options?: any): Observable<BibRecordSummary> {
120 if (bibIds.length === 0) { return from([]); }
121 if (!orgId) { orgId = this.org.root().id(); }
123 let method = 'open-ils.search.biblio.record.catalog_summary';
124 if (isStaff) { method += '.staff'; }
126 return this.net.request('open-ils.search', method, orgId, bibIds, options)
127 .pipe(map(bibSummary => {
128 const summary = new BibRecordSummary(bibSummary.record, orgId);
129 summary.net = this.net; // inject
130 summary.staffViewMetabibId = Number(bibSummary.staff_view_metabib_id);
131 summary.staffViewMetabibRecords = bibSummary.staff_view_metabib_records;
132 summary.staffViewMetabibAttributes = bibSummary.staff_view_metabib_attributes;
133 summary.display = bibSummary.display;
134 summary.attributes = bibSummary.attributes;
135 summary.holdCount = Number(bibSummary.hold_count);
136 summary.recordNoteCount = Number(bibSummary.record_note_count);
137 summary.holdingsSummary = bibSummary.copy_counts;
138 summary.copies = bibSummary.copies;
139 summary.firstCallNumber = bibSummary.first_call_number;
140 summary.prefOuHoldingsSummary = bibSummary.pref_ou_copy_counts;
142 summary.isHoldable = bibSummary.record.deleted() === 'f'
143 && bibSummary.has_holdable_copy
144 || this.allowUnfillableHolds;
146 // De-duplicate urls, frequently caused by multiple subfield 9's
147 // Note that .forEach() and .every() pass their callback functions the
148 // parameters (element, index, array) such that element = array[index]
149 // and they need to be distinctly named when sharing scope.
150 bibSummary.urls.forEach(function (elb, indb, aryb) {
151 if(summary.eResourceUrls.every((els, inds, arys) =>
152 elb.href !== els.href || elb.note !== els.note || elb.label !== els.label)) {
153 summary.eResourceUrls.push(elb);
161 getMetabibSummaries(metabibIds: number[],
162 orgId?: number, isStaff?: boolean, options?: any): Observable<BibRecordSummary> {
164 if (metabibIds.length === 0) { return from([]); }
165 if (!orgId) { orgId = this.org.root().id(); }
167 let method = 'open-ils.search.biblio.metabib.catalog_summary';
168 if (isStaff) { method += '.staff'; }
170 return this.net.request('open-ils.search', method, orgId, metabibIds, options)
171 .pipe(map(metabibSummary => {
172 const summary = new BibRecordSummary(metabibSummary.record, orgId);
173 summary.net = this.net; // inject
174 summary.metabibId = Number(metabibSummary.metabib_id);
175 summary.metabibRecords = metabibSummary.metabib_records;
176 summary.display = metabibSummary.display;
177 summary.attributes = metabibSummary.attributes;
178 summary.holdCount = metabibSummary.hold_count;
179 summary.holdingsSummary = metabibSummary.copy_counts;
180 summary.copies = metabibSummary.copies;
181 summary.firstCallNumber = metabibSummary.first_call_number;
182 summary.prefOuHoldingsSummary = metabibSummary.pref_ou_copy_counts;
184 summary.isHoldable = metabibSummary.record.deleted() === 'f'
185 && metabibSummary.has_holdable_copy
186 || this.allowUnfillableHolds;