ontologytoapi 0.0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Julio César G. Costa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include requirements.txt
4
+ include CHANGELOG.md
5
+
6
+ global-include *.py
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: ontologytoapi
3
+ Version: 0.0.5
4
+ Summary: Multi purpose API Generator based on an Ontology Framework.
5
+ Home-page: https://github.com/JCGCosta/OntologyToAPI
6
+ Author: Júlio César Guimarães Costa
7
+ Author-email: juliocesargcosta123@gmail.com
8
+ License: MIT License
9
+ Keywords: Ontology,API
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: SQLAlchemy
19
+ Requires-Dist: pydantic
20
+ Requires-Dist: requests
21
+ Requires-Dist: pymongo
22
+ Requires-Dist: rdflib
23
+ Requires-Dist: fastapi
24
+ Requires-Dist: uvicorn
25
+ Requires-Dist: aiomysql
26
+ Requires-Dist: motor
27
+ Requires-Dist: aiofiles
28
+ Requires-Dist: python-multipart
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: keywords
36
+ Dynamic: license
37
+ Dynamic: license-file
38
+ Dynamic: requires-dist
39
+ Dynamic: summary
40
+
41
+ # OntologyToAPI
42
+ > This project is an ontology-driven API generator designed for
43
+ > backend development by transforming structured domain
44
+ > knowledge into fully functional APIs. The tool accepts ontologies
45
+ > specified in Turtle (.ttl), Resource Description Framework (.rdf)
46
+ > and Web Ontology Language (.owl).
47
+
48
+ > [![Publish to PyPI and TestPyPI](https://github.com/JCGCosta/OntologyToAPI/actions/workflows/python-publish.yml/badge.svg)](https://github.com/JCGCosta/OntologyToAPI/actions/workflows/python-publish.yml)
49
+
50
+
51
+ ## Ontological Framework:
52
+
53
+ - The following classes, relationships and data properties serve as a semantic blueprint for both metadata and business models.
54
+
55
+ <img src="https://github.com/JCGCosta/OntologyToAPI/blob/master/OntologicalFramework.jpg?raw=true" alt="AbstractOntologyClasses" title="Abstract Ontology Classes.">
56
+
57
+ The ontological framework is composed of two main modules:
58
+
59
+ - **Metadata Ontology Module:** This module defines the essential classes and properties required to describe the metadata and its sources (e.g. Query to be executed on the CommunicationTechnology).
60
+ - **BusinessModel Ontology Module:** This module captures the specific business logic and rules governing some operation, it requires an ExternalCode concretization, and it can require any metadata or parameter (To be sent in the API request).
61
+ - **ExternalCode Ontology Module:** This module has all the technical details to connect to an external code, it also adds the possibility to dynamically require python packages.
62
+ - **Communications Ontology Module:** This module describes the communication technologies that can be used to fetch the data of some metadata in multiple forms (e. g).
63
+
64
+ > From now on you must be ready to go and create your own ontological specification importing the Ontology Modules and extending it. You can do this by using the Protégé ontology editor (https://protege.stanford.edu/). Or if you prefer you can use any text editor to create your ontology files in the supported formats (.ttl, .rdf, .owl).
65
+
66
+ ### Step 1: Installing the Package
67
+
68
+ ```bash
69
+ # Now inside the environment install the python package
70
+ pip install ontologytoapi
71
+ ```
72
+
73
+
74
+ ### Step 2: Running
75
+
76
+ - If you want to see a quick ontology sample in .ttl please access the following link: https://github.com/JCGCosta/OntologyToAPI/tree/master/samples
77
+ - With you metadata and business models ontologies implemented you can generate your API by having the following python file as an entry point:
78
+
79
+ ```python
80
+ import uvicorn
81
+ from core.APIGenerator import Generator
82
+
83
+ if __name__ == "__main__":
84
+ APIGen = Generator(showLogs=True)
85
+ APIGen.load_ontologies(paths=[
86
+ "Your/Metadata/Ontology/.ttl.owl.rdf"
87
+ ])
88
+ APIGen.load_ontologies(paths=[
89
+ "Your/BusinessModel/Ontology/.ttl.owl.rdf"
90
+ ])
91
+ APIGen.serialize_ontologies()
92
+ api_app = APIGen.generate_api_routes()
93
+ uvicorn.run(api_app, host="127.0.0.1", port=5000)
94
+ ```
95
+
96
+
97
+ Change Log
98
+ ===============
99
+ 0.0.5 (27/10/2025)
100
+ ------------------
101
+ FIRST RELEASE
102
+ - First fixes on README file.
103
+ - Added Ontological Framework diagram.
104
+ - Added ontology concrete sample.
105
+ ------------------
106
+
@@ -0,0 +1,54 @@
1
+ # OntologyToAPI
2
+ > This project is an ontology-driven API generator designed for
3
+ > backend development by transforming structured domain
4
+ > knowledge into fully functional APIs. The tool accepts ontologies
5
+ > specified in Turtle (.ttl), Resource Description Framework (.rdf)
6
+ > and Web Ontology Language (.owl).
7
+
8
+ > [![Publish to PyPI and TestPyPI](https://github.com/JCGCosta/OntologyToAPI/actions/workflows/python-publish.yml/badge.svg)](https://github.com/JCGCosta/OntologyToAPI/actions/workflows/python-publish.yml)
9
+
10
+
11
+ ## Ontological Framework:
12
+
13
+ - The following classes, relationships and data properties serve as a semantic blueprint for both metadata and business models.
14
+
15
+ <img src="https://github.com/JCGCosta/OntologyToAPI/blob/master/OntologicalFramework.jpg?raw=true" alt="AbstractOntologyClasses" title="Abstract Ontology Classes.">
16
+
17
+ The ontological framework is composed of two main modules:
18
+
19
+ - **Metadata Ontology Module:** This module defines the essential classes and properties required to describe the metadata and its sources (e.g. Query to be executed on the CommunicationTechnology).
20
+ - **BusinessModel Ontology Module:** This module captures the specific business logic and rules governing some operation, it requires an ExternalCode concretization, and it can require any metadata or parameter (To be sent in the API request).
21
+ - **ExternalCode Ontology Module:** This module has all the technical details to connect to an external code, it also adds the possibility to dynamically require python packages.
22
+ - **Communications Ontology Module:** This module describes the communication technologies that can be used to fetch the data of some metadata in multiple forms (e. g).
23
+
24
+ > From now on you must be ready to go and create your own ontological specification importing the Ontology Modules and extending it. You can do this by using the Protégé ontology editor (https://protege.stanford.edu/). Or if you prefer you can use any text editor to create your ontology files in the supported formats (.ttl, .rdf, .owl).
25
+
26
+ ### Step 1: Installing the Package
27
+
28
+ ```bash
29
+ # Now inside the environment install the python package
30
+ pip install ontologytoapi
31
+ ```
32
+
33
+
34
+ ### Step 2: Running
35
+
36
+ - If you want to see a quick ontology sample in .ttl please access the following link: https://github.com/JCGCosta/OntologyToAPI/tree/master/samples
37
+ - With you metadata and business models ontologies implemented you can generate your API by having the following python file as an entry point:
38
+
39
+ ```python
40
+ import uvicorn
41
+ from core.APIGenerator import Generator
42
+
43
+ if __name__ == "__main__":
44
+ APIGen = Generator(showLogs=True)
45
+ APIGen.load_ontologies(paths=[
46
+ "Your/Metadata/Ontology/.ttl.owl.rdf"
47
+ ])
48
+ APIGen.load_ontologies(paths=[
49
+ "Your/BusinessModel/Ontology/.ttl.owl.rdf"
50
+ ])
51
+ APIGen.serialize_ontologies()
52
+ api_app = APIGen.generate_api_routes()
53
+ uvicorn.run(api_app, host="127.0.0.1", port=5000)
54
+ ```
@@ -0,0 +1,99 @@
1
+ from fastapi import FastAPI, UploadFile
2
+ from fastapi.responses import RedirectResponse
3
+ from typing import List
4
+ import pprint, logging
5
+ from pathlib import Path
6
+ from inspect import Signature, Parameter
7
+ from Settings import auto_config as cfg
8
+ from datetime import date
9
+
10
+ from core.Utility import *
11
+ from core.Ontology import Ontology
12
+
13
+ UPLOAD_DIR = Path(cfg.UPLOAD_DIR)
14
+ UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
15
+
16
+ def create_metadata_handler(connector, query: str, name: str):
17
+ async def handler():
18
+ try:
19
+ result = await connector.exec_query(query)
20
+ return result
21
+ except Exception as e:
22
+ logging.exception(f"Error running query for {name}")
23
+ return {"error": str(e)}
24
+ return handler
25
+
26
+ def create_business_model_handler(required_metadata: list, requiresParameters:dict, external_function):
27
+ async def handler(**kwargs):
28
+ aggregated_res = {"FromParameters": {**kwargs}, "FromMetadata": {}, "UPLOAD_DIR": UPLOAD_DIR}
29
+ for md in required_metadata:
30
+ pkg, name = md.name.split(":")
31
+ try:
32
+ aggregated_res["FromMetadata"][name] = await md.hasSource.comm_technology.exec_query(md.hasSource.query)
33
+ except Exception as e:
34
+ logging.exception(f"Error running query for {name}")
35
+ return {"error": str(e)}
36
+ result = await external_function(aggregated_res)
37
+ return result
38
+ params = []
39
+ for pl, pt in requiresParameters.items(): params.append(Parameter(pl, Parameter.POSITIONAL_OR_KEYWORD, default=None, annotation=pt))
40
+ handler.__signature__ = Signature(parameters=params)
41
+ return handler
42
+
43
+ class APIGenerator:
44
+ def __init__(self, showLogs: bool = False):
45
+ self.ontology = Ontology()
46
+ self.routes = []
47
+ self.app = FastAPI()
48
+ logging.basicConfig(
49
+ level=logging.INFO if showLogs else logging.ERROR,
50
+ format="%(asctime)s %(levelname)s %(message)s",
51
+ datefmt="%d-%m-%Y %H:%M:%S"
52
+ )
53
+ logging.info(f'The {cfg.UPLOAD_DIR} directory is configured for uploaded files.')
54
+ @self.app.get("/", include_in_schema=False)
55
+ async def root():
56
+ return RedirectResponse(url="/docs")
57
+
58
+ def load_ontologies(self, paths: List[str]):
59
+ for path in paths:
60
+ if not Path(path).exists():
61
+ raise FileNotFoundError(f"The path does not exist: {path}")
62
+ self.ontology.parse_ontology(path=path)
63
+
64
+ def serialize_ontologies(self):
65
+ self.ontology.serialize_metadata()
66
+ self.ontology.serialize_business_models()
67
+
68
+ def generate_api_routes(self) -> FastAPI:
69
+ logging.info(f'Generating API routes for the available metadata...')
70
+ for pkg_name, pkg_data in self.ontology.data.items():
71
+ for md in pkg_data:
72
+ if not md.hasSource or not md.hasSource.query:
73
+ continue # Skip metadata without source query
74
+ route_path = f"/{pkg_name}/{md.name.split(':')[-1]}"
75
+ query = md.hasSource.query
76
+ db_connector = md.hasSource.comm_technology
77
+ handler = create_metadata_handler(db_connector, query, md.name)
78
+ self.app.add_api_route(
79
+ path=route_path,
80
+ endpoint=handler,
81
+ methods=["GET"],
82
+ name=md.hasSource.desc,
83
+ tags=[f"Metadata ({pkg_name})"],
84
+ )
85
+ logging.info(f'Added {md.name} API route...')
86
+ logging.info(f'Generating API routes for the business models...')
87
+ for bm_name, bm_dt in self.ontology.bms.items():
88
+ name = bm_name.split(":")
89
+ ec_func = import_function_from_file(filepath=bm_dt.externalCode.pythonFile, function_name=bm_dt.externalCode.function)
90
+ handler = create_business_model_handler(bm_dt.requiresMetadata, bm_dt.requiresParameters, ec_func)
91
+ self.app.add_api_route(
92
+ path=f"/{name[0]}/{name[1]}/run",
93
+ endpoint=handler,
94
+ methods=["POST"],
95
+ name=f"This endpoint executes the {bm_dt.name} business model, and retrieve it`s results.",
96
+ tags=[f"Business Model ({bm_dt.name})"],
97
+ )
98
+ logging.info(f'Added {bm_name} running API route...')
99
+ return self.app
@@ -0,0 +1,18 @@
1
+ from core.Connectors.Stateful.SocketConnection import SocketConnection
2
+ from core.Connectors.Stateless.APIConnection import APIConnection
3
+ from core.Connectors.Stateless.MYSQLConnection import MySQLConnection
4
+ from core.Connectors.Stateless.MongoDBConnection import MongoDBConnection
5
+
6
+ SUPPORTED_CONNECTIONS = {
7
+ "API": APIConnection,
8
+ "MYSQL": MySQLConnection,
9
+ "MONGODB": MongoDBConnection,
10
+ "Socket": SocketConnection
11
+ }
12
+
13
+ def identifyConnector(CommunicationTechnology, args):
14
+ try:
15
+ connector_class = SUPPORTED_CONNECTIONS[str(CommunicationTechnology)]
16
+ return connector_class(args)
17
+ except KeyError as e:
18
+ raise ValueError(f"Unsupported type or technology: {e}")
@@ -0,0 +1,16 @@
1
+ import socket as soc
2
+
3
+ class SocketConnection:
4
+ def __init__(self, args):
5
+ try:
6
+ self.socket = soc.socket(socket.AF_INET, socket.SOCK_STREAM)
7
+ self.socket.connect((args["hasHost"], args["hasPort"]))
8
+ except Exception as e:
9
+ print("Connection failed:", e)
10
+
11
+ def exec_query(self, message: str):
12
+ try:
13
+ self.socket.sendall(message.encode())
14
+ return self.socket.recv(8192).decode()
15
+ except Exception as e:
16
+ return {'error': str(e)}
@@ -0,0 +1,12 @@
1
+ import requests as req
2
+
3
+ class APIConnection:
4
+ def __init__(self, args):
5
+ self.baseURL = args["hasRequestURL"]
6
+
7
+ def exec_query(self, endpoint_params: str):
8
+ fullURL = self.baseURL + endpoint_params
9
+ try:
10
+ return req.get(fullURL).json()
11
+ except Exception as e:
12
+ return {'error': str(e)}
@@ -0,0 +1,27 @@
1
+ from sqlalchemy import text, create_engine
2
+ from sqlalchemy.ext.asyncio import create_async_engine
3
+ from datetime import datetime
4
+
5
+ async def datetime_handler(row, keys):
6
+ return {
7
+ key: (val.isoformat() if isinstance(val, datetime) else val)
8
+ for key, val in zip(keys, row)
9
+ }
10
+
11
+ class MySQLConnection:
12
+ def __init__(self, args):
13
+ self.engine = create_async_engine(args["hasConnectionString"])
14
+
15
+ async def exec_query(self, query: str):
16
+ async with self.engine.connect() as connection:
17
+ result = await connection.execute(text(query))
18
+ rows = result.fetchall()
19
+ keys = result.keys()
20
+ return [await datetime_handler(row, keys) for row in rows]
21
+
22
+ async def exec_insert(self, query: str):
23
+ async with self.engine.begin() as connection:
24
+ await connection.execute(text(query))
25
+ result = await connection.execute(text("SELECT LAST_INSERT_ID() AS id"))
26
+ row = result.fetchone()
27
+ return row.id
@@ -0,0 +1,21 @@
1
+ import asyncio
2
+ from pymongo import AsyncMongoClient
3
+ import json, logging
4
+
5
+ class MongoDBConnection:
6
+ def __init__(self, args):
7
+ connString = args["hasConnectionString"].split('/')
8
+ self.client = AsyncMongoClient("/".join(connString[:-1]))
9
+ self.db = self.client[connString[-1]]
10
+
11
+ async def exec_query(self, collection_query: str):
12
+ query_info = collection_query.split(".")
13
+ collection = self.db[query_info[0]]
14
+ query = json.loads(query_info[1])
15
+ cursor = collection.find({}, query)
16
+ results = await cursor.to_list()
17
+ if not results: return []
18
+ if "_id" in results[0].keys():
19
+ for i in results:
20
+ i["_id"] = str(i["_id"])
21
+ return results
@@ -0,0 +1,11 @@
1
+ from core.DTO.Metadata import Metadata
2
+ from core.DTO.ExternalCode import ExternalCode
3
+
4
+ from typing import List, Optional
5
+ from pydantic import BaseModel
6
+
7
+ class BusinessModel(BaseModel):
8
+ name: str
9
+ requiresMetadata: Optional[List[Metadata]]
10
+ requiresParameters: Optional[dict]
11
+ externalCode: Optional[ExternalCode]
@@ -0,0 +1,7 @@
1
+ from typing import List, Optional
2
+ from pydantic import BaseModel
3
+
4
+ class ExternalCode(BaseModel):
5
+ pythonFile: str
6
+ function: str
7
+ requiresLib: List[str]
@@ -0,0 +1,8 @@
1
+ from core.DTO.Source import Source
2
+
3
+ from pydantic import BaseModel
4
+
5
+ class Metadata(BaseModel):
6
+ name: str
7
+ type: str
8
+ hasSource: Source
@@ -0,0 +1,6 @@
1
+ from pydantic import BaseModel
2
+
3
+ class Source(BaseModel):
4
+ desc: str
5
+ query: str
6
+ comm_technology: object
@@ -0,0 +1,78 @@
1
+ from rdflib import Graph, URIRef
2
+ import logging
3
+ import pprint
4
+
5
+ logging.getLogger("rdflib").setLevel(logging.ERROR)
6
+
7
+ from core.Queries import *
8
+ from core.DTO.Source import *
9
+ from core.DTO.BusinessModel import *
10
+
11
+ from core.Connectors.IndentifyConnector import identifyConnector
12
+ from core.Utility import ensure_package_installed
13
+
14
+ class Ontology:
15
+ def __init__(self):
16
+ self.g = Graph()
17
+ self.data = {}
18
+ self.bms = {}
19
+
20
+ def parse_ontology(self, path: str):
21
+ if path.endswith('.ttl'):
22
+ self.g.parse(path, format='turtle')
23
+ elif path.endswith('.owl') or path.endswith('.rdf'):
24
+ self.g.parse(path, format='xml')
25
+ else:
26
+ raise ValueError(f'File extension must be .ttl or .owl or .rdf ({path} could not be parsed)')
27
+ logging.info(f'Ontology \"{path}\" parsed successfully.')
28
+
29
+ def __repr__(self):
30
+ return (f"data(\n{pprint.pformat(self.data, indent=2)}\n)\n"
31
+ f"business_models(\n{pprint.pformat(self.bms, indent=2)}\n)")
32
+
33
+ def _get_metadata_by_name(self, name: str):
34
+ for namespace, content in self.data.items():
35
+ for metadata in content:
36
+ if metadata.name == name:
37
+ return metadata
38
+ return None
39
+
40
+ def _verify_metadata(self, q: str):
41
+ md = []
42
+ for row in self.g.query(q):
43
+ metadata_obj = self._get_metadata_by_name(self.g.qname(row[1]))
44
+ if not metadata_obj:
45
+ raise ValueError(
46
+ f'The {self.g.qname(row[1])} metadata which is required by the {self.g.qname(row[0])} could not be found in the ontology.')
47
+ md.append(metadata_obj)
48
+ return md
49
+
50
+ def serialize_metadata(self):
51
+ data = {}
52
+ for row in self.g.query(GET_METADATA_QUERY):
53
+ args = {self.g.qname(row[0]).split(":")[-1]: row[1] for row in
54
+ self.g.query(GET_COMMUNICATION_TECH_ARGS_QUERY + URIRef(row[4]) + ">)}")}
55
+ comm_tech = identifyConnector(row[5], args)
56
+ source = Source(desc=row[2], query=row[3], comm_technology=comm_tech)
57
+ onto_pkg = self.g.qname(row[0]).split(":")[0]
58
+ if onto_pkg not in data.keys(): data[onto_pkg] = []
59
+ data[onto_pkg].append(Metadata(name=self.g.qname(row[0]), type=row[1], hasSource=source))
60
+ logging.info(f'{self.g.qname(row[0])} METADATA serialized successfully;')
61
+ self.data = data
62
+
63
+ def serialize_business_models(self):
64
+ BMs = {}
65
+ for ec in self.g.query(GET_EXTERNAL_CODE_FOR_BM_QUERY):
66
+ bm_name = self.g.qname(ec[0])
67
+ required_metadata = self._verify_metadata(GET_REQUIRED_MD_FOR_BM_QUERY + URIRef(ec[0]) + ">)}")
68
+ required_parameters = self.g.query(GET_REQUIRED_PARAMETERS_FOR_BM_QUERY + URIRef(ec[0]) + ">)}")
69
+ for module in str(ec[3]).split(","): ensure_package_installed(module)
70
+ BMs[bm_name] = BusinessModel(name=bm_name.split(":")[-1],
71
+ requiresMetadata=required_metadata,
72
+ requiresParameters={str(l): t for _, l, t in required_parameters},
73
+ externalCode=ExternalCode(
74
+ pythonFile=str(ec[2]),
75
+ function=str(ec[4]),
76
+ requiresLib=str(ec[3]).split(",")))
77
+ logging.info(f'{bm_name} BUSINESS MODEL serialized successfully;')
78
+ self.bms = BMs
@@ -0,0 +1,51 @@
1
+ GET_METADATA_QUERY = """
2
+ SELECT ?m ?t ?d ?q ?ct ?tc
3
+ WHERE {
4
+ ?m <http://www.cedri.com/SmartLEM-Metadata#hasSource> ?s .
5
+ ?m <http://www.cedri.com/SmartLEM-Metadata#hasType> ?t .
6
+ ?s <http://www.cedri.com/SmartLEM-Metadata#hasQuery> ?q .
7
+ ?s <http://www.cedri.com/SmartLEM-Metadata#hasDescription> ?d .
8
+ ?s <http://www.cedri.com/SmartLEM-Metadata#hasCommunicationTechnology> ?ct .
9
+ ?ct <http://www.cedri.com/SmartLEM-Communications#usesTechnology> ?tc .
10
+ }
11
+ """
12
+
13
+ GET_COMMUNICATION_TECH_ARGS_QUERY = """
14
+ SELECT ?arg ?argv
15
+ WHERE {
16
+ ?s <http://www.cedri.com/SmartLEM-Metadata#hasCommunicationTechnology> ?ct .
17
+ ?ct ?arg ?argv .
18
+ FILTER(?arg != <http://www.cedri.com/SmartLEM-Communications#usesTechnology> &&
19
+ ?arg != <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> &&
20
+ ?ct = <"""
21
+
22
+ GET_BM_NAME_QUERY = """
23
+ SELECT ?bm
24
+ WHERE {
25
+ ?bm rdfs:subClassOf <http://www.cedri.com/SmartLEM-BusinessModel#BusinessModel> .
26
+ }
27
+ """
28
+
29
+ GET_REQUIRED_MD_FOR_BM_QUERY = """
30
+ SELECT ?bm ?rm
31
+ WHERE {
32
+ ?bm <http://www.cedri.com/SmartLEM-BusinessModel#requiresMetadata> ?rm .
33
+ FILTER (?bm = <"""
34
+
35
+ GET_REQUIRED_PARAMETERS_FOR_BM_QUERY = """
36
+ SELECT ?bm ?pl ?pt
37
+ WHERE {
38
+ ?bm <http://www.cedri.com/SmartLEM-BusinessModel#hasParameter> ?p .
39
+ ?p <http://www.cedri.com/SmartLEM-BusinessModel#hasParameterLabel> ?pl .
40
+ ?p <http://www.cedri.com/SmartLEM-BusinessModel#hasParameterType> ?pt .
41
+ FILTER (?bm = <"""
42
+
43
+ GET_EXTERNAL_CODE_FOR_BM_QUERY = """
44
+ SELECT ?bm ?ec ?pf ?rl ?hf
45
+ WHERE {
46
+ ?bm <http://www.cedri.com/SmartLEM-BusinessModel#hasExternalCode> ?ec .
47
+ ?ec <http://www.cedri.com/SmartLEM-ExternalCode#hasPythonFile> ?pf .
48
+ ?ec <http://www.cedri.com/SmartLEM-ExternalCode#requiresLib> ?rl .
49
+ ?ec <http://www.cedri.com/SmartLEM-ExternalCode#hasFunction> ?hf .
50
+ }
51
+ """
@@ -0,0 +1,64 @@
1
+ import importlib.util
2
+ import sys
3
+ import subprocess
4
+ import os
5
+ import logging
6
+ from pathlib import Path
7
+ from Settings import auto_config as cfg
8
+
9
+ def handle_upload_file(uploaded_file):
10
+ UPLOAD_DIR = Path(cfg.UPLOAD_DIR)
11
+ if not os.path.exists(UPLOAD_DIR):
12
+ os.makedirs(UPLOAD_DIR)
13
+
14
+ file_path = os.path.join(UPLOAD_DIR, uploaded_file.filename)
15
+
16
+ with open(file_path, "wb") as out_file:
17
+ while chunk := uploaded_file.file.read(1024 * 1024): # Read in 1 MiB chunks
18
+ out_file.write(chunk)
19
+ out_file.close()
20
+
21
+ uploaded_file.file.close()
22
+
23
+ return file_path
24
+
25
+ def import_function_from_file(filepath, function_name):
26
+ # Check if file exists
27
+ if not os.path.isfile(filepath):
28
+ raise FileNotFoundError(f"File not found: {filepath}")
29
+
30
+ module_name = os.path.splitext(os.path.basename(filepath))[0]
31
+
32
+ try:
33
+ spec = importlib.util.spec_from_file_location(module_name, filepath)
34
+ if spec is None:
35
+ raise ImportError(f"Could not load module spec from {filepath}")
36
+
37
+ module = importlib.util.module_from_spec(spec)
38
+ sys.modules[module_name] = module
39
+ spec.loader.exec_module(module)
40
+ except Exception as e:
41
+ raise ImportError(f"Failed to load module '{module_name}': {e}")
42
+
43
+ # Check if function exists
44
+ if not hasattr(module, function_name):
45
+ raise AttributeError(f"Function '{function_name}' not found in '{filepath}'")
46
+
47
+ func = getattr(module, function_name)
48
+ if not callable(func):
49
+ raise TypeError(f"'{function_name}' in '{filepath}' is not a callable function")
50
+
51
+ return func
52
+
53
+ def install_package(package_name):
54
+ try:
55
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
56
+ logging.info(f"Package '{package_name}' installed successfully.")
57
+ except subprocess.CalledProcessError as e:
58
+ logging.info(f"Failed to install '{package_name}': {e}")
59
+
60
+ def ensure_package_installed(package_name):
61
+ if importlib.util.find_spec(package_name) is None:
62
+ install_package(package_name)
63
+ else:
64
+ logging.info(f"Package '{package_name}' is already installed.")
@@ -0,0 +1,2 @@
1
+ # Package
2
+ from APIGenerator import APIGenerator
@@ -0,0 +1,106 @@
1
+ Metadata-Version: 2.4
2
+ Name: ontologytoapi
3
+ Version: 0.0.5
4
+ Summary: Multi purpose API Generator based on an Ontology Framework.
5
+ Home-page: https://github.com/JCGCosta/OntologyToAPI
6
+ Author: Júlio César Guimarães Costa
7
+ Author-email: juliocesargcosta123@gmail.com
8
+ License: MIT License
9
+ Keywords: Ontology,API
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 11
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: SQLAlchemy
19
+ Requires-Dist: pydantic
20
+ Requires-Dist: requests
21
+ Requires-Dist: pymongo
22
+ Requires-Dist: rdflib
23
+ Requires-Dist: fastapi
24
+ Requires-Dist: uvicorn
25
+ Requires-Dist: aiomysql
26
+ Requires-Dist: motor
27
+ Requires-Dist: aiofiles
28
+ Requires-Dist: python-multipart
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: keywords
36
+ Dynamic: license
37
+ Dynamic: license-file
38
+ Dynamic: requires-dist
39
+ Dynamic: summary
40
+
41
+ # OntologyToAPI
42
+ > This project is an ontology-driven API generator designed for
43
+ > backend development by transforming structured domain
44
+ > knowledge into fully functional APIs. The tool accepts ontologies
45
+ > specified in Turtle (.ttl), Resource Description Framework (.rdf)
46
+ > and Web Ontology Language (.owl).
47
+
48
+ > [![Publish to PyPI and TestPyPI](https://github.com/JCGCosta/OntologyToAPI/actions/workflows/python-publish.yml/badge.svg)](https://github.com/JCGCosta/OntologyToAPI/actions/workflows/python-publish.yml)
49
+
50
+
51
+ ## Ontological Framework:
52
+
53
+ - The following classes, relationships and data properties serve as a semantic blueprint for both metadata and business models.
54
+
55
+ <img src="https://github.com/JCGCosta/OntologyToAPI/blob/master/OntologicalFramework.jpg?raw=true" alt="AbstractOntologyClasses" title="Abstract Ontology Classes.">
56
+
57
+ The ontological framework is composed of two main modules:
58
+
59
+ - **Metadata Ontology Module:** This module defines the essential classes and properties required to describe the metadata and its sources (e.g. Query to be executed on the CommunicationTechnology).
60
+ - **BusinessModel Ontology Module:** This module captures the specific business logic and rules governing some operation, it requires an ExternalCode concretization, and it can require any metadata or parameter (To be sent in the API request).
61
+ - **ExternalCode Ontology Module:** This module has all the technical details to connect to an external code, it also adds the possibility to dynamically require python packages.
62
+ - **Communications Ontology Module:** This module describes the communication technologies that can be used to fetch the data of some metadata in multiple forms (e. g).
63
+
64
+ > From now on you must be ready to go and create your own ontological specification importing the Ontology Modules and extending it. You can do this by using the Protégé ontology editor (https://protege.stanford.edu/). Or if you prefer you can use any text editor to create your ontology files in the supported formats (.ttl, .rdf, .owl).
65
+
66
+ ### Step 1: Installing the Package
67
+
68
+ ```bash
69
+ # Now inside the environment install the python package
70
+ pip install ontologytoapi
71
+ ```
72
+
73
+
74
+ ### Step 2: Running
75
+
76
+ - If you want to see a quick ontology sample in .ttl please access the following link: https://github.com/JCGCosta/OntologyToAPI/tree/master/samples
77
+ - With you metadata and business models ontologies implemented you can generate your API by having the following python file as an entry point:
78
+
79
+ ```python
80
+ import uvicorn
81
+ from core.APIGenerator import Generator
82
+
83
+ if __name__ == "__main__":
84
+ APIGen = Generator(showLogs=True)
85
+ APIGen.load_ontologies(paths=[
86
+ "Your/Metadata/Ontology/.ttl.owl.rdf"
87
+ ])
88
+ APIGen.load_ontologies(paths=[
89
+ "Your/BusinessModel/Ontology/.ttl.owl.rdf"
90
+ ])
91
+ APIGen.serialize_ontologies()
92
+ api_app = APIGen.generate_api_routes()
93
+ uvicorn.run(api_app, host="127.0.0.1", port=5000)
94
+ ```
95
+
96
+
97
+ Change Log
98
+ ===============
99
+ 0.0.5 (27/10/2025)
100
+ ------------------
101
+ FIRST RELEASE
102
+ - First fixes on README file.
103
+ - Added Ontological Framework diagram.
104
+ - Added ontology concrete sample.
105
+ ------------------
106
+
@@ -0,0 +1,25 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ requirements.txt
5
+ setup.cfg
6
+ setup.py
7
+ core/APIGenerator.py
8
+ core/Ontology.py
9
+ core/Queries.py
10
+ core/Utility.py
11
+ core/__init__.py
12
+ core/Connectors/IndentifyConnector.py
13
+ core/Connectors/Stateful/SocketConnection.py
14
+ core/Connectors/Stateless/APIConnection.py
15
+ core/Connectors/Stateless/MYSQLConnection.py
16
+ core/Connectors/Stateless/MongoDBConnection.py
17
+ core/DTO/BusinessModel.py
18
+ core/DTO/ExternalCode.py
19
+ core/DTO/Metadata.py
20
+ core/DTO/Source.py
21
+ ontologytoapi.egg-info/PKG-INFO
22
+ ontologytoapi.egg-info/SOURCES.txt
23
+ ontologytoapi.egg-info/dependency_links.txt
24
+ ontologytoapi.egg-info/requires.txt
25
+ ontologytoapi.egg-info/top_level.txt
@@ -0,0 +1,11 @@
1
+ SQLAlchemy
2
+ pydantic
3
+ requests
4
+ pymongo
5
+ rdflib
6
+ fastapi
7
+ uvicorn
8
+ aiomysql
9
+ motor
10
+ aiofiles
11
+ python-multipart
@@ -0,0 +1,12 @@
1
+ setuptools
2
+ SQLAlchemy~=2.0.41
3
+ pydantic~=2.11.7
4
+ requests~=2.32.4
5
+ pymongo~=4.13.2
6
+ rdflib~=7.1.4
7
+ fastapi~=0.115.14
8
+ uvicorn~=0.35.0
9
+ aiomysql~=0.2.0
10
+ motor~=3.7.1
11
+ aiofiles~=25.1.0
12
+ python-multipart~=0.0.20
@@ -0,0 +1,7 @@
1
+ [metadata]
2
+ description-file = README.md
3
+
4
+ [egg_info]
5
+ tag_build =
6
+ tag_date = 0
7
+
@@ -0,0 +1,28 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ classifiers = [
4
+ 'Development Status :: 3 - Alpha',
5
+ 'Intended Audience :: Developers',
6
+ 'Operating System :: Microsoft :: Windows :: Windows 11',
7
+ 'License :: OSI Approved :: MIT License',
8
+ 'Topic :: Software Development :: Libraries :: Python Modules',
9
+ 'Programming Language :: Python :: 3.11'
10
+ ]
11
+
12
+ setup(
13
+ name='ontologytoapi',
14
+ version='0.0.5',
15
+ description='Multi purpose API Generator based on an Ontology Framework.',
16
+ long_description=
17
+ f"{open('README.md').read()}\n\n" +
18
+ f"{open('CHANGELOG.txt').read()}\n\n",
19
+ long_description_content_type="text/markdown",
20
+ url='https://github.com/JCGCosta/OntologyToAPI',
21
+ author='Júlio César Guimarães Costa',
22
+ author_email='juliocesargcosta123@gmail.com',
23
+ license='MIT License',
24
+ classifiers=classifiers,
25
+ keywords=['Ontology', 'API'],
26
+ packages=find_packages(),
27
+ install_requires=["SQLAlchemy","pydantic","requests","pymongo","rdflib","fastapi","uvicorn","aiomysql","motor","aiofiles","python-multipart"]
28
+ )