Skip to content

Commit

Permalink
moved recipe endpoints into own file
Browse files Browse the repository at this point in the history
  • Loading branch information
ReggaeUlli committed Oct 11, 2023
1 parent 09b5e28 commit 2102175
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 408 deletions.
42 changes: 42 additions & 0 deletions server/Functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from werkzeug.utils import secure_filename
from flask import flash, make_response
import os
import owlready2

UPLOAD_FOLDER = './upload/'

ALLOWED_EXTENSIONS = {'owl', 'aasx', 'xml'}

def upload_file(request, subfolder):
print("upload startet")
# check if the post request has the file part
if 'file' not in request.files:
print("no file given")
flash('No file part')
# return redirect(request.url)
return make_response(request.url, 400)
file = request.files['file']
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
print("filename is empty string")
flash('No selected file')
# return redirect(request.url)
return make_response(request.url, 400)
if file and allowed_file(file.filename):
print("file allowed")
filename = secure_filename(file.filename)
filepath = os.path.join(UPLOAD_FOLDER, subfolder, filename)
file.save(filepath)

# add to ontologies dict
onto = owlready2.get_ontology(filepath).load()
# return redirect(url_for('download_file', name=filename))
return make_response(request.url, 200)
print("file not allowed")
make_response("file not allowed or no post request")
return make_response(request.url, 400)

def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
127 changes: 127 additions & 0 deletions server/RecipeAPI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from flask import Blueprint, request, make_response
import os
import xml.etree.ElementTree as ET
from Functions import upload_file
from lxml import etree

RECIPE_FOLDER = "recipes/"
UPLOAD_FOLDER = './upload/'

recipe_api = Blueprint('recipe_api', __name__)

def validate(xml_string: str, xsd_path: str) -> bool:

xmlschema_doc = etree.parse(xsd_path)
xmlschema = etree.XMLSchema(xmlschema_doc)

xml_doc = etree.fromstring(xml_string.encode('utf-8'))
result = xmlschema.validate(xml_doc)
error = xmlschema.assertValid(xml_doc)

return result, error

def load_recipes():
recipes = {}
for filename in os.listdir(os.path.join(UPLOAD_FOLDER, RECIPE_FOLDER)):
if filename.endswith('.xml'):
# with open(os.path.join(UPLOAD_FOLDER, filename), 'r') as file:
# ontologies[filename] = file.read()
complete_path = os.path.join(UPLOAD_FOLDER, RECIPE_FOLDER, filename)
recipes[filename] = ET.parse(complete_path)
return recipes



@recipe_api.route('/recipe', methods=['POST'])
def upload_recipe():
"""Endpoint to upload a new recipe to the server.
---
tags:
- Recipes
parameters:
- name: file
in: formData
type: file
required: true
responses:
200:
description: An ackknowledgement that the upload worked.
examples:
rgb: ['red', 'green', 'blue']
"""
return upload_file(request, "recipes")

@recipe_api.route('/validate')
def validate_batchml():
"""Endpoint to validate a xml string against BatchML xsd schema.
---
tags:
- Recipes
parameters:
- name: xml_string
in: query
type: string
required: true
default: ""
responses:
200:
description: Given String is valid.
400:
description: Given String is not valid.
"""
args = request.args
xml_string = args.get("xml_string", type=str)
print(xml_string)

valid, error = validate(xml_string, "batchml_schemas/schemas/BatchML-GeneralRecipe.xsd")
if valid:
print('Valid! :)')
response = make_response("valid!", 200)
return response
else:
print('Not valid! :(')
response = make_response(error, 400)
return response

@recipe_api.route('/recipes/<recipe_name>/capabilities')
def get_required_capabilities(recipe_name, methods=['GET']):
"""Endpoint returning the list of capabilities present in given recipe.
---
tags:
- Recipes
parameters:
- name: recipe_name
in: path
type: string
required: true
default: all
responses:
200:
description: A list of the capabilities in the recipe.
examples: [{
"ID": "Stirring",
"IRI": "http://www.acplt.de/Capability#Stirring"
}]
"""
# returns a generator therefore we need list()
recipes = load_recipes()
root = recipes[recipe_name]
capabilities = []
#the tag name has a namespace "<aas:capability>"
#therefore we need to take the namespace definiton from the first lines of the xml
#xmlns:aas='{http://www.admin-shell.io/aas/2/0}'
ns='{http://www.mesa.org/xml/B2MML}' #namespace definition

for processElement in root.iter(ns+'ProcessElement'):
otherInfos = processElement.findall(ns+'OtherInformation')
if otherInfos is None or []:
continue
for otherInfo in otherInfos:
otherInfoId = otherInfo.find(ns+'OtherInfoID')
if otherInfoId.text == "OntologyIRI":
capabilities.append({
"ID": processElement.find(ns+'ID').text,
"IRI":otherInfo.find(ns+'OtherValue').find(ns+'ValueString').text
})
response = make_response(capabilities)
return response
163 changes: 6 additions & 157 deletions server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from flask import Flask, flash, request, send_from_directory, make_response, redirect
from waitress import serve #this is for the production server
# from flask_restful import Api, Resource
from werkzeug.utils import secure_filename

from flasgger import Swagger
# ontologies
import owlready2
Expand All @@ -17,6 +17,8 @@
import xml.etree.ElementTree as ET
from io import StringIO

from RecipeAPI import recipe_api
from Functions import upload_file

ontologies = {}
aas = {}
Expand All @@ -26,23 +28,6 @@
ONTO_FOLDER = "ontologies/"
AAS_FOLDER = "aasx/"
RECIPE_FOLDER = "recipes/"
ALLOWED_EXTENSIONS = {'owl', 'aasx', 'xml'}

