Merge pull request #645 from gaphor/c4-model

C4 model
This commit is contained in:
Arjan Molenaar 2021-02-17 22:01:41 +01:00 committed by GitHub
commit 949d134bba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 2181 additions and 91 deletions

View File

@ -24,18 +24,21 @@ icons: ## Generate icons from stensil (requires Inkscape)
translations: ## Translate and update .po and .mo files
poetry run $(MAKE) -C po
models: gaphor/core/modeling/coremodel.py gaphor/UML/uml.py gaphor/SysML/sysml.py ## Generate Python model files from Gaphor models
models: gaphor/core/modeling/coremodel.py gaphor/UML/uml.py gaphor/SysML/sysml.py gaphor/C4Model/c4model.py ## Generate Python model files from Gaphor models
gaphor/core/modeling/coremodel.py: models/Core.gaphor models/Core.override gaphor/codegen/uml_coder.py gaphor/codegen/override.py gaphor/codegen/writer.py
poetry run gaphor/codegen/codegen.py models/Core.gaphor gaphor/core/modeling/coremodel.py models/Core.override && black $@ && mypy gaphor/core/modeling && isort gaphor/core/modeling/coremodel.py
poetry run gaphor/codegen/codegen.py models/Core.gaphor $@ models/Core.override && black $@ && mypy gaphor/core/modeling && isort gaphor/core/modeling/coremodel.py
gaphor/UML/uml.py: models/UML.gaphor models/UML.override gaphor/codegen/uml_coder.py gaphor/codegen/override.py gaphor/codegen/writer.py
poetry run gaphor/codegen/codegen.py models/UML.gaphor gaphor/UML/uml.py models/UML.override && mypy gaphor/UML && isort $@ && black $@
poetry run gaphor/codegen/codegen.py models/UML.gaphor $@ models/UML.override && mypy gaphor/UML && isort $@ && black $@
gaphor/SysML/sysml.py: models/SysML.gaphor models/SysML.override gaphor/codegen/profile_coder.py gaphor/codegen/override.py gaphor/codegen/writer.py
poetry run gaphor/codegen/codegen.py --profile models/SysML.gaphor gaphor/SysML/sysml.py models/SysML.override && black $@ && isort $@ && mypy gaphor/SysML
poetry run gaphor/codegen/codegen.py --profile models/SysML.gaphor $@ models/SysML.override && black $@ && isort $@ && mypy gaphor/SysML
gaphor/RAAML/raaml.py: models/RAAML.gaphor models/RAAML.override gaphor/codegen/profile_coder.py.py gaphor/codegen/override.py gaphor/codegen/writer.py
poetry run gaphor/codegen/codegen.py --profile models/RAAML.gaphor gaphor/RAAML/raaml.py models/RAAML.override && mypy gaphor/RAAML && isort $@ && black $@
poetry run gaphor/codegen/codegen.py --profile models/RAAML.gaphor $@ models/RAAML.override && mypy gaphor/RAAML && isort $@ && black $@
gaphor/C4Model/c4model.py: models/C4Model.gaphor models/C4Model.override gaphor/codegen/profile_coder.py gaphor/codegen/override.py gaphor/codegen/writer.py
poetry run gaphor/codegen/codegen.py --profile models/C4Model.gaphor $@ models/C4Model.override && mypy gaphor/C4Model && isort $@ && black $@
.PHONY: help dist test docs icons translations models

View File

45
gaphor/C4Model/c4model.py Normal file
View File

@ -0,0 +1,45 @@
# This file is generated by profile_coder.py. DO NOT EDIT!
from __future__ import annotations
from gaphor.core.modeling.properties import (
association,
attribute,
relation_many,
relation_one,
)
from gaphor.UML import Actor, Package
class C4Container(Package):
description: attribute[str]
location: attribute[str]
ownerContainer: relation_one[C4Container]
owningContainer: relation_many[C4Container]
technology: attribute[str]
type: attribute[str]
class C4Database(C4Container):
pass
class C4Person(Actor):
description: attribute[str]
location: attribute[str]
C4Container.description = attribute("description", str)
C4Container.location = attribute("location", str)
C4Container.ownerContainer = association(
"ownerContainer", C4Container, upper=1, opposite="owningContainer"
)
C4Container.owningContainer = association(
"owningContainer", C4Container, composite=True, opposite="ownerContainer"
)
C4Container.technology = attribute("technology", str)
C4Container.type = attribute("type", str)
C4Person.description = attribute("description", str)
C4Person.location = attribute("location", str)
C4Container.namespace.subsets.add(C4Container.ownerContainer) # type: ignore[attr-defined]
C4Container.ownedMember.subsets.add(C4Container.owningContainer) # type: ignore[attr-defined]

View File

@ -0,0 +1,3 @@
from gaphor.C4Model.diagramitems.container import C4ContainerItem
from gaphor.C4Model.diagramitems.database import C4ContainerDatabaseItem
from gaphor.C4Model.diagramitems.person import C4PersonItem

View File

@ -0,0 +1,53 @@
from gaphor.C4Model import c4model
from gaphor.core import gettext
from gaphor.core.styling import FontWeight, TextAlign, VerticalAlign
from gaphor.diagram.presentation import ElementPresentation, Named
from gaphor.diagram.shapes import Box, EditableText, Text, draw_border
from gaphor.diagram.support import represents
@represents(c4model.C4Container)
class C4ContainerItem(ElementPresentation, Named):
def __init__(self, diagram, id=None):
super().__init__(diagram, id)
self.watch("subject[NamedElement].name")
self.watch("subject[C4Container].technology")
self.watch("subject[C4Container].description")
self.watch("subject[C4Container].type")
self.watch("children", self.update_shapes)
def update_shapes(self, event=None):
text_align = (
TextAlign.LEFT if self.diagram and self.children else TextAlign.CENTER
)
self.shape = Box(
EditableText(
text=lambda: self.subject.name or "",
style={"font-weight": FontWeight.BOLD, "text-align": text_align},
),
Text(
text=lambda: self.subject.technology
and f"[{gettext(self.subject.type)}: {self.subject.technology}]"
or f"[{gettext(self.subject.type)}]",
style={"font-size": "x-small", "text-align": text_align},
),
*(
()
if self.children
else (
Text(
text=lambda: self.subject.description or "",
width=lambda: self.width - 8,
style={"padding": (4, 0, 0, 0), "text-align": text_align},
),
)
),
style={
"padding": (4, 4, 4, 4),
"vertical-align": VerticalAlign.BOTTOM
if self.diagram and self.children
else VerticalAlign.MIDDLE,
},
draw=draw_border,
)

View File

@ -0,0 +1,66 @@
from gaphas.util import path_ellipse
from gaphor.C4Model import c4model
from gaphor.core import gettext
from gaphor.core.styling import FontWeight, TextAlign, VerticalAlign
from gaphor.diagram.presentation import ElementPresentation, Named
from gaphor.diagram.shapes import Box, EditableText, Text, stroke
from gaphor.diagram.support import represents
@represents(c4model.C4Database)
class C4ContainerDatabaseItem(ElementPresentation, Named):
def __init__(self, diagram, id=None):
super().__init__(diagram, id)
self.watch("subject[NamedElement].name")
self.watch("subject[C4Container].technology")
self.watch("subject[C4Container].description")
self.watch("subject[C4Container].type")
def update_shapes(self, event=None):
self.shape = Box(
Box(
EditableText(
text=lambda: self.subject.name or "",
style={"font-weight": FontWeight.BOLD},
),
Text(
text=lambda: self.subject.technology
and f"[{gettext(self.subject.type)}: {self.subject.technology}]"
or f"[{gettext(self.subject.type)}]",
style={"font-size": "x-small"},
),
Text(
text=lambda: self.subject.description or "",
width=lambda: self.width - 8,
style={"padding": (4, 0, 0, 0)},
),
style={"padding": (4, 4, 4, 4)},
),
style={
"text-align": TextAlign.LEFT
if self.diagram and self.children
else TextAlign.CENTER,
"vertical-align": VerticalAlign.BOTTOM
if self.diagram and self.children
else VerticalAlign.MIDDLE,
},
draw=draw_database,
)
def draw_database(box, context, bounding_box):
cr = context.cairo
d = 0.38
x, y, width, height = bounding_box
x1 = width + x
y1 = height + y
cr.move_to(x1, y)
path_ellipse(cr, width / 2, y, width, height * d)
cr.line_to(x1, y1)
cr.curve_to(x1, y1, width / 2, y1 + height * d, x, y1)
cr.line_to(x, y)
stroke(context)

View File

@ -0,0 +1,70 @@
from math import pi
from gaphor.C4Model import c4model
from gaphor.core import gettext
from gaphor.core.styling import FontWeight, TextAlign, VerticalAlign
from gaphor.diagram.presentation import ElementPresentation, Named
from gaphor.diagram.shapes import Box, EditableText, Text, stroke
from gaphor.diagram.support import represents
@represents(c4model.C4Person)
class C4PersonItem(ElementPresentation, Named):
def __init__(self, diagram, id=None):
super().__init__(diagram, id, width=48, height=48)
self.watch("subject[NamedElement].name")
self.watch("subject[C4Person].description")
def update_shapes(self, event=None):
self.shape = Box(
Box(
EditableText(
text=lambda: self.subject.name or "",
style={"font-weight": FontWeight.BOLD},
),
Text(
text=lambda: f"[{gettext('Person')}]",
style={"font-size": "x-small"},
),
Text(
text=lambda: self.subject.description or "",
width=lambda: self.width - 8,
style={"padding": (4, 0, 0, 0)},
),
style={"padding": (4, 4, 4, 4)},
),
style={
"text-align": TextAlign.LEFT
if self.diagram and self.children
else TextAlign.CENTER,
"vertical-align": VerticalAlign.BOTTOM
if self.diagram and self.children
else VerticalAlign.MIDDLE,
},
draw=draw_person,
)
def draw_person(box, context, bounding_box):
cr = context.cairo
d = context.style.get("border-radius", 24)
x, y, width, height = bounding_box
x1 = width + x
y1 = height + y
d_head = width * 0.19
cr.move_to(x, d)
cr.arc(d, d, d, pi, 1.5 * pi)
cr.line_to(x + width / 2 - d_head / 2, y)
cr.arc(x + width / 2, -d_head * 0.866, d_head, 2 / 3 * pi, 1 / 3 * pi)
cr.line_to(x1 - d, y)
cr.arc(x1 - d, d, d, 1.5 * pi, y)
cr.line_to(x1, y1 - d)
cr.arc(x1 - d, y1 - d, d, 0, 0.5 * pi)
cr.line_to(d, y1)
cr.arc(d, y1 - d, d, 0.5 * pi, pi)
cr.close_path()
stroke(context)

