1 import {Injectable} from '@angular/core';
2 import {Observable, from} from 'rxjs';
3 import {mergeMap, map, tap} from 'rxjs/operators';
4 import {OrgService} from '@eg/core/org.service';
5 import {UnapiService} from '@eg/share/catalog/unapi.service';
6 import {IdlService, IdlObject} from '@eg/core/idl.service';
7 import {NetService} from '@eg/core/net.service';
8 import {PcrudService} from '@eg/core/pcrud.service';
9 import {PermService} from '@eg/core/perm.service';
11 export const NAMESPACE_MAPS = {
12 'mods': 'http://www.loc.gov/mods/v3',
13 'biblio': 'http://open-ils.org/spec/biblio/v1',
14 'holdings': 'http://open-ils.org/spec/holdings/v1',
15 'indexing': 'http://open-ils.org/spec/indexing/v1'
18 export const HOLDINGS_XPATH =
19 '/holdings:holdings/holdings:counts/holdings:count';
21 interface EResourceUrl {
27 export interface HoldingsSummary {
36 export class BibRecordSummary {
37 id: number; // == record.id() for convenience
38 metabibId: number; // If present, this is a metabib summary
39 metabibRecords: number[]; // all constituent bib records
40 staffViewMetabibId: number; // to supplement a record summary
41 staffViewMetabibRecords: number[]; // to supplement a record summary
42 staffViewMetabibAttributes: any; // to supplement a record summary
48 holdingsSummary: HoldingsSummary[];
49 prefOuHoldingsSummary: HoldingsSummary[];
51 bibCallNumber: string;
52 firstCallNumber: string;
54 displayHighlights: {[name: string]: string | string[]} = {};
55 eResourceUrls: EResourceUrl[] = [];
59 constructor(record: IdlObject, orgId: number, orgDepth?: number) {
60 this.id = Number(record.id());
63 this.orgDepth = orgDepth;
66 this.bibCallNumber = null;
67 this.metabibRecords = [];
70 // Get -> Set -> Return bib-level call number
71 getBibCallNumber(): Promise<string> {
73 if (this.bibCallNumber !== null) {
74 return Promise.resolve(this.bibCallNumber);
77 return this.net.request(
79 'open-ils.cat.biblio.record.marc_cn.retrieve',
80 this.id, null, this.orgId
81 ).toPromise().then(cnArray => {
82 if (cnArray && cnArray.length > 0) {
83 const key1 = Object.keys(cnArray[0])[0];
84 this.bibCallNumber = cnArray[0][key1];
86 this.bibCallNumber = '';
88 return this.bibCallNumber;
94 export class BibRecordService {
96 // Cache of bib editor / creator objects
97 // Assumption is this list will be limited in size.
98 userCache: {[id: number]: IdlObject};
99 allowUnfillableHolds: boolean;
102 private idl: IdlService,
103 private net: NetService,
104 private org: OrgService,
105 private unapi: UnapiService,
106 private pcrud: PcrudService,
107 private perm: PermService
110 this.perm.hasWorkPermHere(['PLACE_UNFILLABLE_HOLD'])
112 this.allowUnfillableHolds = perms.PLACE_UNFILLABLE_HOLD;
116 getBibSummary(id: number,
117 orgId?: number, isStaff?: boolean): Observable<BibRecordSummary> {
118 return this.getBibSummaries([id], orgId, isStaff);
121 getBibSummaries(bibIds: number[], orgId?: number,
122 isStaff?: boolean, options?: any): Observable<BibRecordSummary> {
124 if (bibIds.length === 0) { return from([]); }
125 if (!orgId) { orgId = this.org.root().id(); }
127 let method = 'open-ils.search.biblio.record.catalog_summary';
128 if (isStaff) { method += '.staff'; }
130 return this.net.request('open-ils.search', method, orgId, bibIds, options)
131 .pipe(map(bibSummary => {
132 const summary = new BibRecordSummary(bibSummary.record, orgId);
133 summary.net = this.net; // inject
134 summary.staffViewMetabibId = Number(bibSummary.staff_view_metabib_id);
135 summary.staffViewMetabibRecords = bibSummary.staff_view_metabib_records;
136 summary.staffViewMetabibAttributes = bibSummary.staff_view_metabib_attributes;
137 summary.display = bibSummary.display;
138 summary.attributes = bibSummary.attributes;
139 summary.holdCount = bibSummary.hold_count;
140 summary.holdingsSummary = bibSummary.copy_counts;
141 summary.copies = bibSummary.copies;
142 summary.firstCallNumber = bibSummary.first_call_number;
143 summary.prefOuHoldingsSummary = bibSummary.pref_ou_copy_counts;
145 summary.isHoldable = bibSummary.record.deleted() === 'f'
146 && bibSummary.has_holdable_copy
147 || this.allowUnfillableHolds;
149 // De-duplicate urls, frequently caused by multiple subfield 9's
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;