LP1889694 catalog record summary condensed WIP
authorBill Erickson <berickxx@gmail.com>
Fri, 31 Jul 2020 16:05:08 +0000 (12:05 -0400)
committerBill Erickson <berickxx@gmail.com>
Fri, 31 Jul 2020 16:05:08 +0000 (12:05 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts
Open-ILS/src/eg2/src/app/share/catalog/catalog.service.ts
Open-ILS/src/eg2/src/app/staff/cat/vandelay/queued-record-matches.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/cnbrowse/results.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/result/results.component.ts
Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.ts
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm

index e641a11..3d93945 100644 (file)
@@ -44,63 +44,6 @@ export class BibRecordSummary {
         this.metabibRecords = [];
     }
 
-    ingest() {
-        this.compileDisplayFields();
-        this.compileRecordAttrs();
-
-        // Normalize some data for JS consistency
-        this.record.creator(Number(this.record.creator()));
-        this.record.editor(Number(this.record.editor()));
-    }
-
-    compileDisplayFields() {
-        this.record.flat_display_entries().forEach(entry => {
-            if (entry.multi() === 't') {
-                if (this.display[entry.name()]) {
-                    this.display[entry.name()].push(entry.value());
-                } else {
-                    this.display[entry.name()] = [entry.value()];
-                }
-            } else {
-                this.display[entry.name()] = entry.value();
-            }
-        });
-    }
-
-    compileRecordAttrs() {
-        // Any attr can be multi-valued.
-        this.record.mattrs().forEach(attr => {
-            if (this.attributes[attr.attr()]) {
-                // 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()];
-            }
-        });
-    }
-
-    // Get -> Set -> Return bib hold count
-    getHoldCount(): Promise<number> {
-
-        if (Number.isInteger(this.holdCount)) {
-            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', method, target
-        ).toPromise().then(count => this.holdCount = count);
-    }
-
     // Get -> Set -> Return bib-level call number
     getBibCallNumber(): Promise<string> {
 
@@ -141,19 +84,16 @@ export class BibRecordService {
         this.userCache = {};
     }
 
-    // Avoid fetching the MARC blob by specifying which fields on the
-    // 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')
-            .map(f => f.name);
+    getBibSummary(id: number,
+        orgId?: number, isStaff?: boolean): Observable<BibRecordSummary> {
+        return this.getBibSummaries([id], orgId, isStaff);
     }
 
-    // newstyle
     getBibSummaries(bibIds: number[],
-        orgId: number, isStaff?: boolean): Observable<BibRecordSummary> {
+        orgId?: number, isStaff?: boolean): Observable<BibRecordSummary> {
 
         if (bibIds.length === 0) { return from([]); }
+        if (!orgId) { orgId = this.org.root().id(); }
 
         let method = 'open-ils.search.biblio.record.catalog_summary';
         if (isStaff) { method += '.staff'; }
@@ -170,187 +110,27 @@ export class BibRecordService {
         }));
     }
 