View File

@ -0,0 +1,17 @@
from gaphor.C4Model.diagramitems import C4ContainerDatabaseItem, C4ContainerItem
from gaphor.diagram.grouping import AbstractGroup, Group
@Group.register(C4ContainerItem, C4ContainerItem)
@Group.register(C4ContainerItem, C4ContainerDatabaseItem)
class ContainerGroup(AbstractGroup):
"""Add Property to a Block."""
def can_contain(self) -> bool:
return True
def group(self):
self.item.subject.package = self.parent.subject
def ungroup(self):
self.item.subject.package = self.item.diagram.package

View File

@ -0,0 +1,17 @@
from gaphor.C4Model.c4model import C4Container, C4Database
from gaphor.ui.iconname import get_default_icon_name, get_icon_name
@get_icon_name.register(C4Container)
def get_name_for_class(element):
if element.type == "Software System":
return "gaphor-c4-software-system-symbolic"
elif element.type == "Component":
return "gaphor-c4-component-symbolic"
else:
return get_default_icon_name(element)
@get_icon_name.register(C4Database)
def get_database_name(element):
return get_default_icon_name(element)

View File

@ -0,0 +1,26 @@
"""C4 Model Language entrypoint."""
import gaphor.C4Model.grouping # noqa
import gaphor.C4Model.iconname # noqa
import gaphor.C4Model.propertypages # noqa
from gaphor.abc import ModelingLanguage
from gaphor.C4Model import c4model, diagramitems
from gaphor.C4Model.toolbox import c4model_toolbox_actions
from gaphor.core import gettext
from gaphor.diagram.diagramtoolbox import ToolboxDefinition
class C4ModelLanguage(ModelingLanguage):
@property
def name(self) -> str:
return gettext("C4 model")
@property
def toolbox_definition(self) -> ToolboxDefinition:
return c4model_toolbox_actions # type: ignore[no-any-return]
def lookup_element(self, name):
return getattr(c4model, name, None)
def lookup_diagram_item(self, name):
return getattr(diagramitems, name, None)

View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkLabel" id="00-instructions">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">To create a fresh model: create a window, create the
desired element, in the tree view pop upmenu, select
Remove Parent.
Padding between elements is 6px.
Top margin is 6 px, 12px for expanders.
Edit, from the popup menu, will allow you to add cell
renderers and such.</property>
<property name="xalign">0</property>
</object>
<object class="GtkTextBuffer" id="description-text-buffer"/>
<object class="GtkBox" id="description-editor">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<property name="baseline_position">top</property>
<signal name="destroy" handler="container-destroyed" swapped="no"/>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Description</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkAlignment">
<property name="height_request">100</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkTextView" id="description">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="wrap_mode">word</property>
<property name="buffer">description-text-buffer</property>
</object>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<object class="GtkBox" id="technology-editor">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<property name="baseline_position">top</property>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Technology</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="technology">
<property name="visible">True</property>
<property name="can_focus">True</property>
<signal name="changed" handler="technology-changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
</interface>

View File

@ -0,0 +1,93 @@
import importlib.resources
from typing import Union
from gi.repository import Gtk
from gaphor.C4Model import c4model
from gaphor.core import transactional
from gaphor.diagram.propertypages import PropertyPageBase, PropertyPages
def new_builder(*object_ids):
builder = Gtk.Builder()
builder.set_translation_domain("gaphor")
with importlib.resources.path(
"gaphor.C4Model", "propertypages.glade"
) as glade_file:
builder.add_objects_from_file(str(glade_file), object_ids)
return builder
@PropertyPages.register(c4model.C4Container)
@PropertyPages.register(c4model.C4Person)
class DescriptionPropertyPage(PropertyPageBase):
order = 14
def __init__(self, subject: Union[c4model.C4Container, c4model.C4Person]):
super().__init__()
assert subject
self.subject = subject
self.watcher = subject.watcher()
def construct(self):
builder = new_builder("description-editor", "description-text-buffer")
subject = self.subject
description = builder.get_object("description")
buffer = builder.get_object("description-text-buffer")
if subject.description:
buffer.set_text(subject.description)
changed_id = buffer.connect("changed", self._on_description_changed)
def text_handler(event):
if not description.props.has_focus:
buffer.handler_block(changed_id)
buffer.set_text(event.new_value)
buffer.handler_unblock(changed_id)
self.watcher.watch("description", text_handler)
builder.connect_signals(
{
"container-destroyed": (self.watcher.unsubscribe_all,),
}
)
return builder.get_object("description-editor")
@transactional
def _on_description_changed(self, buffer):
self.subject.description = buffer.get_text(
buffer.get_start_iter(), buffer.get_end_iter(), False
)
@PropertyPages.register(c4model.C4Container)
class TechnologyPropertyPage(PropertyPageBase):
order = 15
def __init__(self, subject: c4model.C4Container):
super().__init__()
assert subject
self.subject = subject
def construct(self):
builder = new_builder("technology-editor")
subject = self.subject
technology = builder.get_object("technology")
technology.set_text(subject.technology or "")
builder.connect_signals(
{
"technology-changed": (self._on_technology_changed,),
}
)
return builder.get_object("technology-editor")
@transactional
def _on_technology_changed(self, entry):
self.subject.technology = entry.get_text()

View File

View File

@ -0,0 +1,28 @@
import pytest
from gaphor.C4Model.modelinglanguage import C4ModelLanguage
from gaphor.core.modeling import ElementFactory
from gaphor.core.modeling.elementdispatcher import ElementDispatcher
from gaphor.diagram.tests.fixtures import diagram, event_manager
from gaphor.UML.modelinglanguage import UMLModelingLanguage
class MockModelingLanguage:
def __init__(self):
self._modeling_languages = [UMLModelingLanguage(), C4ModelLanguage()]
def lookup_element(self, name):
return self.first(lambda provider: provider.lookup_element(name))
def first(self, predicate):
for provider in self._modeling_languages:
type = predicate(provider)
if type:
return type
@pytest.fixture
def element_factory(event_manager): # noqa: F811
return ElementFactory(
event_manager, ElementDispatcher(event_manager, MockModelingLanguage())
)

View File

@ -0,0 +1,53 @@
from gaphor import UML
from gaphor.C4Model.c4model import C4Container
from gaphor.C4Model.diagramitems import C4ContainerItem
from gaphor.C4Model.grouping import ContainerGroup
def container(diagram, element_factory):
subject = element_factory.create(C4Container)
return diagram.create(C4ContainerItem, subject=subject)
def test_group_container_can_contain(diagram, element_factory):
parent = container(diagram, element_factory)
child = container(diagram, element_factory)
grouping = ContainerGroup(parent, child)
assert grouping.can_contain()
def test_group_container_can_group(diagram, element_factory):
parent = container(diagram, element_factory)
child = container(diagram, element_factory)
grouping = ContainerGroup(parent, child)
grouping.group()
assert child.subject.namespace is parent.subject
def test_group_container_group_from_package_to_container(diagram, element_factory):
package = element_factory.create(UML.Package)
parent = container(diagram, element_factory)
child = container(diagram, element_factory)
child.package = package
grouping = ContainerGroup(parent, child)
grouping.group()
assert child.subject.namespace is parent.subject
def test_group_container_ungroup(diagram, element_factory):
package = element_factory.create(UML.Package)
diagram.package = package
parent = container(diagram, element_factory)
child = container(diagram, element_factory)
child.subject.package = parent.subject
grouping = ContainerGroup(parent, child)
grouping.ungroup()
assert child.subject.namespace is package

View File

@ -0,0 +1,29 @@
import pytest
from gaphor.C4Model.modelinglanguage import C4ModelLanguage
def test_modeling_language_name():
ml = C4ModelLanguage()
assert ml.name
@pytest.mark.parametrize("name", ["C4Person", "C4Container"])
def test_elements(name):
ml = C4ModelLanguage()
assert ml.lookup_element(name)
@pytest.mark.parametrize(
"name",
[
"C4PersonItem",
"C4ContainerItem",
"C4ContainerDatabaseItem",
],
)
def test_diagram_items(name):
ml = C4ModelLanguage()
assert ml.lookup_diagram_item(name)

126
gaphor/C4Model/toolbox.py Normal file
View File

@ -0,0 +1,126 @@
"""The action definition for the C4 model toolbox."""
from gaphor.C4Model import c4model, diagramitems
from gaphor.core import gettext
from gaphor.diagram.diagramtoolbox import (
ToolboxDefinition,
ToolDef,
ToolSection,
default_namespace,
general_tools,
namespace_config,
)
from gaphor.diagram.diagramtools import new_item_factory
from gaphor.UML import diagramitems as uml_items
from gaphor.UML.actions.actionstoolbox import actions
from gaphor.UML.classes.classestoolbox import classes
from gaphor.UML.interactions.interactionstoolbox import interactions
from gaphor.UML.states.statestoolbox import states
def software_system_config(new_item):
default_namespace(new_item)
subject = new_item.subject
subject.type = "Software System"
subject.name = "NewSoftwareSystem"
def container_config(new_item):
default_namespace(new_item)
subject = new_item.subject
subject.type = "Container"
subject.name = "NewContainer"
def container_database_config(new_item):
default_namespace(new_item)
subject = new_item.subject
subject.type = "Container"
subject.technology = "Database"
subject.name = "NewDatabase"
def component_config(new_item):
default_namespace(new_item)
subject = new_item.subject
subject.type = "Component"
subject.name = "NewComponent"
c4 = ToolSection(
gettext("C4 Model"),
(
ToolDef(
"c4-person",
gettext("Person"),
"gaphor-c4-person-symbolic",
"P",
new_item_factory(
diagramitems.C4PersonItem,
c4model.C4Person,
config_func=namespace_config,
),
),
ToolDef(
"c4-software-system",
gettext("Software System"),
"gaphor-c4-software-system-symbolic",
"s",
new_item_factory(
diagramitems.C4ContainerItem,
c4model.C4Container,
config_func=software_system_config,
),
),
ToolDef(
"c4-container",
gettext("Container"),
"gaphor-c4-container-symbolic",
"o",
new_item_factory(
diagramitems.C4ContainerItem,
c4model.C4Container,
config_func=container_config,
),
),
ToolDef(
"c4-container-database",
gettext("Container: Database"),
"gaphor-c4-database-symbolic",
"d",
new_item_factory(
diagramitems.C4ContainerDatabaseItem,
c4model.C4Database,
config_func=container_database_config,
),
),
ToolDef(
"c4-component",
gettext("Component"),
"gaphor-c4-component-symbolic",
"c",
new_item_factory(
diagramitems.C4ContainerItem,
c4model.C4Container,
config_func=component_config,
),
),
ToolDef(
"c4-dependency",
gettext("Dependency"),
"gaphor-dependency-symbolic",
"<Shift>D",
new_item_factory(uml_items.DependencyItem),
),
),
)
c4model_toolbox_actions: ToolboxDefinition = (
general_tools,
c4,
classes,
actions,
interactions,
states,
)

