Преглед изворни кода

WIP: PM parser considers every 'page' in drawio file to be a model

Joeri Exelmans пре 2 година
родитељ
комит
df96d6644b

+ 2 - 2
drawio2oml/drawio/convert.py

@@ -15,7 +15,7 @@ if __name__ == "__main__":
     names = oml_generator.assign_names(asyntax)
 
     if args.output == None:
-        oml_generator.write_oml(asyntax, names, sys.stdout)
+        oml_generator.write_oml(args.output[0], asyntax.pages[0], names, sys.stdout)
     else:
         with open(args.output[0], 'wt') as f:
-            oml_generator.write_oml(asyntax, names, f)
+            oml_generator.write_oml(args.output[0], asyntax.pages[0], names, f)

+ 17 - 16
drawio2oml/drawio/oml_generator.py

@@ -7,29 +7,27 @@ import jinja2
 from drawio2py import abstract_syntax
 from drawio2oml import util
 
-def assign_names(drawio_file: abstract_syntax.DrawIOFile) -> typing.Dict[abstract_syntax.Element, str]:
+def assign_names(drawio_page: abstract_syntax.Page) -> typing.Dict[abstract_syntax.Element, str]:
     names = {}
-    for page_idx, page in enumerate(drawio_file.pages):
-        names[page] = "p"+str(page_idx)
-        def visit_cell(cell):
-            names[cell.id] = "p"+str(page_idx)+"_cell_"+str(cell.id)
-            for child in cell.children:
-                visit_cell(child)
-        visit_cell(page.root)
+    def visit_cell(cell):
+        names[cell.id] = "cell_"+str(cell.id)
+        for child in cell.children:
+            visit_cell(child)
+    visit_cell(drawio_page.root)
     return names
 
 
 def write_oml(
-    drawio_file: abstract_syntax.DrawIOFile,
-    drawio_names: typing.Dict[abstract_syntax.Element, str],
+    filename: str, # only for traceability/documentation purposes
+    drawio_page: abstract_syntax.Page,
+    names: typing.Dict[abstract_syntax.Element, str],
     ostream: io.TextIOBase,
-    output_namespace: str = "http://ua.be/sdo2l/description/artifacts/my_drawio",
     namespaces=util.DTDESIGN_NAMESPACES):
     """
     Generate an OML description of a drawio diagram.
     Parameters:
-        drawio_file: abstract syntax of the drawio file
-        drawio_names: mapping from every element in the drawio file to a unique name
+        drawio_page: abstract syntax of the drawio page
+        names: mapping from every element in the drawio file to a unique name
         ostream: stream to write OML output to (e.g., stdout, a file, ...)
         output_namespace: namespace for the to-be-generated OML description
         namespaces: namespaces of vocabularies to use
@@ -38,12 +36,15 @@ def write_oml(
     environment = jinja2.Environment(
         loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
     environment.filters['to_oml_string_literal'] = util.to_oml_string_literal
-    template = environment.get_template("oml_template.j2")
+    template = environment.get_template("oml_template_page.j2")
 
     for piece in template.generate(
             minimal=True,
-            file=drawio_file,
+            filename=filename,
+            page=drawio_page,
+            names=names,
             enumerate=enumerate,
-            output_namespace=output_namespace,
+            output_namespace=namespaces['artifacts']+drawio_page.name+"_drawio",
+            shorthand=drawio_page.name+"_drawio",
             namespaces=namespaces):
         ostream.write(piece)

+ 127 - 0
drawio2oml/drawio/oml_template_page.j2

@@ -0,0 +1,127 @@
+// Warning: Generated code! Do not edit!
+// Input: '{{filename}}'
+// Page: '{{page.name}}'
+// Generator: https://msdl.uantwerpen.be/git/jexelmans/drawio2oml/drawio2oml/oml_generator.py
+{%- if minimal %}
+// Minimal mode enabled: Pgae/cell attributes, style, and geometries have been omitted (making the drawio-> OML conversion lossy).
+{%- endif %}
+
+{%- macro point(p) -%}
+drawio:hasX {{p.x}}
+drawio:hasY {{p.y}}
+{%- endmacro -%}
+
+{%- macro write_cell(cell) %}
+  {%- set cell_iri = names[(cell.id|string)] -%}
+  ci {{cell_iri}} : drawio:{{cell.__class__.__name__}} [
+    drawio:hasDrawioId {{cell.id|to_oml_string_literal}}
+    {%- if cell.value != "" %}
+    drawio:hasValue {{cell.value|to_oml_string_literal}}
+    {%- endif %}
+    {%- if cell.parent != None %}
+    drawio:hasParent {{names[cell.parent.id]}}
+    {%- else %}
+    drawio:isRootOf model
+    {%- endif %}
+    object_diagram:inModel model
+
+    {%- if not minimal %}
+    {%- if cell.__class__.__name__ == "Vertex" %}
+    drawio:hasVertexGeometry drawio:VertexGeometry [
+      {{ point(cell.geometry)|indent(6) }}
+      drawio:hasWidth {{cell.geometry.width}}
+      drawio:hasHeight {{cell.geometry.height}}
+    ]
+    {%- endif %}
+    {%- endif %}
+
+    {%- if cell.__class__.__name__ == "Edge" %}
+    {%- if not minimal %}
+    drawio:hasEdgeGeometry drawio:EdgeGeometry [
+      {%- for p in cell.geometry.points -%}
+      drawio:hasPoint drawio:PointListItem [
+        drawio:hasListIndex {{loop.index0}}
+        {{ point(p)|indent(8) }}
+      ]
+      {%- endfor %}
+      {%- if cell.geometry.source_point != None %}
+      drawio:hasSourcePoint drawio:Point [
+        {{ point(cell.geometry.source_point)|indent(8) }}
+      ]
+      {%- endif %}
+      {%- if cell.geometry.target_point != None %}
+      drawio:hasTargetPoint drawio:Point [
+        {{ point(cell.geometry.target_point)|indent(8) }}
+      ]
+      {%- endif %}
+    ]
+    {%- endif %}
+    {%- if cell.source %}
+    drawio:hasSource {{names[cell.source.id]}}
+    {%- endif -%}
+    {%- if cell.target %}
+    drawio:hasTarget {{names[cell.target.id]}}
+    {%- endif -%}
+    {%- endif %}
+  ]
+  {# Cell properties #}
+  {%-for prop_key,prop_val in cell.properties.items() %}
+  ci {{cell_iri}}_prop_{{prop_key}} : drawio:CellProperty [
+    dict:hasKey {{prop_key|to_oml_string_literal}}
+    dict:hasValue {{prop_val|to_oml_string_literal}}
+    drawio:propertyOf {{cell_iri}}
+    object_diagram:inModel model
+  ]
+  {%- endfor %}
+  {%- if not minimal %}
+  {# Cell style #}
+  {%- for style_key,style_val in cell.style.data.items() %}
+  ci {{cell_iri}}_sty_{{style_key}} : drawio:CellStyleEntry [
+    dict:hasKey {{style_key|to_oml_string_literal}}
+    dict:hasValue {{style_val|to_oml_string_literal}}
+    drawio:styleEntryOf {{cell_iri}}
+    object_diagram:inModel model
+  ]
+  {%- endfor %}
+  {# Cell attributes #}
+  {%- for attr_key,attr_val in cell.attributes.items() %}
+  ci {{cell_iri}}_attr_{{attr_key}} : drawio:CellAttribute [
+    dict:hasKey {{attr_key|to_oml_string_literal}}
+    dict:hasValue {{attr_val|to_oml_string_literal}}
+    drawio:attributeOf {{cell_iri}}
+    object_diagram:inModel model
+  ]
+  {%- endfor %}
+  {%- endif %}
+
+  {# Recursively write out children #}
+  {%- for child in cell.children %}
+  {{ write_cell(child) }}
+  {%- endfor -%}
+{%- endmacro %}
+
+description <{{output_namespace}}#> as {{shorthand}} {
+
+  uses <{{namespaces.drawio}}#> as drawio
+  uses <{{namespaces.object_diagram}}#> as object_diagram
+  uses <{{namespaces.dict}}#> as dict
+
+  ci model : drawio:Model [
+    drawio:hasDrawioId {{page.id|to_oml_string_literal}}
+    object_diagram:hasName {{(page.name+"_drawio")|to_oml_string_literal}}
+  ]
+
+  {%- if not minimal %}
+  {%- for attr_key, attr_val in page.attributes.items() %}
+  ci page_a_{{attr_key}} : drawio:PageAttribute [
+    dict:hasKey {{attr_key|to_oml_string_literal}}
+    dict:hasValue {{attr_val|to_oml_string_literal}}
+    drawio:ofPage model
+    object_diagram:inModel model
+  ]
+  {%- endfor %}
+  {%- endif %}
+
+  {# Cells #}
+  {{ write_cell(page.root) }}
+}

+ 43 - 24
drawio2oml/pm/convert.py

@@ -11,50 +11,69 @@ if __name__ == "__main__":
     argparser = argparse.ArgumentParser(
         description = "Parses .drawio files as Process Models and writes them as OML descriptions.")
     argparser.add_argument('inputfile', help="A drawio-diagram containing a process model on the first layer of the first page.")
-    argparser.add_argument('--out-drawio', metavar='FILE', nargs=1, help="Output file of generated OML description of drawio-diagram. If not specified, output will be omitted.")
-    argparser.add_argument('--out-pm', metavar='FILE', nargs=1, help="Output file of generated OML description of process model. If not specified, output will be omitted.")
-    argparser.add_argument('--out-trace', metavar='FILE', nargs=1, help="Output file of generated OML description of traceability links. If not specified, output will be omitted.")
+    argparser.add_argument('--outdir', metavar='DIRECTORY', nargs=1, help="Output directory for generated OML files. If not specified, output will be printed to stdout.")
     args = argparser.parse_args() # exits on error
 
-
     import os
     args.inputfile = os.path.abspath(args.inputfile)
 
     # parse drawio
     dio_asyntax = dio_parser.Parser.parse(args.inputfile)
 
-    # parse PM
-    layer_to_parse = dio_asyntax.pages[0].root.children[0] # 1st layer of 1st page
+    # for every page, parse the first layer:
+    triples = [(dio_page, pm_parser.parsePM(dio_page.root.children[0])) for dio_page in dio_asyntax.pages]
 
-    pm_asyntax, traceability_links = pm_parser.parsePM(layer_to_parse)
+    for (dio_page, (pm_asyntax, traceability_links)) in triples:
+        dio_names = dio_oml_generator.assign_names(dio_page)
+        pm_names = pm_oml_generator.assign_names(pm_asyntax)
 
-    import pprint
-    print("Abstract syntax of PM:")
-    pprint.pprint(pm_asyntax)
+        if args.outdir != None:
+            args.outdir = os.path.abspath(args.outdir[0])
+            dio_oml_file = args.outdir + "/" + dio_page.name + "_drawio.oml"
+            pm_oml_file = args.outdir + "/" + dio_page.name + "_pm.oml"
+            corr_oml_file = args.outdir + "/" + dio_page.name + "_corr.oml"
 
-    dio_names = dio_oml_generator.assign_names(dio_asyntax)
-    pm_names = pm_oml_generator.assign_names(pm_asyntax)
+            print("The following files will be (over)written:")
+            print(dio_oml_file)
+            print(pm_oml_file)
+            print(corr_oml_file)
+            input("Proceed? (Enter)")
 
-    if args.out_drawio != None:
-        with open(args.out_drawio[0], 'wt') as file:
+            with open(dio_oml_file, 'wt') as file:
+                dio_oml_generator.write_oml(
+                    filename=args.inputfile,
+                    drawio_page=dio_page,
+                    names=dio_names,
+                    ostream=file)
+            with open(pm_oml_file, 'wt') as file:
+                pm_oml_generator.write_pm_oml(
+                    input_filename=args.inputfile,
+                    input_page=dio_page,
+                    pm_model=pm_asyntax,
+                    pm_names=pm_names,
+                    ostream=file)
+            with open(corr_oml_file, 'wt') as file:
+                pm_oml_generator.write_corr_oml(
+                    input_filename=args.inputfile,
+                    input_page=dio_page,
+                    traceability_links=traceability_links,
+                    drawio_names=dio_names,
+                    pm_names=pm_names,
+                    ostream=file)
+        else:
             dio_oml_generator.write_oml(
-                drawio_file=dio_asyntax,
+                filename=args.inputfile,
+                drawio_page=dio_page,
                 drawio_names=dio_names,
-                ostream=file)
-
-    if args.out_pm != None:
-        with open(args.out_pm[0], 'wt') as file:
+                ostream=sys.stdout)
             pm_oml_generator.write_pm_oml(
                 input_filename=args.inputfile,
                 pm_model=pm_asyntax,
                 pm_names=pm_names,
-                ostream=file)
-
-    if args.out_trace != None:
-        with open(args.out_trace[0], 'wt') as file:
+                ostream=sys.stdout)
             pm_oml_generator.write_corr_oml(
                 input_filename=args.inputfile,
                 traceability_links=traceability_links,
                 drawio_names=dio_names,
                 pm_names=pm_names,
-                ostream=file)
+                ostream=sys.stdout)

+ 12 - 9
drawio2oml/pm/oml_generator.py

@@ -55,19 +55,19 @@ def assign_names(pm_model: pm_as.ProcessModel) -> typing.Dict[pm_as.Element, str
 
 def write_pm_oml(
     input_filename: str,
+    input_page: dio_as.Page,
     pm_model: pm_as.ProcessModel,
     pm_names: typing.Dict[pm_as.Element, str],
     ostream: io.TextIOBase,
-    output_namespace: str = "http://ua.be/sdo2l/description/artifacts/my_pm",
     namespaces=util.DTDESIGN_NAMESPACES):
     """
     Generate an OML description of a process model.
     Parameters:
       input_filename: has no precise semantics - only written to the output in a comment
