Skip to content

Commit

Permalink
Merge branch 'storybook' of https://github.com/DSD-DBS/capella-model-…
Browse files Browse the repository at this point in the history
…explorer into storybook
  • Loading branch information
MFK-18 committed Apr 5, 2024
2 parents a2dbc28 + 054d0a3 commit 63b17c6
Show file tree
Hide file tree
Showing 57 changed files with 403 additions and 16,515 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,4 @@ cython_debug/
node_modules/

# Mac stuff
.DS_Store
.DS_Store
22 changes: 17 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ repos:
rev: 6.3.0
hooks:
- id: pydocstyle
exclude: '^tests/'
exclude: "^tests/"
additional_dependencies:
- pydocstyle[toml]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies:
- types-pyyaml==6.0.11
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.4
hooks:
Expand All @@ -62,7 +64,7 @@ repos:
- --license-filepath
- LICENSES/.license_header.txt
- --comment-style
- '#'
- "#"
- id: insert-license
name: Insert license headers (XML-style comments)
files: '\.(?:html|md|xml)$'
Expand All @@ -72,7 +74,7 @@ repos:
- --license-filepath
- LICENSES/.license_header.txt
- --comment-style
- '<!--| ~| -->'
- "<!--| ~| -->"
- id: insert-license
name: Insert license headers (C-style comments)
files: '\.(?:css|js|ts)$'
Expand All @@ -82,7 +84,7 @@ repos:
- --license-filepath
- LICENSES/.license_header.txt
- --comment-style
- '/*| *| */'
- "/*| *| */"
- id: insert-license
name: Insert license headers (reST comments)
files: '\.rst$'
Expand All @@ -92,7 +94,17 @@ repos:
- --license-filepath
- LICENSES/.license_header.txt
- --comment-style
- '..| |'
- "..| |"
- id: insert-license
name: Insert license headers (shell-style comments)
files: '(?:^|/)(?:.*\.(?:jsx)|Dockerfile|Makefile)$'
exclude: '(?:^|/)\..+|^docs/Makefile$'
args:
- --detect-license-in-X-top-lines=15
- --license-filepath
- LICENSES/.license_header.txt
- --comment-style
- "//"
- repo: https://github.com/fsfe/reuse-tool
rev: v3.0.1
hooks:
Expand Down
2 changes: 2 additions & 0 deletions .vscode/launch.json.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Copyright DB InfraGO AG and contributors
SPDX-License-Identifier: Apache-2.0
24 changes: 22 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Copyright DB InfraGO AG and contributors
// SPDX-License-Identifier: Apache-2.0

# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

# Build frontend
FROM node:20 as build-frontend
WORKDIR /app
Expand All @@ -12,12 +18,26 @@ WORKDIR /app
COPY ./capella_model_explorer ./capella_model_explorer
COPY ./pyproject.toml ./
COPY ./.git ./.git
RUN apt-get update && apt-get install -y git

USER root

