Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

antimony side sbml diagram integration #128

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions vscode-antimony/all-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ bioservices==1.8.3
# ols_client==0.0.9
AMAS-sb==0.0.1
orjson==3.8.0
SBMLDiagrams
tellurium
11 changes: 11 additions & 0 deletions vscode-antimony/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"onCommand:antimony.switchIndicationOff",
"onCommand:antimony.convertAntimonyToSBML",
"onCommand:antimony.convertSBMLToAntimony",
"onCommand:antimony.convertAntimonyToDiagram",
"onCommand:antimony.startSBMLWebview",
"onCommand:antimony.startAntimonyWebview",
"onCustomEditor:antimony.sbmlEditor",
Expand Down Expand Up @@ -122,6 +123,10 @@
"command": "antimony.convertSBMLToAntimony",
"title": "Convert to Antimony"
},
{
"command": "antimony.convertAntimonyToDiagram",
"title": "Convert to Diagram"
},
{
"command": "antimony.startSBMLWebview",
"title": "preview SBML",
Expand Down Expand Up @@ -189,6 +194,12 @@
"title": "Convert to Antimony",
"group": "1_modification",
"when": "editorLangId == xml"
},
{
"command": "antimony.convertAntimonyToDiagram",
"title": "Convert to Diagram",
"group": "1_modification",
"when": "editorLangId == antimony"
}
],
"editor/title": [
Expand Down
86 changes: 85 additions & 1 deletion vscode-antimony/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ export async function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand('antimony.convertSBMLToAntimony',
(...args: any[]) => convertSBMLToAntimony(context, args)));

// SBMLDiagram
context.subscriptions.push(
vscode.commands.registerCommand('antimony.convertAntimonyToDiagram',
(...args: any[]) => {
convertAntimonyToDiagram(context, args);
}));

// custom editor
context.subscriptions.push(await SBMLEditorProvider.register(context, client));
context.subscriptions.push(await AntimonyEditorProvider.register(context, client));
Expand Down Expand Up @@ -281,6 +288,84 @@ async function checkConversionResult(result, type) {
}
}

async function convertAntimonyToDiagram(context: vscode.ExtensionContext, args: any[]) {
if (!client) {
utils.pythonInterpreterError();
return;
}
await client.onReady();

await vscode.commands.executeCommand("workbench.action.focusActiveEditorGroup");

const doc = vscode.window.activeTextEditor.document;
const uri = doc.uri.toString();

let speciesStr;
vscode.commands.executeCommand('antimony.getDiagramQuickpick', uri).then(async (result) => {
speciesStr = result;
let speciesList = speciesStr.species_list.split(' ');
let selectedSpeciesList = await vscode.window.showQuickPick(speciesList, {canPickMany: true, placeHolder: 'select species to include in your diagram'});
if (selectedSpeciesList && selectedSpeciesList.length === 0) {
vscode.window.showErrorMessage('Please select at least one species!');
} else {
const options: vscode.OpenDialogOptions = {
openLabel: "Select",
canSelectFolders: true,
canSelectFiles: false,
canSelectMany: false,
filters: {
'Images': ['png']
},
title: "Select a location to save your SBML diagram"
};
vscode.window.showOpenDialog(options).then(fileUri => {
if (fileUri && fileUri[0]) {
let diagram;
vscode.commands.executeCommand('antimony.antFiletoDiagram', vscode.window.activeTextEditor.document,
fileUri[0].fsPath, selectedSpeciesList).then(async (result) => {
let error = await checkSBMLDiagramResult(result);
diagram = result;
if (!diagram.error) {
const panel = vscode.window.createWebviewPanel(
'antimony',
'SBMLDiagram',
vscode.ViewColumn.Two,
{
localResourceRoots: [vscode.Uri.file(path.dirname(diagram.file))]
}
);
const pngSrc = panel.webview.asWebviewUri(vscode.Uri.file(diagram.file));
panel.webview.html = getWebviewContent(pngSrc);
}
});
}
});
}
});
}

