Skip to content
Snippets Groups Projects
Commit e878fe27 authored by David Rouquet's avatar David Rouquet
Browse files

new TetrasLab dash import

parent a9d8c3e4
Branches main
No related tags found
No related merge requests found
%% Cell type:code id:0d91640d-23ea-4079-b765-2eea030926c5 tags: %% Cell type:code id:0d91640d-23ea-4079-b765-2eea030926c5 tags:
``` python ``` python
%%capture %%capture
try:
from tetraslab.dash import Dash
except ModuleNotFoundError:
print("ModuleNotFoundError: No module named 'tetraslab', defaulting to regular Dash.")
from dash import Dash
import importlib.util import importlib.util
import re import re
import amrlib import amrlib
from amrlib.graph_processing.amr_plot import AMRPlot from amrlib.graph_processing.amr_plot import AMRPlot
import uuid import uuid
from IPython.display import SVG, display from IPython.display import SVG, display
import os import os
import shutil import shutil
import subprocess import subprocess
from subprocess import Popen, PIPE, STDOUT from subprocess import Popen, PIPE, STDOUT
from glob import glob from glob import glob
import sys import sys
import os import os
from IPython.display import HTML,IFrame from IPython.display import HTML,IFrame
import ipywidgets import ipywidgets
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
from dash import dcc, html, Input, Output from dash import dcc, html, Input, Output
from jupyter_dash import JupyterDash as Dash
from dash.dependencies import Input, Output, State from dash.dependencies import Input, Output, State
import base64 import base64
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import configparser import configparser
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read('config.ini') config.read('config.ini')
#config['LIB'][''] #config['LIB']['']
AMR_BATCH_PATH = config['LIB']['AMR_BATCH_PATH'] AMR_BATCH_PATH = config['LIB']['AMR_BATCH_PATH']
sys.path.insert(0, os.path.abspath(AMR_BATCH_PATH)) sys.path.insert(0, os.path.abspath(AMR_BATCH_PATH))
import amrbatch import amrbatch
TENET_PATH = config['LIB']['TENET_PATH'] TENET_PATH = config['LIB']['TENET_PATH']
sys.path.insert(0, os.path.abspath(TENET_PATH)) sys.path.insert(0, os.path.abspath(TENET_PATH))
import tenet import tenet
BASE_URL = 'https://mars.tetras-lab.io' BASE_URL = 'https://mars.tetras-lab.io'
DASHBOARD_NUM = '1' DASHBOARD_NUM = '1'
onto_prefix="ontologyTarget" onto_prefix="ontologyTarget"
AMR_MODEL_PATH = config['LIB']['AMR_MODEL_PATH'] AMR_MODEL_PATH = config['LIB']['AMR_MODEL_PATH']
AMRLD_PATH = config['LIB']['AMRLD_PATH'] AMRLD_PATH = config['LIB']['AMRLD_PATH']
owl2vowlPath = config['LIB']['owl2vowlPath'] owl2vowlPath = config['LIB']['owl2vowlPath']
WEBVOWL_PATH = config['LIB']['WEBVOWL_PATH'] WEBVOWL_PATH = config['LIB']['WEBVOWL_PATH']
MEDIA_PATH = "/opt/dashboards/media/"+DASHBOARD_NUM+"/" MEDIA_PATH = "/opt/dashboards/media/"+DASHBOARD_NUM+"/"
MEDIA_URL = BASE_URL+"/dashboard/"+DASHBOARD_NUM+"/media/" MEDIA_URL = BASE_URL+"/dashboard/"+DASHBOARD_NUM+"/media/"
# The following is basically `import tenet` # The following is basically `import tenet`
#spec=importlib.util.spec_from_file_location("tenet",TENET_PATH+'tenet/__init__.py') #spec=importlib.util.spec_from_file_location("tenet",TENET_PATH+'tenet/__init__.py')
#tenet = importlib.util.module_from_spec(spec) #tenet = importlib.util.module_from_spec(spec)
#spec.loader.exec_module(tenet) #spec.loader.exec_module(tenet)
``` ```
%% Cell type:code id:295e4aef-bbd8-40f0-8d84-0b8032b7b039 tags: %% Cell type:code id:295e4aef-bbd8-40f0-8d84-0b8032b7b039 tags:
``` python ``` python
#stog = amrlib.load_stog_model(model_dir="/opt/dashboards/TetrasMARS/corpus/cm-tool/amrModel/model_parse_xfm_bart_large-v0_1_0") #stog = amrlib.load_stog_model(model_dir="/opt/dashboards/TetrasMARS/corpus/cm-tool/amrModel/model_parse_xfm_bart_large-v0_1_0")
#prefixPath = uuidDirPath+"file" #prefixPath = uuidDirPath+"file"
#penmanPath = prefixPath+".amr.penman" #penmanPath = prefixPath+".amr.penman"
#svgPath = prefixPath+".amr.svg" #svgPath = prefixPath+".amr.svg"
#ttlFilePath = uuidDirPath+onto_prefix+"-0/"+onto_prefix+"_factoid.ttl" #ttlFilePath = uuidDirPath+onto_prefix+"-0/"+onto_prefix+"_factoid.ttl"
``` ```
%% Cell type:code id:a410a6b3-865d-441f-9b83-90a1badae291 tags: %% Cell type:code id:a410a6b3-865d-441f-9b83-90a1badae291 tags:
``` python ``` python
def clean_sting(string): def clean_sting(string):
""" Sentence cleanup as needed """ """ Sentence cleanup as needed """
return re.sub("(\.)*\\n", "", string) return re.sub("(\.)*\\n", "", string)
def string2amr(string,stog): def string2amr(string,stog):
stog_result = stog.parse_sents([clean_sting(string)], add_metadata=True) stog_result = stog.parse_sents([clean_sting(string)], add_metadata=True)
return stog_result[0] return stog_result[0]
def show_svg(path): def show_svg(path):
display(SVG(filename=path)) display(SVG(filename=path))
def add_id_in_penman_if_needed(penmanStr,uuidStr): def add_id_in_penman_if_needed(penmanStr,uuidStr):
if not(penmanStr.startswith('# ::id')): if not(penmanStr.startswith('# ::id')):
penmanStr = '# ::id '+uuidStr+'\n'+penmanStr penmanStr = '# ::id '+uuidStr+'\n'+penmanStr
return penmanStr return penmanStr
def owl2vowl(ttlFilePath, webvowlFileName, webvowlFilePath, uuid='', importList=[]): def owl2vowl(ttlFilePath, webvowlFileName, webvowlFilePath, uuid='', importList=[]):
# Run java parser # Run java parser
if importList == []: if importList == []:
cmd = ['java', '-jar', owl2vowlPath, cmd = ['java', '-jar', owl2vowlPath,
'-file', ttlFilePath] '-file', ttlFilePath]
else: else:
cmd = ['java', '-jar', owl2vowlPath, cmd = ['java', '-jar', owl2vowlPath,
'-file', ttlFilePath, '-file', ttlFilePath,
'-dependencies'] + importList '-dependencies'] + importList
with Popen(cmd, stdout=PIPE, stderr=STDOUT) as p: with Popen(cmd, stdout=PIPE, stderr=STDOUT) as p:
p.wait() p.wait()
p.stdout.flush() p.stdout.flush()
if p.returncode != 0: if p.returncode != 0:
print("Error in owl2vowl: \n\n"+p.stdout.read().decode()) print("Error in owl2vowl: \n\n"+p.stdout.read().decode())
os.rename(webvowlFileName, webvowlFilePath) os.rename(webvowlFileName, webvowlFilePath)
def localImage2htmlImg(imgPath): def localImage2htmlImg(imgPath):
with open(imgPath, "rb") as image_file: with open(imgPath, "rb") as image_file:
imageStr = image_file.read() imageStr = image_file.read()
root = ET.fromstring(imageStr) root = ET.fromstring(imageStr)
widthInt = int(root.attrib['width'].replace("pt","")) widthInt = int(root.attrib['width'].replace("pt",""))
if widthInt > 1700 : if widthInt > 1700 :
widthStr = "100%" widthStr = "100%"
else: else:
widthStr = str(widthInt)+"pt" widthStr = str(widthInt)+"pt"
img_data = base64.b64encode(imageStr) img_data = base64.b64encode(imageStr)
img_data = img_data.decode() img_data = img_data.decode()
img_data = "data:image/svg+xml;base64,{}".format(img_data) img_data = "data:image/svg+xml;base64,{}".format(img_data)
# ... # ...
return html.Img(id="tag_id", src=img_data, width=widthStr, height="100%", className="img_class")#, alt="my image" return html.Img(id="tag_id", src=img_data, width=widthStr, height="100%", className="img_class")#, alt="my image"
``` ```
%% Cell type:code id:5fd9cf0c-990a-4776-b206-8cc94f87c7be tags: %% Cell type:code id:5fd9cf0c-990a-4776-b206-8cc94f87c7be tags:
``` python ``` python
def processStr(input): def processStr(input):
# Define usefull variable and paths based on a uuid # Define usefull variable and paths based on a uuid
uuidStr = str(uuid.uuid4()) uuidStr = str(uuid.uuid4())
uuidDirPath = "/opt/data/tmp/demo-tetras-mars/"+uuidStr+'/' uuidDirPath = "/opt/data/tmp/demo-tetras-mars/"+uuidStr+'/'
os.mkdir(uuidDirPath) os.mkdir(uuidDirPath)
fullOntoPath = uuidDirPath+'full-ontology.ttl' fullOntoPath = uuidDirPath+'full-ontology.ttl'
ontoBySentencePath = uuidDirPath+'onto-by-sentence/' ontoBySentencePath = uuidDirPath+'onto-by-sentence/'
webvowlFileName = fullOntoPath.split('/')[-1].replace('ttl','json') webvowlFileName = fullOntoPath.split('/')[-1].replace('ttl','json')
webvowlFilePath = WEBVOWL_PATH+uuidStr+'_'+webvowlFileName webvowlFilePath = WEBVOWL_PATH+uuidStr+'_'+webvowlFileName
uuidZipPath = MEDIA_PATH+uuidStr # without the .zip extention uuidZipPath = MEDIA_PATH+uuidStr # without the .zip extention
uuidZipUrl = MEDIA_URL+uuidStr+".zip" uuidZipUrl = MEDIA_URL+uuidStr+".zip"
# Generate an AMR graph by sentence in a subfolder (with companion files such as images of the graphs) # Generate an AMR graph by sentence in a subfolder (with companion files such as images of the graphs)
try: try:
amr_graph_list = amrbatch.parse_document_string_to_produce_amr_graph( amr_graph_list = amrbatch.parse_document_string_to_produce_amr_graph(
input, None, amr_model_path=AMR_MODEL_PATH, output_dirpath=uuidDirPath, input, None, amr_model_path=AMR_MODEL_PATH, output_dirpath=uuidDirPath,
amrld_serialization=True) amrld_serialization=True)
except: except:
errorStr = "Error generating AMR graphs. You can try a simpler input. Sorry :(" errorStr = "Error generating AMR graphs. You can try a simpler input. Sorry :("
# Construct ontologies from each AMR graph plus a "full" one that is the union # Construct ontologies from each AMR graph plus a "full" one that is the union
try: try:
factoids = tenet.create_ontology_from_amrld_dir( factoids = tenet.create_ontology_from_amrld_dir(
uuidDirPath, uuidDirPath,
onto_prefix="http://ontologies", onto_prefix="http://ontologies",
out_file_path=fullOntoPath, out_file_path=fullOntoPath,
technical_dir_path=ontoBySentencePath) technical_dir_path=ontoBySentencePath)
owl2vowl(fullOntoPath, webvowlFileName, webvowlFilePath, uuid=uuidStr) owl2vowl(fullOntoPath, webvowlFileName, webvowlFilePath, uuid=uuidStr)
errorStr = "" errorStr = ""
except: except:
errorStr = "Error generating ontology. You can still clic this link to download AMR graphs or see them below. Try a simpler input, sorry :(" errorStr = "Error generating ontology. You can still clic this link to download AMR graphs or see them below. Try a simpler input, sorry :("
# Create a zip file so the user can download all generated files # Create a zip file so the user can download all generated files
shutil.make_archive(uuidZipPath, 'zip', uuidDirPath) shutil.make_archive(uuidZipPath, 'zip', uuidDirPath)
return uuidDirPath, uuidZipUrl, webvowlFilePath, errorStr return uuidDirPath, uuidZipUrl, webvowlFilePath, errorStr
``` ```
%% Cell type:code id:0cd10e8b-cf7a-4fd4-b8ac-540fcb943325 tags: %% Cell type:code id:0cd10e8b-cf7a-4fd4-b8ac-540fcb943325 tags:
``` python ``` python
################################################################################################## ##################################################################################################
# THE FOLLOWING PART IS SPECIFIC TO TÉTRAS LAB # THE FOLLOWING PART IS SPECIFIC TO TÉTRAS LAB
# #
# The _get_tl_config function gets configuration parameters for your # The _get_tl_config function gets configuration parameters for your
# Tétras Lab instance. # Tétras Lab instance.
# Those parameters are passed when initialising the Dash app. # Those parameters are passed when initialising the Dash app.
################################################################################################## ##################################################################################################
def _get_tl_config(): def _get_tl_config():
import socket, errno, os import socket, errno, os
# Find a free port # Find a free port
host = "0.0.0.0" host = "0.0.0.0"
port = 8050 port = 8050
end = 9999 end = 9999
found = False found = False
while not found: while not found:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try: try:
s.bind((host, port)) s.bind((host, port))
found = True found = True
except socket.error as e: except socket.error as e:
if e.errno == errno.EADDRINUSE: if e.errno == errno.EADDRINUSE:
port = port + 1 port = port + 1
if (port > end): if (port > end):
raise "No available APP port" raise "No available APP port"
else: else:
raise e raise e
if (os.getenv("HOST", None) is not None): if (os.getenv("HOST", None) is not None):
proto = os.getenv("PROTO") proto = os.getenv("PROTO")
actualhost = os.getenv("JUPYTER_HOST", os.getenv("VOILA_HOST", "")) actualhost = os.getenv("JUPYTER_HOST", os.getenv("VOILA_HOST", ""))
localport = os.getenv("PORT", 80) localport = os.getenv("PORT", 80)
intermediatehost = os.getenv("HOST", "localhost") intermediatehost = os.getenv("HOST", "localhost")
base_path = f"/{actualhost}/app_proxy/{port}/" base_path = f"/{actualhost}/app_proxy/{port}/"
proxified= f"{proto}://{intermediatehost}:{localport}{base_path}" proxified= f"{proto}://{intermediatehost}:{localport}{base_path}"
localurl = f"http://{host}:{port}" localurl = f"http://{host}:{port}"
proxy = f"{localurl}::{proxified}" proxy = f"{localurl}::{proxified}"
return ((proxified, host, port, proxy, base_path)) return ((proxified, host, port, proxy, base_path))
return ((f"http://localhost:{port}", host, port, None, "/")) return ((f"http://localhost:{port}", host, port, None, "/"))
server_url, host, port, proxy, base_path = _get_tl_config() server_url, host, port, proxy, base_path = _get_tl_config()
app = Dash( app = Dash(
server_url=server_url,
requests_pathname_prefix=base_path, requests_pathname_prefix=base_path,
external_stylesheets=[dbc.themes.BOOTSTRAP] external_stylesheets=[dbc.themes.BOOTSTRAP]
) )
################################################################################################## ##################################################################################################
################################################################################################## ##################################################################################################
# THE FOLLOWING PART IS GENERIC (JUPYTER)-DASH CODE FROM https://dash.plotly.com/basic-callbacks # THE FOLLOWING PART IS GENERIC (JUPYTER)-DASH CODE FROM https://dash.plotly.com/basic-callbacks
# #
# The _get_tl_config function gets configuration parameters for your # The _get_tl_config function gets configuration parameters for your
# Tétras Lab instance. # Tétras Lab instance.
# Those parameters are passed when initialising the Dash app. # Those parameters are passed when initialising the Dash app.
################################################################################################## ##################################################################################################
app.layout = html.Div([ app.layout = html.Div([
html.H4("Enter an english text and click on the button bellow to construct an ontology.", style={'text-align': 'center'}), html.H4("Enter an english text and click on the button bellow to construct an ontology.", style={'text-align': 'center'}),
html.Br(), html.Br(),
html.P("It should take about 10 to 30 sec/sentence. You can then browse the results online or download them as a zip file.", style={'text-align': 'center'}), html.P("It should take about 10 to 30 sec/sentence. You can then browse the results online or download them as a zip file.", style={'text-align': 'center'}),
html.P("The text must be a simple succession of sentences. Fancy formating is not supported at the moment. ", style={'text-align': 'center'}), html.P("The text must be a simple succession of sentences. Fancy formating is not supported at the moment. ", style={'text-align': 'center'}),
html.Br(), html.Br(),
html.Br(), html.Br(),
html.Div(dbc.Textarea( html.Div(dbc.Textarea(
id='textarea-state', id='textarea-state',
value='The Solar System is the gravitationally bound system of the Sun. The inner system planets are terrestrial planets composed of rock and metal. The outer system planets are giant planets. The two largest planets, Jupiter and Saturn, are gas giants, being composed of hydrogen and helium.',style={'width': '100%', 'height': 130}), value='The Solar System is the gravitationally bound system of the Sun. The inner system planets are terrestrial planets composed of rock and metal. The outer system planets are giant planets. The two largest planets, Jupiter and Saturn, are gas giants, being composed of hydrogen and helium.',style={'width': '100%', 'height': 130}),
style={'width': '60%', 'height': 130, 'margin-left': 'auto', 'margin-right': 'auto'}, style={'width': '60%', 'height': 130, 'margin-left': 'auto', 'margin-right': 'auto'},
), ),
html.Br(), html.Br(), html.Br(), html.Br(),
html.Div( html.Div(
dbc.Button('Construct AMR graphs and ontology', id='textarea-state-button', n_clicks=0, outline=True, color="primary"), dbc.Button('Construct AMR graphs and ontology', id='textarea-state-button', n_clicks=0, outline=True, color="primary"),
className="text-center"), className="text-center"),
html.Br(), html.Br(),
dcc.Loading(html.Div(html.A(children="", href='', target="_blank",id="download-link"), className="text-center")), dcc.Loading(html.Div(html.A(children="", href='', target="_blank",id="download-link"), className="text-center")),
html.Br(), html.Br(),
html.Br(), html.Br(),
dcc.Loading(html.Div(id='my-output'), type='circle'), dcc.Loading(html.Div(id='my-output'), type='circle'),
]) ])
@app.callback( @app.callback(
#Output('textarea-state-output', 'children'), #Output('textarea-state-output', 'children'),
Output(component_id='my-output', component_property='children'), Output(component_id='my-output', component_property='children'),
Output(component_id='download-link', component_property='children'), Output(component_id='download-link', component_property='children'),
Output(component_id='download-link', component_property='href'), Output(component_id='download-link', component_property='href'),
Input('textarea-state-button', 'n_clicks'), Input('textarea-state-button', 'n_clicks'),
State('textarea-state', 'value'), State('textarea-state', 'value'),
prevent_initial_call=True, prevent_initial_call=True,
) )
def update_output(n_clicks, value): def update_output(n_clicks, value):
if n_clicks > 0: if n_clicks > 0:
uuidDirPath, uuidZipUrl, webvowlFilePath, errorStr = processStr(value) uuidDirPath, uuidZipUrl, webvowlFilePath, errorStr = processStr(value)
if "Error" in errorStr: if "Error" in errorStr:
feedbackStr = errorStr feedbackStr = errorStr
else: else:
feedbackStr = "Download Zip File" feedbackStr = "Download Zip File"
#show_svg(svgPath) #show_svg(svgPath)
#display(IFrame(BASE_URL+'''/webvowl/#{}">'''.format(webvowlFilePath.replace("/opt/webvowl/","").replace(".json","")),800,1200)) #display(IFrame(BASE_URL+'''/webvowl/#{}">'''.format(webvowlFilePath.replace("/opt/webvowl/","").replace(".json","")),800,1200))
return [dbc.Row([dbc.Col(), return [dbc.Row([dbc.Col(),
dbc.Col(dbc.Accordion( dbc.Col(dbc.Accordion(
[dbc.AccordionItem( [dbc.AccordionItem(
[ [
localImage2htmlImg(svgPath) localImage2htmlImg(svgPath)
], ],
title="AMR Graph for sentence "+re.sub(r'.document.*', '', svgPath.replace(uuidDirPath+"document-","")), title="AMR Graph for sentence "+re.sub(r'.document.*', '', svgPath.replace(uuidDirPath+"document-","")),
) )
for svgPath in sorted(glob(uuidDirPath+"document-*/*.svg")) for svgPath in sorted(glob(uuidDirPath+"document-*/*.svg"))
]+ ]+
[dbc.AccordionItem( [dbc.AccordionItem(
[ [
"You can click a class to see its instances in the right panel, they are not shown in the graph.", "You can click a class to see its instances in the right panel, they are not shown in the graph.",
html.Br(), html.Br(),
html.A(children="Open ontology browser in separate tab.", href=BASE_URL+'''/webvowl/#{}'''.format(webvowlFilePath.replace("/opt/webvowl/","").replace(".json","")), target="_blank",id="webvowl-link"), html.A(children="Open ontology browser in separate tab.", href=BASE_URL+'''/webvowl/#{}'''.format(webvowlFilePath.replace("/opt/webvowl/","").replace(".json","")), target="_blank",id="webvowl-link"),
html.Br(), html.Br(),
html.Iframe(src=BASE_URL+'''/webvowl/#{}'''.format(webvowlFilePath.replace("/opt/webvowl/","").replace(".json","")),style={"height": "800px", "width": "100%"}), html.Iframe(src=BASE_URL+'''/webvowl/#{}'''.format(webvowlFilePath.replace("/opt/webvowl/","").replace(".json","")),style={"height": "800px", "width": "100%"}),
], ],
title="Browse ontology", item_id='onto' title="Browse ontology", item_id='onto'
) )
] , active_item='onto'), width=10), dbc.Col() ] , active_item='onto'), width=10), dbc.Col()
]), ]),
feedbackStr, uuidZipUrl feedbackStr, uuidZipUrl
] ]
#@app.callback( #@app.callback(
# Output("download-zip", "data"), # Output("download-zip", "data"),
# Input("download-zip-button", "n_clicks"), # Input("download-zip-button", "n_clicks"),
# prevent_initial_call=True, # prevent_initial_call=True,
#) #)
#def func(n_clicks): #def func(n_clicks):
# if n_clicks > 0: # if n_clicks > 0:
# return dcc.send_file(BASE_URL+'/dashboard/17/media/9f7287d0-e7b2-4328-9137-7a7c44225b68.zip') # return dcc.send_file(BASE_URL+'/dashboard/17/media/9f7287d0-e7b2-4328-9137-7a7c44225b68.zip')
app.run_server(mode="inline", app.run_server(mode="inline",
#mode="external", #mode="external",
host=host, port=port, proxy=proxy, height=2000) host=host, port=port, proxy=proxy, height=2000)
################################################################################################## ##################################################################################################
``` ```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment