Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor code #34

Merged
merged 9 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import argparse
import click
import json
import subprocess
from pathlib import Path

from linkml.generators.jsonschemagen import JsonSchemaGenerator
from linkml.generators.shaclgen import ShaclGenerator
Expand All @@ -10,10 +12,7 @@
from linkml.generators.linkmlgen import LinkmlGenerator
from linkml_runtime.utils.schemaview import SchemaView
from linkml_runtime.linkml_model.meta import AnonymousSlotExpression
from pathlib import Path
from pyld import jsonld

from linkml_runtime.linkml_model.meta import SlotDefinition

RESOURCES_PATH = Path('resources')
GENS_PATH = RESOURCES_PATH / 'gens'
Expand Down
26 changes: 18 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
[project]
name = "thing_description_schema"
name = "wotis"
version = "0.1.0"
description = "Thing Description Information Model as a LinkML schema"
description = "A CLI for Web of Things Integrated Schemas (WOTIS)"
authors = [
{ name = "Mahda Noura", email = "mahdanoura@gmail.com" }
{ name = "Mahda Noura", email = "mahda.noura@siemens.com" }
]
license = "MIT"
readme = "README.md"
include = ["README.md", "src/thing_description_schema/schema", "project"]

requires-python = ">=3.12"
include = ["README.md", "resources/schemas/*"]
requires-python = ">=3.11"
dependencies = [
"linkml-runtime>=1.8.2",
"linkml>=1.8.2",
"mkdocs-mermaid2-plugin>=1.1.1",
"mkdocs-material>=9.5.32",
"schemasheets>=0.3.1",
"schemasheets>=0.3.1"
]

[project.optional-dependencies]
cli = [
"click>=8.1.7"
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build]
only-include = ["main.py"]
only-include = ["src/wotis/cli.py"]

[tool.hatch.metadata]
root = "src"

[project.scripts]
wotis = "src.wotis.cli:main"
7 changes: 0 additions & 7 deletions src/data/examples/Thing-001.yaml

This file was deleted.

10 changes: 10 additions & 0 deletions src/wotis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from pathlib import Path


RESOURCES_PATH = Path('resources')
GENS_PATH = RESOURCES_PATH / 'gens'
SCHEMA_PATH = RESOURCES_PATH / 'schemas'
YAML_SCHEMA_PATH = SCHEMA_PATH / 'thing_description.yaml'
DOCDIR = GENS_PATH / 'docs' / 'ontology'

GENERATORS = ['jsonschema', 'shacl', 'jsonldcontext', 'linkml']
130 changes: 130 additions & 0 deletions src/wotis/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Command line interface for WoTIS."""

import click
import logging
from pathlib import Path
import subprocess

import yaml
from linkml.generators.jsonschemagen import JsonSchemaGenerator
from linkml.generators.shaclgen import ShaclGenerator
from linkml.generators.owlgen import OwlSchemaGenerator
from linkml.generators.jsonldcontextgen import ContextGenerator
from linkml.generators.docgen import DocGenerator
from linkml.generators.linkmlgen import LinkmlGenerator
from linkml_runtime.utils.schemaview import SchemaView

from src.wotis import YAML_SCHEMA_PATH, GENS_PATH, GENERATORS, DOCDIR


input_option = click.option('-i', '--input_schema',
type=str,
show_default=True,
help="Path to the input schema specified as LinkML yaml.",
default=YAML_SCHEMA_PATH)
docs_option = click.option('-d',
'--generate_docs',
type=bool,
is_flag=True,
default=False,
show_default=True,
help="Boolean for local documentation generation.")
serve_docs_option = click.option('-s',
'--serve_docs',
type=bool,
is_flag=True,
default=False,
show_default=True,
help="Boolean for serving the generated documentation.")


# Documentation serving function
def serve_documentation():
subprocess.run(['mkdocs', 'serve'], check=True)


def generate_documentation():
DOCDIR.mkdir(parents=True, exist_ok=True)
doc_generator = DocGenerator(YAML_SCHEMA_PATH, mergeimports=False)
doc_generator.serialize(directory=str(DOCDIR))


# Main generation function
def run_generator(schema_view, generator, output_dir):
if generator == 'jsonschema':
logging.info(f"JSON Schema generation")
json_schema_generator = JsonSchemaGenerator(schema_view.schema, mergeimports=True)
(output_dir / 'jsonschema.json').write_text(json_schema_generator.serialize())
elif generator == 'shacl':
shacl_generator = ShaclGenerator(schema_view.schema, mergeimports=False, closed=True, suffix='Shape')
(output_dir / 'shapes.shacl.ttl').write_text(shacl_generator.serialize())
elif generator == 'owl':
owl_generator = OwlSchemaGenerator(schema_view.schema, )
(output_dir / 'ontology.owl.ttl').write_text(owl_generator.serialize())
elif generator == 'jsonldcontext':
context_generator = ContextGenerator(schema_view.schema, mergeimports=True)
# (output_dir / 'context.jsonld').write_text(post_process_jsonld_context(linkml_schema_view,
# context_generator.serialize()))
elif generator == 'linkml':
linkml_generator = LinkmlGenerator(schema_view.schema, mergeimports=True, format='yaml',
output='linkml.yaml')
(output_dir / 'linkml.yaml').write_text(linkml_generator.serialize())
else:
print(f"Unknown generator: {generator}")


@click.group()
@click.option("-v", "--verbose", count=True)
@click.option("-q", "--quiet")
def main(verbose: int, quiet: bool):
"""CLI for WOTIS (Web of Things Integrated Schemas) toolchain.

:param verbose: Verbosity while running.
:param quiet: Boolean to be quiet or verbose.
"""
logger = logging.getLogger()
if verbose >= 2:
logger.setLevel(level=logging.DEBUG)
elif verbose == 1:
logger.setLevel(level=logging.INFO)
else:
logger.setLevel(level=logging.WARNING)
if quiet:
logger.setLevel(level=logging.ERROR)
logger.info(f"Logger {logger.name} set to level {logger.level}")


@main.command()
@input_option
@docs_option
@serve_docs_option
def generate_wot_resources(input_schema: str, generate_docs: bool, serve_docs: bool):
"""
Generating WoT resources (RDF, JSON-LD Context, SHACL Shapes, and JSON Schema) from manually constructed
LinkML-based schemas.
"""
if input_schema and not Path(input_schema).exists():
raise FileNotFoundError(f"Cannot find input LinkML schema file {input_schema}.")
elif not input_schema and not YAML_SCHEMA_PATH.exists():
raise FileNotFoundError(f"Cannot find the default LinkML schema file {YAML_SCHEMA_PATH}.")
else:
try:
linkml_schema_view = SchemaView(input_schema, merge_imports=True)
logging.info(f"Input schema {input_schema} loaded successfully!")
for generator in GENERATORS:
output_dir = GENS_PATH / generator
output_dir.mkdir(parents=True, exist_ok=True)
logging.info(f"Proceeding with WoT resource generation")
run_generator(linkml_schema_view, generator, output_dir)
except yaml.YAMLError as e:
logging.info(f"LinkML schema validation failed: {e}")
if generate_docs:
logging.info(f"Generating documentation locally as markdown files...")
generate_documentation()
if serve_docs:
logging.info(f"GServing documentation...")
serve_documentation()


if __name__ == "__main__":
main()
File renamed without changes.
Empty file added src/wotis/utils/__init__.py
Empty file.
Empty file.
Loading