+      input_page: only used to determine the name of the model
       pm_model: the parsed process model to write as an OML description
       pm_names: mapping from every element in the process model to a unique name
       ostream: stream to write OML output to (e.g., stdout, a file, ...)
-      output_namespace: namespace for the to-be-generated OML description
       namespaces: namespaces of vocabularies to use
     """
 
@@ -80,29 +80,30 @@ def write_pm_oml(
     for piece in template.generate(
             model=pm_model,
             input_filename=input_filename,
+            input_page=input_page,
             enumerate=enumerate,
             concat=util.concat,
             pm_names=pm_names,
             types=TYPENAMES,
-            output_namespace=output_namespace,
+            output_namespace=namespaces['artifacts']+input_page.name+"_pm",
+            shorthand=input_page.name+"_pm",
             namespaces=namespaces):
         ostream.write(piece)
 
 
 def write_corr_oml(
     input_filename: str,
+    input_page: dio_as.Page,
     traceability_links: typing.List[trace_as.TraceabilityLink],
     drawio_names: typing.Dict[dio_as.Element, str],
     pm_names: typing.Dict[pm_as.Element, str],
     ostream: io.TextIOBase,
-    drawio_descr_namespace: str= "http://ua.be/sdo2l/description/artifacts/my_drawio",
-    pm_descr_namespace: str= "http://ua.be/sdo2l/description/artifacts/my_pm",
-    output_namespace: str = "http://ua.be/sdo2l/description/artifacts/my_corr",
     namespaces=util.DTDESIGN_NAMESPACES):
     """
     Generate an OML description of the traceability links between a Drawio-description and a PM-description.
     Parameters:
       input_filename: has no precise semantics - only written to the output in a comment
