|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# VCell Tutorial: importing from Pathway Commons |
| 4 | +# |
| 5 | +# Reproduces the storyline of |
| 6 | +# vcell.org/webstart/VCell_Tutorials/Tutorial06_PathwayCommons_6.0.pdf. |
| 7 | +# See storylines/pathway-commons.md. |
| 8 | +# |
| 9 | +# The only tutorial in the set that depends on a third-party service staying up, and the |
| 10 | +# oldest document in it (2016). Both services it needs are alive: the search goes to |
| 11 | +# Pathway Commons' current pc2 API, and the import pulls BioPAX from Reactome. |
| 12 | +# |
| 13 | +set -euo pipefail |
| 14 | +. "$(cd "$(dirname "$0")" && pwd)/_common.sh" |
| 15 | + |
| 16 | +# The search term. The PDF only ever shows screenshots, so this is a choice: "insulin" |
| 17 | +# is the example the client's own code comment uses, and it returns a page of small, |
| 18 | +# well-formed Reactome pathways. |
| 19 | +QUERY="${QUERY:-insulin}" |
| 20 | +PATHWAY="${PATHWAY:-Acetylcholine regulates insulin secretion}" |
| 21 | + |
| 22 | +dismiss OK |
| 23 | +sleep 1 |
| 24 | + |
| 25 | +step "Search Pathway Commons for '$QUERY'" |
| 26 | +must tab name=LeftBottomTabbedPane "Pathway Comm" >/dev/null; sleep 2 |
| 27 | +must settext name=PathwayCommonsSearchTextField "$QUERY" >/dev/null; sleep 1 |
| 28 | +must click name=PathwayCommonsSearchButton >/dev/null |
| 29 | + |
| 30 | +# A live third-party service, so wait for an answer rather than sleeping on a guess. The |
| 31 | +# client caps the search at a 10s connect and a 30s read; this allows for both plus the |
| 32 | +# tree being built. |
| 33 | +for i in $(seq 1 30); do |
| 34 | + sleep 2 |
| 35 | + HITS=$("$B" findrow name=PathwayCommonsResponseTree "$PATHWAY" \ |
| 36 | + | python3 -c 'import json,sys;print(json.load(sys.stdin)["row"])') |
| 37 | + [ "$HITS" -ge 0 ] && break |
| 38 | +done |
| 39 | +if [ "${HITS:--1}" -lt 0 ]; then |
| 40 | + echo "FATAL: no pathway matching '$PATHWAY' after searching for '$QUERY'." >&2 |
| 41 | + echo " Pathway Commons is a third party; check that" >&2 |
| 42 | + echo " https://www.pathwaycommons.org/pc2/search still answers." >&2 |
| 43 | + exit 1 |
| 44 | +fi |
| 45 | +echo " '$PATHWAY' is row $HITS" >&2 |
| 46 | + |
| 47 | +step "Preview it: the pathway's entities, as a table" |
| 48 | +must row name=PathwayCommonsResponseTree "$HITS" >/dev/null; sleep 2 |
| 49 | +must click name=PathwayCommonsPreviewButton >/dev/null |
| 50 | +# The import pulls BioPAX Level 2 from Reactome, which is a second third-party service and |
| 51 | +# a second thing to wait for rather than sleep on. |
| 52 | +for i in $(seq 1 40); do |
| 53 | + sleep 2 |
| 54 | + ROWS=$(curl -s "http://127.0.0.1:9123/tree" | python3 -c ' |
| 55 | +import json, sys |
| 56 | +def walk(n): |
| 57 | + if n.get("name") == "PathwayPreviewTable" and n.get("table"): |
| 58 | + print(n["table"]["rowCount"]); raise SystemExit |
| 59 | + for c in n.get("children") or []: |
| 60 | + walk(c) |
| 61 | +for root in json.load(sys.stdin): |
| 62 | + walk(root) |
| 63 | +') |
| 64 | + [ "${ROWS:-0}" -gt 0 ] && break |
| 65 | +done |
| 66 | +if [ "${ROWS:-0}" -eq 0 ]; then |
| 67 | + echo "FATAL: the pathway preview stayed empty. The import reads BioPAX from" >&2 |
| 68 | + echo " https://reactome.org/ReactomeRESTfulAPI/RESTfulWS/biopaxExporter/Level2/" >&2 |
| 69 | + echo " - check that it still answers." >&2 |
| 70 | + exit 1 |
| 71 | +fi |
| 72 | +echo " $ROWS entities in '$PATHWAY'" >&2 |
| 73 | + |
| 74 | +step "Import every entity - what ctrl+A and Import > Selected Only mean" |
| 75 | +# The PDF says "hit ctrl+a" and, for a multi-page pathway, "click the right arrow icon and |
| 76 | +# repeat". The keystroke is about the rows the table currently shows, so saying the range |
| 77 | +# says the same thing - and it says it for the whole table at once, with no paging. |
| 78 | +must trows name=PathwayPreviewTable "0-" >/dev/null; sleep 2 |
| 79 | +button_menu name=PathwayPreviewImportButton 'Selected Only'; sleep 10 |
| 80 | + |
| 81 | +IMPORTED=$("$B" readcell name=PathwayPreviewTable 0 'Imported?' \ |
| 82 | + | python3 -c 'import json,sys; print(json.load(sys.stdin).get("value"))') |
| 83 | +if [ "$IMPORTED" != "true" ]; then |
| 84 | + echo "FATAL: the first entity still reads Imported? = $IMPORTED" >&2 |
| 85 | + exit 1 |
| 86 | +fi |
| 87 | + |
| 88 | +step "Pathway Objects: the list form, not the diagram" |
| 89 | +# This is the whole reason this tutorial is reachable. Steps 4 and 5 of the PDF work on the |
| 90 | +# Pathway Diagram - "click a corner of the diagram, drag your cursor over all entities" - |
| 91 | +# which is a pixel gesture on a custom canvas. But the PDF itself offers the other route |
| 92 | +# two pages later: "click Pathway Objects to organize the entities into list form", and |
| 93 | +# from there the same Physiology Links menu. Same statement, addressable. |
| 94 | +navselect 'Pathway Objects'; sleep 4 |
| 95 | +OBJECTS=$(curl -s "http://127.0.0.1:9123/tree" | python3 -c ' |
| 96 | +import json, sys |
| 97 | +def walk(n): |
| 98 | + if n.get("name") == "PathwayObjectsTable" and n.get("table"): |
| 99 | + print(n["table"]["rowCount"]); raise SystemExit |
| 100 | + for c in n.get("children") or []: |
| 101 | + walk(c) |
| 102 | +for root in json.load(sys.stdin): |
| 103 | + walk(root) |
| 104 | +') |
| 105 | +echo " $OBJECTS pathway objects" >&2 |
| 106 | + |
| 107 | +step "Import into the physiology" |
| 108 | +must trows name=PathwayObjectsTable "0-" >/dev/null; sleep 2 |
| 109 | +button_menu name=PhysiologyLinksButton 'Import into Physiology...'; sleep 8 |
| 110 | +# The dialog lists every interaction and participant it is about to create, and lets the |
| 111 | +# expressions be edited first. The tutorial accepts them as they come. |
| 112 | +dialog_button 'Import into Physiology' OK; sleep 10 |
| 113 | +# VCell then reports what it converted. Waiting for that notice is also how we know the |
| 114 | +# conversion finished. |
| 115 | +answer OK; sleep 8 |
| 116 | + |
| 117 | +# The model tree carries the counts in its own labels, which is the shortest honest way |
| 118 | +# to say "the physiology has something in it now". |
| 119 | +SPECIES=$(curl -s "http://127.0.0.1:9123/tree" | python3 -c ' |
| 120 | +import json, sys |
| 121 | +def walk(n): |
| 122 | + if n.get("name") == "bioModelEditorTree" and n.get("tree"): |
| 123 | + for row in n["tree"]["rows"]: |
| 124 | + if str(row.get("text","")).startswith("Species ("): |
| 125 | + print(row["text"]); raise SystemExit |
| 126 | + for c in n.get("children") or []: |
| 127 | + walk(c) |
| 128 | +for root in json.load(sys.stdin): |
| 129 | + walk(root) |
| 130 | +') |
| 131 | +echo " physiology now has $SPECIES" >&2 |
| 132 | +case "$SPECIES" in |
| 133 | + "Species (0)"|"") echo "FATAL: nothing was imported into the physiology" >&2; exit 1 ;; |
| 134 | +esac |
| 135 | + |
| 136 | +step "Link a species of your own to a pathway entity" |
| 137 | +# The PDF adds this species with the species tool on the reaction diagram. The Species |
| 138 | +# table has an "(add new here)" row that does the same thing - it is offered because this |
| 139 | +# model has exactly one structure, which is the condition BioModelEditorSpeciesTableModel |
| 140 | +# puts on it. |
| 141 | +navselect 'Species'; sleep 3 |
| 142 | +ADD_ROW=$(row name=SpeciesTable '(add new here)' --exact) |
| 143 | +must setcell name=SpeciesTable "$ADD_ROW" 0 "Ach_reporter" >/dev/null; sleep 3 |
| 144 | +must trow name=SpeciesTable "$(row name=SpeciesTable 'Ach_reporter' --exact)" >/dev/null; sleep 3 |
| 145 | + |
| 146 | +button_menu name=ModelPathwayLinksButton 'Edit Pathway Links...'; sleep 5 |
| 147 | +LINKS=$(dialog_table 'Edit Pathway Links') |
| 148 | +must setcell "$LINKS" "$(row "$LINKS" 'acetylcholine' --exact --in 'Entity Name')" \ |
| 149 | + "$(col "$LINKS" 'Link')" true >/dev/null; sleep 2 |
| 150 | +dialog_button 'Edit Pathway Links' Close; sleep 4 |
| 151 | + |
| 152 | +LINKED=$("$B" readcell name=SpeciesTable "$(row name=SpeciesTable 'Ach_reporter' --exact)" 'Link' \ |
| 153 | + | python3 -c 'import json,sys; print(json.load(sys.stdin).get("value"))') |
| 154 | +echo " Ach_reporter is linked to: $LINKED" >&2 |
| 155 | +if [ "$LINKED" != "acetylcholine" ]; then |
| 156 | + echo "FATAL: the pathway link did not take (Link column reads '$LINKED')" >&2 |
| 157 | + exit 1 |
| 158 | +fi |
| 159 | + |
| 160 | +step "Done -- pathway searched, previewed, imported, and a species linked to it." |
0 commit comments