UNPKG

61.1 kBJavaScriptView Raw
1import "source-map-support/register";
2// TODO should I get rid of the lib above for production browser build?
3import { assign as assignM } from "lodash";
4import { assign, compact, curry, difference, flow, intersection, isArray, isObject, map, omit, partition, sortBy, startCase, toPairsIn } from "lodash/fp";
5import * as hl from "highland";
6import * as VError from "verror";
7import { CXMLXPath } from "../spinoffs/cxml-xpath";
8import * as GPML2013a from "../../xmlns/pathvisio.org/GPML/2013a";
9import * as GPMLDefaults from "../GPMLDefaults";
10import { Processor } from "../Processor";
11import { preprocessGPML as preprocessEdgeGPML, postprocessPVJSON as postprocessEdgePVJSON } from "../edge/edge";
12import { preprocessGPML as preprocessGroupGPML, postprocessPVJSON as postprocessGroupPVJSON } from "../group";
13import { arrayify, insertIfNotExists, isDefinedCXML, isPvjsonBurr, isPvjsonEdge, isPvjsonGroup, isPvjsonSingleFreeNode, isPvjsonNode, isPvjsonEdgeOrBurr, sortByMap, supportedNamespaces, unionLSV } from "../gpml-utilities";
14import * as VOCABULARY_NAME_TO_IRI from "../spinoffs/VOCABULARY_NAME_TO_IRI.json";
15import * as GPML2013aKeyMappings from "./KeyMappings.json";
16import * as GPML2013aKeyValueMappings from "./KeyValueConverters";
17import * as GPML2013aValueMappings from "./ValueMappings.json";
18import * as GPML2013aValueConverters from "./ValueConverters";
19// TODO get text alignment correctly mapped to Box Model CSS terms
20import * as iassign from "immutable-assign";
21iassign.setOption({
22 // Deep freeze both input and output. Used in development to make sure they don't change.
23 // TODO watch issue and re-enable when addressed: https://github.com/engineforce/ImassignM/issues/11
24 //freeze: true,
25 ignoreIfNoChange: true
26});
27export const RECURSION_LIMIT = 1000;
28function partitionStream(s, partitioner) {
29 const yes = s.fork().filter(x => partitioner(x));
30 const no = s.fork().filter(x => !partitioner(x));
31 return [yes, no];
32}
33function extendDeep(targetOrTargetArray, source) {
34 const target = isArray(targetOrTargetArray)
35 ? targetOrTargetArray[0]
36 : targetOrTargetArray;
37 // TODO: We run into problems if we try to extend both
38 // GraphicalLine and Interaction, because they share
39 // EdgeGraphicsType. To avoid an infinite recursion of
40 // extending, I'm using a short-term solution of just
41 // marking whether a target has been extended, and
42 // if so, skipping it.
43 // Look into a better way of handling this.
44 if (!target.hasOwnProperty("_extended")) {
45 toPairsIn(target)
46 .filter(([targetKey, targetValue]) => source.hasOwnProperty(targetKey) && isObject(source[targetKey]))
47 .forEach(function ([targetKey, targetValue]) {
48 extendDeep(targetValue, source[targetKey]);
49 });
50 assignM(target.constructor.prototype, source);
51 target._extended = true;
52 }
53}
54const stringifyKeyValue = curry(function (source, key) {
55 return source.hasOwnProperty(key)
56 ? startCase(key) + ": " + source[key]
57 : null;
58});
59extendDeep(GPML2013a.document.Pathway.constructor.prototype, GPMLDefaults.Pathway);
60extendDeep(GPML2013a.DataNodeType.prototype, GPMLDefaults.DataNode);
61extendDeep(GPML2013a.GraphicalLineType.prototype, GPMLDefaults.GraphicalLine);
62extendDeep(GPML2013a.GroupType.prototype, GPMLDefaults.Group);
63extendDeep(GPML2013a.InteractionType.prototype, GPMLDefaults.Interaction);
64extendDeep(GPML2013a.LabelType.prototype, GPMLDefaults.Label);
65extendDeep(GPML2013a.ShapeType.prototype, GPMLDefaults.Shape);
66extendDeep(GPML2013a.StateType.prototype, GPMLDefaults.State);
67extendDeep(GPML2013a.EdgeGraphicsType.prototype.Anchor, GPMLDefaults.Anchor);
68// TODO specify types
69export function toPvjson(inputStreamWithMessedUpRDFIDs, pathwayIri) {
70 // NOTE: GPML2013a incorrectly uses "rdf:id" instead of "rdf:ID".
71 // We need to fix this error so that CXML can process the GPML.
72 const inputStream = hl(inputStreamWithMessedUpRDFIDs)
73 .splitBy(' rdf:id="')
74 .intersperse(' rdf:ID="');
75 const selectorToCXML = {
76 // TODO why does TS require that we use the Pathway's "constructor.prototype"
77 // instead of just the Pathway?
78 // Why does Pathway.Graphics not need that?
79 // Why do many of the other require using the prototype?
80 //
81 "/Pathway/@*": GPML2013a.document.Pathway.constructor.prototype,
82 "/Pathway/Comment": GPML2013a.document.Pathway.Comment[0],
83 "/Pathway/Graphics/@*": GPML2013a.document.Pathway.Graphics,
84 "/Pathway/DataNode": GPML2013a.DataNodeType.prototype,
85 "/Pathway/State": GPML2013a.StateType.prototype,
86 "/Pathway/Interaction": GPML2013a.InteractionType.prototype,
87 "/Pathway/GraphicalLine": GPML2013a.GraphicalLineType.prototype,
88 "/Pathway/Label": GPML2013a.LabelType.prototype,
89 "/Pathway/Shape": GPML2013a.ShapeType.prototype,
90 "/Pathway/Group": GPML2013a.GroupType.prototype,
91 "/Pathway/InfoBox": GPML2013a.InfoBoxType.prototype,
92 "/Pathway/Legend": GPML2013a.LegendType.prototype,
93 "/Pathway/Biopax/bp:PublicationXref": GPML2013a.document.Pathway.Biopax.PublicationXref[0],
94 "/Pathway/Biopax/bp:openControlledVocabulary": GPML2013a.document.Pathway.Biopax.openControlledVocabulary[0]
95 };
96 const cxmlXPath = new CXMLXPath(inputStream, GPML2013a, {
97 bp: "http://www.biopax.org/release/biopax-level3.owl#"
98 //rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
99 });
100 const cxmlSources = cxmlXPath.parse(selectorToCXML);
101 const processor = new Processor(GPML2013aKeyMappings, GPML2013aKeyValueMappings, GPML2013aValueMappings, GPML2013aValueConverters);
102 const { fillInGPMLPropertiesFromParent, getPvjsonEntityLatestByGraphId, graphIdsByGraphRef, graphIdToZIndex, getGPMLElementByGraphId, preprocessGPMLElement, processGPMLAndPropertiesAndType, processProperties, processPropertiesAndType, setPvjsonEntity } = processor;
103 if (pathwayIri) {
104 processor.output = iassign(processor.output, function (o) {
105 return o.pathway;
106 }, function (pathway) {
107 pathway.id = pathwayIri;
108 return pathway;
109 });
110 }
111 const sortByZIndex = sortByMap(graphIdToZIndex);
112 const pathwayMetadataStream = hl([
113 cxmlSources["/Pathway/@*"].doto(function (pathway) {
114 if (supportedNamespaces.indexOf(pathway._namespace) === -1) {
115 // TODO should we do anything further?
116 throw new Error(`Unsupported namespace: ${pathway._namespace}`);
117 }
118 }),
119 cxmlSources["/Pathway/Graphics/@*"]
120 ])
121 .merge()
122 .map(processProperties)
123 .reduce({}, function (acc, metadataChunk) {
124 return assign(acc, metadataChunk);
125 })
126 .map(function (metadata) {
127 processor.output = iassign(processor.output, function (o) {
128 return o.pathway;
129 }, function (pathway) {
130 const mergedPathway = assign(pathway, metadata);
131 // NOTE: GPML schema specifies that name is required
132 const { name } = mergedPathway;
133 const splitName = name.split(" (");
134 if (!!splitName &&
135 splitName.length === 2 &&
136 !!name.match(/\(/g) &&
137 name.match(/\(/g).length === 1 &&
138 !!name.match(/\)/g) &&
139 name.match(/\)/g).length === 1) {
140 mergedPathway.standardName = splitName[0];
141 mergedPathway.displayName = splitName[1].replace(")", "");
142 }
143 else {
144 mergedPathway.standardName = name;
145 mergedPathway.displayName = name;
146 }
147 const stringifyKeyValueForPathway = stringifyKeyValue(mergedPathway);
148 mergedPathway.textContent = compact([
149 stringifyKeyValueForPathway("name"),
150 stringifyKeyValueForPathway("license"),
151 stringifyKeyValueForPathway("lastModified"),
152 stringifyKeyValueForPathway("organism")
153 ]).join("\n");
154 const context = [
155 "https://cdn.rawgit.com/wikipathways/WpVocabularies/7a46a05/contexts/pvjs.jsonld"
156 ];
157 if (!!mergedPathway.id) {
158 context.push({
159 "@base": mergedPathway.id + "/"
160 });
161 }
162 else {
163 // If there's no pathway IRI specified, we at least give the user a URL
164 // to search WikiPathways. This way, the user at least has a chance of
165 // to search WikiPathways to possibly find the source for this data.
166 // NOTE: GPML schema specifies that organism is optional
167 const organismIriComponent = mergedPathway.hasOwnProperty("organism")
168 ? `&species=${mergedPathway.organism}`
169 : "";
170 mergedPathway.isSimilarTo = encodeURI(`http://wikipathways.org/index.php/Special:SearchPathways?query=${name}${organismIriComponent}&doSearch=1`);
171 }
172 return assign({
173 "@context": context
174 }, mergedPathway);
175 });
176 return processor.output;
177 })
178 .errors(function (err) {
179 throw new VError(err, ` when processing pathwayMetadataStream
180 `);
181 });
182 const pathwayCommentStream = hl(cxmlSources["/Pathway/Comment"]).map(function (Comment) {
183 processor.output = iassign(processor.output, function (o) {
184 return o.pathway;
185 }, function (pathway) {
186 const comments = pathway.comments || [];
187 comments.push(processProperties(Comment));
188 pathway.comments = comments;
189 return pathway;
190 });
191 return processor.output;
192 });
193 const dataNodeStream = cxmlSources["/Pathway/DataNode"]
194 .map(processGPMLAndPropertiesAndType("DataNode"))
195 .map(function (entity) {
196 // TODO fix type def for unionLSV so I don't have to use "as"
197 entity.type = unionLSV(entity.type, entity.wpType);
198 return entity;
199 });
200 const stateStream = cxmlSources["/Pathway/State"]
201 .map(preprocessGPMLElement)
202 .flatMap(function (gpmlState) {
203 return hl(getGPMLElementByGraphId(gpmlState.GraphRef)).map(function (gpmlDataNode) {
204 return fillInGPMLPropertiesFromParent(gpmlDataNode, gpmlState);
205 });
206 })
207 .map(processPropertiesAndType("State"));
208 const shapeStream = cxmlSources["/Pathway/Shape"]
209 .map(processGPMLAndPropertiesAndType("Shape"))
210 .map(function (pvjsonEntity) {
211 const { cellularComponent } = pvjsonEntity;
212 // CellularComponent is not a BioPAX term, but "PhysicalEntity" is.
213 if (!!cellularComponent) {
214 pvjsonEntity.type = unionLSV(pvjsonEntity.type, "PhysicalEntity", "CellularComponent", cellularComponent);
215 }
216 return pvjsonEntity;
217 });
218 const labelStream = cxmlSources["/Pathway/Label"].map(processGPMLAndPropertiesAndType("Label"));
219 const gpmlInteractionStream = cxmlSources["/Pathway/Interaction"].map(preprocessGPMLElement);
220 const gpmlGraphicalLineStream = cxmlSources["/Pathway/GraphicalLine"].map(preprocessGPMLElement);
221 const edgeStream = hl([
222 gpmlInteractionStream
223 .fork()
224 .map(preprocessEdgeGPML)
225 .map(processPropertiesAndType("Interaction")),
226 gpmlGraphicalLineStream
227 .fork()
228 .map(preprocessEdgeGPML)
229 .map(processPropertiesAndType("GraphicalLine"))
230 ]).merge();
231 const anchorStream = hl([
232 gpmlInteractionStream.fork(),
233 gpmlGraphicalLineStream.fork()
234 ])
235 .merge()
236 .filter(function (gpmlEdge) {
237 return (isDefinedCXML(gpmlEdge.Graphics) &&
238 isDefinedCXML(gpmlEdge.Graphics.Anchor));
239 })
240 .flatMap(function (gpmlEdge) {
241 const { GraphId, Graphics } = gpmlEdge;
242 const fillInGPMLPropertiesFromEdge = fillInGPMLPropertiesFromParent(gpmlEdge);
243 const gpmlAnchors = Graphics.Anchor;
244 return hl(gpmlAnchors)
245 .map(function (gpmlAnchor) {
246 const anchorShape = gpmlAnchor.Shape;
247 if (anchorShape === "None") {
248 // NOTE: For Anchors with Shape="None", PathVisio-Java displays
249 // the anchor as a 4x4 square when nothing is connected,
250 // but does not display it when something is connected.
251 // TODO: right now, PathVisio-Java writes out GPML such that the
252 // Anchor only has a GraphId when an Edge connects to this Anchor,
253 // but we may not be able to rely on this in the future.
254 if (isDefinedCXML(gpmlAnchor.GraphId)) {
255 assignM(gpmlAnchor.Graphics, {
256 Height: 0,
257 Width: 0
258 });
259 }
260 else {
261 gpmlAnchor.Shape = "Rectangle";
262 assignM(gpmlAnchor.Graphics, {
263 Height: 4,
264 Width: 4
265 });
266 }
267 }
268 else if (anchorShape === "Circle") {
269 assignM(gpmlAnchor.Graphics, {
270 Height: 8,
271 Width: 8
272 });
273 }
274 else {
275 throw new Error(`Anchor Shape "${anchorShape}" is not supported.`);
276 }
277 return gpmlAnchor;
278 })
279 .map(preprocessGPMLElement)
280 .map(function (gpmlAnchor) {
281 const filledInAnchor = fillInGPMLPropertiesFromEdge(gpmlAnchor);
282 filledInAnchor.GraphRef = GraphId;
283 return filledInAnchor;
284 })
285 .map(processPropertiesAndType("Anchor"));
286 });
287 const groupStream = cxmlSources["/Pathway/Group"]
288 .map(preprocessGroupGPML(processor))
289 .filter((Group) => !!Group.Contains)
290 .map(processGPMLAndPropertiesAndType("Group"));
291 const EDGES = ["Interaction", "GraphicalLine"];
292 const NODES = ["DataNode", "Shape", "Label", "State", "Group"];
293 function postprocessAll(s) {
294 /*
295 // We are sorting the elements by the order in which we must do their
296 // post-processing, e.g., if one edge is attached to another edge via
297 // an anchor, we must post-process the edge with the anchor before we
298 // post-process the other edge.
299 const isProcessableTests = [
300 curry(function(sortedIds: string[], pvjsonEntity: PvjsonEntity) {
301 // In this test, we ensure the entity
302 // 1) is not a group AND
303 // 2) is not attached to a group and not to an edge
304 // (ie., it is not attached to anything, or
305 // it is attached to something, but that something is neither a group nor an edge)
306
307 return (
308 pvjsonEntity.gpmlElementName !== "Group" &&
309 unionLSV(
310 pvjsonEntity["isAttachedToOrVia"],
311 pvjsonEntity["isAttachedTo"]
312 )
313 .map(
314 (isAttachedToOrViaId: string) =>
315 processor.output.entitiesById[isAttachedToOrViaId].gpmlElementName
316 )
317 .filter(
318 // Entity is attached to neither a group nor an edge.
319 // (Testing that entity is not attached to an edge at all,
320 // whether directly or indirectly via an anchor.)
321 isAttachedToOrViaGpmlElementName =>
322 ["Group", "Interaction", "GraphicalLine", "Anchor"].indexOf(
323 isAttachedToOrViaGpmlElementName
324 ) > -1
325 ).length === 0
326 );
327 }),
328 curry(function(sortedIds: string[], pvjsonEntity: PvjsonEntity) {
329 const gpmlElementName = pvjsonEntity.gpmlElementName;
330 if (
331 ["Interaction", "GraphicalLine", "State", "Anchor"].indexOf(
332 gpmlElementName
333 ) > -1
334 ) {
335 // This entity is an edge, a state or an anchor.
336 // All entities to which this entity is attached must be processable
337 // before it is itself processable.
338 // That means all entities to which this entity is attached to must
339 // be processed before it can be processed.
340 const isAttachedToIds = unionLSV(
341 pvjsonEntity["isAttachedTo"],
342 pvjsonEntity["isAttachedToOrVia"]
343 ) as string[];
344 const isAttachedToInSortedIds = isAttachedToIds.filter(
345 // entity with this id is sortedIds
346 isAttachedToId => sortedIds.indexOf(isAttachedToId) > -1
347 );
348 return isAttachedToIds.length === isAttachedToInSortedIds.length;
349 } else if (gpmlElementName === "Group") {
350 // is processable when group does not contain an entity that is not processable
351 return (
352 arrayify(pvjsonEntity["contains"])
353 .map(isAttachedToId => processor.output.entitiesById[isAttachedToId])
354 .filter(
355 candidateEntity => sortedIds.indexOf(candidateEntity.id) > -1
356 ).length > 0
357 );
358 }
359 })
360 ];
361 //*/
362 function sortUnsortedRecursive({ sortedIds, unsorted }, i = 0) {
363 // TODO is there something better we can do than use RECURSION_LIMIT?
364 // WP2037 revision 90015 won't terminate without a limit, but converts
365 // OK with the limit set.
366 if (unsorted.length === 0 || i > RECURSION_LIMIT) {
367 return { sortedIds, unsorted };
368 }
369 i += 1;
370 return sortUnsortedRecursive(sortUnsortedOnce({ sortedIds, unsorted }), i);
371 }
372 function sortUnsortedOnce({ sortedIds, unsorted }) {
373 let [sortedOnThisIteration, stillUnsorted] = partition(function (pvjsonEntity) {
374 const dependencies = unionLSV(pvjsonEntity.contains, pvjsonEntity["isAttachedToOrVia"], pvjsonEntity.isAttachedTo);
375 return (
376 /*
377 dependencies
378 .map((id: string) => processor.output.entitiesById[id])
379 .indexOf(undefined) === -1 &&
380 //*/
381 intersection(dependencies, sortedIds).length === dependencies.length);
382 }, unsorted);
383 sortedOnThisIteration
384 .forEach(function (pvjsonEntity) {
385 sortedIds.push(pvjsonEntity.id);
386 });
387 return {
388 sortedIds: sortedIds,
389 unsorted: stillUnsorted
390 };
391 }
392 return (s
393 .reduce({
394 sortedIds: [],
395 unsorted: []
396 }, function ({ sortedIds, unsorted }, pvjsonEntity) {
397 unsorted.push(pvjsonEntity);
398 return sortUnsortedOnce({ sortedIds, unsorted });
399 })
400 .map(sortUnsortedRecursive)
401 .map(function (acc) {
402 const { sortedIds, unsorted } = acc;
403 return sortedIds
404 .map((id) => processor.output.entitiesById[id])
405 .concat(unsorted);
406 })
407 .sequence());
408 }
409 const pvjsonEntityStream = hl([
410 hl([dataNodeStream, stateStream, shapeStream, labelStream]).merge(),
411 hl([edgeStream, anchorStream]).merge(),
412 groupStream
413 ])
414 .sequence()
415 .doto(setPvjsonEntity)
416 .through(postprocessAll)
417 .flatMap(function (pvjsonEntity) {
418 const { id, zIndex } = pvjsonEntity;
419 // TODO we might want to sort by other criteria, such as
420 // to order a State above its DataNode, which would be
421 // ordered above its Group, if any
422 const insertEntityIdAndSortByZIndex = flow([
423 insertIfNotExists(id),
424 sortByZIndex
425 ]);
426 let finalSortedStream;
427 if (isPvjsonEdgeOrBurr(pvjsonEntity)) {
428 const isAttachedTo = pvjsonEntity.isAttachedTo;
429 arrayify(isAttachedTo).forEach(function (graphRef) {
430 const graphRefs = graphIdsByGraphRef[graphRef] || [];
431 if (graphRefs.indexOf(id) === -1) {
432 graphRefs.push(id);
433 }
434 graphIdsByGraphRef[graphRef] = graphRefs;
435 });
436 if (isPvjsonBurr(pvjsonEntity)) {
437 finalSortedStream = hl(getPvjsonEntityLatestByGraphId(isAttachedTo)).map(function (referencedEntity) {
438 if (isPvjsonNode(referencedEntity)) {
439 const { attachmentDisplay } = pvjsonEntity;
440 const [relativeOffsetScalarX, relativeOffsetScalarY] = attachmentDisplay.relativeOffset;
441 attachmentDisplay.offset = [
442 relativeOffsetScalarX * referencedEntity.width,
443 relativeOffsetScalarY * referencedEntity.height
444 ];
445 pvjsonEntity.attachmentDisplay = omit(["relativeOffset"], attachmentDisplay);
446 }
447 setPvjsonEntity(pvjsonEntity);
448 // NOTE: burrs are not added to the property "contained".
449 // Rather, they are added to the property "burrs".
450 referencedEntity.burrs = referencedEntity.burrs || [];
451 insertEntityIdAndSortByZIndex(referencedEntity.burrs);
452 setPvjsonEntity(referencedEntity);
453 return processor.output;
454 });
455 }
456 else if (isPvjsonEdge(pvjsonEntity)) {
457 try {
458 const pvjsonEdge = postprocessEdgePVJSON(processor.output.entitiesById, pvjsonEntity);
459 processor.output = iassign(processor.output, function (o) {
460 return o.pathway.contains;
461 }, insertEntityIdAndSortByZIndex);
462 setPvjsonEntity(pvjsonEdge);
463 finalSortedStream = hl([processor.output]);
464 }
465 catch (err) {
466 return hl.fromError(err);
467 }
468 }
469 else {
470 return hl.fromError(new VError(`
471 Unexpected entity type.
472 Only Edge or Burr should return true for
473 isPvjsonEdgeOrBurr(
474 ${JSON.stringify(pvjsonEntity, null, " ")}
475 )
476 `));
477 }
478 }
479 else if (isPvjsonGroup(pvjsonEntity)) {
480 // We still have some GPML files with empty Groups and/or nested Groups
481 // floating around, but we don't process them, because that's a
482 // curation issue, not a gpml2pvjson issue.
483 const containedCount = pvjsonEntity.contains.length;
484 if (containedCount === 0 || pvjsonEntity.hasOwnProperty("groupRef")) {
485 if (containedCount === 0) {
486 return hl.fromError(new Error(`
487 Encountered empty Group:
488 ${JSON.stringify(pvjsonEntity, null, " ")}
489 `));
490 }
491 if (pvjsonEntity.hasOwnProperty("groupRef")) {
492 return hl.fromError(new Error(`
493 Encountered nested Group:
494 ${JSON.stringify(pvjsonEntity, null, " ")}
495 `));
496 }
497 finalSortedStream = hl([processor.output]);
498 }
499 else {
500 const graphIdOfGroup = pvjsonEntity.id;
501 try {
502 finalSortedStream = hl(pvjsonEntity.contains.map(containedId => processor.output.entitiesById[containedId]))
503 .filter(groupedEntity => groupedEntity.kaavioType !== "Group")
504 .collect()
505 .map(function (groupedEntities) {
506 const pvjsonGroup = postprocessGroupPVJSON(groupedEntities, pvjsonEntity);
507 const graphIdToZIndex = processor.graphIdToZIndex;
508 pvjsonGroup.contains = sortBy([
509 function (thisEntityId) {
510 return graphIdToZIndex[thisEntityId];
511 }
512 ], groupedEntities.map(x => x.id));
513 const { id, x, y } = pvjsonGroup;
514 const groupedEntitiesFinal = groupedEntities.map(function (groupedEntity) {
515 if (isPvjsonEdge(groupedEntity)) {
516 groupedEntity.points = map(function (point) {
517 point.x -= x;
518 point.y -= y;
519 return point;
520 }, groupedEntity.points);
521 }
522 else if (isPvjsonSingleFreeNode(groupedEntity)) {
523 groupedEntity.height;
524 groupedEntity.x -= x;
525 groupedEntity.y -= y;
526 }
527 else {
528 return hl.fromError(new Error(`
529 Encountered unexpected entity
530 ${JSON.stringify(groupedEntity, null, " ")}
531 in Group
532 ${JSON.stringify(pvjsonGroup, null, " ")}
533 `));
534 }
535 // NOTE: this is needed for GPML2013a, because GPML2013a uses both
536 // GroupId/GroupRef and GraphId/GraphRef. GPML2017 uses a single
537 // identifier per entity. That identifier can be referenced by
538 // GroupRef and/or GraphRef. Pvjson follows GPML2017 in this, so
539 // we convert from GPML2013a format:
540 // GroupRef="GROUP_ID_VALUE"
541 // to pvjson format:
542 // {isPartOf: "GRAPH_ID_VALUE"}
543 groupedEntity.isPartOf = id;
544 return omit(["groupRef"], groupedEntity);
545 });
546 groupedEntitiesFinal.forEach(function (pvjsonEntity) {
547 setPvjsonEntity(pvjsonEntity);
548 });
549 setPvjsonEntity(pvjsonGroup);
550 processor.output = iassign(processor.output, function (o) {
551 return o.pathway.contains;
552 }, function (contains) {
553 return insertEntityIdAndSortByZIndex(difference(contains, groupedEntitiesFinal.map(x => x.id)), id);
554 });
555 return pvjsonGroup;
556 })
557 .map(function (pvjsonEntity) {
558 return processor.output;
559 });
560 }
561 catch (err) {
562 return hl.fromError(err);
563 }
564 }
565 }
566 else {
567 setPvjsonEntity(pvjsonEntity);
568 processor.output = iassign(processor.output, function (o) {
569 return o.pathway.contains;
570 }, insertEntityIdAndSortByZIndex);
571 finalSortedStream = hl([processor.output]);
572 }
573 return finalSortedStream;
574 });
575 pvjsonEntityStream.observe().last().doto(function () {
576 processor.pvjsonEntityLatestStream.end();
577 });
578 const openControlledVocabularyStream = hl(cxmlSources["/Pathway/Biopax/bp:openControlledVocabulary"])
579 .map(processPropertiesAndType("openControlledVocabulary"))
580 .map(function (openControlledVocabulary) {
581 const vocabularyName = openControlledVocabulary.ontology;
582 let vocabularyIRI = VOCABULARY_NAME_TO_IRI[vocabularyName];
583 if (!vocabularyIRI) {
584 return hl.fromError(new Error(`
585 Encountered unexpected name "${vocabularyName}" for openControlledVocabulary,
586 with xrefIdentifier "${openControlledVocabulary.xrefIdentifier}"
587 `));
588 /* TODO should we use this?
589 vocabularyIRI = `http://www.ebi.ac.uk/miriam/main/search?query=${vocabularyName.replace(
590 /\ /,
591 "+"
592 )}#`;
593 //*/
594 }
595 openControlledVocabulary.id =
596 vocabularyIRI + openControlledVocabulary.xrefIdentifier;
597 return openControlledVocabulary;
598 })
599 .collect()
600 .map(function (openControlledVocabularies) {
601 // TODO should these go through the processor instead?
602 processor.output = iassign(processor.output, function (o) {
603 const { pathway, entitiesById } = o;
604 openControlledVocabularies.forEach(function (openControlledVocabulary) {
605 const { id } = openControlledVocabulary;
606 entitiesById[id] = openControlledVocabulary;
607 if (openControlledVocabulary.ontology === "Pathway Ontology") {
608 pathway.type.push(id);
609 }
610 });
611 return o;
612 });
613 return processor.output;
614 });
615 const publicationXrefStream = hl(cxmlSources["/Pathway/Biopax/bp:PublicationXref"])
616 .map(processPropertiesAndType("PublicationXref"))
617 .collect()
618 .map(function (publicationXrefs) {
619 publicationXrefs
620 .sort(function (a, b) {
621 const yearA = parseInt(a.year);
622 const yearB = parseInt(b.year);
623 if (yearA > yearB) {
624 return 1;
625 }
626 else if (yearA < yearB) {
627 return -1;
628 }
629 else {
630 return 0;
631 }
632 })
633 .forEach(function (publicationXref, i) {
634 publicationXref.textContent = String(i + 1);
635 });
636 return publicationXrefs;
637 })
638 .map(function (publicationXrefs) {
639 // TODO should these go through the processor instead?
640 processor.output = iassign(processor.output, function (o) {
641 return o.entitiesById;
642 }, function (entitiesById) {
643 publicationXrefs.forEach(function (publicationXref) {
644 entitiesById[publicationXref.id] = publicationXref;
645 });
646 return entitiesById;
647 });
648 return processor.output;
649 });
650 /* TODO do we need to handle these?
651 <xsd:element ref="gpml:InfoBox" minOccurs="1" maxOccurs="1" />
652 <xsd:element ref="gpml:Legend" minOccurs="0" maxOccurs="1"/>
653 */
654 return hl([
655 pathwayMetadataStream,
656 pathwayCommentStream,
657 pvjsonEntityStream,
658 openControlledVocabularyStream,
659 publicationXrefStream
660 ])
661 .merge()
662 .errors(function (err) {
663 throw new VError(err, ` when converting pathway ${pathwayIri}
664 `);
665 });
666 // // TODO Double-check old code to make sure nothing is missed.
667 // // TODO does the stream ever end?
668 // // TODO does backpressure work?
669 // TODO compare the old pvjs/kaavio code below for grouping entities to
670 // ensure we don't have any regression errors.
671 /*
672 getGroupedZIndexedEntities(zIndexedEntities) {
673 const { entitiesById } = this.props;
674 return zIndexedEntities
675 .filter(entity => !entity.isPartOf)
676 .reduce(function(acc, entity) {
677 const kaavioType = entity.kaavioType;
678 if (kaavioType === "Group") {
679 // TODO: refactor this so that contains is actually a map of the contained elements. Not just an array of their IDs
680 entity.contains = entity.contains
681 .map(id => entitiesById[id])
682 .sort(function(a, b) {
683 const zIndexA = a.zIndex;
684 const zIndexB = b.zIndex;
685 if (zIndexA < zIndexB) {
686 return 1;
687 } else if (zIndexA > zIndexB) {
688 return -1;
689 } else {
690 return 0;
691 }
692 })
693 .map(entity => entity.id);
694 } else if (entity.hasOwnProperty("burrs")) {
695 entity.burrs = entity.burrs
696 .map(id => entitiesById[id])
697 .sort(function(a, b) {
698 const zIndexA = a.zIndex;
699 const zIndexB = b.zIndex;
700 if (zIndexA < zIndexB) {
701 return 1;
702 } else if (zIndexA > zIndexB) {
703 return -1;
704 } else {
705 return 0;
706 }
707 })
708 .map(entity => entity.id);
709 }
710 if (
711 ["Burr"].indexOf(kaavioType) === -1 &&
712 !entity.hasOwnProperty("isPartOf")
713 ) {
714 acc.push(entity);
715 }
716 return acc;
717 }, []);
718 }
719 //*/
720}
721//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9Qdmpzb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvMjAxM2EvdG9Qdmpzb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyw2QkFBNkIsQ0FBQztBQUNyQyx1RUFBdUU7QUFFdkUsT0FBTyxFQUFFLE1BQU0sSUFBSSxPQUFPLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDM0MsT0FBTyxFQUNMLE1BQU0sRUFFTixPQUFPLEVBRVAsS0FBSyxFQUVMLFVBQVUsRUFHVixJQUFJLEVBQ0osWUFBWSxFQUNaLE9BQU8sRUFFUCxRQUFRLEVBQ1IsR0FBRyxFQUNILElBQUksRUFDSixTQUFTLEVBRVQsTUFBTSxFQUNOLFNBQVMsRUFDVCxTQUFTLEVBRVYsTUFBTSxXQUFXLENBQUM7QUFDbkIsT0FBTyxLQUFLLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDL0IsT0FBTyxLQUFLLE1BQU0sTUFBTSxRQUFRLENBQUM7QUFFakMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBa0JuRCxPQUFPLEtBQUssU0FBUyxNQUFNLHNDQUFzQyxDQUFDO0FBRWxFLE9BQU8sS0FBSyxZQUFZLE1BQU0saUJBQWlCLENBQUM7QUFFaEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN6QyxPQUFPLEVBQ0wsY0FBYyxJQUFJLGtCQUFrQixFQUNwQyxpQkFBaUIsSUFBSSxxQkFBcUIsRUFDM0MsTUFBTSxjQUFjLENBQUM7QUFDdEIsT0FBTyxFQUNMLGNBQWMsSUFBSSxtQkFBbUIsRUFDckMsaUJBQWlCLElBQUksc0JBQXNCLEVBQzVDLE1BQU0sVUFBVSxDQUFDO0FBQ2xCLE9BQU8sRUFDTCxRQUFRLEVBRVIsaUJBQWlCLEVBQ2pCLGFBQWEsRUFDYixZQUFZLEVBQ1osWUFBWSxFQUNaLGFBQWEsRUFDYixzQkFBc0IsRUFDdEIsWUFBWSxFQUNaLGtCQUFrQixFQUNsQixTQUFTLEVBQ1QsbUJBQW1CLEVBQ25C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
\No newline at end of file