RUN apt-get update && \
apt-get install --yes --no-install-recommends \
git \
git-lfs \
libgirepository1.0-dev \
libcairo2-dev \
gir1.2-pango-1.0 \
graphviz \
nodejs \
npm && \
rm -rf /var/lib/apt/lists/*

RUN pip install .
COPY --from=build-frontend /app/dist/ ./frontend/dist/

# Expose the port the app runs in
EXPOSE 8000

# Start the application
CMD ["python", "-m", "capella_model_explorer.backend", "/model", "/views"]
CMD ["python", "-m", "capella_model_explorer.backend", "/model", "/views"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
![image](https://github.com/DSD-DBS/capella-model-explorer/actions/workflows/build-test-publish.yml/badge.svg)
![image](https://github.com/DSD-DBS/capella-model-explorer/actions/workflows/lint.yml/badge.svg)

A webapp for exploring Capella models through simple "auto-generated" textual and graphical views.
A webapp for exploring Capella models through simple "auto-generated" textual and graphical views.

**Longer story**:

Expand Down
2 changes: 2 additions & 0 deletions capella_model_explorer/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
115 changes: 83 additions & 32 deletions capella_model_explorer/backend/explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ def __post_init__(self):
allow_headers=["*"],
)
self.env = Environment()
self.templates = index_templates(self.templates_path)
self.app.state.templates = templates = Jinja2Templates(
directory=PATH_TO_FRONTEND
self.grouped_templates, self.templates = index_templates(
self.templates_path
)
self.app.state.templates = Jinja2Templates(directory=PATH_TO_FRONTEND)

self.configure_routes()

Expand All @@ -59,11 +59,10 @@ def configure_routes(self):
@self.app.get("/api/views")
def read_templates():
# list all templates in the templates folder from .yaml
self.templates = index_templates(self.templates_path)
return [
{"idx": key, **template}
for key, template in self.templates.items()
]
self.grouped_templates, self.templates = index_templates(
self.templates_path
)
return self.grouped_templates

@self.app.get("/api/objects/{uuid}")
def read_object(uuid: str):
Expand All @@ -72,13 +71,21 @@ def read_object(uuid: str):

@self.app.get("/api/views/{template_name}")
def read_template(template_name: str):
template_name = urlparse.unquote(template_name)
if not template_name in self.templates:
return {"error": f"Template {template_name} not found"}
base = self.templates[urlparse.quote(template_name)]
filters = base.get("filters") or None
variable = base["variable"]
below = variable.get("below") or None
attr = variable.get("attr") or None
try:
objects = find_objects(
self.model, variable["type"], below=below, attr=attr
self.model,
variable["type"],
below=below,
attr=attr,
filters=filters,
)
base["objects"] = [
{"idx": obj.uuid, "name": obj.name} for obj in objects
Expand All @@ -90,20 +97,38 @@ def read_template(template_name: str):

@self.app.get("/api/views/{template_name}/{object_id}")
def render_template(template_name: str, object_id: str):
base = self.templates[urlparse.quote(template_name)]
template_filename = base["template"]
# load the template file from the templates folder
content = (self.templates_path / template_filename).read_text(
encoding="utf8"
)
object = self.model.by_uuid(object_id)
content = None
object = None
try:
base = self.templates[urlparse.quote(template_name)]
template_filename = base["template"]
# load the template file from the templates folder
content = (self.templates_path / template_filename).read_text(
encoding="utf8"
)
except Exception as e:
error_message = (
f"<p style='color:red'>Template not found: {str(e)}</p>"
)
return HTMLResponse(content=error_message)
try:
object = self.model.by_uuid(object_id)
except Exception as e:
error_message = (
"<p style='color:red'>Requested object "
f"not found: {str(e)}</p>"
)
return HTMLResponse(content=error_message)
try:
# render the template with the object
template = self.env.from_string(content)
rendered = template.render(object=object)
return HTMLResponse(content=rendered, status_code=200)
except TemplateSyntaxError as e:
error_message = f"<p style='color:red'>Template syntax error: {e.message}, line {e.lineno}</p>"
error_message = (
"<p style='color:red'>Template syntax error: "
f"{e.message}, line {e.lineno}</p>"
)
return HTMLResponse(content=error_message)
except Exception as e:
error_message = (
Expand All @@ -114,38 +139,64 @@ def render_template(template_name: str, object_id: str):
@self.app.get("/api/model-info")
def model_info():
info = self.model.info
return dict(
title=info.title,
revision=info.revision,
hash=info.rev_hash,
capella_version=info.capella_version,
branch=info.branch,
badge=self.model.description_badge
)
return {
"title": info.title,
"revision": info.revision,
"hash": info.rev_hash,
"capella_version": info.capella_version,
"branch": info.branch,
"badge": self.model.description_badge,
}

@self.app.get("/{rest_of_path:path}")
async def catch_all(request: Request, rest_of_path: str):
del rest_of_path
return self.app.state.templates.TemplateResponse(
"index.html", {"request": request}
)


def index_templates(path: pathlib.Path) -> dict[str, t.Any]:
def index_templates(
path: pathlib.Path,
) -> tuple[dict[str, t.Any], dict[str, t.Any]]:
templates_grouped: dict[str, t.Any] = {"other": []}
templates: dict[str, t.Any] = {}
for template_file in path.glob("*.yaml"):
template = yaml.safe_load(template_file.read_text(encoding="utf8"))
idx = urlparse.quote(template_file.name.replace(".yaml", ""))
record = {"idx": idx, **template}
if "category" in template:
category = template["category"]
if category not in templates_grouped:
templates_grouped[category] = []
templates_grouped[category].append(record)
else:
templates_grouped["other"].append(record)
templates[idx] = template
name = template_file.name.replace(".yaml", "")
templates[urlparse.quote(name)] = template
# later we could add here count of objects that can be rendered
# with this template
return templates
return templates_grouped, templates


def find_objects(model, obj_type, below=None, attr=None):
def find_objects(model, obj_type, below=None, attr=None, filters=None):
if attr:
getter = operator.attrgetter(attr)
return getter(model)
if below:
objects = getter(model)
elif below:
getter = operator.attrgetter(below)
return model.search(obj_type, below=getter(model))
return model.search(obj_type)
objects = model.search(obj_type, below=getter(model))
else:
objects = model.search(obj_type)

if filters:
objects = [
obj
for obj in objects
if all(
getattr(obj, key) == value for key, value in filters.items()
)
]

return objects
3 changes: 3 additions & 0 deletions frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright DB InfraGO AG and contributors
// SPDX-License-Identifier: Apache-2.0

module.exports = {
root: true,
env: { browser: true, es2020: true },
Expand Down
6 changes: 6 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0

# Logs
logs
*.log
Expand All @@ -22,3 +25,6 @@ dist-ssr
*.njsproj
*.sln
*.sw?

# Package
package-lock.json
3 changes: 3 additions & 0 deletions frontend/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright DB InfraGO AG and contributors
// SPDX-License-Identifier: Apache-2.0

/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
Expand Down
3 changes: 3 additions & 0 deletions frontend/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright DB InfraGO AG and contributors
// SPDX-License-Identifier: Apache-2.0

import '../src/index.css'

/** @type { import('@storybook/react').Preview } */
Expand Down
5 changes: 5 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<!--
~ Copyright DB InfraGO AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

# React + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Expand Down
5 changes: 5 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<!--
~ Copyright DB InfraGO AG and contributors
~ SPDX-License-Identifier: Apache-2.0
-->

<!doctype html>
<html lang="en">
<head>
Expand Down
Loading

0 comments on commit 63b17c6

Please sign in to comment.