1 import {Component, OnInit, ViewChild, AfterViewInit, Input} from '@angular/core';
2 import {IdlObject, IdlService} from '@eg/core/idl.service';
3 import {PcrudService} from '@eg/core/pcrud.service';
4 import {NetService} from '@eg/core/net.service';
5 import {AuthService} from '@eg/core/auth.service';
6 import {OrgService} from '@eg/core/org.service';
7 import {Tree, TreeNode} from '@eg/share/tree/tree';
8 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
9 import {StringService} from '@eg/share/string/string.service';
10 import {MatchSetNewPointComponent} from './match-set-new-point.component';
13 selector: 'eg-match-set-expression',
14 templateUrl: 'match-set-expression.component.html'
16 export class MatchSetExpressionComponent implements OnInit {
18 // Match set arrives from parent async.
20 @Input() set matchSet(ms: IdlObject) {
22 if (ms && !this.initDone) {
23 this.matchSetType = ms.mtype();
34 // Current type of new match point
38 @ViewChild('newPoint') newPoint: MatchSetNewPointComponent;
41 private idl: IdlService,
42 private pcrud: PcrudService,
43 private net: NetService,
44 private auth: AuthService,
45 private org: OrgService,
46 private strings: StringService
53 refreshTree(): Promise<any> {
54 if (!this.matchSet_) { return Promise.resolve(); }
56 return this.pcrud.search('vmsp',
57 {match_set: this.matchSet_.id()}, {},
58 {atomic: true, authoritative: true}
59 ).toPromise().then(points => this.ingestMatchPoints(points));
62 ingestMatchPoints(points: IdlObject[]) {
64 const idmap: any = {};
66 // massage data, create tree nodes
67 points.forEach(point => {
69 point.negate(point.negate() === 't' ? true : false);
70 point.heading(point.heading() === 't' ? true : false);
73 const node = new TreeNode({
76 callerData: {point: point}
78 idmap[node.id + ''] = node;
79 this.setNodeLabel(node, point).then(() => nodes.push(node));
82 // apply the tree parent/child relationships
83 points.forEach(point => {
84 const node = idmap[point.id() + ''];
86 idmap[point.parent() + ''].children.push(node);
88 this.tree = new Tree(node);
93 setNodeLabel(node: TreeNode, point: IdlObject): Promise<any> {
94 if (node.label) { return Promise.resolve(null); }
96 this.getPointLabel(point, true).then(txt => node.label = txt),
97 this.getPointLabel(point, false).then(
98 txt => node.callerData.slimLabel = txt)
102 getPointLabel(point: IdlObject, showmatch?: boolean): Promise<string> {
103 return this.strings.interpolate(
104 'staff.cat.vandelay.matchpoint.label',
105 {point: point, showmatch: showmatch}
109 nodeClicked(node: TreeNode) {}
112 this.changesMade = true;
113 const node = this.tree.selectedNode()
114 this.tree.removeNode(node);
117 hasSelectedNode(): boolean {
118 return Boolean(this.tree.selectedNode());
121 selectedIsBool(): boolean {
123 const node = this.tree.selectedNode();
124 return node && node.callerData.point.bool_op();
130 this.changesMade = true;
132 const pnode = this.tree.selectedNode();
133 const point = this.idl.create('vmsp');
134 point.id(this.newId--);
136 point.parent(pnode.id);
137 point.match_set(this.matchSet_.id());
140 const ptype = this.newPoint.values.pointType;
142 if (ptype === 'bool') {
143 point.bool_op(this.newPoint.values.boolOp);
147 if (ptype == 'attr') {
148 point.svf(this.newPoint.values.recordAttr);
150 } else if (ptype == 'marc') {
151 point.tag(this.newPoint.values.marcTag);
152 point.subfield(this.newPoint.values.marcSf);
153 } else if (ptype == 'heading') {
157 point.negate(this.newPoint.values.negate);
158 point.quality(this.newPoint.values.matchScore);
161 const node: TreeNode = new TreeNode({
163 callerData: {point: point}
166 // Match points are added to the DB only when the tree is saved.
167 this.setNodeLabel(node, point).then(() => pnode.children.push(node));
170 expressionAsString(): string {
171 if (!this.tree) { return ''; }
173 const renderNode = (node: TreeNode): string => {
174 if (!node) { return ''; }
176 if (node.children.length) {
177 return '(' + node.children.map(renderNode).join(
178 ' ' + node.callerData.slimLabel + ' ') + ')'
179 } else if (!node.callerData.point.bool_op()) {
180 return node.callerData.slimLabel;
186 return renderNode(this.tree.rootNode);
189 // Server API deletes and recreates the tree on update.
190 // It manages parent/child relationships via the children array.
191 // We only need send the current tree in a form the API recognizes.
192 saveTree(): Promise<any> {
195 const compileTree = (node?: TreeNode) => {
197 if (!node) { node = this.tree.rootNode; }
199 const point = node.callerData.point;
201 node.children.forEach(child =>
202 point.children().push(compileTree(child)));
207 const rootPoint: IdlObject = compileTree();
209 return this.net.request(
211 'open-ils.vandelay.match_set.update',
212 this.auth.token(), this.matchSet_.id(), rootPoint
214 ok =>this.refreshTree(),
215 err => console.error(err)