1 import {Injectable, EventEmitter} from '@angular/core';
2 import {map, tap} from 'rxjs/operators';
3 import {StoreService} from '@eg/core/store.service';
4 import {IdlObject} from '@eg/core/idl.service';
5 import {AuthService} from '@eg/core/auth.service';
6 import {NetService} from '@eg/core/net.service';
7 import {PcrudService} from '@eg/core/pcrud.service';
8 import {EventService} from '@eg/core/event.service';
9 import {ContextMenuEntry} from '@eg/share/context-menu/context-menu.service';
11 interface TagTableSelector {
13 marcRecordType?: string;
16 const defaultTagTableSelector: TagTableSelector = {
17 marcFormat : 'marc21',
18 marcRecordType : 'biblio'
22 export class TagTableService {
24 // Current set of tags in list and map form.
25 tagMap: {[tag: string]: any} = {};
26 ffPosMap: {[rtype: string]: any[]} = {};
27 ffValueMap: {[rtype: string]: any} = {};
30 {[valueType: string]: {[which: string]: any}} = {};
33 private store: StoreService,
34 private auth: AuthService,
35 private net: NetService,
36 private pcrud: PcrudService,
37 private evt: EventService
40 this.extractedValuesCache = {
49 // Various data needs munging for display. Cached the modified
50 // values since they are refernced repeatedly by the UI code.
51 fromCache(dataType: string, which?: string, which2?: string): ContextMenuEntry[] {
52 const part1 = this.extractedValuesCache[dataType][which];
62 toCache(dataType: string, which: string,
63 which2: string, values: ContextMenuEntry[]): ContextMenuEntry[] {
64 const base = this.extractedValuesCache[dataType];
65 const part1 = base[which];
68 if (!base[which]) { base[which] = {}; }
69 base[which][which2] = values;
77 getFfPosTable(rtype: string): Promise<any> {
78 const storeKey = 'FFPosTable_' + rtype;
80 if (this.ffPosMap[rtype]) {
81 return Promise.resolve(this.ffPosMap[rtype]);
84 this.ffPosMap[rtype] = this.store.getLocalItem(storeKey);
86 if (this.ffPosMap[rtype]) {
87 return Promise.resolve(this.ffPosMap[rtype]);
90 return this.net.request(
91 'open-ils.fielder', 'open-ils.fielder.cmfpm.atomic',
92 {query: {tag: {'!=' : '006'}, rec_type: rtype}}
94 ).toPromise().then(table => {
95 this.store.setLocalItem(storeKey, table);
96 return this.ffPosMap[rtype] = table;
100 getFfValueTable(rtype: string): Promise<any> {
102 const storeKey = 'FFValueTable_' + rtype;
104 if (this.ffValueMap[rtype]) {
105 return Promise.resolve(this.ffValueMap[rtype]);
108 this.ffValueMap[rtype] = this.store.getLocalItem(storeKey);
110 if (this.ffValueMap[rtype]) {
111 return Promise.resolve(this.ffValueMap[rtype]);
114 return this.net.request(
116 'open-ils.cat.biblio.fixed_field_values.by_rec_type', rtype
118 ).toPromise().then(table => {
119 this.store.setLocalItem(storeKey, table);
120 return this.ffValueMap[rtype] = table;
124 loadTagTable(selector?: TagTableSelector): Promise<any> {
127 if (!selector.marcFormat) {
128 selector.marcFormat = defaultTagTableSelector.marcFormat;
130 if (!selector.marcRecordType) {
131 selector.marcRecordType =
132 defaultTagTableSelector.marcRecordType;
135 selector = defaultTagTableSelector;
138 const cacheKey = 'FFValueTable_' + selector.marcRecordType;
140 this.tagMap = this.store.getLocalItem(cacheKey);
143 return Promise.resolve(this.tagMap);
146 return this.fetchTagTable(selector).then(_ => {
147 this.store.setLocalItem(cacheKey, this.tagMap);
152 fetchTagTable(selector?: TagTableSelector): Promise<any> {
154 return this.net.request(
156 'open-ils.cat.tag_table.all.retrieve.local',
157 this.auth.token(), selector.marcFormat, selector.marcRecordType
158 ).pipe(tap(tagData => {
159 this.tagMap[tagData.tag] = tagData;
163 getSubfieldCodes(tag: string): ContextMenuEntry[] {
164 if (!tag || !this.tagMap[tag]) { return null; }
166 const cached = this.fromCache('sfcodes', tag);
168 const list = this.tagMap[tag].subfields.map(sf => ({
170 label: `${sf.code}: ${sf.description}`
172 .sort((a, b) => a.label < b.label ? -1 : 1);
174 return this.toCache('sfcodes', tag, null, list);
177 getFieldTags(): ContextMenuEntry[] {
179 const cached = this.fromCache('fieldtags');
180 if (cached) { return cached; }
182 return Object.keys(this.tagMap)
183 .filter(tag => Boolean(this.tagMap[tag]))
186 label: `${tag}: ${this.tagMap[tag].name}`
188 .sort((a, b) => a.label < b.label ? -1 : 1);
191 getSubfieldValues(tag: string, sfCode: string): ContextMenuEntry[] {
192 if (!tag || !this.tagMap[tag]) { return []; }
194 const cached = this.fromCache('sfvalues', tag, sfCode);
195 if (cached) { return cached; }
197 const list: ContextMenuEntry[] = [];
199 this.tagMap[tag].subfields
201 sf.code === sfCode && sf.hasOwnProperty('value_list'))
203 sf.value_list.forEach(value => {
205 let label = value.description || value.code;
206 const code = value.code || label;
207 if (code !== label) { label = `${code}: ${label}`; }
209 list.push({value: code, label: label});
213 return this.toCache('sfvalues', tag, sfCode, list);
216 getIndicatorValues(tag: string, which: 'ind1' | 'ind2'): ContextMenuEntry[] {
217 if (!tag || !this.tagMap[tag]) { return; }
219 const cached = this.fromCache('indicators', tag, which);
220 if (cached) { return cached; }
222 let values = this.tagMap[tag][which];
223 if (!values) { return; }
225 values = values.map(value => ({
227 label: `${value.code}: ${value.description}`
229 .sort((a, b) => a.label < b.label ? -1 : 1);
231 return this.toCache('indicators', tag, which, values);
235 getFfFieldMeta(fieldCode: string, recordType: string): Promise<IdlObject> {
236 return this.getFfPosTable(recordType).then(table => {
238 // Note the AngJS MARC editor stores the full POS table
239 // for all record types in every copy of the table, hence
240 // the seemingly extraneous check in recordType.
243 field.fixed_field === fieldCode
244 && field.rec_type === recordType
250 // Assumes getFfPosTable and getFfValueTable have already been
251 // invoked for the request record type.
252 getFfValues(fieldCode: string, recordType: string): ContextMenuEntry[] {
254 const cached = this.fromCache('ffvalues', recordType, fieldCode);
255 if (cached) { return cached; }
257 let values = this.ffValueMap[recordType];
259 if (!values || !values[fieldCode]) { return null; }
261 // extract the canned set of possible values for our
262 // fixed field. Ignore those whose value exceeds the
263 // specified field length.
264 values = values[fieldCode]
265 .filter(val => val[0].length <= val[2])
266 .map(val => ({value: val[0], label: `${val[0]}: ${val[1]}`}))
267 .sort((a, b) => a.label < b.label ? -1 : 1);
269 return this.toCache('ffvalues', recordType, fieldCode, values);