]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/share/marc-edit/tagtable.service.ts
3d2fc7370f63df67c3ba2f8b179a02b542da76c9
[Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / marc-edit / tagtable.service.ts
1 import {Injectable, EventEmitter} from '@angular/core';
2 import {Observable} from 'rxjs';
3 import {map, tap, distinct} from 'rxjs/operators';
4 import {StoreService} from '@eg/core/store.service';
5 import {IdlObject} from '@eg/core/idl.service';
6 import {AuthService} from '@eg/core/auth.service';
7 import {NetService} from '@eg/core/net.service';
8 import {PcrudService} from '@eg/core/pcrud.service';
9 import {EventService} from '@eg/core/event.service';
10 import {ContextMenuEntry} from '@eg/share/context-menu/context-menu.service';
11
12 interface TagTableSelector {
13     marcFormat?: string;
14     marcRecordType?: string;
15 }
16
17 const defaultTagTableSelector: TagTableSelector = {
18     marcFormat     : 'marc21',
19     marcRecordType : 'biblio'
20 };
21
22 @Injectable()
23 export class TagTableService {
24
25     // Current set of tags in list and map form.
26     tagMap: {[tag: string]: any} = {};
27     ffPosMap: {[rtype: string]: any[]} = {};
28     ffValueMap: {[rtype: string]: any} = {};
29     controlledBibTags: string[];
30
31     extractedValuesCache:
32         {[valueType: string]: {[which: string]: any}} = {};
33
34     constructor(
35         private store: StoreService,
36         private auth: AuthService,
37         private net: NetService,
38         private pcrud: PcrudService,
39         private evt: EventService
40     ) {
41
42         this.extractedValuesCache = {
43             fieldtags: {},
44             indicators: {},
45             sfcodes: {},
46             sfvalues: {},
47             ffvalues: {}
48         };
49     }
50
51     // Various data needs munging for display.  Cached the modified
52     // values since they are refernced repeatedly by the UI code.
53     fromCache(dataType: string, which?: string, which2?: string): ContextMenuEntry[] {
54         const part1 = this.extractedValuesCache[dataType][which];
55         if (which2) {
56             if (part1) {
57                 return part1[which2];
58             }
59         } else {
60             return part1;
61         }
62     }
63
64     toCache(dataType: string, which: string,
65         which2: string, values: ContextMenuEntry[]): ContextMenuEntry[] {
66         const base = this.extractedValuesCache[dataType];
67         const part1 = base[which];
68
69         if (which2) {
70             if (!base[which]) { base[which] = {}; }
71             base[which][which2] = values;
72         } else {
73             base[which] = values;
74         }
75
76         return values;
77     }
78
79     getFfPosTable(rtype: string): Promise<any> {
80         const storeKey = 'FFPosTable_' + rtype;
81
82         if (this.ffPosMap[rtype]) {
83             return Promise.resolve(this.ffPosMap[rtype]);
84         }
85
86         this.ffPosMap[rtype] = this.store.getLocalItem(storeKey);
87
88         if (this.ffPosMap[rtype]) {
89             return Promise.resolve(this.ffPosMap[rtype]);
90         }
91
92         return this.net.request(
93             'open-ils.fielder', 'open-ils.fielder.cmfpm.atomic',
94             {query: {tag: {'!=' : '006'}, rec_type: rtype}}
95
96         ).toPromise().then(table => {
97             this.store.setLocalItem(storeKey, table);
98             return this.ffPosMap[rtype] = table;
99         });
100     }
101
102     getFfValueTable(rtype: string): Promise<any> {
103
104         const storeKey = 'FFValueTable_' + rtype;
105
106         if (this.ffValueMap[rtype]) {
107             return Promise.resolve(this.ffValueMap[rtype]);
108         }
109
110         this.ffValueMap[rtype] = this.store.getLocalItem(storeKey);
111
112         if (this.ffValueMap[rtype]) {
113             return Promise.resolve(this.ffValueMap[rtype]);
114         }
115
116         return this.net.request(
117             'open-ils.cat',
118             'open-ils.cat.biblio.fixed_field_values.by_rec_type', rtype
119
120         ).toPromise().then(table => {
121             this.store.setLocalItem(storeKey, table);
122             return this.ffValueMap[rtype] = table;
123         });
124     }
125
126     loadTagTable(selector?: TagTableSelector): Promise<any> {
127
128         if (selector) {
129             if (!selector.marcFormat) {
130                 selector.marcFormat = defaultTagTableSelector.marcFormat;
131             }
132             if (!selector.marcRecordType) {
133                 selector.marcRecordType =
134                     defaultTagTableSelector.marcRecordType;
135             }
136         } else {
137             selector = defaultTagTableSelector;
138         }
139
140         const cacheKey =
141             `current_tag_table_${selector.marcFormat}_${selector.marcRecordType}`;
142
143         this.tagMap = this.store.getLocalItem(cacheKey);
144
145         if (this.tagMap) {
146             return Promise.resolve(this.tagMap);
147         }
148
149         return this.fetchTagTable(selector).then(_ => {
150             this.store.setLocalItem(cacheKey, this.tagMap);
151             return this.tagMap;
152         });
153     }
154
155     fetchTagTable(selector?: TagTableSelector): Promise<any> {
156         this.tagMap = [];
157         return this.net.request(
158             'open-ils.cat',
159             'open-ils.cat.tag_table.all.retrieve.local',
160             this.auth.token(), selector.marcFormat, selector.marcRecordType
161         ).pipe(tap(tagData => {
162             this.tagMap[tagData.tag] = tagData;
163         })).toPromise();
164     }
165
166     getSubfieldCodes(tag: string): ContextMenuEntry[] {
167         if (!tag || !this.tagMap[tag]) { return null; }
168
169         const cached = this.fromCache('sfcodes', tag);
170
171         const list = this.tagMap[tag].subfields.map(sf => ({
172             value: sf.code,
173             label: `${sf.code}: ${sf.description}`
174         }))
175         .sort((a, b) => a.label < b.label ? -1 : 1);
176
177         return this.toCache('sfcodes', tag, null, list);
178     }
179
180     getFieldTags(): ContextMenuEntry[] {
181
182         const cached = this.fromCache('fieldtags');
183         if (cached) { return cached; }
184
185         return Object.keys(this.tagMap)
186         .filter(tag => Boolean(this.tagMap[tag]))
187         .map(tag => ({
188             value: tag,
189             label: `${tag}: ${this.tagMap[tag].name}`
190         }))
191         .sort((a, b) => a.label < b.label ? -1 : 1);
192     }
193
194     getSubfieldValues(tag: string, sfCode: string): ContextMenuEntry[] {
195         if (!tag || !this.tagMap[tag]) { return []; }
196
197         const cached = this.fromCache('sfvalues', tag, sfCode);
198         if (cached) { return cached; }
199
200         const list: ContextMenuEntry[] = [];
201
202         this.tagMap[tag].subfields
203         .filter(sf =>
204             sf.code === sfCode && sf.hasOwnProperty('value_list'))
205         .forEach(sf => {
206             sf.value_list.forEach(value => {
207
208                 let label = value.description || value.code;
209                 const code = value.code || label;
210                 if (code !== label) { label = `${code}: ${label}`; }
211
212                 list.push({value: code, label: label});
213             });
214         });
215
216         return this.toCache('sfvalues', tag, sfCode, list);
217     }
218
219     getIndicatorValues(tag: string, which: 'ind1' | 'ind2'): ContextMenuEntry[] {
220         if (!tag || !this.tagMap[tag]) { return; }
221
222         const cached = this.fromCache('indicators', tag, which);
223         if (cached) { return cached; }
224
225         let values = this.tagMap[tag][which];
226         if (!values) { return; }
227
228         values = values.map(value => ({
229             value: value.code,
230             label: `${value.code}: ${value.description}`
231         }))
232         .sort((a, b) => a.label < b.label ? -1 : 1);
233
234         return this.toCache('indicators', tag, which, values);
235     }
236
237
238     getFfFieldMeta(fieldCode: string, recordType: string): Promise<IdlObject> {
239         return this.getFfPosTable(recordType).then(table => {
240
241             // Note the AngJS MARC editor stores the full POS table
242             // for all record types in every copy of the table, hence
243             // the seemingly extraneous check in recordType.
244             return table.filter(
245                 field =>
246                     field.fixed_field === fieldCode
247                  && field.rec_type === recordType
248             )[0];
249         });
250     }
251
252
253     // Assumes getFfPosTable and getFfValueTable have already been
254     // invoked for the request record type.
255     getFfValues(fieldCode: string, recordType: string): ContextMenuEntry[] {
256
257         const cached = this.fromCache('ffvalues', recordType, fieldCode);
258         if (cached) { return cached; }
259
260         let values = this.ffValueMap[recordType];
261
262         if (!values || !values[fieldCode]) { return null; }
263
264         // extract the canned set of possible values for our
265         // fixed field.  Ignore those whose value exceeds the
266         // specified field length.
267         values = values[fieldCode]
268             .filter(val => val[0].length <= val[2])
269             .map(val => ({value: val[0], label: `${val[0]}: ${val[1]}`}))
270             .sort((a, b) => a.label < b.label ? -1 : 1);
271
272         return this.toCache('ffvalues', recordType, fieldCode, values);
273     }
274
275     getControlledBibTags(): Promise<string[]> {
276         if (this.controlledBibTags) {
277             return Promise.resolve(this.controlledBibTags);
278         }
279
280         this.controlledBibTags = [];
281         return this.pcrud.retrieveAll('acsbf', {select: ['tag']})
282         .pipe(
283             map(field => field.tag()),
284             distinct(),
285             map(tag => this.controlledBibTags.push(tag))
286         ).toPromise().then(_ => this.controlledBibTags);
287     }
288 }
289
290
291