def validate(xml_string: str, xsd_path: str) -> bool:

xmlschema_doc = etree.parse(xsd_path)
xmlschema = etree.XMLSchema(xmlschema_doc)

xml_doc = etree.fromstring(xml_string.encode('utf-8'))
result = xmlschema.validate(xml_doc)
error = xmlschema.assertValid(xml_doc)

return result, error

def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def recursivly_add_subclasses(super_class):
output_obj = {
Expand Down Expand Up @@ -105,47 +90,6 @@ def load_aas():
aas[filename] = ET.parse(complete_path)
return aas

def load_recipes():
recipes = {}
for filename in os.listdir(os.path.join(UPLOAD_FOLDER, RECIPE_FOLDER)):
if filename.endswith('.xml'):
# with open(os.path.join(UPLOAD_FOLDER, filename), 'r') as file:
# ontologies[filename] = file.read()
complete_path = os.path.join(UPLOAD_FOLDER, RECIPE_FOLDER, filename)
recipes[filename] = ET.parse(complete_path)
return recipes

def upload_file(app, request, subfolder):
print("upload startet")
# check if the post request has the file part
if 'file' not in request.files:
print("no file given")
flash('No file part')
# return redirect(request.url)
return make_response(request.url, 400)
file = request.files['file']
# If the user does not select a file, the browser submits an
# empty file without a filename.
if file.filename == '':
print("filename is empty string")
flash('No selected file')
# return redirect(request.url)
return make_response(request.url, 400)
if file and allowed_file(file.filename):
print("file allowed")
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], subfolder, filename)
file.save(filepath)

# add to ontologies dict
onto = owlready2.get_ontology(filepath).load()
ontologies[filename] = onto
# return redirect(url_for('download_file', name=filename))
return make_response(request.url, 200)
print("file not allowed")
make_response("file not allowed or no post request")
return make_response(request.url, 400)


def create_app():
app = Flask(__name__)
Expand Down Expand Up @@ -236,7 +180,7 @@ def upload_onto():
examples:
rgb: ['red', 'green', 'blue']
"""
upload_file(app, request, "ontologies")
upload_file(request, "ontologies")


# Method to load an ontology
Expand Down Expand Up @@ -354,39 +298,6 @@ def get_subclasses(onto_name="acplt", super_class="GeneralCapabilityEffecting"):
processes = json.dumps(classes_list, ensure_ascii=False, indent=4)
response = make_response(processes)
return response


@app.route('/validate')
def validate_batchml():
"""Endpoint to validate a xml string against BatchML xsd schema.
---
tags:
- Recipes
parameters:
- name: xml_string
in: query
type: string
required: true
default: ""
responses:
200:
description: Given String is valid.
400:
description: Given String is not valid.
"""
args = request.args
xml_string = args.get("xml_string", type=str)
print(xml_string)

valid, error = validate(xml_string, "batchml_schemas/schemas/BatchML-GeneralRecipe.xsd")
if valid:
print('Valid! :)')
response = make_response("valid!", 200)
return response
else:
print('Not valid! :(')
response = make_response(error, 400)
return response

@app.route('/AASX', methods=['POST'])
def upload_aasx():
Expand All @@ -405,7 +316,7 @@ def upload_aasx():
examples:
rgb: ['red', 'green', 'blue']
"""
return upload_file(app, request, "aasx")
return upload_file(request, "aasx")

@app.route('/aas/<aas_name>/capabilities')
def get_availible_capabilities(aas_name, methods=['GET']):
Expand Down Expand Up @@ -446,74 +357,12 @@ def get_availible_capabilities(aas_name, methods=['GET']):
})
response = make_response(capabilities)
return response

@app.route('/recipe', methods=['POST'])
def upload_recipe():
"""Endpoint to upload a new recipe to the server.
---
tags:
- Recipes
parameters:
- name: file
in: formData
type: file
required: true
responses:
200:
description: An ackknowledgement that the upload worked.
examples:
rgb: ['red', 'green', 'blue']
"""
return upload_file(app, request, "recipes")


@app.route('/recipes/<recipe_name>/capabilities')
def get_required_capabilities(recipe_name, methods=['GET']):
"""Endpoint returning the list of capabilities present in given recipe.
---
tags:
- Recipes
parameters:
- name: recipe_name
in: path
type: string
required: true
default: all
responses:
200:
description: A list of the capabilities in the recipe.
examples: [{
"ID": "Stirring",
"IRI": "http://www.acplt.de/Capability#Stirring"
}]
"""
# returns a generator therefore we need list()
root = recipes[recipe_name]
capabilities = []
#the tag name has a namespace "<aas:capability>"
#therefore we need to take the namespace definiton from the first lines of the xml
#xmlns:aas='{http://www.admin-shell.io/aas/2/0}'
ns='{http://www.mesa.org/xml/B2MML}' #namespace definition

for processElement in root.iter(ns+'ProcessElement'):
otherInfos = processElement.findall(ns+'OtherInformation')
if otherInfos is None or []:
continue
for otherInfo in otherInfos:
otherInfoId = otherInfo.find(ns+'OtherInfoID')
if otherInfoId.text == "OntologyIRI":
capabilities.append({
"ID": processElement.find(ns+'ID').text,
"IRI":otherInfo.find(ns+'OtherValue').find(ns+'ValueString').text
})
response = make_response(capabilities)
return response

app.register_blueprint(recipe_api)

#on initializing app we load the ontologies ans aas present at the server
ontologies = load_ontologies()
aas = load_aas()
recipes = load_recipes()
#ontologies = {} #uncomment this is for offline development
return app

Expand Down
Loading

0 comments on commit 2102175

Please sign in to comment.