LP#1779158 Match overlay target support
authorBill Erickson <berickxx@gmail.com>
Wed, 11 Jul 2018 22:01:32 +0000 (18:01 -0400)
committerBill Erickson <berickxx@gmail.com>
Thu, 11 Oct 2018 18:56:30 +0000 (14:56 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/grid/grid.ts
Open-ILS/src/eg2/src/app/staff/cat/vandelay/import.component.html
Open-ILS/src/eg2/src/app/staff/cat/vandelay/import.component.ts
Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.html
Open-ILS/src/eg2/src/app/staff/cat/vandelay/queue.component.ts
Open-ILS/src/eg2/src/app/staff/cat/vandelay/queued-record-matches.component.html
Open-ILS/src/eg2/src/app/staff/cat/vandelay/queued-record-matches.component.ts
Open-ILS/src/eg2/src/app/staff/cat/vandelay/queued-record.component.html
Open-ILS/src/eg2/src/app/staff/cat/vandelay/vandelay.service.ts

index e04940d..5493bc0 100644 (file)
@@ -396,7 +396,7 @@ export class GridRowSelector {
 
 export interface GridRowFlairEntry {
     icon: string;   // name of material icon
-    title: string;  // tooltip string
+    title?: string;  // tooltip string
 }
 
 export class GridColumnPersistConf {
index 079fe59..5e20104 100644 (file)
@@ -1,5 +1,5 @@
 <div class="row mb-3" *ngIf="importSelection()">
-  <div class="col-lg-2">
+  <div class="col-lg-2" *ngIf="selectedQueue">
     <button class="btn btn-info label-with-material-icon"
       routerLink="/staff/cat/vandelay/queue/{{recordType}}/{{selectedQueue.id}}">
       <span class="material-icons">arrow_back</span>
index ad25acb..035379c 100644 (file)
@@ -1,4 +1,4 @@
-import {Component, OnInit, AfterViewInit, Input, ViewChild} from '@angular/core';
+import {Component, OnInit, AfterViewInit, Input, ViewChild, OnDestroy} from '@angular/core';
 import {tap} from 'rxjs/operators/tap';
 import {IdlObject} from '@eg/core/idl.service';
 import {NetService} from '@eg/core/net.service';
@@ -15,7 +15,7 @@ import {ProgressInlineComponent} from '@eg/share/dialog/progress-inline.componen
 import {Subject} from 'rxjs/Subject';
 
 interface ImportOptions {
-    overlay_map?: any;
+    overlay_map?: {[qrId: number]: /* breId */ number};
     import_no_match?: boolean;
     auto_overlay_exact?: boolean;
     auto_overlay_best_match?: boolean;
@@ -30,7 +30,7 @@ interface ImportOptions {
   templateUrl: 'import.component.html',
   styleUrls: ['import.component.css']
 })
-export class ImportComponent implements OnInit, AfterViewInit {
+export class ImportComponent implements OnInit, AfterViewInit, OnDestroy {
 
     recordType: string;
     selectedQueue: ComboboxEntry; // freetext enabled
@@ -95,21 +95,26 @@ export class ImportComponent implements OnInit, AfterViewInit {
         this.selectedBibSource = 1; // default to system local
         this.recordType = 'bib';
 
-        if (!this.vandelay.importSelection) {
-            return;
-        }
+        if (this.vandelay.importSelection) {
+
+            if (!this.vandelay.importSelection.queue) {
+                // Incomplete import selection, clear it.
+                this.vandelay.importSelection = null;
+                return;
+            }
 
-        const queue = this.vandelay.importSelection.queue;
-        this.recordType = queue.queue_type();
-        this.selectedMatchSet = queue.match_set();
+            const queue = this.vandelay.importSelection.queue;
+            this.recordType = queue.queue_type();
+            this.selectedMatchSet = queue.match_set();
 
-        // This will be propagated to selectedQueue as a combobox
-        // entry via the combobox
-        this.startQueueId = queue.id();
+            // This will be propagated to selectedQueue as a combobox
+            // entry via the combobox
+            this.startQueueId = queue.id();
 
-        if (this.recordType === 'bib') {
-            this.selectedBucket = queue.match_bucket();
-            this.selectedHoldingsProfile = queue.item_attr_def();
+            if (this.recordType === 'bib') {
+                this.selectedBucket = queue.match_bucket();
+                this.selectedHoldingsProfile = queue.item_attr_def();
+            }
         }
     }
 
@@ -119,6 +124,14 @@ export class ImportComponent implements OnInit, AfterViewInit {
         this.loadStartupData();
     }
 
+    ngOnDestroy() {
+        // If we successfully completed the most recent 
+        // upload/import assume the importSelection can be cleared.
+        if (this.uploadComplete) {
+            this.clearSelection();
+        }
+    }
+
     importSelection(): VandelayImportSelection {
         return this.vandelay.importSelection;
     }
@@ -414,7 +427,6 @@ export class ImportComponent implements OnInit, AfterViewInit {
     compileImportOptions(): ImportOptions {
 
         const options: ImportOptions = {
-            overlay_map: null, // TODO
             import_no_match: this.importNonMatching,
             auto_overlay_exact: this.mergeOnExact,
             auto_overlay_best_match: this.mergeOnBestMatch,
@@ -425,6 +437,10 @@ export class ImportComponent implements OnInit, AfterViewInit {
             strip_field_groups: null // TODO
         };
 
+        if (this.vandelay.importSelection) {
+            options.overlay_map = this.vandelay.importSelection.overlayMap;
+        }
+
         return options;
     }
 
index 5414c85..dab90bc 100644 (file)
 </ng-container>
 
 <ng-template #matchesTmpl let-row="row">
-  <a i18n 
+  <a i18n [ngClass]="{'font-weight-bold': hasOverlayTarget(row.id)}"
     routerLink="/staff/cat/vandelay/queue/{{queueType}}/{{queueId}}/record/{{row.id}}/matches">
       ({{row.matches.length}})
+      {{hasOverlayTarget(row.id) ? '*' : ''}}
   </a>
 </ng-template>
 
@@ -120,13 +121,13 @@ because there are a lot of them.
   (onRowActivate)="openRecord($event)"
   hideFields="language,pagination,price,rec_identifier,eg_tcn_source,eg_identifier,item_barcode,zsource">
 
-  <eg-grid-toolbar-checkbox i18n-label label="Limit to Records With Matches"
+  <eg-grid-toolbar-checkbox i18n-label label="Records With Matches"
     [onChange]="limitToMatches"></eg-grid-toolbar-checkbox>
 
-  <eg-grid-toolbar-checkbox i18n-label label="Limit to Non-Imported Records"
+  <eg-grid-toolbar-checkbox i18n-label label="Non-Imported Records"
     [onChange]="limitToNonImported"></eg-grid-toolbar-checkbox>
 
-  <eg-grid-toolbar-checkbox i18n-label label="Limit to Records with Import Errors"
+  <eg-grid-toolbar-checkbox i18n-label label="Records with Import Errors"
     [onChange]="limitToImportErrors"></eg-grid-toolbar-checkbox>
 
   <eg-grid-column name="id" [index]="true" 
index 07fb4a5..4d34b84 100644 (file)
@@ -4,7 +4,6 @@ import 'rxjs/add/observable/of';
 import {map} from 'rxjs/operators/map';
 import {filter} from 'rxjs/operators/filter';
 import {Router, ActivatedRoute, ParamMap} from '@angular/router';              
-import {HttpClient} from '@angular/common/http';
 import {Pager} from '@eg/share/util/pager';                                    
 import {IdlObject} from '@eg/core/idl.service';
 import {EventService} from '@eg/core/event.service';
@@ -48,7 +47,6 @@ export class QueueComponent implements OnInit, AfterViewInit {
     constructor(
         private router: Router,
         private route: ActivatedRoute,
-        private http: HttpClient,
         private evt: EventService,
         private net: NetService,
         private auth: AuthService,
@@ -81,8 +79,6 @@ export class QueueComponent implements OnInit, AfterViewInit {
     }
 
     ngOnInit() {
-        // Clear previous selection
-        this.vandelay.importSelection = null;
     }
 
     ngAfterViewInit() {
@@ -176,21 +172,34 @@ export class QueueComponent implements OnInit, AfterViewInit {
         }));
     }
 
+    findOrCreateImportSelection() {
+        let selection = this.vandelay.importSelection;
+        if (!selection) {
+            selection = new VandelayImportSelection();
+            this.vandelay.importSelection = selection;
+        }
+        selection.queue = this.queueSummary.queue;
+        return selection;
+    }
+
+    hasOverlayTarget(rid: number): boolean {
+        return this.vandelay.importSelection &&
+            Boolean(this.vandelay.importSelection.overlayMap[rid]);
+    }
+
     importSelected() {
         const rows = this.queueGrid.context.getSelectedRows();
-        if (rows.length === 0) { return; }
-        const selection = new VandelayImportSelection();
-        selection.recordIds = rows.map(row => row.id);
-        selection.queue = this.queueSummary.queue;
-        this.vandelay.importSelection = selection;
-        this.router.navigate(['/staff/cat/vandelay/import']);
+        if (rows.length) {
+            const selection = this.findOrCreateImportSelection();
+            selection.recordIds = rows.map(row => row.id);
+            console.log('importing: ', this.vandelay.importSelection);
+            this.router.navigate(['/staff/cat/vandelay/import']);
+        }
     }
 
     importAll() {
-        const selection = new VandelayImportSelection();
-        selection.queue = this.queueSummary.queue;
+        const selection = this.findOrCreateImportSelection();
         selection.importQueue = true;
-        this.vandelay.importSelection = selection;
         this.router.navigate(['/staff/cat/vandelay/import']);
     }
 
index a543bc5..b6215d4 100644 (file)
@@ -1,12 +1,18 @@
 
 <ng-template #bibIdTemplate let-row="row">
   <a routerLink="/staff/catalog/record/{{row.eg_record}}/marc_view" i18n>
-    View In Catalog
+    {{row.eg_record}}
   </a>
 </ng-template>
 
 <ng-container *ngIf="queueType == 'bib'">
-  <eg-grid #bibGrid [dataSource]="bibDataSource" [disableMultiSelect]="true">
+  <eg-grid #bibGrid [dataSource]="bibDataSource" 
+    [rowFlairIsEnabled]="true"                                                   
+    [rowFlairCallback]="matchRowCallback"
+    [disableMultiSelect]="true">
+    <eg-grid-toolbar-action i18n-label label="Mark As Overlay Target"
+      [action]="markOverlayTarget">
+    </eg-grid-toolbar-action>
     <eg-grid-column name="id" [index]="true" [hidden]="true" 
       i18n-label label="Match ID">
     </eg-grid-column>
index c20da4d..32e7917 100644 (file)
@@ -5,14 +5,14 @@ import 'rxjs/add/observable/of';
 import {map} from 'rxjs/operators/map';
 import {Pager} from '@eg/share/util/pager';
 import {GridComponent} from '@eg/share/grid/grid.component';
-import {GridDataSource, GridColumn} from '@eg/share/grid/grid';
+import {GridDataSource, GridColumn, GridRowFlairEntry} from '@eg/share/grid/grid';
 import {IdlObject} from '@eg/core/idl.service';
 import {EventService} from '@eg/core/event.service';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
-import {VandelayService} from './vandelay.service';
+import {VandelayService, VandelayImportSelection} from './vandelay.service';
 
 @Component({
   selector: 'eg-queued-record-matches',
@@ -28,6 +28,8 @@ export class QueuedRecordMatchesComponent implements OnInit {
     queuedRecord: IdlObject;
     bibDataSource: GridDataSource;
     authDataSource: GridDataSource;
+    markOverlayTarget: (rows: any[]) => any;
+    matchRowCallback: (row: any) => GridRowFlairEntry;
 
     constructor(
         private router: Router,
@@ -50,6 +52,37 @@ export class QueuedRecordMatchesComponent implements OnInit {
         this.authDataSource.getRows = (pager: Pager) => {
         }
         */
+
+        this.markOverlayTarget = (rows: any[]) => {
+
+            // Start a new import selection object if needed.
+            // This will be adopted by the queue UI before import.
+            let selection = this.vandelay.importSelection;
+            if (!selection) {
+                selection = new VandelayImportSelection();
+                this.vandelay.importSelection = selection;
+            }
+
+            // Grid is configured for single-row-select.
+            const match = rows[0];
+            selection.overlayMap[this.recordId] = match.eg_record;
+        }
+
+        this.matchRowCallback = (row: any) => {
+            const selection = this.vandelay.importSelection;
+            if (selection) {
+                const target = selection.overlayMap[this.recordId];
+                if (target === row.eg_record) {
+                    return {icon: 'check_circle'};
+                }
+            }
+            return null;
+        }
+    }
+
+    hasOverlayTarget(rid: number): boolean {
+        return this.vandelay.importSelection &&
+            Boolean(this.vandelay.importSelection.overlayMap[rid]);
     }
 
     ngOnInit() {}
index 47173ce..a9a0d20 100644 (file)
@@ -10,7 +10,7 @@
 </div>
 
 <ngb-tabset #recordTabs [activeId]="recordTab" (tabChange)="onTabChange($event)">
-       <ngb-tab title="Queued Record HTML" i18n-title id="marc">
+       <ngb-tab title="Queued Record MARC" i18n-title id="marc">
                <ng-template ngbTabContent>
       <eg-marc-html [recordId]="recordId" [recordType]="'vandelay-'+queueType">
       </eg-marc-html>
index 840b47e..178b2f0 100644 (file)
@@ -20,6 +20,12 @@ export class VandelayImportSelection {
     recordIds: number[];
     queue: IdlObject;
     importQueue: boolean; // import the whole queue
+    overlayMap: {[qrId: number]: /* breId */ number};
+
+    constructor() {
+       this.recordIds = [];
+       this.overlayMap = {};
+    }
 }
 
 @Injectable()
@@ -270,6 +276,7 @@ export class VandelayService {
             method, this.auth.token(), queueId, options);
     }
 
+    // Download a queue as a MARC file.
     exportQueue(queue: IdlObject, nonImported?: boolean) {
 
         const etype = queue.queue_type().match(/auth/) ? 'auth' : 'bib';