+      input_page: only used to determine the name of the model
       traceability_links: list of traceability links to write as an OML description
       ostream: stream to write OML output to (e.g., stdout, a file, ...)
       output_namespace: namespace for the to-be-generated OML description
@@ -118,11 +119,13 @@ def write_corr_oml(
             pm_names=pm_names,
             drawio_names=drawio_names,
             input_filename=input_filename,
+            input_page=input_page,
             enumerate=enumerate,
             concat=util.concat,
             types=TYPENAMES,
-            drawio_descr_namespace=drawio_descr_namespace,
-            pm_descr_namespace=pm_descr_namespace,
-            output_namespace=output_namespace,
+            drawio_descr_namespace=namespaces['artifacts']+input_page.name+"_drawio",
+            pm_descr_namespace=namespaces['artifacts']+input_page.name+"_pm",
+            output_namespace=namespaces['artifacts']+input_page.name+"_corr",
+            shorthand=input_page.name+"_corr",
             namespaces=namespaces):
         ostream.write(piece)

+ 5 - 2
drawio2oml/pm/oml_template_corr.j2

@@ -1,9 +1,10 @@
 // Warning: Generated Code! Do not edit!
 // This file contains the correspondence links between concrete syntax (Drawio) and abstract syntax (Process Model).
 // Input file: {{input_filename}}
