From 66f0c39c4591cf6140855aea7d92526ebb7adf2a Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Tue, 18 Dec 2018 13:15:56 -0500 Subject: [PATCH] LP1806087 Group formats and editions (WIP) Signed-off-by: Bill Erickson --- Open-ILS/examples/fm_IDL.xml | 16 +++- .../src/app/share/catalog/bib-record.service.ts | 92 ++++++++++++++++++++-- .../src/app/share/catalog/catalog-url.service.ts | 5 +- .../eg2/src/app/share/catalog/catalog.service.ts | 32 ++++++-- .../eg2/src/app/share/catalog/search-context.ts | 2 +- .../app/staff/catalog/result/record.component.html | 15 ++-- .../app/staff/catalog/search-form.component.html | 2 +- 7 files changed, 141 insertions(+), 23 deletions(-) diff --git a/Open-ILS/examples/fm_IDL.xml b/Open-ILS/examples/fm_IDL.xml index 1be2c885e8..e934ae6142 100644 --- a/Open-ILS/examples/fm_IDL.xml +++ b/Open-ILS/examples/fm_IDL.xml @@ -3899,18 +3899,25 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + + + + + + + + @@ -3943,7 +3950,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - + @@ -3953,6 +3960,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + + + + { if (this.attributes[attr.attr()]) { - this.attributes[attr.attr()].push(attr.value()); + // Avoid dupes + if (this.attributes[attr.attr()].indexOf(attr.value()) < 0) { + this.attributes[attr.attr()].push(attr.value()); + } } else { this.attributes[attr.attr()] = [attr.value()]; } @@ -83,9 +87,16 @@ export class BibRecordSummary { return Promise.resolve(this.holdCount); } + let method = 'open-ils.circ.bre.holds.count'; + let target = this.id; + + if (this.metabibId) { + method = 'open-ils.circ.mmr.holds.count'; + target = this.metabibId; + } + return this.net.request( - 'open-ils.circ', - 'open-ils.circ.bre.holds.count', this.id + 'open-ils.circ', method, target ).toPromise().then(count => this.holdCount = count); } @@ -133,7 +144,7 @@ export class BibRecordService { } // Avoid fetching the MARC blob by specifying which fields on the - // bre to select. Note that fleshed fields are explicitly selected. + // bre to select. Note that fleshed fields are implicitly selected. fetchableBreFields(): string[] { return this.idl.classes.bre.fields .filter(f => !f.virtual && f.name !== 'marc') @@ -169,6 +180,75 @@ export class BibRecordService { })); } + // A Metabib Summary is a BibRecordSummary with the lead record as + // its core bib record plus attributes (e.g. formats) from related + // records. + getMetabibSummary(metabibIds: number | number[], + orgId?: number, orgDepth?: number): Observable { + + const ids = [].concat(metabibIds); + + if (ids.length === 0) { + return from([]); + } + + return this.pcrud.search('mmr', {id: ids}, + {flesh: 1, flesh_fields: {mmr: ['source_maps']}}, + {anonymous: true} + ).pipe(mergeMap(mmr => this.compileMetabib(mmr, orgId, orgDepth))); + } + + // 'metabib' must have its "source_maps" field fleshed. + // Get bib summaries for all related bib records so we can + // extract data that must be appended to the master record summary. + compileMetabib(metabib: IdlObject, + orgId?: number, orgDepth?: number): Observable { + + const bibIds = metabib.source_maps().map(map => map.source()); + let observer; + const observable = + new Observable(o => observer = o); + + let master; + const summaries = []; + + // NOTE if we only need to augment with 'mattrs' it would be + // faster to just grab those than to fetch the full bib-summary + // for related records. Revisit. + + this.getBibSummary(bibIds, orgId, orgDepth).subscribe( + sum => { + summaries.push(sum); + if (sum.id === Number(metabib.master_record())) { + master = sum; + master.metabibId = metabib.id(); + } + }, + err => {}, + () => { + summaries.forEach(sum => { + if (sum.id !== master.id) { + master.record.mattrs( + master.record.mattrs().concat(sum.record.mattrs())) + } + }); + + // Recompile attributes now that the data has been augmented + master.compileRecordAttrs(); + + // Fetch holdings data for the metarecord + this.getHoldingsSummary(metabib.id(), orgId, orgDepth, true) + .then(holdingsSummary => { + master.holdingsSummary = holdingsSummary; + observer.next(master); + observer.complete(); + }); + } + ); + + return observable; + } + // Flesh the creator and editor fields. // Handling this separately lets us pull from the cache and // avoids the requirement that the main bib query use a staff @@ -209,12 +289,12 @@ export class BibRecordService { } getHoldingsSummary(recordId: number, - orgId: number, orgDepth: number): Promise { + orgId: number, orgDepth: number, isMetarecord?: boolean): Promise { const holdingsSummary = []; return this.unapi.getAsXmlDocument({ - target: 'bre', + target: isMetarecord ? 'mmr' : 'bre', id: recordId, extras: '{holdings_xml}', format: 'holdings_xml', diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts index 60c31284a5..1b85d71a8d 100644 --- a/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts +++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog-url.service.ts @@ -80,7 +80,8 @@ export class CatalogUrlService { params.joinOp = []; params.matchOp = []; - ['format', 'available', 'hasBrowseEntry', 'date1', 'date2', 'dateOp'] + ['format', 'available', 'hasBrowseEntry', + 'date1', 'date2', 'dateOp', 'isMetarecord'] .forEach(field => { if (ts[field]) { params[field] = ts[field]; @@ -195,7 +196,7 @@ export class CatalogUrlService { } else if (params.has('query')) { // Scalars - ['format', 'available', 'date1', 'date2', 'dateOp'] + ['format', 'available', 'date1', 'date2', 'dateOp', 'isMetarecord'] .forEach(field => { if (params.has(field)) { ts[field] = params.get(field); diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts index 93eb964533..b210103937 100644 --- a/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts +++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts @@ -114,7 +114,6 @@ export class CatalogService { let fullQuery; if (ctx.identSearch.isSearchable()) { - console.log('IDENT IS SEARCHABLE'); fullQuery = ctx.compileIdentSearchQuery(); } else { fullQuery = ctx.compileTermSearchQuery(); @@ -123,6 +122,10 @@ export class CatalogService { console.debug(`search query: ${fullQuery}`); let method = 'open-ils.search.biblio.multiclass.query'; + if (ctx.termSearch.isSearchable() && ctx.termSearch.isMetarecord) { + method = 'open-ils.search.metabib.multiclass.query'; + } + if (ctx.isStaff) { method += '.staff'; } @@ -166,11 +169,30 @@ export class CatalogService { ctx.org.root().ou_type().depth() : ctx.searchOrg.ou_type().depth(); - return this.bibService.getBibSummary( - ctx.currentResultIds(), ctx.searchOrg.id(), depth) - .pipe(map(summary => { + const isMeta = ( + ctx.termSearch.isSearchable() && + ctx.termSearch.isMetarecord + ); + + let observable: Observable; + + if (isMeta) { + observable = this.bibService.getMetabibSummary( + ctx.currentResultIds(), ctx.searchOrg.id(), depth); + } else { + observable = this.bibService.getBibSummary( + ctx.currentResultIds(), ctx.searchOrg.id(), depth); + } + + return observable.pipe(map(summary => { // Responses are not necessarily returned in request-ID order. - const idx = ctx.currentResultIds().indexOf(summary.record.id()); + let idx; + if (isMeta) { + idx = ctx.currentResultIds().indexOf(summary.metabibId); + } else { + idx = ctx.currentResultIds().indexOf(summary.id); + } + if (ctx.result.records) { // May be reset when quickly navigating results. ctx.result.records[idx] = summary; diff --git a/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts b/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts index e4d65d4a35..c4bb4933cd 100644 --- a/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts +++ b/Open-ILS/src/eg2/src/app/share/catalog/search-context.ts @@ -122,7 +122,7 @@ export class CatalogTermContext { ccvmFilters: {[ccvmCode: string]: string[]}; facetFilters: FacetFilter[]; copyLocations: string[]; // ID's, but treated as strings in the UI. - isMetarecord: boolean; // TODO + isMetarecord: boolean; hasBrowseEntry: string; // "entryId,fieldId" date1: number; date2: number; diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html index 54b79c195b..928d1a1152 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html +++ b/Open-ILS/src/eg2/src/app/staff/catalog/result/record.component.html @@ -48,12 +48,15 @@
- - - - {{iconFormatLabel(summary.attributes.icon_format[0])}} - + + + + + {{iconFormatLabel(icon)}} + + + {{summary.display.edition}} {{summary.display.pubdate}}
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html index 8b6cc8ce15..4327e8d8ab 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html +++ b/Open-ILS/src/eg2/src/app/staff/catalog/search-form.component.html @@ -112,7 +112,7 @@ TODO focus search input
-- 2.11.0