-    // Note when multiple IDs are provided, responses are emitted in order
-    // of receipt, not necessarily in the requested ID order.
-    getBibSummary(bibIds: number | number[],
-        orgId?: number, orgDepth?: number): Observable<BibRecordSummary> {
+    getMetabibSummaries(metabibIds: number[],
+        orgId?: number, isStaff?: boolean): Observable<BibRecordSummary> {
 
-        const ids = [].concat(bibIds);
+        if (metabibIds.length === 0) { return from([]); }
+        if (!orgId) { orgId = this.org.root().id(); }
 
-        if (ids.length === 0) {
-            return from([]);
-        }
+        let method = 'open-ils.search.biblio.metabib.catalog_summary';
+        if (isStaff) { method += '.staff'; }
 
-        return this.pcrud.search('bre', {id: ids},
-            {   flesh: 1,
-                flesh_fields: {bre: ['flat_display_entries', 'mattrs']},
-                select: {bre : this.fetchableBreFields()}
-            },
-            {anonymous: true} // skip unneccesary auth
-        ).pipe(mergeMap(bib => {
-            const summary = new BibRecordSummary(bib, orgId, orgDepth);
+        return this.net.request('open-ils.search', method, orgId, metabibIds)
+        .pipe(map(metabibSummary => {
+            const summary = new BibRecordSummary(metabibSummary.record, orgId);
             summary.net = this.net; // inject
-            summary.ingest();
-            return this.getHoldingsSummary(bib.id(), orgId, orgDepth)
-            .then(holdingsSummary => {
-                summary.holdingsSummary = holdingsSummary;
-                return summary;
-            });
+            summary.metabibId = Number(metabibSummary.metabib_id);
+            summary.display = metabibSummary.display;
+            summary.attributes = metabibSummary.attributes;
+            summary.holdCount = metabibSummary.hold_count;
+            summary.holdingsSummary = metabibSummary.copy_counts;
+            return summary;
         }));
     }
-
-    // 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<BibRecordSummary> {
-
-        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<BibRecordSummary> {
-
-        // TODO: Create an API similar to the one that builds a combined
-        // mods blob for metarecords, except using display fields, etc.
-        // For now, this seems to get the job done.
-
-        // Non-master records
-        const relatedBibIds = metabib.source_maps()
-            .map(m => m.source())
-            .filter(id => id !== metabib.master_record());
-
-        let observer;
-        const observable = new Observable<BibRecordSummary>(o => observer = o);
-
-        // NOTE: getBibSummary calls getHoldingsSummary against
-        // the bib record unnecessarily.  It's called again below.
-        // Reconsider this approach (see also note above about API).
-        this.getBibSummary(metabib.master_record(), orgId, orgDepth)
-        .subscribe(summary => {
-            summary.metabibId = Number(metabib.id());
-            summary.metabibRecords =
-                metabib.source_maps().map(m => Number(m.source()));
-
-            let promise;
-
-            if (relatedBibIds.length > 0) {
-
-                // Grab data for MR bib summary augmentation
-                promise = this.pcrud.search('mraf', {id: relatedBibIds})
-                    .pipe(tap(attr => summary.record.mattrs().push(attr)))
-                    .toPromise();
-            } else {
-
-                // Metarecord has only one constituent bib.
-                promise = Promise.resolve();
-            }
-
-            promise.then(() => {
-
-                // Re-compile with augmented data
-                summary.compileRecordAttrs();
-
-                // Fetch holdings data for the metarecord
-                this.getHoldingsSummary(metabib.id(), orgId, orgDepth, true)
-                .then(holdingsSummary => {
-                    summary.holdingsSummary = holdingsSummary;
-                    observer.next(summary);
-                    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
-    // (VIEW_USER) auth token.
-    fleshBibUsers(records: IdlObject[]): Promise<void> {
-
-        const search = [];
-
-        records.forEach(rec => {
-            ['creator', 'editor'].forEach(field => {
-                const id = rec[field]();
-                if (Number.isInteger(id)) {
-                    if (this.userCache[id]) {
-                        rec[field](this.userCache[id]);
-                    } else if (!search.includes(id)) {
-                        search.push(id);
-                    }
-                }
-            });
-        });
-
-        if (search.length === 0) {
-            return Promise.resolve();
-        }
-
-        return this.pcrud.search('au', {id: search})
-        .pipe(map(user => {
-            this.userCache[user.id()] = user;
-            records.forEach(rec => {
-                if (user.id() === rec.creator()) {
-                    rec.creator(user);
-                }
-                if (user.id() === rec.editor()) {
-                    rec.editor(user);
-                }
-            });
-        })).toPromise();
-    }
-
-    getHoldingsSummary(recordId: number,
-        orgId: number, orgDepth: number, isMetarecord?: boolean): Promise<any> {
-
-        const holdingsSummary = [];
-
-        return this.unapi.getAsXmlDocument({
-            target: isMetarecord ? 'mmr' : 'bre',
-            id: recordId,
-            extras: '{holdings_xml}',
-            format: 'holdings_xml',
-            orgId: orgId,
-            depth: orgDepth
-        }).then(xmlDoc => {
-
-            // namespace resolver
-            const resolver: any = (prefix: string): string => {
-                return NAMESPACE_MAPS[prefix] || null;
-            };
-
-            // Extract the holdings data from the unapi xml doc
-            const result = xmlDoc.evaluate(HOLDINGS_XPATH,
-                xmlDoc, resolver, XPathResult.ANY_TYPE, null);
-
-            let node;
-            while (node = result.iterateNext()) {
-                const counts = {type : node.getAttribute('type')};
-                ['depth', 'org_unit', 'transcendant',
-                    'available', 'count', 'unshadow'].forEach(field => {
-                    counts[field] = Number(node.getAttribute(field));
-                });
-                holdingsSummary.push(counts);
-            }
-
-            return holdingsSummary;
-        });
-    }
 }
 
 
index 2e7f164..4fe389e 100644 (file)
@@ -142,7 +142,6 @@ export class CatalogService {
             }, fullQuery, true
         ).toPromise()
         .then(result => this.applyResultData(ctx, result))
-        .then(_ => this.fetchFieldHighlights(ctx))
         .then(_ => {
             ctx.searchState = CatalogSearchState.COMPLETE;
             this.onSearchComplete.emit(ctx);
@@ -190,8 +189,8 @@ export class CatalogService {
         let observable: Observable<BibRecordSummary>;
 
         if (isMeta) {
-            observable = this.bibService.getMetabibSummary(
-                ctx.currentResultIds(), ctx.searchOrg.id(), depth);
+            observable = this.bibService.getMetabibSummaries(
+                ctx.currentResultIds(), ctx.searchOrg.id(), ctx.isStaff);
         } else {
             observable = this.bibService.getBibSummaries(
                 ctx.currentResultIds(), ctx.searchOrg.id(), ctx.isStaff);
@@ -239,9 +238,13 @@ export class CatalogService {
             // them to bib IDs for highlighting.
             ids = ctx.currentResultIds();
             if (ctx.termSearch.groupByMetarecord) {
+                // TODO: at this point in loading we don't yet
+                // have result records to comb through
+                /*
                 ids = ids.map(mrId =>
                     ctx.result.records.filter(r => mrId === r.metabibId)[0].id
                 );
+                */
             }
         }
 
index dfbee69..10060e0 100644 (file)
@@ -118,28 +118,24 @@ export class QueuedRecordMatchesComponent implements OnInit {
                 });
 
                 const bibSummaries: {[id: number]: BibRecordSummary} = {};
-                this.bib.getBibSummary(recIds).subscribe(
+                this.bib.getBibSummaries(recIds).subscribe(
                     summary => bibSummaries[summary.id] = summary,
                     err => {},
                     ()  => {
-                        this.bib.fleshBibUsers(
-                            Object.values(bibSummaries).map(sum => sum.record)
-                        ).then(() => {
-                            matches.forEach(match => {
-                                const row = {
-                                    id: match.id(),
-                                    eg_record: match.eg_record(),
-                                    bre_quality: match.quality(),
-                                    vqbr_quality: this.queuedRecord.quality(),
-                                    match_score: match.match_score(),
-                                    bib_summary: bibSummaries[match.eg_record()]
-                                };
-
-                                observer.next(row);
-                            });
-
-                            observer.complete();
+                        matches.forEach(match => {
+                            const row = {
+                                id: match.id(),
+                                eg_record: match.eg_record(),
+                                bre_quality: match.quality(),
+                                vqbr_quality: this.queuedRecord.quality(),
+                                match_score: match.match_score(),
+                                bib_summary: bibSummaries[match.eg_record()]
+                            };
+
+                            observer.next(row);
                         });
+
+                        observer.complete();
                     }
                 );
             });
index 464f443..9c0dab3 100644 (file)
@@ -119,9 +119,9 @@ export class CnBrowseResultsComponent implements OnInit, OnDestroy {
         };
 
         const bres: IdlObject[] = [];
-        this.bib.getBibSummary(
+        this.bib.getBibSummaries(
             bibIds.filter(distinct),
-            this.searchContext.searchOrg.id(), depth
+            this.searchContext.searchOrg.id(), this.searchContext.isStaff
         ).subscribe(
             summary => {
                 // Response order not guaranteed.  Match the summary
@@ -134,10 +134,6 @@ export class CnBrowseResultsComponent implements OnInit, OnDestroy {
 
                 // Use _ since result is an 'acn' object.
                 bibResults.forEach(r => r._bibSummary = summary);
-            },
-            err => {},
-            ()  => {
-                this.bib.fleshBibUsers(bres);
             }
         );
     }
index e68e017..fc7ba54 100644 (file)
@@ -156,7 +156,6 @@ export class RecordComponent implements OnInit {
         .then(summary => {
             this.summary =
                 this.staffCat.currentDetailRecordSummary = summary;
-            this.bib.fleshBibUsers([summary.record]);
         });
     }
 
index c84d4df..5de5535 100644 (file)
@@ -109,7 +109,9 @@ export class ResultsComponent implements OnInit, OnDestroy {
     // To reduce page display shuffling, avoid showing the list of
     // records until the first few are ready to render.
     shouldStartRendering(): boolean {
+        return this.searchHasResults();
 
+        /*
         if (this.searchHasResults()) {
             const pageCount = this.searchContext.currentResultIds().length;
             switch (pageCount) {
@@ -122,6 +124,7 @@ export class ResultsComponent implements OnInit, OnDestroy {
         }
 
         return false;
+        */
     }
 
     searchIsDone(): boolean {
index 39b8944..84719fd 100644 (file)
@@ -66,7 +66,6 @@ export class BibSummaryComponent implements OnInit {
         this.bib.getBibSummary(this.recordId).toPromise()
         .then(summary => {
             summary.getBibCallNumber();
-            this.bib.fleshBibUsers([summary.record]);
             this.summary = summary;
         });
     }
index 3254c4f..adbe0e7 100644 (file)
@@ -2757,7 +2757,6 @@ __PACKAGE__->register_method(
         }
     }
 );
-
 __PACKAGE__->register_method(
     method    => 'catalog_record_summary',
     api_name  => 'open-ils.search.biblio.record.catalog_summary.staff',
@@ -2765,37 +2764,77 @@ __PACKAGE__->register_method(
     max_bundle_count => 1,
     signature => q/see open-ils.search.biblio.record.catalog_summary/
 );
+__PACKAGE__->register_method(
+    method    => 'catalog_record_summary',
+    api_name  => 'open-ils.search.biblio.metabib.catalog_summary',
+    stream    => 1,
+    max_bundle_count => 1,
+    signature => q/see open-ils.search.biblio.record.catalog_summary/
+);
+
+__PACKAGE__->register_method(
+    method    => 'catalog_record_summary',
+    api_name  => 'open-ils.search.biblio.metabib.catalog_summary.staff',
+    stream    => 1,
+    max_bundle_count => 1,
+    signature => q/see open-ils.search.biblio.record.catalog_summary/
+);
+
 
 sub catalog_record_summary {
     my ($self, $client, $org_id, $record_ids) = @_;
-
     my $e = new_editor();
 
+    my $is_meta = ($self->api_name =~ /metabib/);
+    my $is_staff = ($self->api_name =~ /staff/);
+
+    my $holds_method = $is_meta ? 
+        'open-ils.circ.mmr.holds.count' : 
+        'open-ils.circ.bre.holds.count';
+
+    my $copy_method = $is_meta ? 
+        'open-ils.search.biblio.metarecord.copy_count':
+        'open-ils.search.biblio.record.copy_count';
+
+    $copy_method .= '.staff' if $is_staff;
+
+    $copy_method = $self->method_lookup($copy_method); # local method
+
     for my $rec_id (@$record_ids) {
 
-        my ($bre, $display, $attributes) = get_one_catalog_record($e, $rec_id);
+        my $response = $is_meta ? 
+            get_one_metarecord_summary($e, $rec_id) :
+            get_one_record_summary($e, $rec_id);
 
-        my $copy_counts = 
-            record_id_to_copy_count($self, $client, $org_id, $rec_id);
+        ($response->{copy_counts}) = $copy_method->run($org_id, $rec_id);
 
-        my $hold_count = $U->simplereq(
-            'open-ils.circ',
-            'open-ils.circ.bre.holds.count', $rec_id);
+        $response->{hold_count} = 
+            $U->simplereq('open-ils.circ', $holds_method, $rec_id);
 
-        $client->respond({
-            id => $rec_id,
-            record => $bre,
-            display => $display,
-            attributes => $attributes,
-            copy_counts => $copy_counts,
-            hold_count => $hold_count
-        });
+        $client->respond($response);
     }
 
     return undef;
 }
 
-sub get_one_catalog_record {
+sub get_one_metarecord_summary {
+    my ($e, $rec_id) = @_;
+
+    my $meta = $e->retrieve_metabib_metarecord($rec_id) or return {};
+    my $maps = $e->search_metabib_metarecord_source_map({metarecord => $rec_id});
+
+    my $bre_id = $meta->master_record; 
+
+    my $response = get_one_record_summary($e, $bre_id);
+
+    $response->{metabib_id} = $rec_id;
+    $response->{metabib_records} = [map {$_->source} @$maps];
+
+    # augment mraf
+
+}
+
+sub get_one_record_summary {
     my ($e, $rec_id) = @_;
 
     my $bre = $e->retrieve_biblio_record_entry([$rec_id, {
@@ -2803,7 +2842,7 @@ sub get_one_catalog_record {
         flesh_fields => {
             bre => [qw/compressed_display_entries mattrs creator editor/]
         }
-    }]) or return ();
+    }]) or return {};
 
     my $display = {};
     $display->{$_->name} = OpenSRF::Utils::JSON->JSON2perl($_->value)
@@ -2823,7 +2862,12 @@ sub get_one_catalog_record {
     $bre->clear_mattrs;
     $bre->clear_compressed_display_entries;
 
-    return ($bre, $display, $attributes);
+    return {
+        id => $rec_id,
+        record => $bre,
+        display => $display,
+        attributes => $attributes
+    };
 }