gps-global-types 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/confused.js +236 -0
- package/mst.types.tsx +4 -3
- package/package.json +1 -1
- package/pie.types.tsx +2 -1
package/confused.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
//var d3 = require('d3');//
|
|
2
|
+
import { interpolateRgb } from 'd3-interpolate';
|
|
3
|
+
import countryDict from '../static/coords.js'; // this file contains all facilities w corresponding countries as a dictionary
|
|
4
|
+
const rainbow = ["ac9f9d", "beada1", "c0b7a6", "bcbbaa", "b8bfad", "a4c0b8", "9cbdbe", "97b6c2", "96acc1", "9ba5bb"]; //DO NOT DELETE
|
|
5
|
+
import { isolateModel } from './isolate.model.tsx';
|
|
6
|
+
import { toHex, dateToYMD } from '../helpers/reformatData.tsx';
|
|
7
|
+
import Node from './node.model.tsx';
|
|
8
|
+
import { colorFields, CustomDict, dateFields, dateKey, MSTGraph, MSTLegend, SnapshotType } from 'gps-global-types/mst.types.tsx';
|
|
9
|
+
import { NodeType } from 'gps-global-types/pie.types.tsx';
|
|
10
|
+
import { EdgeType } from 'gps-global-types/link.types.tsx';
|
|
11
|
+
import { ObjectId } from 'gps-global-types/global.types.tsx';
|
|
12
|
+
|
|
13
|
+
interface graphProps {
|
|
14
|
+
name:string,
|
|
15
|
+
snapshotData:SnapshotType
|
|
16
|
+
}
|
|
17
|
+
// console.log('LBL fields', lblFields)
|
|
18
|
+
export class Graph implements MSTGraph { //implements MSTGraph
|
|
19
|
+
name:string;
|
|
20
|
+
// scheme:string;
|
|
21
|
+
//colorCalendar:lol;
|
|
22
|
+
|
|
23
|
+
nodes:NodeType[] = [];
|
|
24
|
+
links:EdgeType[] = [];
|
|
25
|
+
legend:MSTLegend;
|
|
26
|
+
custom:CustomDict= {}
|
|
27
|
+
snapshotData:SnapshotType
|
|
28
|
+
latest = { armordCultureDate: new Date('1900-01-01'), isos: [], _id: [] };
|
|
29
|
+
missing: Record<string,any>[] = []
|
|
30
|
+
constructor(props:graphProps) {
|
|
31
|
+
this.name = props.name;
|
|
32
|
+
var tempLegend:Record<string, any> = {};
|
|
33
|
+
colorFields.forEach((c) => {
|
|
34
|
+
tempLegend[c] = new Set();
|
|
35
|
+
});
|
|
36
|
+
dateFields.forEach((d) => {
|
|
37
|
+
tempLegend[d] = 'date'; //[{color:'#e4f1f1',label:'freshest'}, {color:'#062a51',label:'most stale'}];;
|
|
38
|
+
});
|
|
39
|
+
tempLegend['custom color'] = [{ color: '#ffeeff', label: 'No custom file imported yet' }]; //before custom field is initialized
|
|
40
|
+
tempLegend.beforeAndAfter = 'beforeAndAfter';//changed this because otherwise the 'Most Recent' entry duplicates
|
|
41
|
+
this.legend = tempLegend as MSTLegend
|
|
42
|
+
this.snapshotData = props.snapshotData ;
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @summary populate included nodes with relevant sequencedIsolates data in a format d3tree.js can parse
|
|
48
|
+
* @param {isolateModel[]} isolateData
|
|
49
|
+
* @param {Object<ObjectId,ObjectId[]>} mappings parent id, baby ids
|
|
50
|
+
* @adescription HOWDY - if you AREN'T criminally obsessed with dicitonaries, this code will change that
|
|
51
|
+
*/
|
|
52
|
+
buildNodes(mappings:Record<ObjectId,ObjectId[]>, isolateData:typeof isolateModel[]) {
|
|
53
|
+
var graphCalendar = {}; //used to determine color gradients after all dates have been collected
|
|
54
|
+
dateFields.forEach((field) => {
|
|
55
|
+
graphCalendar[field] = []
|
|
56
|
+
})
|
|
57
|
+
/** maps matrix index to node
|
|
58
|
+
* @type {Object<integer,Node>}
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
var nodeDict = {}; //initialize with parent nodes omg obvi . u DUMB O.DUMBO MSCGEEE
|
|
62
|
+
var missingCount = 0; //keep track of the missing agenda
|
|
63
|
+
// isolateData.forEach((iso) => {
|
|
64
|
+
var i = 0;
|
|
65
|
+
while (i < isolateData.length) {
|
|
66
|
+
var id = isolateData[i]._id.toString();
|
|
67
|
+
// console.log(i, '...,', iso._id)
|
|
68
|
+
if (missingCount < this.missing.length) {
|
|
69
|
+
for (var m = this.missing.length - 1; m > -1; m--) { //backwards cuz splice is a dangerous beast
|
|
70
|
+
var missId = this.missing[m];
|
|
71
|
+
if (missId === id) {
|
|
72
|
+
|
|
73
|
+
console.log(m, ' m/ss/ng ', missingCount, '!!!!', id, isolateData[i])
|
|
74
|
+
this.missing.splice(m, 1);
|
|
75
|
+
//ITS A MISSING NODE!!!!!!!!!!;
|
|
76
|
+
missingCount++;
|
|
77
|
+
|
|
78
|
+
this.missing.push(isolateData[i]);
|
|
79
|
+
var deleted = isolateData.splice(i, 1);
|
|
80
|
+
|
|
81
|
+
m = -1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var iso = isolateData[i]
|
|
87
|
+
if (isolateData[i]) {
|
|
88
|
+
|
|
89
|
+
iso._id = iso._id.toString();
|
|
90
|
+
var node;
|
|
91
|
+
var nodeId = mappings[iso._id];
|
|
92
|
+
//
|
|
93
|
+
var node = nodeDict[nodeId];
|
|
94
|
+
|
|
95
|
+
if (!node) {
|
|
96
|
+
node = new Node();
|
|
97
|
+
if (nodeId) { //dad uninitialized
|
|
98
|
+
node.id = nodeId;
|
|
99
|
+
} else { //no dad
|
|
100
|
+
node.id = iso._id.split(' ')[0];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// iso.armordMrsnId = iso.mstMrsnId;
|
|
104
|
+
iso.armordCultureDate = iso.mstCultureDate;
|
|
105
|
+
node.nodeids.push(iso._id)
|
|
106
|
+
node.mrsnIds.push(iso.mrsnIds); //for custom
|
|
107
|
+
this.custom[iso._id] = { mrsnId: iso.mrsnId }; //estabish custom entry. mrsnId for csv unless hmm
|
|
108
|
+
|
|
109
|
+
//1. COLOR FIELDS + LEGEND
|
|
110
|
+
colorFields.forEach((field) => {
|
|
111
|
+
if (field === 'country') {
|
|
112
|
+
iso.country = countryDict[iso.armordFacility];
|
|
113
|
+
}
|
|
114
|
+
const value = toHex(iso[field])
|
|
115
|
+
const isNotCustom = (field.includes('custom') == false);
|
|
116
|
+
// console.log(isNotCustom, isNotCustom && this.snapshotData )
|
|
117
|
+
if (isNotCustom) {
|
|
118
|
+
this.legend[field].add(value + '&' + iso[field]); //add entry to legend
|
|
119
|
+
node.populate(field, value);
|
|
120
|
+
this.custom[iso._id][field] = value;
|
|
121
|
+
} else if (this.snapshotData) {//populate initial snapshot colors
|
|
122
|
+
const snapColor = this.snapshotData?.nodes[iso._id]?.color ? this.snapshotData.nodes[iso._id].color : 'grey'
|
|
123
|
+
|
|
124
|
+
node.populate(field, snapColor);
|
|
125
|
+
this.custom[iso._id][field] = snapColor
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
//2. LBL FIELDS
|
|
130
|
+
lblFields.forEach((field) => {
|
|
131
|
+
var isoField = iso[field.slice(0, -3)];
|
|
132
|
+
if (field.match('custom') && this.snapshotData && this.snapshotData?.nodes[iso._id]) { //populate initial snapshot labels
|
|
133
|
+
isoField = this.snapshotData?.nodes[iso._id]?.label //? this.snapshotData?.nodes[iso._id]?.label : 'grey' ;
|
|
134
|
+
this.custom[iso._id][field] = this.snapshotData?.nodes[iso._id]?.label;
|
|
135
|
+
}
|
|
136
|
+
else if (!isoField) isoField = '-';
|
|
137
|
+
if (field == 'noLabelLBL') node[field] = ' ';
|
|
138
|
+
else node[field] += isoField + '&';
|
|
139
|
+
this.custom[iso._id][field] = isoField;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// 3. DATE FIELDS
|
|
143
|
+
const nodeDate = new Date(iso.armordCultureDate);
|
|
144
|
+
if (nodeDate > new Date(this.latest.armordCultureDate)) this.latest = { armordCultureDate: nodeDate, isos: [iso] }; // parent_id: [node.id], armordMrsnId: [iso.armordMrsnId]
|
|
145
|
+
else if (nodeDate.getTime() === this.latest.armordCultureDate.getTime()) {
|
|
146
|
+
this.latest.isos.push(iso);
|
|
147
|
+
}
|
|
148
|
+
const nodeDateStr = dateToYMD(nodeDate);
|
|
149
|
+
const nodeYear = nodeDate.getFullYear();
|
|
150
|
+
const nodeMonth = nodeYear + '-' + nodeDate.getMonth();
|
|
151
|
+
const nodeQuarter = nodeYear + 'QTR' + ((nodeMonth <= 3) ? 1 : (nodeMonth <= 6) ? 2 : (nodeMonth <= 9) ? 3 : 4);
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
var fieldMap = { 'armordCultureDate': nodeDateStr, 'quarter': nodeQuarter, 'month': nodeMonth, 'year': nodeYear };
|
|
155
|
+
this.custom[iso._id] = { ...fieldMap, ...this.custom[iso._id] };
|
|
156
|
+
dateFields.forEach((field) => {
|
|
157
|
+
node.populate(field, fieldMap[field]);
|
|
158
|
+
graphCalendar[field].push(fieldMap[field]);
|
|
159
|
+
})
|
|
160
|
+
// 3.5 sneaky before and after array
|
|
161
|
+
node.dateArr.push(nodeDate);
|
|
162
|
+
|
|
163
|
+
// 4. dont FORGET THE HOVER !!!
|
|
164
|
+
node.hover = node.hover.concat(iso.tier1);
|
|
165
|
+
|
|
166
|
+
//ADD HER TO THE NODES
|
|
167
|
+
nodeDict[node.id] = node;
|
|
168
|
+
i++
|
|
169
|
+
|
|
170
|
+
} //missing
|
|
171
|
+
//0. get mapping/ parent id of iso
|
|
172
|
+
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// configure colorCalendar based on graphCalendar
|
|
176
|
+
/**
|
|
177
|
+
* return a color based on its field and date
|
|
178
|
+
* @type {Object<String,Object<Date,String>>}
|
|
179
|
+
* */
|
|
180
|
+
var colorCalendar = {}
|
|
181
|
+
Object.entries(graphCalendar).forEach(([field, arr]) => { //gradient time light (recent) -> dark (oldest)
|
|
182
|
+
arr = arr.sort()
|
|
183
|
+
var temp = {};
|
|
184
|
+
const max = arr.length
|
|
185
|
+
var normal = function (x) { return (max < 2) ? 1 : (x) / (max - 1) }
|
|
186
|
+
var gradient = interpolateRgb('#e4f1f1', '#062a51');
|
|
187
|
+
arr.forEach((date, i) => {
|
|
188
|
+
temp[date] = gradient(normal(i));
|
|
189
|
+
})
|
|
190
|
+
colorCalendar[field] = temp;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
this.latest._id = this.latest.isos.map(i => { return i._id });
|
|
194
|
+
|
|
195
|
+
Object.entries(nodeDict).forEach(([key, node]) => {
|
|
196
|
+
if (this.snapshotData && this.snapshotData?.transforms[key]) {//initialize node position if snapshot is included
|
|
197
|
+
node.x = this.snapshotData.transforms[key].x;
|
|
198
|
+
node.y = this.snapshotData.transforms[key].y;
|
|
199
|
+
}
|
|
200
|
+
node.hover = Array.from(new Set(node.hover)); //convert array of tier 1 alleles into a string of unique values
|
|
201
|
+
this.tier1s = this.tier1s.concat(node.hover)
|
|
202
|
+
node.hover = node.hover.reduce((prev, curr) => {
|
|
203
|
+
return prev + ', ' + curr;
|
|
204
|
+
}, '');
|
|
205
|
+
node.hover = node.hover.substring(2) //remove silly first comma
|
|
206
|
+
|
|
207
|
+
if (!this.snapshotData) node['custom color'] = { '#fff': 1 }; //initialize dummy customcolor
|
|
208
|
+
node.beforeAndAfter = [{ color: '#fff', value: 1 }]; //initialize dummy ColorField[] obj to avoid crashes (if beforeAndAfter is selected as initial color field, and before and Afte colors haven't been calculated yet, firefox convluses in a horrific spasm)
|
|
209
|
+
})
|
|
210
|
+
Object.entries(this.custom).forEach(([id, dict]) => {
|
|
211
|
+
dateFields.forEach((field) => {
|
|
212
|
+
this.custom[id][field] = colorCalendar[field][this.custom[id][field]]; //brain = gone ; goodbye
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
colorFields.forEach((field) => {
|
|
216
|
+
if (field.match('custom')) return
|
|
217
|
+
this.legend[field] = Array.from(this.legend[field]).map(v => {
|
|
218
|
+
var temp = {};
|
|
219
|
+
var clrLbl = v.split('&');
|
|
220
|
+
temp.color = clrLbl[0];
|
|
221
|
+
temp.label = clrLbl[1];
|
|
222
|
+
return temp;
|
|
223
|
+
})
|
|
224
|
+
// this.legend[field].push(JSON.parse(process.env.REACT_APP_MOST_RECENT_LEGEND))
|
|
225
|
+
});
|
|
226
|
+
this.nodes = Object.values(nodeDict);
|
|
227
|
+
this.latest.armordCultureDate = dateToYMD(this.latest.armordCultureDate);
|
|
228
|
+
this.colorCalendar = colorCalendar;
|
|
229
|
+
this.tier1s = Array.from(new Set(this.tier1s))
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
// export Graph //typedefs
|
package/mst.types.tsx
CHANGED
|
@@ -32,7 +32,7 @@ export type labelKey = typeof labelFields[number];
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
type CustomDictEntry = Record<colorKey
|
|
35
|
+
type CustomDictEntry = Partial<Record<colorKey & dateKey, Color>> | Partial<Record<labelKey, string>>;
|
|
36
36
|
export type CustomDict = Record<ObjectId, CustomDictEntry>
|
|
37
37
|
|
|
38
38
|
|
|
@@ -90,12 +90,13 @@ interface MSTResult {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
interface LegendBullet { //or custom upload data
|
|
93
|
+
export interface LegendBullet { //or custom upload data
|
|
94
94
|
color: Color,
|
|
95
95
|
label: string
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
export type MSTLegend = Record<colorKey, LegendBullet[]> & Record<dateKey, 'date'
|
|
98
|
+
export type MSTLegend = Record<colorKey&'beforeAndAfter'&'custom color', LegendBullet[]> & Record<dateKey, 'date'>;
|
|
99
|
+
// & { 'beforeAndAfter': [{ color: '#666666', label: 'Before ' }, { color: '#4a64aa', label: 'After ' }] }
|
|
99
100
|
|
|
100
101
|
|
|
101
102
|
|
package/package.json
CHANGED
package/pie.types.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BaseType, Selection, SimulationNodeDatum } from "d3";
|
|
2
|
-
import type { ObjectId, Color, RegexMatchedString } from "./global.types.tsx"
|
|
2
|
+
import type { ObjectId, Color, RegexMatchedString, mrsnId } from "./global.types.tsx"
|
|
3
3
|
import type { colorKey, dateKey, labelKey } from "./mst.types.tsx"
|
|
4
4
|
|
|
5
5
|
export interface ColorField {//TYPES OF GRAPH FIELDS ColorField datefields become colorFields but require additional processing
|
|
@@ -23,6 +23,7 @@ export interface ExtraNodeTypeFields extends SimulationNodeDatum {
|
|
|
23
23
|
dateArr: DateType[], // array of armordCultureDates used for beforeAndAfter coloring
|
|
24
24
|
hover?: string, // [=""] //unique fieldfields \/ \/ list seperated by ', ' (only tier1 alleles for now)
|
|
25
25
|
nodeids: ObjectId[] // for selection logic between table and node=
|
|
26
|
+
mrsnIds:mrsnId[],
|
|
26
27
|
x?: number,
|
|
27
28
|
y?: number,
|
|
28
29
|
fx?: number | null,
|