Browse Source

Create graph exploring tool user interface

Arkadiusz Ryś 2 years ago
parent
commit
89071a62c2

+ 9 - 0
README.rst

@@ -7,10 +7,19 @@ Installation
 
 .. code-block:: shell
 
+   sudo apt-get install libglfw3
+   sudo apt-get install libglfw3-dev
+   sudo apt-get install freeglut3-dev
    pip install graph-exploring-tool
 
 or
 
 .. code-block:: shell
 
+   sudo apt-get install libglfw3
+   sudo apt-get install libglfw3-dev
+   sudo apt-get install freeglut3-dev
    pip install --index-url https://pip:glpat-m8mNfhxZAUnWvy7rLS1x@git.rys.one/api/v4/projects/264/packages/pypi/simple --no-deps graph-exploring-tool
+
+
+For WSL check out: https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps

+ 3 - 0
graph_exploring_tool/__main__.py

@@ -0,0 +1,3 @@
+from graph_exploring_tool.main import launch
+
+launch()

+ 319 - 0
graph_exploring_tool/graphical/interface.py

@@ -0,0 +1,319 @@
+# TODO Lot of hardcoded shizzle
+import logging
+from dataclasses import dataclass, field
+from itertools import groupby
+from typing import Optional, List
+from urllib.error import URLError
+
+import arklog
+import dearpygui.dearpygui as dpg
+import dearpygui.demo as demo
+
+from graph_exploring_tool import query
+
+arklog.set_config_logging()
+
+example_prefix = """PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
+PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
+PREFIX dtf:  <https://ontology.rys.app/dt/function/>
+PREFIX owl:  <http://www.w3.org/2002/07/owl#>
+"""
+example_query = """SELECT ?outlier ?outlier_relation ?outlier_value WHERE {
+  SERVICE <http://127.0.0.1:8000/> {
+    SELECT ?outlier ?outlier_relation ?outlier_value WHERE {
+      BIND(dtf:outlier("rotation.csv", "2", "<http://ua.be/drivetrain/description/artifacts/artifacts#drivetrain-sensor-data-v1>") AS ?outlier)
+    }
+  }
+}"""
+
+
+@dataclass(init=True, repr=True, order=False, frozen=True)
+class Replacement:
+    origin: str
+    description: str
+
+@dataclass(init=True, repr=True, order=False, frozen=True)
+class QueryTemplate:
+    group: str
+    name: str
+    prefix: str
+    query: str
+    description: str
+    visual_support: bool = False
+    replacements: List[Replacement] = field(default_factory=list)
+
+
+# TODO Grab from config files?
+query_palette = (
+    QueryTemplate(group="Types", name="Find Types", prefix=query.prefixes, query=query.get_types(), description="Find all the types stored in the knowledge graph.", visual_support=True),
+    QueryTemplate(group="Service", name="Example Function", prefix=query.prefixes, query=query.get_example(), description="Example query calling the example function.", visual_support=True),
+    QueryTemplate(group="Service", name="Outlier Function", prefix=query.prefixes, query=query.get_outlier(), description="Find an outlier in a tabular format.", visual_support=True, replacements=[
+        Replacement(origin="http://127.0.0.1:8000/", description="Which service do you want to use?"),
+        Replacement(origin="rotation.csv", description="In which file is the outlier located?"),
+        Replacement(origin="2222", description="In which column?"),
+        Replacement(origin="<http://ua.be/drivetrain/description/artifacts/artifacts#drivetrain-sensor-data-v1>", description="In which artifact?"),
+    ]),
+    QueryTemplate(group="Traceability", name="Find Versions", prefix=query.prefixes, query=query.get_versions(), description="Find all the older versions of an artifact.", visual_support=True, replacements=[Replacement(origin="art:drivetrain-sensor-data-v2", description="For which artifact are you looking for older versions?")]),
+    QueryTemplate(group="Individuals", name="Find Properties", prefix=query.prefixes, query=query.get_properties(), description="Find all possible properties of an individual.", visual_support=True, replacements=[Replacement(origin="ftg:Formalism", description="What class of individual are you looking for?")]),
+    QueryTemplate(group="Individuals", name="Find Individuals", prefix=query.prefixes, query=query.get_individuals(), description="Find all individuals.", visual_support=True, replacements=[Replacement(origin="ftg:Formalism", description="What class of individual are you looking for?"), Replacement(origin="base:hasName", description="What is the relation you are looking for?")]),
+    QueryTemplate(group="Traceability", name="Find Class Property", prefix=query.prefixes, query=query.get_class_property(), description="Find all the properties for a class.", visual_support=True, replacements=[Replacement(origin="base:ArtifactType", description="For which class do you want to find properties?")]),
+    QueryTemplate(group="Traceability", name="Match Artifact Formalism", prefix=query.prefixes, query=query.get_art_formalism(), description="Find the formalism corresponding to an artifact.", visual_support=True, replacements=[
+        Replacement(origin="art:torque-profile", description="For which artifact do you want to find formalism?")
+    ]),
+    QueryTemplate(group="Traceability", name="Find PM Relation", prefix=query.prefixes, query=query.get_pm(), description="Find the PM.", visual_support=True, replacements=[
+        Replacement(origin="ftg:Transformation", description="This should be a transformation?"),
+        Replacement(origin="ftg:outputs", description="This should be a relation from the transformation?"),
+        Replacement(origin="ftg:Formalism", description="The class of the formalism?"),
+        Replacement(origin="base:hasName", description="Relation from formalism?"),
+    ]),
+)
+
+
+def _config(sender, keyword, user_data):
+
+    widget_type = dpg.get_item_type(sender)
+    items = user_data
+
+    if widget_type == "mvAppItemType::mvRadioButton":
+        value = True
+
+    else:
+        keyword = dpg.get_item_label(sender)
+        value = dpg.get_value(sender)
+
+    if isinstance(user_data, list):
+        for item in items:
+            dpg.configure_item(item, **{keyword: value})
+    else:
+        dpg.configure_item(items, **{keyword: value})
+
+def add_main_menu():
+    with dpg.menu_bar():
+        with dpg.menu(label="Menu"):
+            dpg.add_menu_item(label="New")
+            dpg.add_menu_item(label="Open")
+            with dpg.menu(label="Open Recent"):
+                dpg.add_menu_item(label="patty.h")
+                dpg.add_menu_item(label="nick.py")
+            dpg.add_menu_item(label="Save")
+            dpg.add_menu_item(label="Exit")
+
+
+# TODO Add tab for prefixes
+def create_query_palette():
+    demo_layout_child = dpg.generate_uuid()
+    with dpg.child_window(tag=demo_layout_child, label="Query Palette", width=220, menubar=True):
+        with dpg.menu_bar():
+            dpg.add_menu(label="Query Palette", enabled=False)
+        dpg.add_input_text(label="Filter", callback=lambda s, a: dpg.set_value("__query_filter", a))
+        with dpg.filter_set(tag="__query_filter"):
+            # TODO Populate from onto/base
+            # TODO Bug fix this; actually figure out groups
+            # TODO Fix filtering
+            grouped_query_palette = groupby(sorted(query_palette, key=lambda palette_query: palette_query.group), key=lambda palette_query: palette_query.group)
+            for query_template_group, query_template_queries in grouped_query_palette:
+                with dpg.tree_node(label=query_template_group, tag=query_template_group, default_open=True, filter_key=""):
+                    full_filter = ""
+                    for query_template in query_template_queries:
+                        query_button = dpg.add_button(label=query_template.name, filter_key=query_template.name, callback=_query_palette_click, user_data=query_template)
+                        with dpg.tooltip(dpg.last_item()):
+                            dpg.add_text(query_template.description)
+                        full_filter =f"{full_filter},{query_template.name}"
+                        # TODO Make theme-ing more generic
+                        with dpg.theme() as item_theme:
+                            with dpg.theme_component(dpg.mvAll):
+                                dpg.add_theme_color(dpg.mvThemeCol_Text, (200, 200, 100), category=dpg.mvThemeCat_Core)
+                                dpg.add_theme_style(dpg.mvStyleVar_FrameRounding, 0, category=dpg.mvThemeCat_Core)
+                        if query_template.visual_support:
+                            dpg.bind_item_theme(query_button, item_theme)
+                    dpg.configure_item(query_template_group, filter_key=full_filter)
+
+def create_query_options(endpoint: str):
+    with dpg.group(horizontal=True):
+        dpg.add_combo(("Visual", "Textual"), default_value="Visual", callback=_mode_select, width=100, tag="__editor_selector")
+        dpg.add_input_text(default_value=endpoint, width=300, enabled=False, tag="__endpoint_input")
+        dpg.add_checkbox(label="debug", callback=_config)
+        dpg.add_checkbox(label="annotate", default_value=False, callback=_config) # TODO Actually make this annotate the result data with its type
+
+def create_query_editor_visual(show: bool = False):
+    with dpg.child_window(autosize_x=True, height=500, menubar=True, show=show, tag="__query_editor_visual"):
+        with dpg.menu_bar():
+            dpg.add_menu(label="Visual Query Editor", enabled=False)
+        with dpg.tab_bar():
+            with dpg.tab(label="Query", tag="__query_tab"):
+                dpg.add_input_text(default_value=example_query, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__query_editor_visual_input")
+            with dpg.tab(label="Prefix", tag="__prefix_tab"):
+                dpg.add_input_text(default_value=example_prefix, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__prefix_editor_visual_input")
+        with dpg.group(tag="__visual_editor_fields"):
+            pass
+        with dpg.group(horizontal=True):
+            dpg.add_button(label="Query", callback=_perform_query)
+            dpg.add_button(label="Save", callback=_select_directory) # TODO
+            dpg.add_button(label="Load") # TODO
+
+def create_query_editor_textual(show: bool = False):
+    with dpg.child_window(autosize_x=True, height=500, menubar=True, show=show, tag="__query_editor_textual"):
+        with dpg.menu_bar():
+            dpg.add_menu(label="Textual Query Editor", enabled=False)
+        with dpg.tab_bar():
+            with dpg.tab(label="Query", tag="__query_textual_tab"):
+                dpg.add_input_text(default_value=example_query, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__query_editor_textual_input")
+            with dpg.tab(label="Prefix", tag="__prefix_textual_tab"):
+                dpg.add_input_text(default_value=example_prefix, callback=_log, multiline=True, on_enter=True, height=300, width=-1, tag="__prefix_editor_textual_input")
+        with dpg.group(horizontal=True):
+            dpg.add_button(label="Query", callback=_perform_query)
+            dpg.add_button(label="Save", callback=_select_directory) # TODO
+            dpg.add_button(label="Load") # TODO
+
+# TODO Not at the bottom because fok you
+def create_status_console():
+    with dpg.child_window(autosize_x=True, height=35):
+        with dpg.group(horizontal=True):
+            dpg.add_text(default_value="Ready.", tag="__status_console")
+
+def set_copy(s, a):
+    dpg.set_value(s, shorten(a))
+    i = dpg.get_item_label(s)
+    dpg.set_value(f"__copy_{i}", shorten(a))
+    dpg.configure_item(f"__copy_drag_payload_{i}", drag_data=shorten(a))
+    dpg.set_value(f"__copy_drag_{i}", shorten(a))
+
+def create_query_results():
+    with dpg.child_window(autosize_x=True, autosize_y=True, menubar=True, tag="__query_results_window"):
+        with dpg.menu_bar():
+            dpg.add_menu(label="Results", enabled=False)
+        # dpg.add_input_text(label="Filter", tag="__result_filter_input", callback=lambda s, a: dpg.set_value("__result_filter", a))
+        with dpg.tab_bar():
+            with dpg.tab(label="Query Result", tag="__query_result_tab"):
+                with dpg.table(header_row=True, policy=dpg.mvTable_SizingFixedFit, row_background=True, reorderable=True,
+                               resizable=True, no_host_extendX=False, hideable=True,
+                               borders_innerV=True, delay_search=True, borders_outerV=True, borders_innerH=True,
+                               borders_outerH=True, tag="__result_table"):
+                    pass
+            with dpg.tab(label="Saved"):
+                with dpg.group(horizontal=True):
+                    with dpg.group():
+                        for i in range(0, 10):
+                            dpg.add_input_text(label=f"{i}", payload_type="string", width=500, drop_callback=lambda s, a: set_copy(s,a))
+                    with dpg.group():
+                        for i in range(0, 10):
+                            dpg.add_text(tag=f"__copy_{i}", default_value="Empty")
+                            with dpg.drag_payload(parent=dpg.last_item(), drag_data="Empty", payload_type="string", tag=f"__copy_drag_payload_{i}"):
+                                dpg.add_text(tag=f"__copy_drag_{i}", default_value="Empty")
+
+                # dpg.add_text("TODO") # TODO Add ability to save some results for reuse in templates
+            with dpg.tab(label="Debug"):
+                dpg.add_text("This is the debug tab!")
+
+
+def _log(sender: int, app_data: str, user_data: Optional[dict]):
+    logging.debug(f"sender: {sender}, \t app_data: {app_data}, \t user_data: {user_data}")
+
+def _perform_text_query(sender: int, app_data: str, user_data: Optional[dict]):
+    logging.debug(f"sender: {sender}, \t app_data: {app_data}, \t user_data: {user_data}")
+
+def _mode_select(sender: int, mode: str, user_data: Optional[dict]):
+    mode = mode.lower().strip()
+    visual = mode == "visual"
+    # TODO When showing visual we need to fix the custom fields
+    dpg.configure_item("__query_editor_visual", show=visual)
+    dpg.configure_item("__query_editor_textual", show=not visual)
+
+def shorten(identifier: str) -> str:
+    for key, value in query.reverse_prefix.items():
+        identifier = identifier.replace(key, f"{value}:")
+    return identifier
+
+def _query_palette_click(sender: int, mode: str, user_data: Optional[QueryTemplate]):
+    mode = dpg.get_value("__editor_selector").lower().strip()
+    if mode == "visual" and not user_data.visual_support:
+        logging.warning(f"Visual mode for template '{user_data.name}' not implemented yet!")
+        dpg.set_value("__status_console", f"Visual mode for template '{user_data.name}' not implemented yet!")
+        return
+    dpg.set_value("__status_console", f"Using {mode} template '{user_data.name}'.")
+    dpg.set_value(f"__prefix_editor_{mode}_input", user_data.prefix)
+    dpg.set_value(f"__query_editor_{mode}_input", user_data.query)
+    dpg.delete_item("__visual_editor_fields", children_only=True)
+    if mode == "visual" and user_data.visual_support:
+        # TODO set visual components
+        for replacement in user_data.replacements:
+            dpg.add_input_text(label=f"{replacement.description}",default_value=replacement.origin, payload_type="string", width=500, parent="__visual_editor_fields", drop_callback=lambda s, a: dpg.set_value(s, shorten(a)), tag=f"__{replacement.origin}", user_data=replacement)
+
+def _perform_query(sender: int, mode: str, user_data: Optional[dict]):
+    mode = dpg.get_value("__editor_selector").lower().strip()
+    prefix_text = dpg.get_value(f"__prefix_editor_{mode}_input")
+    query_text = dpg.get_value(f"__query_editor_{mode}_input")
+    endpoint = dpg.get_value("__endpoint_input")
+    try:
+        if mode=="visual":
+            for replacement_field in dpg.get_item_children("__visual_editor_fields", 1):
+                value = dpg.get_value(replacement_field)
+                origin = dpg.get_item_user_data(replacement_field).origin
+                query_text = query_text.replace(origin, value)
+        query_result = query.perform_query(endpoint, prefix_text + "\n" + query_text)
+    except URLError as e:
+        logging.error(f"Connection to '{endpoint}' failed.")
+        dpg.set_value(f"__status_console", f"Connection to '{endpoint}' failed.")
+        return
+    result_items = query_result["results"]["bindings"]
+    if not result_items:
+        dpg.delete_item("__result_table", children_only=True) # TODO Clear table https://github.com/hoffstadt/DearPyGui/issues/1350
+        logging.debug(f"No results returned.")
+        dpg.set_value(f"__status_console", "No results.")
+        return
+    dpg.delete_item("__result_table", children_only=True)
+    dpg.set_value(f"__status_console", "Query successful.")
+    columns = result_items[0].keys()
+    for column in columns:
+        dpg.add_table_column(label=column, width_fixed=True, parent="__result_table")
+        # dpg.add_table_column(label="CCC", width_stretch=True, init_width_or_weight=0.0)
+
+    # TODO This is as brittle as my ego, needs a fix asap
+    # TODO Fix filter
+    # with dpg.filter_set(tag="__result_filter", parent="__query_result_tab"):
+    for result in result_items:
+        with dpg.table_row(parent=f"__result_table"):
+            for key, value in result.items():
+                shortened = shorten(f"{value.get('value')}")
+                dpg.add_text(shortened, filter_key=shortened)
+                with dpg.drag_payload(parent=dpg.last_item(), drag_data=shortened, payload_type="string"):
+                    dpg.add_text(shortened, filter_key=shortened)
+
+
+def _select_directory(sender: int, mode: str, user_data: Optional[dict]):
+    with dpg.file_dialog(directory_selector=True, show=True, callback=_process_directory):
+        dpg.add_file_extension(".*")
+
+def _process_directory(sender: int, app_data: str, user_data: Optional[dict]):
+    directory = app_data["file_path_name"]
+    # files = listdir(directory)
+    # dpg.configure_item("files_listbox", items=files)
+    # dpg.set_value("file_text", directory)
+
+def _select_file(sender: int, app_data: str, user_data: Optional[dict]):
+    selected_file = app_data
+    cwd = dpg.get_value("file_text")
+    selected_file = cwd + "/" + selected_file
+    dpg.set_value("file_info_n", "file :" + selected_file)
+
+def interface(width=1200, height=900, endpoint: str = "localhost"):
+    endpoint_middleware = "localhost:8000" # TODO Grab this from onto
+    dpg.create_context()
+    dpg.create_viewport(title="Graph Exploring Tool", width=width, height=height)
+    with dpg.window(tag="primary", label="Graph Exploring Tool", menubar=True):
+        add_main_menu()
+        with dpg.group(horizontal=True, label="Main"):
+            create_query_palette()
+            with dpg.group(horizontal=False):
+                create_query_options(endpoint)
+                create_query_editor_visual(show=True)
+                create_query_editor_textual(show=False)
+                create_status_console()
+                create_query_results()
+    # demo.show_demo()
+    dpg.setup_dearpygui()
+    dpg.show_viewport()
+    dpg.set_primary_window("primary", True)
+    dpg.start_dearpygui()
+    dpg.destroy_context()

+ 23 - 0
graph_exploring_tool/main.py

@@ -0,0 +1,23 @@
+import logging
+import arklog
+import sys
+from signal import SIGINT, SIGTERM, signal
+from graph_exploring_tool import __version__
+from graph_exploring_tool.graphical.interface import interface
+
+def handler(signal_code, _) -> None:
+    """Signal handler."""
+    logging.debug(f"Shutting down because signal {signal_code} was received.")
+    sys.exit(1)
+
+def launch():
+    """"""
+    signal(SIGINT, handler)
+    signal(SIGTERM, handler)
+    arklog.set_config_logging()
+    logging.info(f"GET {__version__}.")
+    endpoint_fuseki = "http://127.0.0.1:3030/Drivetrain/sparql" # Fuseki SPARQL endpoint for the drivetrain
+    interface(endpoint=endpoint_fuseki)
+
+if __name__ == "__main__":
+    launch()

+ 174 - 0
graph_exploring_tool/query.py

@@ -0,0 +1,174 @@
+import logging
+from pathlib import Path
+from urllib.error import URLError
+
+import toml
+import arklog
+from SPARQLWrapper import SPARQLWrapper, JSON
+from SPARQLWrapper.SPARQLExceptions import QueryBadFormed
+
+arklog.set_config_logging()
+
+# TODO Rewrite this
+prefixes = "\n".join((
+    "PREFIX dtf:        <https://ontology.rys.app/dt/function/>",
+    "PREFIX owl:        <http://www.w3.org/2002/07/owl#>",
+    "PREFIX rdf:        <http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
+    "PREFIX xsd:        <http://www.w3.org/2001/XMLSchema#>",
+    "PREFIX rdfs:       <http://www.w3.org/2000/01/rdf-schema#>",
+    "PREFIX art:        <http://ua.be/drivetrain/description/artifacts/artifacts#>",
+    "PREFIX tabular:    <http://ua.be/sdo2l/vocabulary/base/tabular#>",
+    "PREFIX dc:         <http://purl.org/dc/elements/1.1/>",
+    "PREFIX ftg:        <http://ua.be/sdo2l/vocabulary/ftg#>",
+    "PREFIX base:       <http://ua.be/sdo2l/vocabulary/base#>",
+    "PREFIX vim4:       <http://bipm.org/jcgm/vim4#>",
+    "PREFIX comp:       <http://ua.be/sdo2l/vocabulary/component#>",
+    "PREFIX code:       <http://ua.be/sdo2l/vocabulary/base/code#>",
+    "PREFIX script:     <http://ua.be/sdo2l/vocabulary/base/script#>",
+    "PREFIX file:       <http://ua.be/sdo2l/vocabulary/base/file#>",
+    "PREFIX swrl:       <http://www.w3.org/2003/11/swrl#>",
+    "PREFIX traces:     <http://ua.be/sdo2l/vocabulary/processtraces#>",
+    "PREFIX wf:         <http://ua.be/sdo2l/vocabulary/workflow#>",
+    "PREFIX text:       <http://ua.be/sdo2l/vocabulary/base/text#>",
+    "PREFIX federation: <http://ua.be/sdo2l/vocabulary/federation#>",
+    "PREFIX dftg:       <http://ua.be/drivetrain/description/ftg#>",
+))
+
+reverse_prefix = {
+    "https://ontology.rys.app/dt/function/": "dtf",
+    "http://www.w3.org/2002/07/owl#": "owl",
+    "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
+    "http://www.w3.org/2001/XMLSchema#": "xsd",
+    "http://www.w3.org/2000/01/rdf-schema#": "rdfs",
+    "http://ua.be/drivetrain/description/artifacts/artifacts#": "art",
+    "http://ua.be/sdo2l/vocabulary/base/tabular#": "tabular",
+    "http://purl.org/dc/elements/1.1/": "dc",
+    "http://ua.be/sdo2l/vocabulary/ftg#": "ftg",
+    "http://ua.be/sdo2l/vocabulary/base#": "base",
+    "http://bipm.org/jcgm/vim4#": "vim4",
+    "http://ua.be/sdo2l/vocabulary/component#": "comp",
+    "http://ua.be/sdo2l/vocabulary/base/code#":"code",
+    "http://ua.be/sdo2l/vocabulary/base/script#": "script",
+    "http://ua.be/sdo2l/vocabulary/base/file#": "file",
+    "http://www.w3.org/2003/11/swrl#": "swrl",
+    "http://ua.be/sdo2l/vocabulary/processtraces#":"traces",
+    "http://ua.be/sdo2l/vocabulary/workflow#": "wf",
+    "http://ua.be/sdo2l/vocabulary/base/text#": "text",
+    "http://ua.be/sdo2l/vocabulary/federation#": "federation",
+    "http://ua.be/drivetrain/description/ftg#": "dftg",
+}
+
+
+# https://realpython.com/python-toml/
+def query():
+    """"""
+    config = toml.loads(Path("data/fuseki.toml").read_text())
+    server = config.get("endpoint").get("uri")
+    dataset_name = config.get("dataset")[0].get("name")
+    api_uri = "/".join((server, dataset_name))
+    logging.info(api_uri)
+    sparql = SPARQLWrapper(api_uri)
+    sparql.setReturnFormat(JSON)
+    sparql.setQuery(
+        prefixes +
+        """
+        SELECT DISTINCT ?class ?label ?description
+        WHERE {
+          ?class a owl:Class.
+          OPTIONAL { ?class rdfs:label ?label }
+          OPTIONAL { ?class rdfs:comment ?description }
+        }
+        """
+    )
+
+    ret = sparql.queryAndConvert()
+    for r in ret["results"]["bindings"]:
+        logging.info(r)
+
+
+def perform_query(endpoint: str, query_text: str) -> dict:
+    """"""
+    sparql = SPARQLWrapper(endpoint)
+    sparql.setReturnFormat(JSON)
+    sparql.setQuery(query_text)
+    try:
+        ret = sparql.query().convert()
+    except QueryBadFormed:
+        raise
+    except URLError:
+        raise
+    return ret
+
+
+
+def get_types() -> str:
+    """"""
+    return """SELECT DISTINCT ?type WHERE {
+  ?entity a ?type .
+  FILTER(isURI(?type))
+}"""
+
+def get_example() -> str:
+    """"""
+    return """SELECT ?outlier ?outlier_value WHERE {
+  SERVICE <http://localhost:8000/> {
+    SELECT ?outlier ?outlier_value WHERE {
+      BIND(dtf:example("data2.csv", "1") AS ?outlier)
+    }
+  }
+}"""
+
+def get_versions() -> str:
+    """"""
+    return """SELECT ?older WHERE {
+  art:drivetrain-sensor-data-v2 base:newVersionOf+ ?older
+}"""
+
+def get_properties() -> str:
+    """"""
+    return """SELECT DISTINCT ?property WHERE {
+  ?at a ftg:Formalism .
+  ?at ?property ?value .
+}"""
+
+def get_individuals() -> str:
+    """"""
+    return """SELECT DISTINCT ?individual ?name WHERE {
+  ?individual a ftg:Formalism .
+  ?individual base:hasName ?name .
+}"""
+
+def get_class_property() -> str:
+    """"""
+    return """SELECT DISTINCT ?class WHERE {
+  ?subject base:ArtifactType ?o .
+  ?subject a ?class .
+}
+"""
+
+def get_outlier() -> str:
+    """"""
+    return """SELECT ?outlier ?outlier_relation ?outlier_value WHERE {
+  SERVICE <http://127.0.0.1:8000/> {
+    SELECT ?outlier ?outlier_relation ?outlier_value WHERE {
+      BIND(dtf:outlier("rotation.csv", "2222", "<http://ua.be/drivetrain/description/artifacts/artifacts#drivetrain-sensor-data-v1>") AS ?outlier)
+    }
+  }
+}"""
+
+def get_pm() -> str:
+    """"""
+    return """SELECT DISTINCT ?from ?output ?atname WHERE {
+  ?from a ftg:Transformation .
+  ?from ftg:outputs ?output .
+  ?output a ftg:Formalism .
+  ?output base:hasName ?atname .
+}"""
+
+def get_art_formalism() -> str:
+    """"""
+    return """SELECT ?pmArtInstance ?formalism WHERE {
+  art:torque-profile traces:relatesTo ?pmArtInstance .
+  ?pmArtInstance base:isDefinedBy ?formalism .
+}"""
+

+ 3 - 1
requirements.txt

@@ -1,4 +1,5 @@
 # Graph Exploring Tool
+toml          ~= 0.10.2
 arklog        ~= 0.5.1
 rdflib        ~= 6.2.0
 sparqlwrapper ~= 2.0.0
@@ -22,4 +23,5 @@ coverage ~= 7.2.1
 # pydot         ~= 1.4.2
 # Graphical
 glfw        ~= 2.5.6
-imgui[glfw] ~= 1.4.1
+imgui[full] ~= 1.4.1
+dearpygui   ~= 1.8.0