+// Page: {{input_page.name}}
 // Generator: https://msdl.uantwerpen.be/git/jexelmans/drawio2oml
 
-description <{{output_namespace}}#> as my_corr {
+description <{{output_namespace}}#> as {{shorthand}} {
 
   uses <{{namespaces.cs_as}}#> as cs_as
   uses <{{namespaces.object_diagram}}#> as object_diagram
@@ -11,7 +12,9 @@ description <{{output_namespace}}#> as my_corr {
   extends <{{drawio_descr_namespace}}#> as my_drawio
   extends <{{pm_descr_namespace}}#> as my_pm
 
-  ci model : cs_as:CorrespondenceModel []
+  ci model : cs_as:CorrespondenceModel [
+    object_diagram:hasName {{(input_page.name+"_corr")|to_oml_string_literal}}
+  ]
 
   {%- for trace_link in traceability_links %}
 

+ 5 - 2
drawio2oml/pm/oml_template_pm.j2

@@ -22,14 +22,17 @@ pm:dataTo {{pm_names[outgoing]}} // for some reason, we have to explicitly creat
 
 // Warning: Generated Code! Do not edit!
 // Input file: {{input_filename}}
+// Page: {{input_page.name}}
 // Generator: https://msdl.uantwerpen.be/git/jexelmans/drawio2oml
 
