]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts
LP1582720: Ignore Duplicate Scoped URIs - Staff
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / share / catalog / bib-record.service.ts
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';
10
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'
16 };
17
18 export const HOLDINGS_XPATH =
19     '/holdings:holdings/holdings:counts/holdings:count';
20
21 interface EResourceUrl {
22     href: string;
23     note: string;
24     label: string;
25 }
26
27 export interface HoldingsSummary {
28     org_unit: number;
29     depth: number;
30     unshadow: number;
31     count: number;
32     available: number;
33     transcendant: number;
34 }
35
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
43     orgId: number;
44     orgDepth: number;
45     record: IdlObject;
46     display: any;
47     attributes: any;
48     holdingsSummary: HoldingsSummary[];
49     prefOuHoldingsSummary: HoldingsSummary[];
50     holdCount: number;
51     bibCallNumber: string;
52     firstCallNumber: string;
53     net: NetService;
54     displayHighlights: {[name: string]: string | string[]} = {};
55     eResourceUrls: EResourceUrl[] = [];
56     copies: any[];
57     isHoldable: boolean;
58
59     constructor(record: IdlObject, orgId: number, orgDepth?: number) {
60         this.id = Number(record.id());
61         this.record = record;
62         this.orgId = orgId;
63         this.orgDepth = orgDepth;
64         this.display = {};
65         this.attributes = {};
66         this.bibCallNumber = null;
67         this.metabibRecords = [];
68     }
69
70     // Get -> Set -> Return bib-level call number
71     getBibCallNumber(): Promise<string> {
72
73         if (this.bibCallNumber !== null) {
74             return Promise.resolve(this.bibCallNumber);
75         }
76
77         return this.net.request(
78             'open-ils.cat',
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];
85             } else {
86                 this.bibCallNumber = '';
87             }
88             return this.bibCallNumber;
89         });
90     }
91 }
92
93 @Injectable()
94 export class BibRecordService {
95
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;
100
101     constructor(
102         private idl: IdlService,
103         private net: NetService,
104         private org: OrgService,
105         private unapi: UnapiService,
106         private pcrud: PcrudService,
107         private perm: PermService
108     ) {
109         this.userCache = {};
110         this.perm.hasWorkPermHere(['PLACE_UNFILLABLE_HOLD'])
111             .then(perms => {
112                 this.allowUnfillableHolds = perms.PLACE_UNFILLABLE_HOLD;
113             });
114     }
115
116     getBibSummary(id: number,
117         orgId?: number, isStaff?: boolean): Observable<BibRecordSummary> {
118         return this.getBibSummaries([id], orgId, isStaff);
119     }
120
121     getBibSummaries(bibIds: number[], orgId?: number,
122         isStaff?: boolean, options?: any): Observable<BibRecordSummary> {
123
124         if (bibIds.length === 0) { return from([]); }
125         if (!orgId) { orgId = this.org.root().id(); }
126
127         let method = 'open-ils.search.biblio.record.catalog_summary';
128         if (isStaff) { method += '.staff'; }
129
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;
144
145                 summary.isHoldable = bibSummary.record.deleted() === 'f'
146                 && bibSummary.has_holdable_copy
147                 || this.allowUnfillableHolds;
148
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);
154                     }
155                 });
156
157                 return summary;
158             }));
159     }
160
161     getMetabibSummaries(metabibIds: number[],
162         orgId?: number, isStaff?: boolean, options?: any): Observable<BibRecordSummary> {
163
164         if (metabibIds.length === 0) { return from([]); }
165         if (!orgId) { orgId = this.org.root().id(); }
166
167         let method = 'open-ils.search.biblio.metabib.catalog_summary';
168         if (isStaff) { method += '.staff'; }
169
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;
183
184                 summary.isHoldable = metabibSummary.record.deleted() === 'f'
185                 && metabibSummary.has_holdable_copy
186                 || this.allowUnfillableHolds;
187
188                 return summary;
189             }));
190     }
191 }
192
193