2020-04-13 21:14:06 +02:00
|
|
|
#!/usr/bin/env python3
|
2020-04-14 13:48:11 +02:00
|
|
|
import tempfile
|
2020-04-14 17:03:15 +02:00
|
|
|
import os
|
2020-04-14 13:48:11 +02:00
|
|
|
from flask import request, jsonify, current_app, abort, Response
|
2020-04-13 21:14:06 +02:00
|
|
|
from flask_classful import FlaskView, route
|
2020-04-14 13:48:11 +02:00
|
|
|
from model import db, Default, AIModel
|
|
|
|
from minio.error import BucketAlreadyExists, BucketAlreadyOwnedByYou, ResponseError, NoSuchKey
|
2020-04-14 17:03:15 +02:00
|
|
|
from schemas import AIModelSchema, DefaultSchema, InfoSchema
|
2020-04-14 14:02:35 +02:00
|
|
|
from marshmallow.exceptions import ValidationError
|
2020-04-14 13:48:11 +02:00
|
|
|
from utils import json_required, storage
|
2020-04-14 17:03:15 +02:00
|
|
|
from pyAudioAnalysis.audioTrainTest import load_model, load_model_knn
|
2020-04-13 21:14:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
class ModelView(FlaskView):
|
2020-04-14 13:48:11 +02:00
|
|
|
aimodel_schema = AIModelSchema(many=False)
|
2020-04-14 19:42:43 +02:00
|
|
|
aimodels_schema = AIModelSchema(many=True,
|
|
|
|
exclude=['timestamp', 'mid_window', 'mid_step', 'short_window', 'short_step',
|
|
|
|
'compute_beat'])
|
2020-04-14 02:01:44 +02:00
|
|
|
default_schema = DefaultSchema(many=False)
|
2020-04-14 17:03:15 +02:00
|
|
|
info_schema = InfoSchema(many=False)
|
|
|
|
|
|
|
|
def _ensure_buckets(self):
|
|
|
|
for bucket_name in [current_app.config['MINIO_MEANS_BUCKET_NAME'],
|
|
|
|
current_app.config['MINIO_MODEL_BUCKET_NAME']]:
|
|
|
|
try:
|
|
|
|
storage.connection.make_bucket(bucket_name)
|
|
|
|
|
|
|
|
except BucketAlreadyOwnedByYou as err:
|
|
|
|
pass
|
|
|
|
except BucketAlreadyExists as err:
|
|
|
|
pass
|
|
|
|
# Everything else should be raised
|
2020-04-14 02:01:44 +02:00
|
|
|
|
2020-04-14 19:42:43 +02:00
|
|
|
def index(self):
|
|
|
|
models = AIModel.query.all()
|
|
|
|
return jsonify(self.aimodels_schema.dump(models)), 200
|
|
|
|
|
2020-04-13 21:14:06 +02:00
|
|
|
def post(self):
|
2020-04-14 17:03:15 +02:00
|
|
|
|
|
|
|
# get important data from the request
|
|
|
|
try:
|
|
|
|
info = self.info_schema.loads(request.form.get('info'))
|
|
|
|
except ValidationError as e:
|
|
|
|
abort(400, str(e))
|
|
|
|
|
|
|
|
# check for conflict
|
|
|
|
m = AIModel.query.filter_by(id=info['id']).first()
|
|
|
|
if m:
|
|
|
|
abort(409)
|
|
|
|
|
|
|
|
# get and validate file
|
|
|
|
model_file = request.files['modelFile']
|
|
|
|
|
|
|
|
if model_file.content_length <= 0:
|
|
|
|
abort(411, f"Content length for modelFile is not a positive integer or missing.")
|
|
|
|
|
|
|
|
means_file = request.files['meansFile']
|
|
|
|
|
|
|
|
if means_file.content_length <= 0:
|
|
|
|
abort(411, f"Content length for meansFile is not a positive integer or missing.")
|
|
|
|
|
|
|
|
# create bucket if necessary
|
|
|
|
self._ensure_buckets()
|
|
|
|
|
|
|
|
# Temporarily save the file, because pyAudioAnalysis can only read files
|
|
|
|
_, temp_model_filename = tempfile.mkstemp()
|
|
|
|
temp_means_filename = temp_model_filename + "MEANS"
|
|
|
|
|
|
|
|
model_file.save(temp_model_filename)
|
|
|
|
means_file.save(temp_means_filename)
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
if info['type'] == 'knn':
|
|
|
|
_, _, _, _, mid_window, mid_step, short_window, short_step, compute_beat \
|
|
|
|
= load_model_knn(temp_model_filename)
|
|
|
|
else:
|
|
|
|
_, _, _, _, mid_window, mid_step, short_window, short_step, compute_beat \
|
|
|
|
= load_model(temp_model_filename)
|
|
|
|
|
|
|
|
# Because of pyAudiomeme the files already saved, so we just use the file uploader functions
|
|
|
|
storage.connection.fput_object(
|
|
|
|
current_app.config['MINIO_MODEL_BUCKET_NAME'],
|
|
|
|
str(info['id']),
|
|
|
|
temp_model_filename
|
|
|
|
)
|
|
|
|
|
|
|
|
storage.connection.fput_object(
|
|
|
|
current_app.config['MINIO_MEANS_BUCKET_NAME'],
|
|
|
|
str(info['id']),
|
|
|
|
temp_means_filename
|
|
|
|
)
|
|
|
|
|
|
|
|
finally:
|
|
|
|
os.remove(temp_model_filename)
|
|
|
|
os.remove(temp_means_filename)
|
|
|
|
|
2020-04-14 19:42:43 +02:00
|
|
|
m = AIModel(id=info['id'], mid_window=mid_window, mid_step=mid_step, short_window=short_window,
|
|
|
|
short_step=short_step, compute_beat=compute_beat, type=info['type'])
|
2020-04-14 17:03:15 +02:00
|
|
|
|
|
|
|
db.session.add(m)
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
|
return jsonify(self.aimodel_schema.dump(m)), 200
|
2020-04-13 21:14:06 +02:00
|
|
|
|
|
|
|
def get(self, _id: str):
|
2020-04-14 13:48:11 +02:00
|
|
|
|
|
|
|
if _id == "$default":
|
|
|
|
default = Default.query.first_or_404() # TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
2020-04-14 19:42:43 +02:00
|
|
|
m = default.aimodel
|
|
|
|
else:
|
|
|
|
m = AIModel.query.filter_by(id=_id).first_or_404()
|
|
|
|
|
|
|
|
if "means" in request.args:
|
|
|
|
bucket = current_app.config['MINIO_MEANS_BUCKET_NAME']
|
2020-04-14 13:48:11 +02:00
|
|
|
else:
|
2020-04-14 19:42:43 +02:00
|
|
|
bucket = current_app.config['MINIO_MODEL_BUCKET_NAME']
|
|
|
|
|
|
|
|
try:
|
|
|
|
data = storage.connection.get_object(bucket, str(m.id))
|
|
|
|
except NoSuchKey:
|
|
|
|
abort(500, "The ID is stored in the database but not int the Object Store")
|
2020-04-14 13:48:11 +02:00
|
|
|
|
2020-04-14 19:42:43 +02:00
|
|
|
return Response(data.stream(), mimetype=data.headers['Content-type'])
|
2020-04-13 21:14:06 +02:00
|
|
|
|
|
|
|
@route('<_id>/details')
|
|
|
|
def get_details(self, _id: str):
|
|
|
|
|
2020-04-14 13:48:11 +02:00
|
|
|
if _id == "$default":
|
|
|
|
default = Default.query.first_or_404() # TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
2020-04-14 17:26:33 +02:00
|
|
|
m = default.aimodel
|
2020-04-14 13:48:11 +02:00
|
|
|
else:
|
2020-04-14 17:26:33 +02:00
|
|
|
m = AIModel.query.filter_by(id=_id).first_or_404()
|
2020-04-13 21:14:06 +02:00
|
|
|
|
2020-04-14 13:48:11 +02:00
|
|
|
return jsonify(self.aimodel_schema.dump(m))
|
2020-04-14 02:01:44 +02:00
|
|
|
|
2020-04-14 13:48:11 +02:00
|
|
|
def delete(self, _id: str):
|
|
|
|
|
2020-04-14 17:26:33 +02:00
|
|
|
if _id == '$default':
|
|
|
|
default = Default.query.first_or_404()
|
|
|
|
m = default.aimodel
|
|
|
|
else:
|
2020-04-14 19:42:43 +02:00
|
|
|
m = AIModel.query.filter_by(id=_id).first_or_404()
|
2020-04-14 02:01:44 +02:00
|
|
|
|
2020-04-14 17:26:33 +02:00
|
|
|
storage.connection.remove_object(current_app.config['MINIO_MODEL_BUCKET_NAME'], str(m.id))
|
|
|
|
storage.connection.remove_object(current_app.config['MINIO_MEANS_BUCKET_NAME'], str(m.id))
|
2020-04-14 02:01:44 +02:00
|
|
|
|
2020-04-14 13:48:11 +02:00
|
|
|
db.session.delete(m)
|
2020-04-14 02:01:44 +02:00
|
|
|
db.session.commit()
|
|
|
|
|
2020-04-14 13:48:11 +02:00
|
|
|
return '', 204
|
|
|
|
|
|
|
|
@json_required
|
|
|
|
@route('$default', methods=['PUT'])
|
|
|
|
def put_default(self):
|
2020-04-14 14:02:35 +02:00
|
|
|
|
|
|
|
try:
|
2020-04-14 17:26:33 +02:00
|
|
|
req = self.default_schema.load(request.json)
|
2020-04-14 14:02:35 +02:00
|
|
|
except ValidationError as e:
|
|
|
|
abort(404, str(e))
|
|
|
|
|
2020-04-14 17:26:33 +02:00
|
|
|
m = AIModel.query.filter_by(id=req['id']).first_or_404()
|
2020-04-14 14:02:35 +02:00
|
|
|
|
|
|
|
Default.query.delete()
|
2020-04-14 17:26:33 +02:00
|
|
|
new_default = Default(aimodel=m)
|
2020-04-14 14:02:35 +02:00
|
|
|
db.session.add(new_default)
|
|
|
|
db.session.commit()
|
|
|
|
|
2020-04-14 17:03:15 +02:00
|
|
|
return '', 204
|