Commit b3b9ce72 authored by Ruben Beck's avatar Ruben Beck

Merge branch 'feature-convert_LD'

# Conflicts:
#	src/api/app.py
#	src/api/routes_validation_v2.py
parents 3579cba4 b4353b76
......@@ -23,33 +23,43 @@ ENV SPECS_BASE_PATH=https://gitlab.distantaccess.com/naiades/dmv_public/-/raw/ma
#________________________________________
ENV ALERT_SPECS_URL=$SPECS_BASE_PATH/Specs_Alert.json
ENV V2_ALERT_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_Alert.json
ENV LD_ALERT_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_Alert.json
ENV DEVICE_SPECS_URL=$SPECS_BASE_PATH/Specs_Device.json
ENV V2_DEVICE_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_Device.json
ENV LD_DEVICE_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_Device.json
ENV FLOWERBED_SPECS_URL=$SPECS_BASE_PATH/Specs_FlowerBed.json
ENV V2_FLOWERBED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_FlowerBed.json
ENV LD_FLOWERBED_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_FlowerBed.json
ENV FOUNTAINUSAGEOBSERVED_SPECS_URL=$SPECS_BASE_PATH/Specs_FountainUsageObserved.json
ENV V2_FOUNTAINUSAGEOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_FountainUsageObserved.json
ENV LD_FOUNTAINUSAGEOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_FountainUsageObserved.json
ENV NOISE_SPECS_URL=$SPECS_BASE_PATH/Specs_Noise.json
ENV V2_NOISE_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_Noise.json
ENV LD_NOISE_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_Noise.json
ENV WATERCONSUMPTIONOBSERVED_SPECS_URL=$SPECS_BASE_PATH/Specs_WaterConsumptionObserved.json
ENV V2_WATERCONSUMPTIONOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WaterConsumptionObserved.json
ENV LD_WATERCONSUMPTIONOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_WaterConsumptionObserved.json
ENV WATERQUALITYFORECAST_SPECS_URL=$SPECS_BASE_PATH/Specs_WaterQualityForecast.json
ENV WATERQUALITYFORECAST_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WaterQualityForecast.json
ENV V2_WATERQUALITYFORECAST_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WaterQualityForecast.json
ENV LD_WATERQUALITYFORECAST_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_WaterQualityForecast.json
ENV WATERQUALITYOBSERVED_SPECS_URL=$SPECS_BASE_PATH/Specs_WaterQualityObserved.json
ENV WATERQUALITYOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WaterQualityObserved.json
ENV V2_WATERQUALITYOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WaterQualityObserved.json
ENV LD_WATERQUALITYOBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_WaterQualityObserved.json
ENV WEATHERFORECAST_SPECS_URL=$SPECS_BASE_PATH/Specs_WeatherForecast.json
ENV WEATHERFORECAST_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WeatherForecast.json
ENV V2_WEATHERFORECAST_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WeatherForecast.json
ENV LD_WEATHERFORECAST_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_WeatherForecast.json
ENV WEATHEROBSERVED_SPECS_URL=$SPECS_BASE_PATH/Specs_WeatherObserved.json
ENV WEATHEROBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WeatherObserved.json
ENV V2_WEATHEROBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/V2_Blueprint_WeatherObserved.json
ENV LD_WEATHEROBSERVED_BLUEPRINT_URL=$SPECS_BASE_PATH/LD_Blueprint_WeatherObserved.json
#________________________________________
RUN useradd -ms /bin/bash naiades
......
......@@ -9,16 +9,31 @@ APP_CONFIG = {
'PLATFORM_ENDPOINT': "127.0.0.1:5005/redirect",
'PLATFORM_ENDPOINT_HISTORIC': "127.0.0.1:5005/redirect",
'BLUEPRINTS_ABS_PATH': SPECS_DIR,
'SPECS_ABS_PATH': SPECS_DIR,
# URL resources
'SPECS_ABS_PATH': SPECS_DIR
}
URL_RESOURCES = {
"URL_NAME_CONVENTIONS": {
"SPECS": '{modelname}_SPECS_URL',
"BLUEPRINTS": '{modeltype}_{modelname}_BLUEPRINT_URL'
},
# FlowerBed
"FLOWERBED_SPECS_URL": f'{BASE_SPECS_URL}/Specs_FlowerBed.json',
"V2_FLOWERBED_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_FlowerBed.json',
"WATERQUALITYOBSERVED_SPECS_URL": f'{BASE_SPECS_URL}/Specs_WaterQualityObserved.json',
"V2_WATERQUALITYOBSERVED_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_WaterQualityObserved.json',
"WEATHERFORECAST_SPECS_URL": f'{BASE_SPECS_URL}/Specs_WeatherForecast.json',
"V2_WEATHERFORECAST_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_WeatherForecast.json',
"LD_FLOWERBED_BLUEPRINT_URL": f'{BASE_SPECS_URL}/LD_Blueprint_FlowerBed.json',
# WaterQuality
# "WATERQUALITYOBSERVED_SPECS_URL": f'{BASE_SPECS_URL}/Specs_WaterQualityObserved.json',
# "V2_WATERQUALITYOBSERVED_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_WaterQualityObserved.json',
# WeatherForecast
# "WEATHERFORECAST_SPECS_URL": f'{BASE_SPECS_URL}/Specs_WeatherForecast.json',
# "V2_WEATHERFORECAST_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_WeatherForecast.json',
# Device
"DEVICE_SPECS_URL": f'{BASE_SPECS_URL}/Specs_Device.json',
"V2_DEVICE_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_Device.json'
"V2_DEVICE_BLUEPRINT_URL": f'{BASE_SPECS_URL}/V2_Blueprint_Device.json',
"LD_DEVICE_BLUEPRINT_URL": f'{BASE_SPECS_URL}/LD_Blueprint_Device.json'
}
CACHE_CONFIG = {
......
{
"source" : {
"nav": "type==type&value=value",
"type": "Property"
},
"dataProvider" : {
"nav": "type==type&value=value",
"type": "Property"
},
"category": {
"nav": "type==type&value=value",
"type": "Property"
},
"subCategory": {
"nav": "type==type&value=value",
"type": "Property"
},
"pHAlarm" : {
"nav": "type==type&value=value",
"type": "Property"
},
"pHPredictionAlarm" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorinePredictionAlarm" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorineAlarm" : {
"nav": "type==type&value=value",
"type": "Property"
},
"salineDetection" : {
"nav": "type==type&value=value",
"type": "Property"
},
"leakagesDetection" : {
"nav": "type==type&value=value",
"type": "Property"
},
"location" : {
"nav": "type==type&value=value",
"type": "GeoProperty"
},
"address" : {
"nav": "type==type&value=value",
"type": "Property"
},
"dateIssued": {
"nav": "type==type&value=value",
"type": "Property"
},
"validFrom": {
"nav": "type==type&value=value",
"type": "Property"
},
"validTo": {
"nav": "type==type&value=value",
"type": "Property"
},
"description" : {
"nav": "type==type&value=value",
"type": "Property"
},
"alertSource" : {
"nav": "type==type&value=value",
"type": "Property"
},
"data" : {
"nav": "type==type&value=value",
"type": "Property"
},
"serverity" : {
"nav": "type==type&value=value",
"type": "Property"
}
}
{
"dateObserved" : {
"nav": "type==type&value=value",
"type": "Property"
},
"location" : {
"nav": "type==type&value=value",
"type": "GeoProperty"
},
"observations" : {
"nav": "type==type&value=value",
"type": "Property"
},
"temperature" : {
"nav": "type==type&value=value",
"type": "Property"
},
"temperatureWater" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorineContent" : {
"nav": "type==type&value=value",
"type": "Property"
},
"phWater" : {
"nav": "type==type&value=value",
"type": "Property"
},
"waterRecycle" : {
"nav": "type==type&value=value",
"type": "Property"
},
"swimmers" : {
"nav": "type==type&value=value",
"type": "Property"
},
"animals" : {
"nav": "type==type&value=value",
"type": "Property"
},
"controlHours" : {
"nav": "type==type&value=value",
"type": "Property"
}
}
{
"value": {
"nav": "type==type&value=value",
"type": "Property"
},
"serialNumber": {
"nav": "type==type&value=value",
"type": "Property"
},
"name": {
"nav": "type==type&value=value",
"type": "Property"
},
"controlledProperty": {
"nav": "type==type&value=value",
"type": "Property"
},
"location" : {
"nav": "type==type&value=value",
"type": "GeoProperty"
}
}
\ No newline at end of file
{
"waterConsumption" : {
"nav": "type==type&value=value",
"type": "Property"
},
"name" : {
"nav": "type==type&value=value",
"type": "Property"
},
"location": {
"nav": "type==type&value=value",
"type": "GeoProperty"
},
"domainActivity": {
"nav": "type==type&value=value",
"type": "Property"
},
"numberOfUsers" : {
"nav": "type==type&value=value",
"type": "Property"
}
}
{
"treatmentChlorinationDosagePrediction" : {
"nav": "type==type&value=value",
"type": "Property"
},
"treatmentFiltrationTimePrediction" : {
"nav": "type==type&value=value",
"type": "Property"
},
"treatmentCoagulantDosagePrediction": {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorinationDosageRecommendation" : {
"nav": "type==type&value=value",
"type": "Property"
},
"coagulantDosageRecommendation" : {
"nav": "type==type&value=value",
"type": "Property"
},
"filtrationTimeRecommendation": {
"nav": "type==type&value=value",
"type": "Property"
},
"pHPrediction" : {
"nav": "type==type&value=value",
"type": "Property"
},
"totalChlorinePrediction" : {
"nav": "type==type&value=value",
"type": "Property"
},
"freeChlorinePrediction": {
"nav": "type==type&value=value",
"type": "Property"
},
"turbidityPrediction" : {
"nav": "type==type&value=value",
"type": "Property"
},
"conductivityPrediction" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chloratePrediction": {
"nav": "type==type&value=value",
"type": "Property"
},
"validFrom" : {
"nav": "type==type&value=value",
"type": "Property"
},
"validTo" : {
"nav": "type==type&value=value",
"type": "Property"
},
"location": {
"nav": "type==type&value=value",
"type": "GeoProperty"
}
}
{
"dateObserved" : {
"nav": "type==type&value=value",
"type": "Property"
},
"pH" : {
"nav": "type==type&value=value",
"type": "Property"
},
"temperature" : {
"nav": "type==type&value=value",
"type": "Property"
},
"turbidity" : {
"nav": "type==type&value=value",
"type": "Property"
},
"freeChlorine" : {
"nav": "type==type&value=value",
"type": "Property"
},
"waterFlow" : {
"nav": "type==type&value=value",
"type": "Property"
},
"totalChlorine" : {
"nav": "type==type&value=value",
"type": "Property"
},
"conductivity" : {
"nav": "type==type&value=value",
"type": "Property"
},
"treatmentChlorinationDosage" : {
"nav": "type==type&value=value",
"type": "Property"
},
"treatmentFiltrationTime" : {
"nav": "type==type&value=value",
"type": "Property"
},
"treatmentCoagulantDosage" : {
"nav": "type==type&value=value",
"type": "Property"
},
"redox" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorateEstimation" : {
"nav": "type==type&value=value",
"type": "Property"
},
"phThresholdHigh" : {
"nav": "type==type&value=value",
"type": "Property"
},
"phThresholdLow" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorineThresholdHigh" : {
"nav": "type==type&value=value",
"type": "Property"
},
"chlorineThresholdLow" : {
"nav": "type==type&value=value",
"type": "Property"
},
"location" : {
"nav": "type==type&value=value",
"type": "GeoProperty"
},
"refDevice" : {
"nav": "type==type&value=value",
"type": "Property"
}
}
""" Factory method for flask server"""
from ast import literal_eval
from logging import DEBUG, INFO, WARNING
from logging.handlers import TimedRotatingFileHandler
from logging import DEBUG, INFO
from flask.logging import default_handler
from flask import Flask, request, jsonify
from flask.logging import default_handler
from flask_caching import Cache
from werkzeug.exceptions import HTTPException
from src.api import config
from src.api.routes_validation_v2 import validation_v2_entities, historic_validation_v2_entities
from src.api.server_utils import ValidationErrorResponse, random_id
from src.api.routes import validation_v2_entities, historic_validation_v2_entities, validation_ld_entities, \
historic_validation_ld_entities
from src.api.server_utils import ValidationErrorResponse, random_id, parse_model
from src.logger import FORMATTER
# from src.api.validation_ld import validation_ld_entities
def create_app():
""" Return the flask api object that contains the routes and views. Function is called for instantiation
by gunicorn server """
develop = False if config.DEPLOYMENT_ENV == 'PROD' else True
develop = config.DEPLOYMENT_ENV == 'DEV'
# configure app and routes
app = Flask(__name__, instance_relative_config=True)
app.register_blueprint(validation_v2_entities)
app.register_blueprint(historic_validation_v2_entities)
# app.register_blueprint(validation_ld_entities)
app.register_blueprint(validation_ld_entities)
app.register_blueprint(historic_validation_ld_entities)
if develop:
from src.api import develop_config
log_file_handler = TimedRotatingFileHandler(filename="dmv_log", when='M', interval=1, backupCount=5, utc=True)
app.config.from_mapping(develop_config.APP_CONFIG)
app.config['URL_RESOURCES'] = develop_config.URL_RESOURCES
@app.route("/dev/<path:url>", methods=["GET"])
def read_dev(url):
return jsonify(code=200, dev=True)
else:
app.config.from_mapping(config.APP_CONFIG)
app.config['URL_RESOURCES'] = config.URL_RESOURCES
log_file_handler = TimedRotatingFileHandler(filename=config.LOG_CONFIG['LOG_FILE_NAME'],
when=config.LOG_CONFIG['LOG_WHEN'],
backupCount=config.LOG_CONFIG['LOG_BACKUP_COUNT'],
......@@ -50,6 +50,7 @@ def create_app():
default_handler.setFormatter(FORMATTER)
app.logger.addHandler(log_file_handler)
app.logger.setLevel(INFO)
app.logger.setLevel(WARNING)
if develop:
app.logger.setLevel(DEBUG)
......@@ -58,33 +59,16 @@ def create_app():
cache.init_app(app)
app.config['APP_CACHE'] = cache # to access cache from different modules
app.logger.info(f'Setup of Flask app in [{config.DEPLOYMENT_ENV}] mode was successful')
app.logger.warning(f'Setup of Flask in app [{config.DEPLOYMENT_ENV}]-mode was successful')
@app.before_request
def before_request():
""" Is called every time a request context is created before it enters the routing. """
request.custom_id = random_id()
app.logger.info(f'<<{__name__}>> [{request.method}]{request.full_path} from client: {request.remote_addr}')
if request.data: # parse body to check if data is valid json or list of json
try:
data_decoded = literal_eval(request.data.decode('UTF-8'))
except (SyntaxError, ValueError):
raise ValidationErrorResponse(msg=f'Json-model can not be evaluated since it contains structural flaws',
module=__name__)
if isinstance(data_decoded, list): # json array only supported in historic api
if 'historic' not in request.full_path:
raise ValidationErrorResponse(msg=f'Json-Array only supported for historic api',
module=__name__)
for model in data_decoded: # check each json
if not isinstance(model, dict):
raise ValidationErrorResponse(msg=f'Json-model can not be evaluated since it contains '
f'structural flaws', module=__name__)
elif isinstance(data_decoded, dict):
pass
else:
raise ValidationErrorResponse(msg=f'Json-model can not be evaluated since it contains structural flaws',
module=__name__)
if request.data:
# ensure payload is valid json model and has compliant format
parse_model()
@app.errorhandler(HTTPException)
def handle_http_exc(e):
......@@ -110,4 +94,15 @@ def create_app():
""" For developing purposes: Executed after every request, regardless of any occurring error. """
app.logger.debug('Teardown request')
@app.before_first_request
def load_validation_context():
# """ Initially loads all URL resources to cache `APP_CACHE`. """
# request.custom_id = 'before_first_request'
# TODO TBD maybe remove this condition
# if not app.config["READ_SPECS_FROM_DISK"] and not app.config["READ_BLUEPRINTS_FROM_DISK"]:
# try:
# load_url_resources(model_name='*')
# except ValidationException:
pass
return app
......@@ -12,28 +12,45 @@ APP_CONFIG = {
'PLATFORM_ENDPOINT': os.environ.get('PLATFORM_ENDPOINT'),
'PLATFORM_ENDPOINT_HISTORIC': os.environ.get('PLATFORM_ENDPOINT_HISTORIC'),
'BLUEPRINTS_ABS_PATH': os.environ.get('BLUEPRINTS_ABS_PATH', '/home/naiades/specs/'),
'SPECS_ABS_PATH': os.environ.get('SPECS_ABS_PATH', '/home/naiades/specs/'),
# URL resources
'SPECS_ABS_PATH': os.environ.get('SPECS_ABS_PATH', '/home/naiades/specs/')
}
URL_RESOURCES = {
"URL_NAME_CONVENTIONS": {
"SPECS": '{modelname}_SPECS_URL',
"BLUEPRINTS": '{modeltype}_{modelname}_BLUEPRINTS_URL'
},
"ALERT_SPECS_URL": os.environ.get("ALERT_SPECS_URL"),
"V2_ALERT_BLUEPRINT_URL": os.environ.get("V2_ALERT_BLUEPRINT_URL"),
"LD_ALERT_BLUEPRINT_URL": os.environ.get("LD_ALERT_BLUEPRINT_URL"),
"DEVICE_SPECS_URL": os.environ.get("DEVICE_SPECS_URL"),
"V2_DEVICE_BLUEPRINT_URL": os.environ.get("V2_DEVICE_BLUEPRINT_URL"),
"LD_DEVICE_BLUEPRINT_URL": os.environ.get("LD_DEVICE_BLUEPRINT_URL"),
"FLOWERBED_SPECS_URL": os.environ.get("FLOWERBED_SPECS_URL"),
"V2_FLOWERBED_BLUEPRINT_URL": os.environ.get("V2_FLOWERBED_BLUEPRINT_URL"),
"LD_FLOWERBED_BLUEPRINT_URL": os.environ.get("LD_FLOWERBED_BLUEPRINT_URL"),
"FOUNTAINUSAGEOBSERVED_SPECS_URL": os.environ.get("FOUNTAINUSAGEOBSERVED_SPECS_URL"),
"V2_FOUNTAINUSAGEOBSERVED_BLUEPRINT_URL": os.environ.get("V2_FOUNTAINUSAGEOBSERVED_BLUEPRINT_URL"),
"LD_FOUNTAINUSAGEOBSERVED_BLUEPRINT_URL": os.environ.get("LD_FOUNTAINUSAGEOBSERVED_BLUEPRINT_URL"),
"NOISE_SPECS_URL": os.environ.get("NOISE_SPECS_URL"),
"V2_NOISE_BLUEPRINT_URL": os.environ.get("V2_NOISE_BLUEPRINT_URL"),
"LD_NOISE_BLUEPRINT_URL": os.environ.get("LD_NOISE_BLUEPRINT_URL"),
"WATERCONSUMPTIONOBSERVED_SPECS_URL": os.environ.get("WATERCONSUMPTIONOBSERVED_SPECS_URL"),
"V2_WATERCONSUMPTIONOBSERVED_BLUEPRINT_URL": os.environ.get("V2_FLOWERBED_BLUEPRINT_URL"),
"LD_WATERCONSUMPTIONOBSERVED_BLUEPRINT_URL": os.environ.get("LD_FLOWERBED_BLUEPRINT_URL"),
"WATERQUALITYFORECAST_SPECS_URL": os.environ.get("WATERQUALITYFORECAST_SPECS_URL"),
"V2_WATERQUALITYFORECAST_BLUEPRINT_URL": os.environ.get("WATERQUALITYFORECAST_BLUEPRINT_URL"),
"V2_WATERQUALITYFORECAST_BLUEPRINT_URL": os.environ.get("V2_WATERQUALITYFORECAST_BLUEPRINT_URL"),
"LD_WATERQUALITYFORECAST_BLUEPRINT_URL": os.environ.get("LD_WATERQUALITYFORECAST_BLUEPRINT_URL"),
"WATERQUALITYOBSERVED_SPECS_URL": os.environ.get("WATERQUALITYOBSERVED_SPECS_URL"),
"V2_WATERQUALITYOBSERVED_BLUEPRINT_URL": os.environ.get("WATERQUALITYOBSERVED_BLUEPRINT_URL"),
"V2_WATERQUALITYOBSERVED_BLUEPRINT_URL": os.environ.get("V2_WATERQUALITYOBSERVED_BLUEPRINT_URL"),
"LD_WATERQUALITYOBSERVED_BLUEPRINT_URL": os.environ.get("LD_WATERQUALITYOBSERVED_BLUEPRINT_URL"),
"WEATHERFORECAST_SPECS_URL": os.environ.get("WEATHERFORECAST_SPECS_URL"),
"V2_WEATHERFORECAST_BLUEPRINT_URL": os.environ.get("WEATHERFORECAST_BLUEPRINT_URL"),
"V2_WEATHERFORECAST_BLUEPRINT_URL": os.environ.get("V2_WEATHERFORECAST_BLUEPRINT_URL"),
"LD_WEATHERFORECAST_BLUEPRINT_URL": os.environ.get("LD_WEATHERFORECAST_BLUEPRINT_URL"),
"WEATHEROBSERVED_SPECS_URL": os.environ.get("WEATHEROBSERVED_SPECS_URL"),
"V2_WEATHEROBSERVED_BLUEPRINT_URL": os.environ.get("WEATHEROBSERVED_BLUEPRINT_URL")
"V2_WEATHEROBSERVED_BLUEPRINT_URL": os.environ.get("V2_WEATHEROBSERVED_BLUEPRINT_URL"),
"LD_WEATHEROBSERVED_BLUEPRINT_URL": os.environ.get("LD_WEATHEROBSERVED_BLUEPRINT_URL")
}
LOG_CONFIG = {
......
from flask import Blueprint
validation_ld_entities = Blueprint('val_ld_ent', __name__, url_prefix='/validation/ld/entities')
historic_validation_ld_entities = Blueprint('val_ld_ent', __name__, url_prefix='historic/validation/ld/entities')
This diff is collapsed.
......@@ -37,7 +37,9 @@ class ValidationException(Exception):
def __repr__(self) -> str:
""" Represents the instance by listing the internal and external logging dictionaries."""
return f'{[self.intern_logging, self.extern_logging]}'
if self.extern_logging:
return f'{self.extern_logging}'
return 'ValidationException()'
class Validator(ABC):
......@@ -134,15 +136,17 @@ class StructuredValue(Validator):
"""
try:
res = ast.literal_eval(value)
if not isinstance(res, dict) and not isinstance(res, list):
raise ValueError("Structured Value needs to be in form of key-value-pairs or as listed values")
if isinstance(res, list):
if isinstance(res, dict):
return
elif isinstance(res, list):
for v in res:
if not (isinstance(v, str) or isinstance(v, int) or isinstance(v, float)):
raise ValueError(f'Value "{v}" is not of fiware-type Text or Number')
except (SyntaxError, ValueError):
raise ValueError("Structured Value needs to be in form of key-value-pairs")
else:
raise ValueError("Structured Value needs to be in form of key-value-pairs or as listed values")
except (SyntaxError, ValueError, TypeError):
raise ValidationException(extern=f"Structured Value `{value}` needs to be in form of key-value-pairs or as "
f"list", module=__name__, func='StructuredValue.validate')
class Number(Validator):
......@@ -316,9 +320,17 @@ class Location(Validator):
def transform_geo_json(geo_json: dict) -> dict:
return {'type': f'geo:{geo_json["value"]["type"].lower()}', 'value': geo_json["value"]["coordinates"]}
def correct_coord_type(coord_type: str) -> any:
mapping = {'Line': 'geo:line', 'Point': 'geo:point', 'Box': 'geo:box', 'Polygon': 'geo:polygon'}
if coord_type in mapping.keys():
return mapping[coord_type]
return None
try:
value = ast.literal_eval(value) # Convert str to dict
coord_type = value.get('type', None) # geo:point, geo:line, geo:box, geo:polygon or geo:json
if correct_coord_type(coord_type=coord_type):
coord_type = correct_coord_type(coord_type=coord_type)
if not coord_type:
raise ValueError('Missing "type" attribute in location')
......@@ -327,7 +339,14 @@ class Location(Validator):
value = transform_geo_json(geo_json=value)
coord_type = value['type']
coords = value['value']
# coordinates might be named 'value' in case of V2 and KV but might be named 'coordinates' in case of LD
if value.get('value'):
coords = value['value']
elif value.get('coordinates'):
coords = value['coordinates']
else:
raise KeyError('Model does not match to fiware `Location`-specifications')
if coord_type.lower() == 'geo:point': # "2,3" or [2, 3]
check_point_geom(point=coords)
......@@ -380,6 +399,12 @@ class DateTime(Validator):
' YYYY-MM-ddTHH:mm:ssZ, YYYY-MM-ddTHH:mm:ss.SSSSZ ')
# TODO used in Device
class Boolean(Validator):
def validate(self, value):
pass
class FiwareElement(object):
""" Serves as a wrapper or container that initializes the desired Class that represents a fiware datatype.
It is mainly used by the workflow method and instantiated by the eval function but can of course be instantiated
......@@ -503,7 +528,7 @@ class ValidationLogic(ABC):
return f'FiwareElement({fiware_type}="{model_value_}")' # e.g ex = FiwareElement(text="urn") / (number="110")
def _parse_expression(self, expression: str) -> None:
def parse_expression(self, expression: str) -> None:
""" Accepts an expression that will be evaluated.The goal is to create an FiwareElement that features an
attribute that was extracted from a fiware model. Any occurring errors during the creation will be registered
and a ValidationException will be raised. A regular expression will ensure the given expression does not
......@@ -557,7 +582,7 @@ class Updater(ValidationLogic):
# Iterate model and parse every key-value pair according to specs-dict
for model_key, model_value in self.model.items():
ex = self._prepare_expression(model_key_=model_key, model_value_=model_value)
self._parse_expression(ex)
self.parse_expression(ex)
except ValidationException as v_err:
raise ValidationException(extern=f'Parsing error occurred when evaluating "{model_key}": "{model_value}". '
......@@ -594,7 +619,7 @@ class Creator(ValidationLogic):
# Iterate model and parse every key-value pair according to specs-dict
for model_key, model_value in self.model.items():
ex = self._prepare_expression(model_key_=model_key, model_value_=model_value)
self._parse_expression(ex)
self.parse_expression(ex)
except ValidationException as v_err:
raise ValidationException(extern=f'Parsing error when evaluating "{model_key}": "{model_value}". Caused by:'
......
......@@ -358,14 +358,14 @@ class ParseExpressionTest(TestCase):
vl = ValidationLogic(specs='', model='', logger='')
self.text = "urn:"
expr = f'FiwareElement(text="{self.text}")'
vl._parse_expression(expression=expr)
vl.parse_expression(expression=expr)
def test_expression_wrong_fiware_type_should_raise_validation_exception(self):
vl = ValidationLogic(specs='', model='', logger='')
expr = 'FiwareElement(hello="world")'
expected = "__init__() got an unexpected keyword argument 'hello'"
with self.assertRaises(ValidationException) as err:
vl._parse_expression(expression=expr)
vl.parse_expression(expression=expr)
exception = err.exception
self.assertEqual(exception.intern_logging, f"Expression {expr} is unsuitable for parsing. Caused by: "
f"{expected}")
......@@ -374,7 +374,7 @@ class ParseExpressionTest(TestCase):
vl = ValidationLogic(specs='', model='', logger='')
expr = 'Element(text="world")'
with self.assertRaises(ValidationException) as err:
vl._parse_expression(expression=expr)