LP#1779158 Ang vandelay import create queue, process spool
authorBill Erickson <berickxx@gmail.com>
Mon, 2 Jul 2018 20:38:44 +0000 (16:38 -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/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/vandelay.service.ts

index ca70ad2..9bfc50d 100644 (file)
   <div class="row">
     <div class="col-lg-6 offset-lg-3">
       <button class="btn btn-success btn-lg btn-block font-weight-bold"
-        (click)="upload()" i18n>Upload</button>
+        [disabled]="isUploading" (click)="upload()" i18n>Upload</button>
     </div>
   </div>
   <!-- hide instead of *ngIf so ViewChild can find the progress bars -->
       <eg-progress-inline #uploadProgress></eg-progress-inline>
     </div>
   </div>
+  <div class="row" [hidden]="!showProgress">
+    <div class="col-lg-3">
+      <label i18n>Enqueue Progress</label>
+    </div>
+    <div class="col-lg-3">
+      <eg-progress-inline #enqueueProgress></eg-progress-inline>
+    </div>
+  </div>
+  <div class="row" [hidden]="!showProgress">
+    <div class="col-lg-3">
+      <label i18n>Import Progress</label>
+    </div>
+    <div class="col-lg-3">
+      <eg-progress-inline #importProgress></eg-progress-inline>
+    </div>
+  </div>
 </div>
 
 
index ee49b5f..8f933ef 100644 (file)
@@ -1,6 +1,10 @@
 import {Component, OnInit, AfterViewInit, Input, ViewChild} from '@angular/core';
+import {tap} from 'rxjs/operators/tap';
 import {IdlObject} from '@eg/core/idl.service';
+import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
 import {OrgService} from '@eg/core/org.service';
+import {AuthService} from '@eg/core/auth.service';
 import {ToastService} from '@eg/share/toast/toast.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 import {VandelayService} from './vandelay.service';
@@ -37,15 +41,30 @@ export class ImportComponent implements OnInit, AfterViewInit {
     minQualityRatio: number;
     autoOverlayAcqCopies: boolean;
 
+    // True after the first upload, then remains true.
     showProgress: boolean;
 
+    // Upload in progress.
+    isUploading: boolean;
+
+    // Upload / processsing session key
+    // Generated by the server
+    sessionKey: string;
+
     @ViewChild('fileSelector') private fileSelector;
     @ViewChild('uploadProgress') 
         private uploadProgress: ProgressInlineComponent;
+    @ViewChild('enqueueProgress') 
+        private enqueueProgress: ProgressInlineComponent;
+    @ViewChild('importProgress') 
+        private importProgress: ProgressInlineComponent;
 
     constructor(
         private http: HttpClient,
         private toast: ToastService,
+        private evt: EventService,
+        private net: NetService,
+        private auth: AuthService,
         private org: OrgService,
         private vandelay: VandelayService
     ) {
@@ -125,27 +144,90 @@ export class ImportComponent implements OnInit, AfterViewInit {
        this.selectedFile = $event.target.files[0]; 
     }
 
+    // Required form data varies depending on context.
+    confirmNeededData(): boolean {
+        if (!this.selectedQueue) {
+            return false;
+        }
+        return true;
+    }
+
+    // 1. create queue if necessary
+    // 2. upload MARC file
+    // 3. Enqueue MARC records
+    // 4. Import records
     upload() {
+        if (!this.confirmNeededData()) { return; }
+
+        this.sessionKey = null;
         this.showProgress = true;
+        this.isUploading = true;
         this.uploadProgress.reset();
+        this.enqueueProgress.update({value: 0, max: 1});
+        this.importProgress.update({value: 0, max: 1});
+
+        let useQueueId; // find or create
+
+        this.resolveQueue()
+        .then(
+            queueId => {
+                useQueueId = queueId;
+                return this.uploadFile();
+            },
+            err => {
+                this.isUploading = false;
+            }
+        ).then(
+            ok => this.processSpool(useQueueId),
+            err => {
+                this.isUploading = false;
+            }
+        );
+    }
+
+    // Extract selected queue ID or create a new queue when requested.
+    resolveQueue(): Promise<number> {
 
+        if (this.selectedQueue.freetext) {
+            // Free text queue selector means create a new entry.
+            // TODO: first check for name dupes
+
+            return this.vandelay.createQueue(
+                this.selectedQueue.label,
+                this.recordType,
+                this.selectedHoldingsProfile,
+                this.selectedMatchSet,
+                this.selectedBucket
+            );
+
+        } else {
+            return Promise.resolve(this.selectedQueue.id);
+        }
+    }
+
+    uploadFile(): Promise<any> {
         const formData: FormData = new FormData();
 
-        formData.append(
-            'marc_upload', this.selectedFile, this.selectedFile.name);
-        // TODO: fill in other fields
+        formData.append('marc_upload', 
+            this.selectedFile, this.selectedFile.name);
+
+        if (this.selectedBibSource) {
+            formData.append('bib_source', ''+this.selectedBibSource);
+        }
 
         const req = new HttpRequest('POST', VAND_UPLOAD_URL, formData, 
             {reportProgress: true, responseType: 'text'});
 
-        this.http.request(req).subscribe(
+        return this.http.request(req).pipe(tap(
             evt => {
                 if (evt.type === HttpEventType.UploadProgress) {
                     this.uploadProgress.update(
                         {value: evt.loaded, max: evt.total});
 
                 } else if (evt instanceof HttpResponse) {
-                    console.log('file uploaded OK');
+                    this.sessionKey = evt.body as string;
+                    console.log(
+                        'Vandelay file uploaded OK with key '+this.sessionKey);
                 }
             },
 
@@ -154,8 +236,34 @@ export class ImportComponent implements OnInit, AfterViewInit {
                 console.error(err);
                 this.toast.danger(err.error.error);
             }
-        );
+        )).toPromise();
+    }
 
+    //processSpool(key, queueId, type, onload) {
+    processSpool(queueId: number):  Promise<any> {
+        const rtype = this.recordType === 'bib' ? 'bib' : 'authority';
+        const method = `open-ils.vandelay.${rtype}.process_spool`;
+
+        return this.net.request(
+            'open-ils.vandelay', method, 
+            this.auth.token(), this.sessionKey, queueId
+        ).pipe(tap(
+            resp => {
+                const e = this.evt.parse(resp);
+                if (e) {
+                    console.log(e);
+                    return;
+                }
+    
+                // the enqeueu process currently only returns the 
+                // number of items processed.
+                this.enqueueProgress.update({max: null, value: resp});
+            },
+            err => {},
+            () => this.enqueueProgress.update({max: 1, value: 1})
+
+        )).toPromise();
     }
+
 }
 
index caa7ca3..99cc673 100644 (file)
@@ -1,6 +1,7 @@
 import {Injectable, EventEmitter} from '@angular/core';
 import {Observable} from 'rxjs/Observable';
 import {tap} from 'rxjs/operators/tap';
+import {map} from 'rxjs/operators/map';
 import {IdlService, IdlObject} from '@eg/core/idl.service';
 import {OrgService} from '@eg/core/org.service';
 import {NetService} from '@eg/core/net.service';
@@ -180,5 +181,36 @@ export class VandelayService {
         });
     }
 
+
+    // Create a queue and return the ID of the new queue via promise.
+    createQueue(
+        queueName: string, 
+        recordType: string, 
+        importDefId: number, 
+        matchSet: number, 
+        matchBucket: number): Promise<number> {
+
+        const name = recordType === 'bib' ? 'bib' : 'authority';
+        const method = `open-ils.vandelay.${name}_queue.create`;
+
+        let qType = name;
+        if (recordType.match(/acq/)) {
+            let qType = 'acq';
+        }
+
+        return this.net.request(
+            'open-ils.vandelay', method, 
+            this.auth.token(), queueName, null, qType, 
+            matchSet, importDefId, matchBucket
+        ).pipe(map(queue => {
+            const e = this.evt.parse(queue);
+            if (e) { 
+                console.error(e); 
+                return null;
+            }
+            return queue.id();
+        })).toPromise();
+    }
+
 }