function getWebviewContent(uri: vscode.Uri) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SBMLDiagram</title>
</head>
<body>
<img src=${uri} width="300" />
</body>
</html>`;
}

async function checkSBMLDiagramResult(result) {
if (result.error) {
vscode.window.showErrorMessage(`Could not convert file to diagram: ${result.error}`);
} else {
vscode.window.showInformationMessage(`${result.msg}`);
}
}

async function createAnnotationDialog(context: vscode.ExtensionContext, args: any[]) {
// wait till client is ready, or the Python server might not have started yet.
// note: this is necessary for any command that might use the Python language server.
Expand All @@ -290,7 +375,6 @@ async function createAnnotationDialog(context: vscode.ExtensionContext, args: an
}
await client.onReady();
await vscode.commands.executeCommand("workbench.action.focusActiveEditorGroup");

// dialog for annotation
const selection = vscode.window.activeTextEditor.selection;

Expand Down
44 changes: 43 additions & 1 deletion vscode-antimony/src/server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import time
from AMAS import recommender, species_annotation
from bioservices import ChEBI
import SBMLDiagrams
import tellurium as te

# TODO remove this for production
logging.basicConfig(filename='vscode-antimony-dep.log', filemode='w', level=logging.DEBUG)
Expand Down Expand Up @@ -149,6 +151,47 @@ def sbml_file_to_ant_file(ls: LanguageServer, args):
'file': full_path_name
}

@server.thread()
@server.command('antimony.getDiagramQuickpick')
def ant_file_to_sbml_file(ls: LanguageServer, args):
uri = args[0]
doc = server.workspace.get_document(uri)
antfile_cache = get_antfile(doc)
reaction_list = antfile_cache.analyzer.reaction_list
species_set = set()
for species_names, reaction_part_str in reaction_list:
species_set.update(species_names)
species_list = list(species_set)
species_list.sort()
return {
'species_list': " ".join(species_list)
}

@server.thread()
@server.command('antimony.antFiletoDiagram')
def ant_file_to_sbml_file(ls: LanguageServer, args):
ant = args[0].fileName
output_dir = args[1]
selected_species_list = args[2]
model_str = 'model *temp()\n'
reaction_list = antfile_cache.analyzer.reaction_list
for react_prod_list, reaction_str in reaction_list:
if any((match := item) in react_prod_list for item in selected_species_list):
model_str += reaction_str + '\n'
model_str += 'end'
r = te.loada(model_str)
sbmlStr = r.getSBML()
df = SBMLDiagrams.load(sbmlStr)
model_name = os.path.basename(ant)
full_path_name = os.path.join(output_dir, os.path.splitext(model_name)[0]+'_diagram.png')
df.autolayout()
df.draw(output_fileName=full_path_name,showReactionIds=True)
return {
'msg': 'Diagram has been exported to {}'.format(output_dir),
'file': full_path_name
}


@server.thread()
@server.command('antimony.sendType')
def get_type(ls: LanguageServer, args) -> dict[str, str]:
Expand All @@ -167,7 +210,6 @@ def get_type(ls: LanguageServer, args) -> dict[str, str]:
symbols= antfile_cache.symbols_at(position)[0]

symbol = symbols[0].type.__str__()
vscode_logger.info("symbol: " + symbol)
return {
'symbol': symbol
}
Expand Down
7 changes: 7 additions & 0 deletions vscode-antimony/src/server/stibium/stibium/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def __init__(self, root: FileNode, path: str):
self.unnamed_events_num = 0
base_scope = BaseScope()
self.reaction_item = set()
self.reaction_list = list()
for child in root.children:
if isinstance(child, ErrorToken):
continue
Expand Down Expand Up @@ -492,13 +493,19 @@ def handle_reaction(self, scope: AbstractScope, reaction: Reaction, insert: bool
else:
self.table.insert(QName(scope, name), SymbolType.Reaction, reaction, comp=comp)

species_names = set()
for species in chain(reaction.get_reactants(), reaction.get_products()):
if insert:
self.import_table.insert(QName(scope, species.get_name()), SymbolType.Species, comp=comp)
self.import_table.get(QName(scope, species.get_name()))[0].in_reaction = True
else:
self.table.insert(QName(scope, species.get_name()), SymbolType.Species, comp=comp)
self.table.get(QName(scope, species.get_name()))[0].in_reaction = True
species_names.add(species.get_name_text())
reaction_str = reaction.to_string()
reaction_part_str = reaction_str.split(';')[0] + ';'
self.reaction_list.append((species_names, reaction_part_str))

rate_law = reaction.get_rate_law()
if rate_law is not None:
self.handle_arith_expr(scope, rate_law, insert)
Expand Down
11 changes: 11 additions & 0 deletions vscode-antimony/src/server/stibium/stibium/ant_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,17 @@ def get_comp(self):
if self.children[6] is not None:
return self.children[6]
return None

def to_string(self) -> str:
ret = ''
if isinstance(self, LeafNode):
ret += self.text
else:
for node in self.descendants():
if isinstance(node, LeafNode):
ret += node.text
return ret


@dataclass
class InteractionName(TrunkNode):
Expand Down