LP#1942220: teach LI title sort to ignore common articles
authorGalen Charlton <gmc@equinoxOLI.org>
Tue, 7 Dec 2021 17:41:18 +0000 (12:41 -0500)
committerGalen Charlton <gmc@equinoxOLI.org>
Tue, 7 Dec 2021 17:41:18 +0000 (12:41 -0500)
Also move a couple convenience methods related to LI sorting
to LineitemService

Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/acq/lineitem/lineitem-list.component.ts
Open-ILS/src/eg2/src/app/staff/acq/lineitem/lineitem.service.ts
Open-ILS/src/eg2/src/app/staff/acq/po/print.component.ts

index 90c63e4..3a0bc03 100644 (file)
@@ -257,7 +257,7 @@ export class LineitemListComponent implements OnInit {
         this.lineitemIds = [];
 
         const searchTerms = {};
-        const opts = { id_list: true, limit: 10000 };
+        const opts = { limit: 10000 };
 
         if (this.picklistId) {
             Object.assign(searchTerms, { jub: [ { picklist: this.picklistId } ] });
@@ -276,6 +276,21 @@ export class LineitemListComponent implements OnInit {
         }
         Object.assign(opts, SORT_ORDER_MAP[this.sortOrder]);
 
+        let _doingTitleSort = false;
+        if (this.sortOrder === 'title_asc' ||
+            this.sortOrder === 'title_desc') {
+            // if we're going to sort by title, we'll need
+            // to actually fetch LI attributes so that we can
+            // do a client-side sorting pass that ignores
+            // articles
+            _doingTitleSort = true;
+            opts['flesh_attrs'] = true;
+        } else {
+            // not doing a title sort, so we can rely on
+            // the server-side sort as is
+            opts['id_list'] = true;
+        }
+
         return this.net.request(
             'open-ils.acq',
             'open-ils.acq.lineitem.unified_search.atomic',
@@ -285,7 +300,26 @@ export class LineitemListComponent implements OnInit {
             null,
             opts
         ).toPromise().then(resp => {
-            this.lineitemIds = resp.map(i => Number(i));
+            if (_doingTitleSort) {
+                const sortOrder = this.sortOrder;
+                const liService = this.liService;
+                function _compareLIs(a, b) {
+                    const direction = sortOrder.match(/_asc$/) ? 'asc' : 'desc';
+                    const field = sortOrder.replace(/_asc|_desc$/, '');
+
+                    const a_val = liService.getLISortKey(a, field);
+                    const b_val = liService.getLISortKey(b, field);
+
+                    if (direction === 'asc') {
+                        return  liService.nullableCompare(a_val, b_val);
+                    } else {
+                        return -liService.nullableCompare(a_val, b_val);
+                    }
+                }
+                this.lineitemIds = resp.sort(_compareLIs).map(l => Number(l.id()));
+            } else {
+                this.lineitemIds = resp.map(i => Number(i));
+            }
             this.pager.resultCount = resp.length;
         });
     }
index fd48a56..315a116 100644 (file)
@@ -11,6 +11,11 @@ import {ItemLocationService} from '@eg/share/item-location-select/item-location-
 const COPY_ORDER_DISPOSITIONS:
     'canceled' | 'delayed' | 'received' | 'on-order' | 'pre-order' = null;
 export type COPY_ORDER_DISPOSITION = typeof COPY_ORDER_DISPOSITIONS;
+const ORDER_IDENT_ATTRS = [
+    'isbn',
+    'issn',
+    'upc'
+];
 
 export interface BatchLineitemStruct {
     id: number;
@@ -372,5 +377,42 @@ export class LineitemService {
             return 'on-order';
         } else { return 'pre-order'; }
     }
+
+    // convenience function for sorting values
+    nullableCompare(a_val: any, b_val: any): number {
+        return   a_val === b_val ?  0 :
+                 a_val === null  ?  1 :
+                 b_val === null  ? -1 :
+                 a_val > b_val   ?  1 :
+                 a_val < b_val   ? -1 : 0;
+    }
+
+    // Given a line item, get its sort key
+    getLISortKey(li: IdlObject, field: string): any {
+        let vals = [];
+        switch (field) {
+            case 'li_id':
+                return li.id();
+                break;
+            case 'title':
+                vals = li.attributes().filter(x => x.attr_name() === 'title');
+                return vals.length ? vals[0].attr_value().replace(/^(a|an|the|el|la) /i, '') : null;
+                break;
+            case 'author':
+                vals = li.attributes().filter(x => x.attr_name() === 'author');
+                return vals.length ? vals[0].attr_value() : null;
+                break;
+            case 'publisher':
+                vals = li.attributes().filter(x => x.attr_name() === 'publisher');
+                return vals.length ? vals[0].attr_value() : null;
+                break;
+            case 'order_ident':
+                vals = li.attributes().filter(x => ORDER_IDENT_ATTRS.includes(x.attr_name()));
+                return vals.length ? vals[0].attr_value() : null;
+                break;
+            default:
+                return li.id();
+        }
+    }
 }
 
index bbcfbd8..d0f0ea3 100644 (file)
@@ -12,6 +12,7 @@ import {OrgService} from '@eg/core/org.service';
 import {PrintService} from '@eg/share/print/print.service';
 import {BroadcastService} from '@eg/share/util/broadcast.service';
 import {PoService} from './po.service';
+import {LineitemService} from '../lineitem/lineitem.service';
 
 const DEFAULT_SORT_ORDER = 'li_id_asc';
 const SORT_ORDERS = [
@@ -53,6 +54,7 @@ export class PrintComponent implements OnInit, AfterViewInit {
         private store: ServerStoreService,
         private pcrud: PcrudService,
         private poService: PoService,
+        private liService: LineitemService,
         private broadcaster: BroadcastService,
         private printer: PrintService) {
     }
@@ -94,56 +96,22 @@ export class PrintComponent implements OnInit, AfterViewInit {
         .then(_ => this.initDone = true);
     }
 
-    _getLISortKey(li: IdlObject, field: string): any {
-        let vals = [];
-        switch (field) {
-            case 'li_id':
-                return li.id();
-                break;
-            case 'title':
-                vals = li.attributes().filter(x => x.attr_name() === 'title');
-                return vals.length ? vals[0].attr_value() : null;
-                break;
-            case 'author':
-                vals = li.attributes().filter(x => x.attr_name() === 'author');
-                return vals.length ? vals[0].attr_value() : null;
-                break;
-            case 'publisher':
-                vals = li.attributes().filter(x => x.attr_name() === 'publisher');
-                return vals.length ? vals[0].attr_value() : null;
-                break;
-            case 'order_ident':
-                vals = li.attributes().filter(x => ORDER_IDENT_ATTRS.includes(x.attr_name()));
-                return vals.length ? vals[0].attr_value() : null;
-                break;
-            default:
-                return li.id();
-        }
-    }
-
     sortLineItems(): Promise<any> {
         return this.store.getItem('acq.lineitem.sort_order').then(sortOrder => {
             if (!sortOrder || !SORT_ORDERS.includes(sortOrder)) {
                 sortOrder = DEFAULT_SORT_ORDER;
             }
-            const _getLISortKey = this._getLISortKey;
-            function nullableCompare(a_val: any, b_val: any): number {
-                return   a_val === b_val ?  0 :
-                         a_val === null  ?  1 :
-                         b_val === null  ? -1 :
-                         a_val > b_val   ?  1 :
-                         a_val < b_val   ? -1 : 0;
-            }
+            const liService = this.liService;
             function _compareLIs(a, b) {
                 const direction = sortOrder.match(/_asc$/) ? 'asc' : 'desc';
                 const field = sortOrder.replace(/_asc|_desc$/, '');
-                const a_val = _getLISortKey(a, field);
-                const b_val = _getLISortKey(b, field);
+                const a_val = liService.getLISortKey(a, field);
+                const b_val = liService.getLISortKey(b, field);
 
                 if (direction === 'asc') {
-                    return  nullableCompare(a_val, b_val);
+                    return  liService.nullableCompare(a_val, b_val);
                 } else {
-                    return -nullableCompare(a_val, b_val);
+                    return -liService.nullableCompare(a_val, b_val);
                 }
             }
             this.po.lineitems().sort(_compareLIs);