Source code for buildingmotif.api.views.template
from io import StringIO
from typing import Dict
import flask
from flask import Blueprint, current_app, jsonify, request
from flask_api import status
from rdflib import Literal, URIRef
from rdflib.term import Node
from sqlalchemy.orm.exc import NoResultFound
from buildingmotif.api.serializers.template import serialize
from buildingmotif.dataclasses import Model, Template
from buildingmotif.ingresses import CSVIngress, TemplateIngress
blueprint = Blueprint("templates", __name__)
[docs]@blueprint.route("", methods=(["GET"]))
def get_all_templates() -> flask.Response:
"""Get all templates.
:return: all templates
:rtype: flask.Response
"""
db_templates = current_app.building_motif.table_connection.get_all_db_templates()
return jsonify(serialize(db_templates)), status.HTTP_200_OK
[docs]@blueprint.route("/<templates_id>", methods=(["GET"]))
def get_template(templates_id: int) -> flask.Response:
"""Get template by id.
:param templates_id: template id
:type templates_id: int
:return: requested template
:rtype: flask.Response
"""
include_parameters = request.args.get("parameters", False)
try:
template = current_app.building_motif.table_connection.get_db_template(
templates_id
)
except NoResultFound:
return {
"message": f"No template with id {templates_id}"
}, status.HTTP_404_NOT_FOUND
return jsonify(serialize(template, include_parameters)), status.HTTP_200_OK
[docs]@blueprint.route("/<template_id>/evaluate/ingress", methods=(["POST"]))
def evaluate_ingress(template_id: int) -> flask.Response:
# get template
try:
template = Template.load(template_id)
except NoResultFound:
return {
"message": f"No template with id {template_id}"
}, status.HTTP_404_NOT_FOUND
# get model
model_id = request.args.get("model_id")
if model_id is None:
return {
"message": "must contain query param 'model_id'"
}, status.HTTP_400_BAD_REQUEST
try:
model = Model.load(model_id)
except NoResultFound:
return {"message": f"No model with id {model_id}"}, status.HTTP_404_NOT_FOUND
# get file
raw_data = flask.request.get_data()
if raw_data is None:
return {"message": "no file recieved."}, status.HTTP_404_NOT_FOUND
# evaluate template
try:
data = StringIO(raw_data.decode("utf-8"))
csv_ingress = CSVIngress(data=data)
template_ingress = TemplateIngress(
template.inline_dependencies(), None, csv_ingress
)
graph_or_template = template_ingress.graph(model.name)
except Exception:
return {"message": "Invalid csv."}, status.HTTP_400_BAD_REQUEST
# parse bindings from input JSON
if isinstance(graph_or_template, Template):
graph = graph_or_template.body
else:
graph = graph_or_template
return graph.serialize(format="ttl"), status.HTTP_200_OK
[docs]@blueprint.route("/<template_id>/evaluate/bindings", methods=(["POST"]))
def evaluate_bindings(template_id: int) -> flask.Response:
"""evaluate template with giving binding
:param template_id: id of template
:type template_id: int
:return: evaluated Group
:rtype: flask.Response
"""
try:
template = Template.load(template_id)
except NoResultFound:
return {
"message": f"No template with id {template_id}"
}, status.HTTP_404_NOT_FOUND
if request.content_type != "application/json":
return {
"message": "request content type must be json"
}, status.HTTP_400_BAD_REQUEST
model_id = request.get_json().get("model_id")
if model_id is None:
return {"message": "body must contain 'model_id'"}, status.HTTP_400_BAD_REQUEST
try:
model = Model.load(model_id)
except NoResultFound:
return {"message": f"No model with id {model_id}"}, status.HTTP_404_NOT_FOUND
bindings = request.get_json().get("bindings")
if bindings is None:
return {"message": "body must contain 'bindings'"}, status.HTTP_400_BAD_REQUEST
bindings = get_bindings(bindings)
bindings = {k: model.name.rstrip("/") + "/" + v for k, v in bindings.items()}
# parse bindings from input JSON
graph_or_template = template.evaluate(bindings=bindings)
if isinstance(graph_or_template, Template):
graph = graph_or_template.body
else:
graph = graph_or_template
return graph.serialize(format="ttl"), status.HTTP_200_OK
[docs]def get_bindings(binding_dict) -> Dict[str, Node]:
"""type binding_dict values to nodes
given:
{name: {@id or @literal: value}}
return:
{name: typed value}
:param binding_dict: untyped bindings
:type binding_dict: dict
:return: typed dict
:rtype: Dict[str, Node]
"""
bindings = {}
for param, definition in binding_dict.items():
if "@id" in definition:
bindings[param] = URIRef(definition["@id"])
if "@literal" in definition:
dtype = definition.get("@datatype")
bindings[param] = Literal(
definition["@literal"], datatype=URIRef(dtype) if dtype else None
)
return bindings