LPXXX MARC Batch update Angular port
authorBill Erickson <berickxx@gmail.com>
Thu, 21 May 2020 19:29:07 +0000 (15:29 -0400)
committerBill Erickson <berickxx@gmail.com>
Thu, 21 May 2020 19:29:07 +0000 (15:29 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/cat/marcbatch/marcbatch.component.html
Open-ILS/src/eg2/src/app/staff/cat/marcbatch/marcbatch.component.ts
Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts

index 68c2bde..82a91f9 100644 (file)
@@ -1,7 +1,7 @@
 <eg-staff-banner bannerText="MARC Batch Edit" i18n-bannerText></eg-staff-banner>
 
 <div class="row">
-  <div class="col-lg-7">
+  <div class="col-lg-7 common-form striped-odd">
     <ng-container *ngFor="let rule of templateRules; let idx = index">
       <hr *ngIf="idx > 0"/>
       <div class="row mb-2">
       <div class="row mb-2">
         <div class="col-lg-3" i18n>Action (Rule Type)</div>
         <div class="col-lg-3">
-          <select class="form-control" [(ngModel)]="rule.ruleType">
-            <option value='replace' i18n>Replace</option>
-            <option value='add' i18n>Add</option>
-            <option value='delete' i18n>Delete</option>
+          <select class="form-control" [(ngModel)]="rule.ruleType"
+            (change)="rulesetToRecord()">
+            <option value='r' i18n>Replace</option>
+            <option value='a' i18n>Add</option>
+            <option value='d' i18n>Delete</option>
           </select>
         </div>
         <div class="col-lg-6" i18n>How to change the existing record.</div>
@@ -23,7 +24,8 @@
       <div class="row mb-2">
         <div class="col-lg-3" i18n>MARC Tag</div>
         <div class="col-lg-3">
-          <input type="text" class="form-control" [(ngModel)]="rule.marcTag"/>
+          <input type="text" class="form-control"
+            (change)="rulesetToRecord(true)" [(ngModel)]="rule.marcTag"/>
         </div>
         <div class="col-lg-6" i18n>
           Three characters, no spaces, no indicators, etc. eg: 245
       <div class="row mb-2">
         <div class="col-lg-3" i18n>Subfields (optional)</div>
         <div class="col-lg-3">
-          <input type="text" class="form-control" [(ngModel)]="rule.marcSubfields"/>
+          <input type="text" class="form-control" 
+            (change)="rulesetToRecord(true)" [(ngModel)]="rule.marcSubfields"/>
         </div>
         <div class="col-lg-6" i18n>No spaces, no delimiters, eg: abcnp</div>
       </div>
       <div class="row mb-2">
         <div class="col-lg-3" i18n>MARC Data</div>
         <div class="col-lg-3">
-          <input type="text" class="form-control" [(ngModel)]="rule.marcData"/>
+          <input type="text" class="form-control" 
+            (change)="rulesetToRecord()" [(ngModel)]="rule.marcData"/>
         </div>
         <div class="col-lg-6" i18n>
           MARC-Breaker formatted data with indicators and subfield delimiters, 
       </div>
       <div class="row mb-2">
         <div class="col-lg-3" i18n>Subfield</div>
-        <div class="col-lg-3"></div>
-        <div class="col-lg-6" i18n></div>
+        <div class="col-lg-3">
+          <input type="text" class="form-control" 
+            (change)="rulesetToRecord()" [(ngModel)]="rule.advSubfield"/>
+        </div>
+        <div class="col-lg-6" i18n>
+          A single subfield code, no delimiters, eg: a
+        </div>
       </div>
       <div class="row mb-2">
         <div class="col-lg-3" i18n>Expression</div>
-        <div class="col-lg-3"></div>
-        <div class="col-lg-6" i18n></div>
+        <div class="col-lg-3">
+          <input type="text" class="form-control" 
+            (change)="rulesetToRecord()" [(ngModel)]="rule.advRegex"/>
+        </div>
+        <div class="col-lg-6" i18n>
+          See the 
+          <a href="https://perldoc.perl.org/perlre.html#Regular-Expressions">
+            Perl documentation
+          </a> for an explanation of Regular Expressions.
+        </div>
+      </div>
+      <div class="row mb-2">
+        <div class="col-lg-12 d-flex justify-content-end">
+          <button class="btn btn-outline-danger label-with-material-icon"
+            (click)="removeRule(idx)" i18n>
+            <span>Remove this Template Rule</span>
+            <span class="material-icons ml-2">delete</span>
+          </button>
+        </div>
       </div>
     </ng-container>
+    <div class="row mb-2">
+      <div class="col-lg-6">
+        <button class="btn btn-outline-dark label-with-material-icon" 
+          (click)="addRule()">
+          <span i18n>Add a New Merge Rule</span>
+          <span class="material-icons ml-2">arrow_downward</span>
+        </button>
+      </div>
+    </div>
   </div>
   <div class="col-lg-5">
     <div class="row pb-2 pt-2 border">
       <div class="col-lg-12">
         <div i18n>Update Template Preview</div>
         <div>
-          <textarea class="form-control" [(ngModel)]="templateBreaker" disabled>
-          </textarea>
+          <textarea class="form-control" [ngModel]="breaker()" 
+            disabled rows="{{breakerRows()}}"></textarea>
         </div>
       </div>
     </div>
index b79cf10..44e132c 100644 (file)
@@ -5,9 +5,10 @@ import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PcrudService} from '@eg/core/pcrud.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {MarcRecord, MarcField} from '@eg/staff/share/marc-edit/marcrecord';
 
 interface TemplateRule {
-    ruleType: 'replace' | 'add' | 'delete';
+    ruleType: 'r' | 'a' | 'd';
     marcTag?: string;
     marcSubfields?: string;
     marcData?: string;
@@ -28,8 +29,8 @@ export class MarcBatchComponent implements OnInit, AfterViewInit {
     csvColumn = 0;
     selectedFile: File;
     xactPerRecord = false;
-    templateBreaker = '';
     templateRules: TemplateRule[] = [];
+    record: MarcRecord;
 
     constructor(
         private router: Router,
@@ -49,10 +50,89 @@ export class MarcBatchComponent implements OnInit, AfterViewInit {
     }
 
     load() {
-        this.templateRules = [{ruleType: 'replace'}];
+        this.addRule();
         this.getBuckets();
     }
 
+    rulesetToRecord(resetRuleData?: boolean) {
+        this.record = new MarcRecord();
+
+        this.templateRules.forEach(rule => {
+
+            let ruleText = rule.marcTag + (rule.marcSubfields || '');
+            if (rule.advSubfield) {
+                ruleText += `[${rule.advSubfield} ~ ${rule.advRegex}]`;
+            }
+
+            // Merge behavior is encoded in the 905 field.
+            const ruleTag = this.record.newField({
+                tag: '905',
+                ind1: ' ',
+                ind2: ' ',
+                subfields: [[rule.ruleType, ruleText, 0]]
+            });
+
+            this.record.insertOrderedFields(ruleTag);
+
+            if (rule.ruleType === 'd') { return; }
+
+            const dataRec = new MarcRecord();
+            if (resetRuleData) {
+
+                // Build a new value for the 'MARC Data' field based on
+                // changes to the selected tag or subfields.
+
+                const subfields = rule.marcSubfields ?
+                    rule.marcSubfields.split('').map((sf, idx) => [sf, '', idx])
+                    : [];
+
+                dataRec.appendFields(
+                    dataRec.newField({
+                        tag: rule.marcTag,
+                        ind1: ' ',
+                        ind2: ' ',
+                        subfields: subfields
+                    })
+                );
+
+                console.log(dataRec.toBreaker());
+                rule.marcData = dataRec.toBreaker().split(/\n/)[1];
+
+            } else {
+
+                // Absorb the breaker data already in the 'MARC Data' field
+                // so it can be added to the template record in progress.
+
+                dataRec.breakerText = rule.marcData;
+                dataRec.absorbBreakerChanges();
+            }
+
+            this.record.appendFields(dataRec.fields[0]);
+        });
+    }
+
+    breakerRows(): number {
+        if (this.record) {
+            const breaker = this.record.toBreaker();
+            if (breaker) {
+                return breaker.split(/\n/).length + 1;
+            }
+        }
+        return 3;
+    }
+
+    breaker(): string {
+        return this.record ? this.record.toBreaker() : '';
+    }
+
+    addRule() {
+        this.templateRules.push({ruleType: 'r'});
+    }
+
+    removeRule(idx: number) {
+        this.templateRules.splice(idx, 1);
+    }
+
     getBuckets(): Promise<any> {
         if (this.buckets) { return Promise.resolve(); }
 
index d49f4ef..4ba850b 100644 (file)
@@ -60,7 +60,7 @@ export class MarcRecord {
         this.record.fields = f;
     }
 
-    constructor(xml: string) {
+    constructor(xml?: string) {
         this.record = new MARC21.Record({marcxml: xml, delimiter: DELIMITER});
         this.breakerText = this.record.toBreaker();
         this.fixedFieldChange = new EventEmitter<string>();
@@ -113,6 +113,11 @@ export class MarcRecord {
         return this.record.field(spec, wantArray);
     }
 
+    appendFields(...newFields: MarcField[]) {
+        this.record.appendFields.apply(this.record, newFields);
+        this.stampFieldIds();
+    }
+
     insertFieldsBefore(field: MarcField, ...newFields: MarcField[]) {
         this.record.insertFieldsBefore.apply(
             this.record, [field].concat(newFields));