-description <{{output_namespace}}#> as my_pm {
+description <{{output_namespace}}#> as {{shorthand}} {
 
   uses <{{namespaces.pm}}#> as pm
   uses <{{namespaces.object_diagram}}#> as object_diagram
 
-  ci {{pm_names[model]}} : {{types[model.__class__]}} []
+  ci {{pm_names[model]}} : {{types[model.__class__]}} [
+    object_diagram:hasName {{(input_page.name+"_pm")|to_oml_string_literal}}
+  ]
 
   ci {{pm_names[model.initial]}} : {{types[model.initial.__class__]}} [
     object_diagram:inModel model

+ 0 - 3
drawio2oml/pm/parser.py

@@ -43,7 +43,6 @@ def parsePM(parent: dio_as.Cell) -> (pm_as.ProcessModel, List[trace_as.Traceabil
         try:
             pm_role = cell.properties["pmRole"]
         except KeyError:
-            print(cell.id, cell.properties)
             continue
         if pm_role == "activity":
             # Parse ports...
@@ -97,8 +96,6 @@ def parsePM(parent: dio_as.Cell) -> (pm_as.ProcessModel, List[trace_as.Traceabil
             continue
         src = dio_to_pm[cell.source.id]
         tgt = dio_to_pm[cell.target.id]
-        print(cell.properties["pmRole"], cell.source.properties["pmRole"], "->", cell.target.properties["pmRole"])
-        print("src", src, "tgt", tgt)
         getattr(src, attr_prefix+"_outgoing").append(tgt)
         getattr(tgt, attr_prefix+"_incoming").append(src)
         flow = constructor(src=src, tgt=tgt)

+ 1 - 0
drawio2oml/util.py

@@ -27,4 +27,5 @@ DTDESIGN_NAMESPACES = {
     "drawio": "http://ua.be/sdo2l/vocabulary/formalisms/drawio",
     "object_diagram": "http://ua.be/sdo2l/vocabulary/formalisms/object_diagram",
     "pm": "http://ua.be/sdo2l/vocabulary/formalisms/pm",
+    "artifacts": "http://ua.be/sdo2l/description/artifacts/",
 }

Разлика између датотеке није приказан због своје велике величине
+ 1 - 0
test/data/pm/MyFancyPM.drawio