View File

@ -67,10 +67,10 @@ class AdjuntProperty(Property):
class DirectedRelationshipPropertyPath(DirectedRelationship):
sourceContext: relation_one[Classifier]
sourcePropertyPath: relation_many[Property]
targetContext: relation_one[Classifier]
targetPropertyPath: relation_many[Property]
sourceContext: relation_one[Classifier]
class Allocate(Abstraction, DirectedRelationshipPropertyPath):
@ -275,7 +275,7 @@ ChangeSructuralFeatureEvent.structuralFeature = association(
)
ConnectorProperty.connector = association("connector", Connector, upper=1)
DirectedFeature.featureDirection = enumeration(
"kind", ("providedRequired", "provided", "required"), "providedRequired"
"featureDirection", ("required", "providedRequired", "provided"), "required"
)
DirectedRelationshipPropertyPath.sourceContext = association(
"sourceContext", Classifier, upper=1
@ -292,7 +292,7 @@ DirectedRelationshipPropertyPath.targetPropertyPath = association(
ElementGroup.name = attribute("name", str)
ElementGroup.orderedMember = association("orderedMember", Element)
ElementPropertyPath.propertyPath = association("propertyPath", Property, lower=1)
FlowProperty.direction = enumeration("kind", ("inout", "in", "out"), "inout")
FlowProperty.direction = enumeration("direction", ("out", "inout", "in"), "out")
InvocationOnNestedPortAction.onNestedPort = association("onNestedPort", Port, lower=1)
ParticipantProperty.end_ = association("end_", Property, upper=1)
Probability.probability = attribute("probability", str)

View File

@ -57,6 +57,10 @@ def find_enumerations(
return classes, enumerations
def filter_out_gaphor_profile(classes: List[UML.Class]) -> List[UML.Class]:
return [cls for cls in classes if cls.qualifiedName[0] != "Gaphor Profile"]
def get_class_extensions(cls: UML.Class):
"""Get the meta classes connected with extensions."""
for a in cls.attribute["it.association"]: # type: ignore
@ -65,7 +69,9 @@ def get_class_extensions(cls: UML.Class):
yield meta_cls
def create_class_trees(classes: List[UML.Class]) -> Dict[UML.Class, List[UML.Class]]:
def create_class_hierarchy(
classes: List[UML.Class],
) -> Dict[UML.Class, List[UML.Class]]:
"""Create a tree of UML.Class elements.
The relationship between the classes is a generalization. Since the
@ -73,38 +79,23 @@ def create_class_trees(classes: List[UML.Class]) -> Dict[UML.Class, List[UML.Cla
the children know who their parents are, the parents don't know the
children.
"""
trees = {}
hierarchy = {}
for cls in classes:
base_classes = [base_cls for base_cls in cls.general]
meta_classes = [meta_cls for meta_cls in get_class_extensions(cls)]
# Lambda key sort issue in mypy: https://github.com/python/mypy/issues/9656
trees[cls] = sorted(base_classes + meta_classes, key=lambda e: e.name) # type: ignore
return trees
hierarchy[cls] = sorted(base_classes + meta_classes, key=lambda e: e.name) # type: ignore
return hierarchy
def create_referenced(classes: List[UML.Class]) -> Set[UML.Class]:
"""UML.Class elements that are referenced by others.
We consider a UML.Class referenced when its child UML.Class has a
generalization relationship to it.
"""
referenced = set()
for cls in classes:
for gen in cls.general:
referenced.add(gen)
for meta_cls in get_class_extensions(cls):
referenced.add(meta_cls)
return referenced
def write_class_def(cls, trees, f, cls_written=set()) -> None:
def write_class_def(cls, hierarchy, f, cls_written=set()) -> None:
"""Write the Class definition."""
if cls in cls_written:
return
generalizations = trees[cls]
generalizations = hierarchy[cls]
for g in generalizations:
write_class_def(g, trees, f, cls_written)
write_class_def(g, hierarchy, f, cls_written)
f.write(f"class {cls.name}({', '.join(g.name for g in generalizations)}):\n")
write_attributes(cls, f)
@ -115,7 +106,7 @@ def write_attributes(cls: UML.Class, f: TextIO) -> None:
"""Write attributes based on attribute type."""
written = False
for a in cls.attribute: # type: ignore
for a in sorted(cls.attribute, key=lambda a: a.name or ""): # type: ignore
# TODO: do write derived values if override is available
if not a.name or a.name == "baseClass" or a.isDerived:
continue
@ -144,35 +135,41 @@ def write_properties(
for a in cls.attribute:
if not a.name or a.name == "baseClass" or a.isDerived:
continue
type_value = type_converter(a)
if type_value in ("int", "str"):
# TODO: add default value, if there is one
f.write(f'{cls.name}.{a.name} = attribute("{a.name}", {type_value})\n')
elif not type_value:
print(f"No type for {cls.name}.{a.name}")
elif "Kind" in type_value:
enum = enumerations.get(type_value)
if enum:
values = tuple(a.name for a in enum.attribute)
f.write(
f'{cls.name}.{a.name} = enumeration("{a.name}", {values}, "{values[0]}")\n'
)
else:
if not type_value:
print(f"No type for {cls.name}.{a.name}")
continue
if "Kind" in type_value:
enum = enumerations.get(type_value)
if enum:
values = tuple(a.name for a in enum.attribute)
f.write(
f'{cls.name}.{a.name} = enumeration("kind", {values}, "{values[0]}")\n'
)
else:
lower = "" if a.lowerValue in (None, "0") else f", lower={a.lowerValue}"
upper = (
"" if a.upperValue == "*" else f", upper=" f"{a.upperValue or 1}"
)
composite = ", composite=True" if a.aggregation == "composite" else ""
opposite = (
f', opposite="{a.opposite.name}"'
if a.opposite and a.opposite.name and a.opposite.class_
else ""
)
lower = "" if a.lowerValue in (None, "0") else f", lower={a.lowerValue}"
upper = "" if a.upperValue == "*" else f", upper=" f"{a.upperValue or 1}"
composite = ", composite=True" if a.aggregation == "composite" else ""
opposite = (
f', opposite="{a.opposite.name}"'
if a.opposite and a.opposite.name and a.opposite.class_
else ""
)
f.write(
f'{cls.name}.{a.name} = association("{a.name}", {type_value}{lower}{upper}{composite}{opposite})\n'
)
def write_subsets(cls, f):
for a in cls.attribute:
for slot in a.appliedStereotype[:].slot:
if slot.definingFeature.name == "subsets":
f.write(
f'{cls.name}.{a.name} = association("{a.name}", {type_value}{lower}{upper}{composite}{opposite})\n'
f"{cls.name}.{slot.value}.subsets.add({cls.name}.{a.name}) # type: ignore[attr-defined]\n"
)
@ -195,31 +192,36 @@ def generate(
element_factory,
modeling_language,
)
classes: List = element_factory.lselect(UML.Class)
classes, enumerations = find_enumerations(classes)
classes = filter_out_gaphor_profile(classes)
# Lambda key sort issue in mypy: https://github.com/python/mypy/issues/9656
classes = sorted(
(cls for cls in classes if cls.name[0] != "~"), key=lambda c: c.name # type: ignore
)
hierarchy = create_class_hierarchy(classes)
uml_classes = filter_uml_classes(classes, modeling_language)
with open(outfile, "w") as f:
f.write(header)
classes: List = element_factory.lselect(UML.Class)
classes, enumerations = find_enumerations(classes)
# Lambda key sort issue in mypy: https://github.com/python/mypy/issues/9656
classes = sorted(
(cls for cls in classes if cls.name[0] != "~"), key=lambda c: c.name # type: ignore
)
trees = create_class_trees(classes)
create_referenced(classes)
uml_classes = filter_uml_classes(classes, modeling_language)
for cls in uml_classes:
f.write(f"from gaphor.UML import {cls.name}\n")
cls_written: Set[Element] = set(uml_classes)
for cls in trees.keys():
for cls in hierarchy.keys():
cls.attribute.sort(key=lambda a: a.name or "") # type: ignore[attr-defined]
write_class_def(cls, trees, f, cls_written)
write_class_def(cls, hierarchy, f, cls_written)
f.write("\n\n")
for cls in trees.keys():
for cls in hierarchy.keys():
write_properties(cls, f, enumerations)
for cls in hierarchy.keys():
write_subsets(cls, f)
element_factory.shutdown()

View File

@ -4,7 +4,6 @@ import pytest
from gaphor.application import distribution
from gaphor.codegen.profile_coder import (
create_referenced,
filter_uml_classes,
find_enumerations,
generate,
@ -302,7 +301,7 @@ def test_write_properties_for_enumeration(filename, element_factory):
assert (
filename.data
== f'{book.name}.{a.name} = enumeration("kind", (\'{e.name}\',), "{e.name}")\n'
== f'{book.name}.{a.name} = enumeration("duration", (\'{e.name}\',), "{e.name}")\n'
)
@ -327,16 +326,6 @@ def test_write_properties_for_association(filename, element_factory):
)
def test_create_referenced(classes):
"""Test list of referenced UML.Class objects."""
referenced = create_referenced(classes)
assert len(referenced) == 3
nodes = [0, 2, 5]
for node in nodes:
assert classes[node] in referenced
def test_model_header(tmp_path):
"""Load a model with no relationships to test header."""
path = distribution().locate_file("test-models/multiple-messages.gaphor")

View File

@ -18,11 +18,13 @@ from gaphor.diagram.diagramtools import new_item_factory
ItemFactory = Callable[[Diagram, Optional[Presentation]], Presentation]
def default_namespace(new_item):
new_item.subject.package = new_item.diagram.namespace
def namespace_config(new_item):
subject = new_item.subject
diagram = new_item.diagram
subject.package = diagram.namespace
subject.name = f"New{type(subject).__name__}"
default_namespace(new_item)
new_item.subject.name = f"New{type(new_item.subject).__name__}"
class ToolDef(NamedTuple):

View File

@ -3,7 +3,7 @@
import re
from functools import singledispatch
TO_KEBAB = re.compile(r"([a-z])([A-Z]+)")
TO_KEBAB = re.compile(r"([a-z0-9])([A-Z]+)")
def to_kebab_case(s):

View File

@ -1,3 +1,4 @@
MAKEFLAGS += -j4
STENSIL=stensil.svg
@ -70,6 +71,11 @@ ICONS=diagram \
verify \
constraint \
view-editor \
c4-person \
c4-software-system \
c4-container \
c4-database \
c4-component \
and \
or \
not \

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="16.000013"
height="16"
viewBox="0 0 4.2333368 4.2333335"
version="1.1"
id="svg4268">
<defs
id="defs4262" />
<metadata
id="metadata4265">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 0.26367188 0 A 0.26460996 0.26460996 0 0 0 0 0.26367188 L 0 3.96875 A 0.26460996 0.26460996 0 0 0 0.26367188 4.2324219 L 3.96875 4.2324219 A 0.26460996 0.26460996 0 0 0 4.2324219 3.96875 L 4.2324219 0.26367188 A 0.26460996 0.26460996 0 0 0 3.96875 0 L 0.26367188 0 z M 0.52929688 0.52929688 L 3.7050781 0.52929688 L 3.7050781 3.7050781 L 0.52929688 3.7050781 L 0.52929688 0.52929688 z "
id="rect1264-7-9" />
<g
aria-label="CP"
id="text84-8-3-5-4"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;line-height:1.25;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
transform="translate(-127,9.7895831)">
<path
d="m 128.49482,-7.2242332 q 0.11606,0 0.19755,-0.027164 0.084,-0.029633 0.11853,-0.061736 0.0346,-0.034572 0.0692,-0.061736 0.037,-0.029633 0.0691,-0.029633 0.0494,0 0.0864,0.037042 0.037,0.037042 0.037,0.08643 0,0.1061859 -0.1704,0.2049635 -0.16792,0.098778 -0.41486,0.098778 -0.31362,0 -0.52105,-0.1827385 -0.20497,-0.185208 -0.20497,-0.4667242 v -0.162983 q 0,-0.2963328 0.19262,-0.4963575 0.19262,-0.2000246 0.47907,-0.2000246 0.17286,0 0.35066,0.083961 l 0.0247,0.00988 q 0.042,-0.059267 0.10371,-0.059267 0.0716,0 0.0963,0.04198 0.0272,0.041981 0.0272,0.1210026 v 0.207433 q 0,0.162983 -0.12347,0.162983 -0.0444,0 -0.0741,-0.024694 -0.0272,-0.024694 -0.0346,-0.04445 -0.005,-0.022225 -0.0124,-0.061736 -0.0148,-0.074083 -0.0914,-0.1185331 -0.0741,-0.046919 -0.13829,-0.059267 -0.0642,-0.012347 -0.11853,-0.012347 -0.18768,0 -0.31115,0.1284109 -0.12347,0.1259414 -0.12347,0.3210272 v 0.1580441 q 0,0.185208 0.13088,0.2963328 0.13335,0.1111248 0.3556,0.1111248 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal"
id="path1240" />
<path
d="m 130.4679,-7.9848207 q 0,0.066675 -0.0148,0.1284109 -0.0148,0.061736 -0.0568,0.1284109 -0.042,0.064205 -0.10618,0.1135942 -0.0617,0.046919 -0.16793,0.079022 -0.10371,0.029633 -0.23953,0.029633 h -0.24201 v 0.246944 h 0.24448 q 0.16298,0 0.16298,0.123472 0,0.123472 -0.16298,0.123472 h -0.52846 q -0.16299,0 -0.16299,-0.123472 0,-0.123472 0.16299,-0.123472 h 0.037 v -0.9457955 h -0.037 q -0.16299,0 -0.16299,-0.123472 0,-0.071614 0.042,-0.096308 0.042,-0.027164 0.12101,-0.027164 h 0.57538 q 0.23459,0 0.38523,0.1308803 0.15064,0.1308803 0.15064,0.3358438 z m -0.82727,-0.2197801 v 0.4519075 h 0.2914 q 0.12841,0 0.20002,-0.059267 0.0741,-0.061736 0.0741,-0.1654525 0,-0.1061859 -0.0741,-0.1654524 -0.0716,-0.061736 -0.20002,-0.061736 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal"
id="path1242" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="16.000013"
height="16"
viewBox="0 0 4.2333368 4.2333335"
version="1.1"
id="svg4268">
<defs
id="defs4262" />
<metadata
id="metadata4265">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 0.26367188 0 A 0.26460996 0.26460996 0 0 0 0 0.26367188 L 0 3.96875 A 0.26460996 0.26460996 0 0 0 0.26367188 4.2324219 L 3.96875 4.2324219 A 0.26460996 0.26460996 0 0 0 4.2324219 3.96875 L 4.2324219 0.26367188 A 0.26460996 0.26460996 0 0 0 3.96875 0 L 0.26367188 0 z M 0.52929688 0.52929688 L 3.7050781 0.52929688 L 3.7050781 3.7050781 L 0.52929688 3.7050781 L 0.52929688 0.52929688 z "
id="rect1264-7" />
<g
aria-label="CT"
id="text84-8-3-5"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;line-height:1.25;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
transform="translate(-119.0625,9.7895831)">
<path
d="m 120.55731,-7.2242327 q 0.11606,0 0.19755,-0.027164 0.084,-0.029633 0.11854,-0.061736 0.0346,-0.034572 0.0691,-0.061736 0.037,-0.029633 0.0692,-0.029633 0.0494,0 0.0864,0.037042 0.037,0.037042 0.037,0.08643 0,0.1061859 -0.17039,0.2049635 -0.16793,0.098778 -0.41487,0.098778 -0.31362,0 -0.52105,-0.1827385 -0.20496,-0.185208 -0.20496,-0.4667242 v -0.162983 q 0,-0.2963328 0.19261,-0.4963575 0.19262,-0.2000246 0.47907,-0.2000246 0.17286,0 0.35066,0.083961 l 0.0247,0.00988 q 0.042,-0.059267 0.10371,-0.059267 0.0716,0 0.0963,0.04198 0.0272,0.041981 0.0272,0.1210026 v 0.2074329 q 0,0.1629831 -0.12348,0.1629831 -0.0444,0 -0.0741,-0.024694 -0.0272,-0.024694 -0.0346,-0.04445 -0.005,-0.022225 -0.0123,-0.061736 -0.0148,-0.074083 -0.0914,-0.1185332 -0.0741,-0.046919 -0.13829,-0.059266 -0.0642,-0.012347 -0.11853,-0.012347 -0.18768,0 -0.31115,0.1284109 -0.12347,0.1259414 -0.12347,0.3210272 v 0.1580441 q 0,0.185208 0.13088,0.2963328 0.13335,0.1111248 0.3556,0.1111248 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal"
id="path1234" />
<path
d="m 122.24887,-7.2588048 q 0.16298,0 0.16298,0.123472 0,0.123472 -0.16298,0.123472 h -0.57044 q -0.16298,0 -0.16298,-0.123472 0,-0.071614 0.042,-0.096308 0.042,-0.027164 0.121,-0.027164 h 0.16051 v -0.9457956 h -0.26423 v 0.229658 q 0,0.162983 -0.12347,0.162983 -0.12347,0 -0.12347,-0.162983 v -0.476602 h 1.27423 v 0.476602 q 0,0.079022 -0.0272,0.1210025 -0.0247,0.041981 -0.0963,0.041981 -0.12347,0 -0.12347,-0.162983 v -0.229658 h -0.26917 v 0.9457956 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal"
id="path1236" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="16.000013"
height="15.980063"
viewBox="0 0 4.2333368 4.2280586"
version="1.1"
id="svg4268">
<defs
id="defs4262" />
<metadata
id="metadata4265">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 2.1210938 0 C 1.5805108 -8.29307e-07 1.0900849 0.091243073 0.7109375 0.25390625 C 0.52136381 0.33523784 0.35794168 0.43320081 0.23046875 0.5625 C 0.10299582 0.69179919 0.0078124943 0.8653442 0.0078125 1.0585938 C 0.0078124943 1.2518433 0.10299582 1.4234352 0.23046875 1.5527344 C 0.35794168 1.6820336 0.52136381 1.7819497 0.7109375 1.8632812 C 1.0900849 2.0259444 1.5805108 2.1171883 2.1210938 2.1171875 C 2.6616746 2.1171871 3.1521046 2.0259444 3.53125 1.8632812 C 3.7208227 1.7819497 3.8842466 1.6820333 4.0117188 1.5527344 C 4.1391909 1.4234354 4.2324219 1.2518426 4.2324219 1.0585938 C 4.2324219 0.86534492 4.1391909 0.69179897 4.0117188 0.5625 C 3.8842466 0.43320103 3.7208227 0.33523784 3.53125 0.25390625 C 3.1521046 0.091243061 2.6616746 4.2721713e-07 2.1210938 0 z M 2.1210938 0.53320312 C 2.599893 0.5332035 3.0314279 0.62020164 3.3203125 0.74414062 C 3.4647548 0.80611012 3.5715316 0.87732201 3.6308594 0.9375 C 3.6901872 0.99767799 3.6992188 1.0331727 3.6992188 1.0585938 C 3.6992188 1.0840148 3.6901872 1.1195095 3.6308594 1.1796875 C 3.5715316 1.2398655 3.4647548 1.3110774 3.3203125 1.3730469 C 3.0314279 1.4969859 2.599893 1.583984 2.1210938 1.5839844 C 1.6422927 1.5839851 1.2088085 1.496986 0.91992188 1.3730469 C 0.77547857 1.3110773 0.66870332 1.2398658 0.609375 1.1796875 C 0.55004668 1.1195092 0.54101562 1.0840149 0.54101562 1.0585938 C 0.54101562 1.0331726 0.55004668 0.9976783 0.609375 0.9375 C 0.66870332 0.8773217 0.77547857 0.80611021 0.91992188 0.74414062 C 1.2088085 0.62020145 1.6422927 0.53320239 2.1210938 0.53320312 z "
id="path1355" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 0 1.0527344 L 0 3.4335938 C 4.5832942e-06 3.5548601 0.060167255 3.651874 0.11914062 3.7148438 C 0.178114 3.7778135 0.24312623 3.8197493 0.31445312 3.859375 C 0.45710692 3.9386263 0.62922731 3.999555 0.82421875 4.0527344 C 1.2142016 4.1590932 1.6909143 4.2285156 2.1171875 4.2285156 C 2.5434607 4.2285156 3.0201744 4.1590933 3.4101562 4.0527344 C 3.6051472 3.9995549 3.7772688 3.9386269 3.9199219 3.859375 C 3.9912484 3.819749 4.0562614 3.7778151 4.1152344 3.7148438 C 4.1742074 3.6518724 4.2324219 3.5548609 4.2324219 3.4335938 L 4.2324219 1.0527344 L 3.7050781 1.0527344 L 3.7050781 3.3671875 C 3.6942632 3.375668 3.6882468 3.3819635 3.6621094 3.3964844 C 3.5815209 3.4412559 3.4402947 3.4969295 3.2714844 3.5429688 C 2.9338638 3.6350473 2.4846643 3.6992188 2.1171875 3.6992188 C 1.7497107 3.6992188 1.3005127 3.6350474 0.96289062 3.5429688 C 0.79407957 3.4969294 0.65090308 3.4412565 0.5703125 3.3964844 C 0.54553747 3.3827206 0.54037427 3.3776923 0.52929688 3.3691406 L 0.52929688 1.0527344 L 0 1.0527344 z "
id="path1360" />
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="16.000013"
height="16.087656"
viewBox="0 0 4.2333368 4.2565258"
version="1.1"
id="svg4268">
<defs
id="defs4262" />
<metadata
id="metadata4265">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 0.79296875 1.6113281 C 0.35780688 1.6113281 1.4802974e-16 1.9691349 0 2.4042969 L 0 3.4628906 C -5.4933562e-17 3.8980526 0.35780682 4.2558594 0.79296875 4.2558594 L 3.4394531 4.2558594 C 3.8746151 4.2558594 4.2324219 3.8980526 4.2324219 3.4628906 L 4.2324219 2.4042969 C 4.2324219 1.9691349 3.874615 1.6113281 3.4394531 1.6113281 L 0.79296875 1.6113281 z M 0.79296875 2.140625 L 3.4394531 2.140625 C 3.5906113 2.140625 3.7050781 2.2531423 3.7050781 2.4042969 L 3.7050781 3.4628906 C 3.7050781 3.6140452 3.5906112 3.7265625 3.4394531 3.7265625 L 0.79296875 3.7265625 C 0.64181068 3.7265625 0.52929687 3.6140452 0.52929688 3.4628906 L 0.52929688 2.4042969 C 0.52929688 2.2531423 0.64181062 2.140625 0.79296875 2.140625 z "
id="rect427" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 2.1191406 0 C 1.4647112 1.1731144e-06 0.92773435 0.53697682 0.92773438 1.1914062 C 0.92773442 1.8458356 1.4647112 2.3808582 2.1191406 2.3808594 C 2.77357 2.3808582 3.3105468 1.8458356 3.3105469 1.1914062 C 3.3105469 0.53697682 2.7735701 1.1731144e-06 2.1191406 0 z M 2.1191406 0.52929688 C 2.4875873 0.52929754 2.78125 0.82295957 2.78125 1.1914062 C 2.78125 1.5598529 2.4875873 1.8515618 2.1191406 1.8515625 C 1.750694 1.8515618 1.4570313 1.5598529 1.4570312 1.1914062 C 1.4570312 0.82295957 1.7506939 0.52929754 2.1191406 0.52929688 z "
id="path1252" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 1.1914062 2.9335938 L 1.1914062 3.7265625 L 1.4550781 3.7265625 L 1.4550781 2.9335938 L 1.1914062 2.9335938 z "
id="path1258" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 2.7773438 2.9335938 L 2.7773438 3.7265625 L 3.0429688 3.7265625 L 3.0429688 2.9335938 L 2.7773438 2.9335938 z "
id="path1261" />
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="16.000013"
height="16"
viewBox="0 0 4.2333368 4.2333335"
version="1.1"
id="svg4268">
<defs
id="defs4262" />
<metadata
id="metadata4265">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:1.05833, 1.05833000000000022;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1"
d="M 0.26367188 0 A 0.2645835 0.2645835 0 0 0 0.1796875 0.013671875 A 0.2645835 0.2645835 0 0 0 0.140625 0.03125 A 0.2645835 0.2645835 0 0 0 0.044921875 0.1171875 A 0.2645835 0.2645835 0 0 0 0.001953125 0.23828125 A 0.2645835 0.2645835 0 0 0 0 0.26367188 A 0.2645835 0.2645835 0 0 0 0.013671875 0.34960938 A 0.2645835 0.2645835 0 0 0 0.1953125 0.51953125 A 0.2645835 0.2645835 0 0 0 0.23828125 0.52734375 A 0.2645835 0.2645835 0 0 0 0.26367188 0.52929688 L 1.3222656 0.52929688 A 0.2645835 0.2645835 0 0 0 1.5878906 0.26367188 A 0.2645835 0.2645835 0 0 0 1.3222656 0 L 0.26367188 0 z M 2.3808594 0 A 0.2645835 0.2645835 0 0 0 2.1171875 0.26367188 A 0.2645835 0.2645835 0 0 0 2.3808594 0.52929688 L 3.4394531 0.52929688 A 0.2645835 0.2645835 0 0 0 3.7050781 0.26367188 A 0.2645835 0.2645835 0 0 0 3.4394531 0 L 2.3808594 0 z M 3.96875 0.52929688 A 0.2645835 0.2645835 0 0 0 3.7050781 0.79296875 L 3.7050781 1.8515625 A 0.2645835 0.2645835 0 0 0 3.96875 2.1171875 A 0.2645835 0.2645835 0 0 0 4.2324219 1.8515625 L 4.2324219 0.79296875 A 0.2645835 0.2645835 0 0 0 3.96875 0.52929688 z M 0.26367188 1.0585938 A 0.2645835 0.2645835 0 0 0 0 1.3222656 L 0 2.3808594 A 0.2645835 0.2645835 0 0 0 0.26367188 2.6464844 A 0.2645835 0.2645835 0 0 0 0.52929688 2.3808594 L 0.52929688 1.3222656 A 0.2645835 0.2645835 0 0 0 0.26367188 1.0585938 z M 3.96875 2.6464844 A 0.2645835 0.2645835 0 0 0 3.7050781 2.9101562 L 3.7050781 3.96875 A 0.2645835 0.2645835 0 0 0 3.96875 4.2324219 A 0.2645835 0.2645835 0 0 0 4.2324219 3.96875 L 4.2324219 2.9101562 A 0.2645835 0.2645835 0 0 0 3.96875 2.6464844 z M 0.26367188 3.1757812 A 0.2645835 0.2645835 0 0 0 0 3.4394531 L 0 3.96875 A 0.26460996 0.26460996 0 0 0 0.26367188 4.2324219 L 0.79296875 4.2324219 A 0.2645835 0.2645835 0 0 0 1.0585938 3.96875 A 0.2645835 0.2645835 0 0 0 0.79296875 3.7050781 L 0.52929688 3.7050781 L 0.52929688 3.4394531 A 0.2645835 0.2645835 0 0 0 0.26367188 3.1757812 z M 1.8515625 3.7050781 A 0.2645835 0.2645835 0 0 0 1.5878906 3.96875 A 0.2645835 0.2645835 0 0 0 1.8515625 4.2324219 L 2.9101562 4.2324219 A 0.2645835 0.2645835 0 0 0 3.1757812 3.96875 A 0.2645835 0.2645835 0 0 0 2.9101562 3.7050781 L 1.8515625 3.7050781 z "
id="rect1264" />
<g
aria-label="S"
id="text84-8-3"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
transform="translate(-111.125,9.7895831)">
<path
d="m 114.00571,-8.9810385 q 0.127,0 0.1651,0.071967 0.0423,0.071967 0.0423,0.2074332 v 0.372533 q 0,0.2836331 -0.20743,0.2836331 -0.0339,0 -0.0635,-0.00423 -0.0254,-0.00847 -0.0466,-0.016933 -0.0169,-0.0127 -0.0339,-0.029633 -0.0169,-0.021167 -0.0254,-0.033867 -0.008,-0.0127 -0.0169,-0.0381 -0.008,-0.0254 -0.0127,-0.033867 0,-0.0127 -0.008,-0.0381 -0.004,-0.029633 -0.004,-0.033867 -0.0296,-0.1269999 -0.16934,-0.2031998 -0.13546,-0.080433 -0.33443,-0.080433 -0.19897,0 -0.33867,0.093133 -0.1397,0.093133 -0.1397,0.2285998 0,0.1439332 0.1397,0.2243664 0.1397,0.0762 0.32597,0.1016 0.18627,0.021167 0.41487,0.084667 0.22859,0.059267 0.35983,0.1650998 0.24976,0.1989665 0.24976,0.5291662 0,0.3301998 -0.2667,0.5333996 -0.26669,0.2031998 -0.70273,0.2031998 -0.35136,0 -0.6604,-0.1650998 -0.0847,0.1015999 -0.16933,0.1015999 -0.2159,0 -0.2159,-0.2793998 v -0.2793998 q 0,-0.1396998 0.0466,-0.2074331 0.0466,-0.071967 0.1651,-0.071967 0.0381,0 0.0677,0.00847 0.0339,0.00847 0.055,0.0254 0.0212,0.016933 0.0339,0.033867 0.0169,0.0127 0.0254,0.0381 0.0127,0.0254 0.0169,0.0381 0.004,0.0127 0.008,0.033867 0.008,0.021167 0.008,0.0254 0.0296,0.1142999 0.2032,0.1947332 0.17356,0.080433 0.38523,0.080433 0.23707,0 0.39793,-0.093133 0.16087,-0.093133 0.16087,-0.2285998 0,-0.1269999 -0.127,-0.2031998 -0.12277,-0.0762 -0.28787,-0.1016 -0.16086,-0.0254 -0.381,-0.0762 -0.2159,-0.0508 -0.34713,-0.1227666 -0.16087,-0.093133 -0.254,-0.2497664 -0.0931,-0.1566332 -0.0931,-0.3386664 0,-0.3217331 0.26246,-0.5333996 0.26247,-0.2158998 0.65617,-0.2158998 0.32597,0 0.55033,0.1396999 0.0339,-0.080433 0.0677,-0.1100666 0.0339,-0.029633 0.0974,-0.029633 z"
id="path1229" />
</g>
<g
id="text1294"
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
transform="translate(-111.125,9.7895831)" />
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -60,16 +60,16 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5035565"
inkscape:cx="303.86588"
inkscape:cy="155.53198"
inkscape:cx="451.24217"
inkscape:cy="12.754987"
inkscape:document-units="px"
inkscape:current-layer="xor"
inkscape:current-layer="c4-database"
showgrid="true"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-y="0"
inkscape:window-maximized="1"
showborder="false"
inkscape:document-rotation="0">
@ -1420,6 +1420,81 @@
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.100285;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
d="m 42.396034,0.55713887 c 0,4.31100603 0,8.62201173 0,12.93301713 -3.607969,0.623265 -7.095256,1.928571 -10.228951,3.821636 -3.611368,2.174516 -6.753415,5.122442 -9.152573,8.588705 -2.295205,3.304952 -3.912211,7.079698 -4.707058,11.024835 -0.423268,2.074454 -0.616996,4.193952 -0.59647,6.31061 -0.0022,12.254683 -2.77e-4,24.509367 -9.16e-4,36.764051 2.166792,0 4.333582,0 6.500372,0 0,4.698119 0,9.396237 0,14.094357 3.436902,0 6.873803,0 10.310704,0 0,-4.69812 1e-6,-9.396238 0,-14.094357 8.687258,0 17.374514,0 26.061772,0 0,4.698119 0,9.396237 0,14.094357 3.436901,0 6.873802,0 10.310704,0 0,-4.69812 0,-9.396238 0,-14.094357 2.166789,0 4.33358,0 6.500371,0 -5.26e-4,-12.45406 7.88e-4,-24.908141 -0.0028,-37.362188 -0.03013,-4.446347 -1.07028,-8.883843 -3.032874,-12.874574 -1.999252,-4.07951 -4.943175,-7.6912 -8.535791,-10.471963 -3.353582,-2.602624 -7.267294,-4.484061 -11.402198,-5.450548 -0.567627,-0.133246 -1.139125,-0.250178 -1.713617,-0.349794 0,-4.8478505 0,-9.6957012 0,-14.5435516 -3.4369,0 -6.873802,0 -10.310703,0 0,0.5365878 0,1.07317663 0,1.60976447 z M 47.830241,21.804751 c 3.222899,0.03846 6.43654,0.823273 9.305004,2.295718 3.244286,1.657126 6.046704,4.168343 8.044242,7.215206 1.579433,2.396381 2.657443,5.120716 3.143035,7.949514 0.225054,1.286844 0.3196,2.594434 0.308924,3.900256 4.2e-5,9.357342 3.9e-5,18.714694 3.5e-5,28.07204 -14.053397,0 -28.106792,0 -42.160189,0 6.57e-4,-9.53837 -8.41e-4,-19.076762 0.003,-28.615118 0.03503,-3.364549 0.888161,-6.719513 2.483264,-9.683403 1.81876,-3.400911 4.588897,-6.285842 7.915922,-8.237181 2.557829,-1.507803 5.439372,-2.461379 8.391874,-2.776017 0.852276,-0.09315 1.714454,-0.130823 2.564963,-0.121015 z" />
</g>
<text
id="text408"
y="-11.365278"
x="103.18754"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.41111px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
xml:space="preserve"><tspan
style="stroke-width:0.264583"
y="-11.365278"
x="103.18754"
sodipodi:role="line"
id="tspan406">C4 Model</tspan></text>
<rect
inkscape:label="512x512"
y="-9.789587"
x="103.18754"
height="4.2333331"
width="4.2333331"
id="rect410"
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate" />
<rect
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate"
id="rect412"
width="4.2333331"
height="4.2333331"
x="111.12502"
y="-9.789587"
inkscape:label="512x512" />
<rect
inkscape:label="512x512"
y="-9.789587"
x="119.06252"
height="4.2333331"
width="4.2333331"
id="rect414"
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate" />
<rect
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate"
id="rect416"
width="4.2333331"
height="4.2333331"
x="126.99995"
y="-9.789587"
inkscape:label="512x512" />
<rect
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate"
id="rect418"
width="4.2333331"
height="4.2333331"
x="103.18754"
y="-4.4979243"
inkscape:label="512x512" />
<rect
inkscape:label="512x512"
y="-4.4979167"
x="119.06252"
height="4.2333331"
width="4.2333331"
id="rect420"
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate" />
<rect
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate"
id="rect422"
width="4.2333331"
height="4.2333331"
x="111.12502"
y="-4.4979167"
inkscape:label="512x512" />
<rect
style="display:inline;overflow:visible;visibility:visible;fill:#f0f0f0;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.132292;marker:none;enable-background:accumulate"
id="rect424"
width="4.2333331"
height="4.2333331"
x="126.99995"
y="-4.4979243"
inkscape:label="512x512" />
</g>
<g
style="display:inline"
@ -2013,7 +2088,6 @@
sodipodi:nodetypes="cc" />
</g>
<g
sodipodi:insensitive="true"
style="display:inline"
inkscape:label="block"
id="block"
@ -2025,7 +2099,7 @@
height="3.7041669"
width="3.7041667"
id="rect4893-7"
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.529167;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.52916667;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text84-8"
y="7.983397"
@ -2980,4 +3054,130 @@
d="m 28.310417,-1.9783284 v 0.52832 h -0.529166 v 0.52829352 h 0.529166 v 0.5282936 h 0.529167 v -0.5282936 h 0.529167 V -1.4500084 h -0.529167 v -0.52832 z"
style="display:inline;fill-opacity:1;stroke:none;stroke-width:0.264583;enable-background:new" />
</g>
<g
inkscape:groupmode="layer"
id="c4-person"
inkscape:label="c4-person">
<rect
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect427"
width="3.7041705"
height="2.1166666"
x="103.45208"
y="-7.9375"
ry="0.52916664" />
<circle
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1252"
cx="105.30659"
cy="-8.6221504"
r="0.92604166" />
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 104.51042,-6.8791666 v 0.79375"
id="path1258" />
<path
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 106.09792,-6.8791666 v 0.79375"
id="path1261" />
</g>
<g
inkscape:groupmode="layer"
id="c4-software-system"
inkscape:label="c4-software-system">
<rect
style="fill:none;stroke:#000000;stroke-width:0.52916698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:1.05833397, 1.05833397000000007;stroke-opacity:1;stroke-dashoffset:0"
id="rect1264"
width="3.7041667"
height="3.7041667"
x="111.38958"
y="-9.5249996"
ry="0" />
<text
id="text84-8-3"
y="-6.4537406"
x="112.02451"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333px;line-height:1.25;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
xml:space="preserve"><tspan
y="-6.4537406"
x="112.02451"
id="tspan456-6"
sodipodi:role="line">S</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
x="117.475"
y="-8.7312498"
id="text1294"><tspan
sodipodi:role="line"
id="tspan1292"
x="117.475"
y="-8.7312498"
style="stroke-width:0.264583" /></text>
</g>
<g
inkscape:groupmode="layer"
id="c4-container"
inkscape:label="c4-container">
<rect
style="fill:none;stroke:#000000;stroke-width:0.52916698;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect1264-7"
width="3.7041667"
height="3.7041667"
x="119.32708"
y="-9.5249996"
ry="0" />
<text
id="text84-8-3-5"
y="-7.0118608"
x="119.74239"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;line-height:1.25;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
xml:space="preserve"><tspan
y="-7.0118608"
x="119.74239"
id="tspan456-6-3"
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal">CT</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="c4-component"
inkscape:label="c4-component">
<rect
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect1264-7-9"
width="3.7041667"
height="3.7041667"
x="127.26458"
y="-9.5249996"
ry="0" />
<text
id="text84-8-3-5-4"
y="-7.0118613"
x="127.6799"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;line-height:1.25;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
xml:space="preserve"><tspan
y="-7.0118613"
x="127.6799"
id="tspan456-6-3-8"
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:2.46944px;font-family:FreeMono;-inkscape-font-specification:'FreeMono, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal">CP</tspan></text>
</g>
<g
inkscape:groupmode="layer"
id="c4-database"
inkscape:label="c4-database">
<ellipse
style="fill:none;stroke:#000000;stroke-width:0.532917;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path1355"
ry="0.79187459"
rx="1.8457534"
cy="-3.4343085"
cx="105.30788" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 103.45208,-3.4395833 0,2.38125 c 1e-5,0.26458326 1.05834,0.52916663 1.85209,0.52916663 0.79375,0 1.85208,-0.26458337 1.85208,-0.52916663 l 0,-2.38125"
id="path1360"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

@ -5,3 +5,4 @@ def test_kebab_case():
assert to_kebab_case("Class") == "class"
assert to_kebab_case("FooBar") == "foo-bar"
assert to_kebab_case("FooBarBaz") == "foo-bar-baz"
assert to_kebab_case("C4FooBar") == "c4-foo-bar"

924
models/C4Model.gaphor Normal file
View File

@ -0,0 +1,924 @@
<?xml version="1.0" encoding="utf-8"?>
<gaphor xmlns="http://gaphor.sourceforge.net/model" version="3.0" gaphor-version="2.2.2">
<StyleSheet id="2e656716-6cae-11eb-aee1-750f3fed8beb"/>
<Package id="2e656717-6cae-11eb-aee1-750f3fed8beb">
<name>
<val>C4</val>
</name>
<ownedClassifier>
<reflist>
<ref refid="872907b8-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="97b4c158-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="ee5ba098-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="2daf9a4b-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="6e5e9410-6faf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedClassifier>
<ownedDiagram>
<reflist>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</reflist>
</ownedDiagram>
<presentation>
<reflist>
<ref refid="3a8987f4-6ddd-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Package>
<Diagram id="2e656718-6cae-11eb-aee1-750f3fed8beb">
<name>
<val>C4</val>
</name>
<ownedPresentation>
<reflist>
<ref refid="872907b9-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="905056c1-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="96f0c884-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="b680213b-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="ca16bf74-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="2daf9a4a-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="5630b2f6-6faf-11eb-bdd8-859c1b7b5447"/>
<ref refid="6e5e9411-6faf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedPresentation>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<canvas>
<item id="872907b9-6cae-11eb-aee1-750f3fed8beb" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 87.0, 252.0)</val>
</matrix>
<width>
<val>182.0</val>
</width>
<height>
<val>100.0</val>
</height>
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<subject>
<ref refid="872907b8-6cae-11eb-aee1-750f3fed8beb"/>
</subject>
</item>
<item id="905056c1-6cae-11eb-aee1-750f3fed8beb" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 128.0, 100.0)</val>
</matrix>
<width>
<val>100.0</val>
</width>
<height>
<val>71.0</val>
</height>
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<show_attributes>
<val>0</val>
</show_attributes>
<show_operations>
<val>0</val>
</show_operations>
<subject>
<ref refid="905056c0-6cae-11eb-aee1-750f3fed8beb"/>
</subject>
</item>
<item id="96f0c884-6cae-11eb-aee1-750f3fed8beb" type="ExtensionItem">
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<horizontal>
<val>0</val>
</horizontal>
<subject>
<ref refid="97b4c158-6cae-11eb-aee1-750f3fed8beb"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 80.0, 281.0)</val>
</matrix>
<points>
<val>[(96.0, -110.0), (96.0, -29.0)]</val>
</points>
<head-connection>
<ref refid="905056c1-6cae-11eb-aee1-750f3fed8beb"/>
</head-connection>
<tail-connection>
<ref refid="872907b9-6cae-11eb-aee1-750f3fed8beb"/>
</tail-connection>
</item>
<item id="b680213b-6d81-11eb-bdd8-859c1b7b5447" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 349.0, 100.0)</val>
</matrix>
<width>
<val>128.0</val>
</width>
<height>
<val>71.0</val>
</height>
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<subject>
<ref refid="b680213a-6d81-11eb-bdd8-859c1b7b5447"/>
</subject>
</item>
<item id="ca16bf74-6d81-11eb-bdd8-859c1b7b5447" type="ExtensionItem">
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<horizontal>
<val>0</val>
</horizontal>
<subject>
<ref refid="ee5ba098-6d81-11eb-bdd8-859c1b7b5447"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 399.0, 827.0)</val>
</matrix>
<points>
<val>[(9.71247902554223, -656.0), (11.721649621102245, -548.7179487179487)]</val>
</points>
<head-connection>
<ref refid="b680213b-6d81-11eb-bdd8-859c1b7b5447"/>
</head-connection>
<tail-connection>
<ref refid="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447"/>
</tail-connection>
</item>
<item id="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 347.0, 278.28205128205127)</val>
</matrix>
<width>
<val>130.0</val>
</width>
<height>
<val>134.0</val>
</height>
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<subject>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</subject>
</item>
<item id="2daf9a4a-6dde-11eb-bdd8-859c1b7b5447" type="AssociationItem">
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<head_subject>
<ref refid="2daf9a4c-6dde-11eb-bdd8-859c1b7b5447"/>
</head_subject>
<horizontal>
<val>1</val>
</horizontal>
<orthogonal>
<val>1</val>
</orthogonal>
<subject>
<ref refid="2daf9a4b-6dde-11eb-bdd8-859c1b7b5447"/>
</subject>
<tail_subject>
<ref refid="2daf9a4d-6dde-11eb-bdd8-859c1b7b5447"/>
</tail_subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 195.0, 500.046875)</val>
</matrix>
<points>
<val>[(282.0, -183.046875), (475.0, -183.046875), (475.0, -109.76482371794873), (282.0, -109.76482371794873)]</val>
</points>
<head-connection>
<ref refid="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447"/>
</head-connection>
<tail-connection>
<ref refid="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447"/>
</tail-connection>
</item>
<item id="5630b2f6-6faf-11eb-bdd8-859c1b7b5447" type="GeneralizationItem">
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<horizontal>
<val>0</val>
</horizontal>
<subject>
<ref refid="74ad084c-6faf-11eb-bdd8-859c1b7b5447"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 375.0, 354.0)</val>
</matrix>
<points>
<val>[(38.0, 58.28205128205127), (38.0, 135.0)]</val>
</points>
<head-connection>
<ref refid="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447"/>
</head-connection>
<tail-connection>
<ref refid="6e5e9411-6faf-11eb-bdd8-859c1b7b5447"/>
</tail-connection>
</item>
<item id="6e5e9411-6faf-11eb-bdd8-859c1b7b5447" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 349.0, 489.0)</val>
</matrix>
<width>
<val>128.0</val>
</width>
<height>
<val>91.0</val>
</height>
<diagram>
<ref refid="2e656718-6cae-11eb-aee1-750f3fed8beb"/>
</diagram>
<subject>
<ref refid="6e5e9410-6faf-11eb-bdd8-859c1b7b5447"/>
</subject>
</item>
</canvas>
</Diagram>
<Stereotype id="872907b8-6cae-11eb-aee1-750f3fed8beb">
<name>
<val>C4Person</val>
</name>
<ownedAttribute>
<reflist>
<ref refid="97b4c159-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="db6499c8-6ddf-11eb-bdd8-859c1b7b5447"/>
<ref refid="e17a5dac-6ddf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedAttribute>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="872907b9-6cae-11eb-aee1-750f3fed8beb"/>
</reflist>
</presentation>
</Stereotype>
<Class id="905056c0-6cae-11eb-aee1-750f3fed8beb">
<name>
<val>Actor</val>
</name>
<ownedAttribute>
<reflist>
<ref refid="97b4c15a-6cae-11eb-aee1-750f3fed8beb"/>
</reflist>
</ownedAttribute>
<package>
<ref refid="336043b6-6caf-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="905056c1-6cae-11eb-aee1-750f3fed8beb"/>
</reflist>
</presentation>
</Class>
<Extension id="97b4c158-6cae-11eb-aee1-750f3fed8beb">
<memberEnd>
<reflist>
<ref refid="97b4c159-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="97b4c15a-6cae-11eb-aee1-750f3fed8beb"/>
</reflist>
</memberEnd>
<ownedEnd>
<ref refid="97b4c15a-6cae-11eb-aee1-750f3fed8beb"/>
</ownedEnd>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="96f0c884-6cae-11eb-aee1-750f3fed8beb"/>
</reflist>
</presentation>
</Extension>
<Property id="97b4c159-6cae-11eb-aee1-750f3fed8beb">
<association>
<ref refid="97b4c158-6cae-11eb-aee1-750f3fed8beb"/>
</association>
<class_>
<ref refid="872907b8-6cae-11eb-aee1-750f3fed8beb"/>
</class_>
<name>
<val>baseClass</val>
</name>
<type>
<ref refid="905056c0-6cae-11eb-aee1-750f3fed8beb"/>
</type>
</Property>
<ExtensionEnd id="97b4c15a-6cae-11eb-aee1-750f3fed8beb">
<aggregation>
<val>composite</val>
</aggregation>
<association>
<ref refid="97b4c158-6cae-11eb-aee1-750f3fed8beb"/>
</association>
<class_>
<ref refid="905056c0-6cae-11eb-aee1-750f3fed8beb"/>
</class_>
<type>
<ref refid="872907b8-6cae-11eb-aee1-750f3fed8beb"/>
</type>
</ExtensionEnd>
<Package id="336043b6-6caf-11eb-aee1-750f3fed8beb">
<name>
<val>UML</val>
</name>
<ownedClassifier>
<reflist>
<ref refid="905056c0-6cae-11eb-aee1-750f3fed8beb"/>
<ref refid="b680213a-6d81-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedClassifier>
<presentation>
<reflist>
<ref refid="3dab404e-6ddd-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Package>
<Class id="b680213a-6d81-11eb-bdd8-859c1b7b5447">
<name>
<val>Package</val>
</name>
<ownedAttribute>
<reflist>
<ref refid="ee5ba09a-6d81-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedAttribute>
<package>
<ref refid="336043b6-6caf-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="b680213b-6d81-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Class>
<Stereotype id="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447">
<name>
<val>C4Container</val>
</name>
<ownedAttribute>
<reflist>
<ref refid="dff5b962-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="e3249522-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="e6f6ef88-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="ee5ba099-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="2daf9a4c-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="2daf9a4d-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="f1db5b42-6ddf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedAttribute>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="d7d28eb9-6d81-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Stereotype>
<Property id="dff5b962-6d81-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>description</val>
</name>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<Property id="e3249522-6d81-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>location</val>
</name>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<Property id="e6f6ef88-6d81-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>technology</val>
</name>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<Extension id="ee5ba098-6d81-11eb-bdd8-859c1b7b5447">
<memberEnd>
<reflist>
<ref refid="ee5ba099-6d81-11eb-bdd8-859c1b7b5447"/>
<ref refid="ee5ba09a-6d81-11eb-bdd8-859c1b7b5447"/>
</reflist>
</memberEnd>
<ownedEnd>
<ref refid="ee5ba09a-6d81-11eb-bdd8-859c1b7b5447"/>
</ownedEnd>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="ca16bf74-6d81-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Extension>
<Property id="ee5ba099-6d81-11eb-bdd8-859c1b7b5447">
<association>
<ref refid="ee5ba098-6d81-11eb-bdd8-859c1b7b5447"/>
</association>
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>baseClass</val>
</name>
<type>
<ref refid="b680213a-6d81-11eb-bdd8-859c1b7b5447"/>
</type>
</Property>
<ExtensionEnd id="ee5ba09a-6d81-11eb-bdd8-859c1b7b5447">
<aggregation>
<val>composite</val>
</aggregation>
<association>
<ref refid="ee5ba098-6d81-11eb-bdd8-859c1b7b5447"/>
</association>
<class_>
<ref refid="b680213a-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<type>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</type>
</ExtensionEnd>
<Diagram id="33146bc4-6ddd-11eb-bdd8-859c1b7b5447">
<name>
<val>Overview</val>
</name>
<ownedPresentation>
<reflist>
<ref refid="3a8987f4-6ddd-11eb-bdd8-859c1b7b5447"/>
<ref refid="3dab404e-6ddd-11eb-bdd8-859c1b7b5447"/>
<ref refid="421620c3-6ddd-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedPresentation>
<canvas>
<item id="3a8987f4-6ddd-11eb-bdd8-859c1b7b5447" type="PackageItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 143.0, 117.0)</val>
</matrix>
<width>
<val>131.0</val>
</width>
<height>
<val>96.0</val>
</height>
<diagram>
<ref refid="33146bc4-6ddd-11eb-bdd8-859c1b7b5447"/>
</diagram>
<subject>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</subject>
</item>
<item id="3dab404e-6ddd-11eb-bdd8-859c1b7b5447" type="PackageItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 425.0, 117.0)</val>
</matrix>
<width>
<val>137.0</val>
</width>
<height>
<val>97.0</val>
</height>
<diagram>
<ref refid="33146bc4-6ddd-11eb-bdd8-859c1b7b5447"/>
</diagram>
<subject>
<ref refid="336043b6-6caf-11eb-aee1-750f3fed8beb"/>
</subject>
</item>
<item id="421620c3-6ddd-11eb-bdd8-859c1b7b5447" type="PackageItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 377.0, 259.0)</val>
</matrix>
<width>
<val>137.0</val>
</width>
<height>
<val>70.0</val>
</height>
<diagram>
<ref refid="33146bc4-6ddd-11eb-bdd8-859c1b7b5447"/>
</diagram>
<subject>
<ref refid="421620c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</subject>
</item>
</canvas>
</Diagram>
<Profile id="421620c2-6ddd-11eb-bdd8-859c1b7b5447">
<name>
<val>Gaphor Profile</val>
</name>
<ownedClassifier>
<reflist>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="8b905b0e-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="8fa07206-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedClassifier>
<ownedDiagram>
<reflist>
<ref refid="e93225c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedDiagram>
<presentation>
<reflist>
<ref refid="421620c3-6ddd-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Profile>
<Diagram id="e93225c2-6ddd-11eb-bdd8-859c1b7b5447">
<name>
<val>Gaphor Profile</val>
</name>
<ownedPresentation>
<reflist>
<ref refid="86a99615-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="8b905b0f-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="8ea384b0-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedPresentation>
<package>
<ref refid="421620c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</package>
<canvas>
<item id="86a99615-6dde-11eb-bdd8-859c1b7b5447" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 233.44921875, 186.61328125)</val>
</matrix>
<width>
<val>129.0</val>
</width>
<height>
<val>83.0</val>
</height>
<diagram>
<ref refid="e93225c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</diagram>
<subject>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
</subject>
</item>
<item id="8b905b0f-6dde-11eb-bdd8-859c1b7b5447" type="ClassItem">
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 247.94921875, 32.61328125)</val>
</matrix>
<width>
<val>100.0</val>
</width>
<height>
<val>58.0</val>
</height>
<diagram>
<ref refid="e93225c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</diagram>
<subject>
<ref refid="8b905b0e-6dde-11eb-bdd8-859c1b7b5447"/>
</subject>
</item>
<item id="8ea384b0-6dde-11eb-bdd8-859c1b7b5447" type="ExtensionItem">
<diagram>
<ref refid="e93225c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</diagram>
<horizontal>
<val>0</val>
</horizontal>
<subject>
<ref refid="8fa07206-6dde-11eb-bdd8-859c1b7b5447"/>
</subject>
<matrix>
<val>(1.0, 0.0, 0.0, 1.0, 295.44921875, 60.61328125)</val>
</matrix>
<points>
<val>[(1.1373015873015788, 30.0), (3.6500000000000057, 126.0)]</val>
</points>
<head-connection>
<ref refid="8b905b0f-6dde-11eb-bdd8-859c1b7b5447"/>
</head-connection>
<tail-connection>
<ref refid="86a99615-6dde-11eb-bdd8-859c1b7b5447"/>
</tail-connection>
</item>
</canvas>
</Diagram>
<Association id="2daf9a4b-6dde-11eb-bdd8-859c1b7b5447">
<memberEnd>
<reflist>
<ref refid="2daf9a4c-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="2daf9a4d-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</memberEnd>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="2daf9a4a-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Association>
<Property id="2daf9a4c-6dde-11eb-bdd8-859c1b7b5447">
<appliedStereotype>
<reflist>
<ref refid="b5fb0092-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</appliedStereotype>
<association>
<ref refid="2daf9a4b-6dde-11eb-bdd8-859c1b7b5447"/>
</association>
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<lowerValue>
<val>0</val>
</lowerValue>
<lowerValue>
<val>0</val>
</lowerValue>
<name>
<val>ownerContainer</val>
</name>
<type>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</type>
<upperValue>
<val>1</val>
</upperValue>
<upperValue>
<val>1</val>
</upperValue>
</Property>
<Property id="2daf9a4d-6dde-11eb-bdd8-859c1b7b5447">
<aggregation>
<val>composite</val>
</aggregation>
<appliedStereotype>
<reflist>
<ref refid="bffddfa6-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</appliedStereotype>
<association>
<ref refid="2daf9a4b-6dde-11eb-bdd8-859c1b7b5447"/>
</association>
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>owningContainer</val>
</name>
<type>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</type>
<upperValue>
<val>*</val>
</upperValue>
<upperValue>
<val>*</val>
</upperValue>
</Property>
<Stereotype id="86a99614-6dde-11eb-bdd8-859c1b7b5447">
<name>
<val>Tagged</val>
</name>
<ownedAttribute>
<reflist>
<ref refid="8fa07207-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="95b4032e-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedAttribute>
<package>
<ref refid="421620c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</package>
<presentation>
<reflist>
<ref refid="86a99615-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Stereotype>
<Class id="8b905b0e-6dde-11eb-bdd8-859c1b7b5447">
<name>
<val>Property</val>
</name>
<ownedAttribute>
<reflist>
<ref refid="8fa07208-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</ownedAttribute>
<package>
<ref refid="421620c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</package>
<presentation>
<reflist>
<ref refid="8b905b0f-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Class>
<Extension id="8fa07206-6dde-11eb-bdd8-859c1b7b5447">
<memberEnd>
<reflist>
<ref refid="8fa07207-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="8fa07208-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</memberEnd>
<ownedEnd>
<ref refid="8fa07208-6dde-11eb-bdd8-859c1b7b5447"/>
</ownedEnd>
<package>
<ref refid="421620c2-6ddd-11eb-bdd8-859c1b7b5447"/>
</package>
<presentation>
<reflist>
<ref refid="8ea384b0-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Extension>
<Property id="8fa07207-6dde-11eb-bdd8-859c1b7b5447">
<association>
<ref refid="8fa07206-6dde-11eb-bdd8-859c1b7b5447"/>
</association>
<class_>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>baseClass</val>
</name>
<type>
<ref refid="8b905b0e-6dde-11eb-bdd8-859c1b7b5447"/>
</type>
</Property>
<ExtensionEnd id="8fa07208-6dde-11eb-bdd8-859c1b7b5447">
<aggregation>
<val>composite</val>
</aggregation>
<association>
<ref refid="8fa07206-6dde-11eb-bdd8-859c1b7b5447"/>
</association>
<class_>
<ref refid="8b905b0e-6dde-11eb-bdd8-859c1b7b5447"/>
</class_>
<type>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
</type>
</ExtensionEnd>
<Property id="95b4032e-6dde-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>subsets</val>
</name>
<slot>
<reflist>
<ref refid="bd877458-6dde-11eb-bdd8-859c1b7b5447"/>
<ref refid="c3561f1a-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</slot>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<InstanceSpecification id="b5fb0092-6dde-11eb-bdd8-859c1b7b5447">
<classifier>
<reflist>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</classifier>
<extended>
<reflist>
<ref refid="2daf9a4c-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</extended>
<slot>
<reflist>
<ref refid="bd877458-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</slot>
</InstanceSpecification>
<Slot id="bd877458-6dde-11eb-bdd8-859c1b7b5447">
<definingFeature>
<ref refid="95b4032e-6dde-11eb-bdd8-859c1b7b5447"/>
</definingFeature>
<owningInstance>
<ref refid="b5fb0092-6dde-11eb-bdd8-859c1b7b5447"/>
</owningInstance>
<value>
<val>namespace</val>
</value>
</Slot>
<InstanceSpecification id="bffddfa6-6dde-11eb-bdd8-859c1b7b5447">
<classifier>
<reflist>
<ref refid="86a99614-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</classifier>
<extended>
<reflist>
<ref refid="2daf9a4d-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</extended>
<slot>
<reflist>
<ref refid="c3561f1a-6dde-11eb-bdd8-859c1b7b5447"/>
</reflist>
</slot>
</InstanceSpecification>
<Slot id="c3561f1a-6dde-11eb-bdd8-859c1b7b5447">
<definingFeature>
<ref refid="95b4032e-6dde-11eb-bdd8-859c1b7b5447"/>
</definingFeature>
<owningInstance>
<ref refid="bffddfa6-6dde-11eb-bdd8-859c1b7b5447"/>
</owningInstance>
<value>
<val>ownedMember</val>
</value>
</Slot>
<Property id="db6499c8-6ddf-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="872907b8-6cae-11eb-aee1-750f3fed8beb"/>
</class_>
<name>
<val>description</val>
</name>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<Property id="e17a5dac-6ddf-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="872907b8-6cae-11eb-aee1-750f3fed8beb"/>
</class_>
<name>
<val>location</val>
</name>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<Property id="f1db5b42-6ddf-11eb-bdd8-859c1b7b5447">
<class_>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</class_>
<name>
<val>type</val>
</name>
<typeValue>
<val>str</val>
</typeValue>
</Property>
<Class id="6e5e9410-6faf-11eb-bdd8-859c1b7b5447">
<generalization>
<reflist>
<ref refid="74ad084c-6faf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</generalization>
<name>
<val>C4Database</val>
</name>
<package>
<ref refid="2e656717-6cae-11eb-aee1-750f3fed8beb"/>
</package>
<presentation>
<reflist>
<ref refid="6e5e9411-6faf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
</Class>
<Generalization id="74ad084c-6faf-11eb-bdd8-859c1b7b5447">
<general>
<ref refid="d7d28eb8-6d81-11eb-bdd8-859c1b7b5447"/>
</general>
<presentation>
<reflist>
<ref refid="5630b2f6-6faf-11eb-bdd8-859c1b7b5447"/>
</reflist>
</presentation>
<specific>
<ref refid="6e5e9410-6faf-11eb-bdd8-859c1b7b5447"/>
</specific>
</Generalization>
</gaphor>

13
models/C4Model.override Normal file
View File

@ -0,0 +1,13 @@
comment
This is a file with custom definitions for Gaphor's data model.
Parts are separated by '%%' (no training spaces) on a line.
Comment parts start with 'comment' on the line below the percentage
symbols, 'override' is used to define a overridden variable.
Overrides may in their turn derive from other properties, in that case
the 'derives' keyword may be used. It's only useful to declare the
associations (and other derived properties) an overridden value depends
on, since attributes have been written anyway. Note that no smart things
wrt inheritance is done.
%%

View File

@ -94,6 +94,7 @@ gaphorconvert = 'gaphor.plugins.diagramexport.gaphorconvert:main'
[tool.poetry.plugins."gaphor.modelinglanguages"]
"UML" = "gaphor.UML.modelinglanguage:UMLModelingLanguage"
"SysML" = "gaphor.SysML.modelinglanguage:SysMLModelingLanguage"
"C4Model" = "gaphor.C4Model.modelinglanguage:C4ModelLanguage"
[tool.pytest.ini_options]
testpaths = [