This commit is contained in:
parent
7650ae2369
commit
38509c5a39
@ -11,7 +11,7 @@ from model import db
|
|||||||
from utils import register_all_error_handlers, storage
|
from utils import register_all_error_handlers, storage
|
||||||
|
|
||||||
# import views
|
# import views
|
||||||
from views import SVMView, CNNView
|
from views import SVMView, CNNView, RootView
|
||||||
|
|
||||||
# Setup sentry
|
# Setup sentry
|
||||||
SENTRY_DSN = os.environ.get("SENTRY_DSN")
|
SENTRY_DSN = os.environ.get("SENTRY_DSN")
|
||||||
@ -56,7 +56,7 @@ def create_db():
|
|||||||
register_all_error_handlers(app)
|
register_all_error_handlers(app)
|
||||||
|
|
||||||
# register views
|
# register views
|
||||||
for view in [SVMView, CNNView]:
|
for view in [SVMView, CNNView, RootView]:
|
||||||
view.register(app, trailing_slash=False, route_prefix='/model')
|
view.register(app, trailing_slash=False, route_prefix='/model')
|
||||||
|
|
||||||
# start debuggig if needed
|
# start debuggig if needed
|
||||||
|
@ -7,8 +7,9 @@ import enum
|
|||||||
|
|
||||||
|
|
||||||
class AIModelType(enum.Enum):
|
class AIModelType(enum.Enum):
|
||||||
SVM = 1
|
# Optimally this would be upper case (as a convention for enums) But we want this to be the same as in the url schema
|
||||||
CNN = 2
|
svm = 1
|
||||||
|
cnn = 2
|
||||||
|
|
||||||
|
|
||||||
class AIModel(db.Model):
|
class AIModel(db.Model):
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from .require_decorators import json_required
|
from .require_decorators import json_required, multipart_required
|
||||||
from .error_handlers import register_all_error_handlers
|
from .error_handlers import register_all_error_handlers
|
||||||
from .storage import storage, ensure_buckets
|
from .storage import storage, ensure_buckets
|
@ -14,3 +14,15 @@ def json_required(f):
|
|||||||
abort(400, "JSON required")
|
abort(400, "JSON required")
|
||||||
|
|
||||||
return call
|
return call
|
||||||
|
|
||||||
|
|
||||||
|
def multipart_required(f):
|
||||||
|
@wraps(f)
|
||||||
|
def call(*args, **kwargs):
|
||||||
|
|
||||||
|
if request.form:
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
abort(400, "multipart/form-data required")
|
||||||
|
|
||||||
|
return call
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from .svm_view import SVMView
|
from .svm_view import SVMView
|
||||||
from .cnn_view import CNNView
|
from .cnn_view import CNNView
|
||||||
|
from .root_view import RootView
|
@ -5,21 +5,16 @@ from model import db, Default, AIModel, AIModelType
|
|||||||
from minio.error import NoSuchKey
|
from minio.error import NoSuchKey
|
||||||
from schemas import AIModelSchema, DefaultSchema, InfoSchema
|
from schemas import AIModelSchema, DefaultSchema, InfoSchema
|
||||||
from marshmallow.exceptions import ValidationError
|
from marshmallow.exceptions import ValidationError
|
||||||
from utils import json_required, storage, ensure_buckets
|
from utils import multipart_required, storage, ensure_buckets
|
||||||
|
|
||||||
|
|
||||||
class CNNView(FlaskView):
|
class CNNView(FlaskView):
|
||||||
route_base = 'cnn'
|
route_base = 'cnn'
|
||||||
|
|
||||||
aimodel_schema = AIModelSchema(many=False)
|
aimodel_schema = AIModelSchema(many=False)
|
||||||
aimodels_schema = AIModelSchema(many=True, exclude=['timestamp', 'details'])
|
|
||||||
default_schema = DefaultSchema(many=False)
|
|
||||||
info_schema = InfoSchema(many=False)
|
info_schema = InfoSchema(many=False)
|
||||||
|
|
||||||
def index(self):
|
@multipart_required
|
||||||
models = AIModel.query.filter_by(type=AIModelType.CNN).all()
|
|
||||||
return jsonify(self.aimodels_schema.dump(models)), 200
|
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
|
|
||||||
# get important data from the request
|
# get important data from the request
|
||||||
@ -48,7 +43,7 @@ class CNNView(FlaskView):
|
|||||||
ensure_buckets()
|
ensure_buckets()
|
||||||
|
|
||||||
# Create the entry in the db
|
# Create the entry in the db
|
||||||
m = AIModel(id=info['id'], type=AIModelType.CNN, target_class_name=info['target_class_name'])
|
m = AIModel(id=info['id'], type=AIModelType.cnn, target_class_name=info['target_class_name'])
|
||||||
|
|
||||||
# Put files into MinIO
|
# Put files into MinIO
|
||||||
storage.connection.put_object(current_app.config['MINIO_CNN_BUCKET_NAME'], "model/" + str(m.id), model_file,
|
storage.connection.put_object(current_app.config['MINIO_CNN_BUCKET_NAME'], "model/" + str(m.id), model_file,
|
||||||
@ -62,14 +57,30 @@ class CNNView(FlaskView):
|
|||||||
|
|
||||||
return jsonify(self.aimodel_schema.dump(m)), 200
|
return jsonify(self.aimodel_schema.dump(m)), 200
|
||||||
|
|
||||||
def get(self, _id: str):
|
def delete(self, _id: str):
|
||||||
|
|
||||||
if _id == "$default":
|
if _id == "$default":
|
||||||
# TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
default = Default.query.filter_by(type=AIModelType.cnn).first_or_404()
|
||||||
default = Default.query.filter_by(type=AIModelType.CNN).first_or_404()
|
|
||||||
m = default.aimodel
|
m = default.aimodel
|
||||||
else:
|
else:
|
||||||
m = AIModel.query.filter_by(type=AIModelType.CNN, id=_id).first_or_404()
|
m = AIModel.query.filter_by(type=AIModelType.cnn, id=_id).first_or_404()
|
||||||
|
|
||||||
|
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'], "weights/" + str(m.id))
|
||||||
|
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'], "model/" + str(m.id))
|
||||||
|
|
||||||
|
db.session.delete(m)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return '', 204
|
||||||
|
|
||||||
|
@route('<_id>/file')
|
||||||
|
def get_file(self, _id: str):
|
||||||
|
|
||||||
|
if _id == "$default":
|
||||||
|
default = Default.query.filter_by(type=AIModelType.cnn).first_or_404()
|
||||||
|
m = default.aimodel
|
||||||
|
else:
|
||||||
|
m = AIModel.query.filter_by(type=AIModelType.cnn, id=_id).first_or_404()
|
||||||
|
|
||||||
if "weights" in request.args:
|
if "weights" in request.args:
|
||||||
path = "weights/" + str(m.id)
|
path = "weights/" + str(m.id)
|
||||||
@ -82,50 +93,3 @@ class CNNView(FlaskView):
|
|||||||
abort(500, "The ID is stored in the database but not int the Object Store")
|
abort(500, "The ID is stored in the database but not int the Object Store")
|
||||||
|
|
||||||
return Response(data.stream(), mimetype=data.headers['Content-type'])
|
return Response(data.stream(), mimetype=data.headers['Content-type'])
|
||||||
|
|
||||||
@route('<_id>/details')
|
|
||||||
def get_details(self, _id: str):
|
|
||||||
|
|
||||||
if _id == "$default":
|
|
||||||
# TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
|
||||||
default = Default.query.filter_by(type=AIModelType.CNN).first_or_404()
|
|
||||||
m = default.aimodel
|
|
||||||
else:
|
|
||||||
m = AIModel.query.filter_by(type=AIModelType.CNN, id=_id).first_or_404()
|
|
||||||
|
|
||||||
return jsonify(self.aimodel_schema.dump(m))
|
|
||||||
|
|
||||||
def delete(self, _id: str):
|
|
||||||
|
|
||||||
if _id == "$default":
|
|
||||||
# TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
|
||||||
default = Default.query.filter_by(type=AIModelType.CNN).first_or_404()
|
|
||||||
m = default.aimodel
|
|
||||||
else:
|
|
||||||
m = AIModel.query.filter_by(type=AIModelType.CNN, id=_id).first_or_404()
|
|
||||||
|
|
||||||
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'], "weights/" + str(m.id))
|
|
||||||
storage.connection.remove_object(current_app.config['MINIO_CNN_BUCKET_NAME'], "model/" + str(m.id))
|
|
||||||
|
|
||||||
db.session.delete(m)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return '', 204
|
|
||||||
|
|
||||||
@json_required
|
|
||||||
@route('$default', methods=['PUT'])
|
|
||||||
def put_default(self):
|
|
||||||
|
|
||||||
try:
|
|
||||||
req = self.default_schema.load(request.json)
|
|
||||||
except ValidationError as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
m = AIModel.query.filter_by(type=AIModelType.CNN, id=req['id']).first_or_404()
|
|
||||||
|
|
||||||
Default.query.filter_by(type=AIModelType.CNN).delete()
|
|
||||||
new_default = Default(type=AIModelType.CNN, aimodel=m)
|
|
||||||
db.session.add(new_default)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return '', 204
|
|
||||||
|
73
model_service/views/root_view.py
Normal file
73
model_service/views/root_view.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from flask import jsonify, abort, request
|
||||||
|
from flask_classful import FlaskView, route
|
||||||
|
from marshmallow import ValidationError
|
||||||
|
|
||||||
|
from utils import json_required
|
||||||
|
|
||||||
|
from model import db, AIModel, AIModelType, Default
|
||||||
|
|
||||||
|
from schemas import AIModelSchema, DefaultSchema
|
||||||
|
|
||||||
|
|
||||||
|
class RootView(FlaskView):
|
||||||
|
route_base = '/'
|
||||||
|
|
||||||
|
aimodels_schema = AIModelSchema(many=True, exclude=['timestamp', 'details', 'target_class_name'])
|
||||||
|
aimodel_schema = AIModelSchema(many=False)
|
||||||
|
|
||||||
|
default_schema = DefaultSchema(many=False)
|
||||||
|
|
||||||
|
## Shared stuff goes here
|
||||||
|
|
||||||
|
def index(self):
|
||||||
|
models = AIModel.query.all()
|
||||||
|
return jsonify(self.aimodels_schema.dump(models))
|
||||||
|
|
||||||
|
@route('/<type_>')
|
||||||
|
def get_models(self, type_: str):
|
||||||
|
try:
|
||||||
|
aimodel_type = AIModelType[type_]
|
||||||
|
except KeyError:
|
||||||
|
abort(404, "Unknown type")
|
||||||
|
|
||||||
|
models = AIModel.query.filter_by(type=aimodel_type).all()
|
||||||
|
return jsonify(self.aimodels_schema.dump(models)), 200
|
||||||
|
|
||||||
|
@route('/<type_>/<id_>')
|
||||||
|
def get_model(self, type_: str, id_: str):
|
||||||
|
try:
|
||||||
|
aimodel_type = AIModelType[type_]
|
||||||
|
except KeyError:
|
||||||
|
abort(404, "Unknown type")
|
||||||
|
|
||||||
|
if id_ == "$default":
|
||||||
|
default = Default.query.filter_by(type=aimodel_type).first_or_404()
|
||||||
|
m = default.aimodel
|
||||||
|
else:
|
||||||
|
m = AIModel.query.filter_by(type=aimodel_type, id=id_).first_or_404()
|
||||||
|
|
||||||
|
return jsonify(self.aimodel_schema.dump(m))
|
||||||
|
|
||||||
|
@json_required
|
||||||
|
@route('/<type_>/$default', methods=['PUT'])
|
||||||
|
def put_default(self, type_: str):
|
||||||
|
|
||||||
|
try:
|
||||||
|
aimodel_type = AIModelType[type_]
|
||||||
|
except KeyError:
|
||||||
|
abort(404, "Unknown type")
|
||||||
|
|
||||||
|
try:
|
||||||
|
req = self.default_schema.load(request.json)
|
||||||
|
except ValidationError as e:
|
||||||
|
abort(400, str(e))
|
||||||
|
|
||||||
|
m = AIModel.query.filter_by(type=aimodel_type, id=req['id']).first_or_404()
|
||||||
|
|
||||||
|
Default.query.filter_by(type=aimodel_type).delete()
|
||||||
|
new_default = Default(type=aimodel_type, aimodel=m)
|
||||||
|
db.session.add(new_default)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return '', 204
|
@ -1,28 +1,23 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
from flask import request, jsonify, current_app, abort, Response
|
from flask import request, jsonify, current_app, abort, Response, url_for
|
||||||
from flask_classful import FlaskView, route
|
from flask_classful import FlaskView, route
|
||||||
from model import db, Default, AIModel, AIModelType, SVMDetails
|
from model import db, Default, AIModel, AIModelType, SVMDetails
|
||||||
from minio.error import NoSuchKey
|
from minio.error import NoSuchKey
|
||||||
from schemas import AIModelSchema, DefaultSchema, InfoSchema
|
from schemas import AIModelSchema, InfoSchema
|
||||||
from marshmallow.exceptions import ValidationError
|
from marshmallow.exceptions import ValidationError
|
||||||
from utils import json_required, storage, ensure_buckets
|
from utils import storage, ensure_buckets, multipart_required
|
||||||
from pyAudioAnalysis.audioTrainTest import load_model, load_model_knn
|
from pyAudioAnalysis.audioTrainTest import load_model
|
||||||
|
|
||||||
|
|
||||||
class SVMView(FlaskView):
|
class SVMView(FlaskView):
|
||||||
route_base = 'svm'
|
route_base = 'svm'
|
||||||
|
|
||||||
aimodel_schema = AIModelSchema(many=False)
|
aimodel_schema = AIModelSchema(many=False)
|
||||||
aimodels_schema = AIModelSchema(many=True, exclude=['timestamp', 'details'])
|
|
||||||
default_schema = DefaultSchema(many=False)
|
|
||||||
info_schema = InfoSchema(many=False)
|
info_schema = InfoSchema(many=False)
|
||||||
|
|
||||||
def index(self):
|
@multipart_required
|
||||||
models = AIModel.query.filter_by(type=AIModelType.SVM).all()
|
|
||||||
return jsonify(self.aimodels_schema.dump(models)), 200
|
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
|
|
||||||
# get important data from the request
|
# get important data from the request
|
||||||
@ -84,7 +79,7 @@ class SVMView(FlaskView):
|
|||||||
os.remove(temp_model_filename)
|
os.remove(temp_model_filename)
|
||||||
os.remove(temp_means_filename)
|
os.remove(temp_means_filename)
|
||||||
|
|
||||||
m = AIModel(id=info['id'], type=AIModelType.SVM, target_class_name=info['target_class_name'])
|
m = AIModel(id=info['id'], type=AIModelType.svm, target_class_name=info['target_class_name'])
|
||||||
|
|
||||||
d = SVMDetails(
|
d = SVMDetails(
|
||||||
aimodel=m,
|
aimodel=m,
|
||||||
@ -101,14 +96,31 @@ class SVMView(FlaskView):
|
|||||||
|
|
||||||
return jsonify(self.aimodel_schema.dump(m)), 200
|
return jsonify(self.aimodel_schema.dump(m)), 200
|
||||||
|
|
||||||
def get(self, _id: str):
|
def delete(self, _id: str):
|
||||||
|
|
||||||
if _id == "$default":
|
if _id == "$default":
|
||||||
# TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
default = Default.query.filter_by(type=AIModelType.svm).first_or_404()
|
||||||
default = Default.query.filter_by(type=AIModelType.SVM).first_or_404()
|
|
||||||
m = default.aimodel
|
m = default.aimodel
|
||||||
else:
|
else:
|
||||||
m = AIModel.query.filter_by(type=AIModelType.SVM, id=_id).first_or_404()
|
m = AIModel.query.filter_by(type=AIModelType.svm, id=_id).first_or_404()
|
||||||
|
|
||||||
|
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'], "means/" + str(m.id))
|
||||||
|
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'], "model/" + str(m.id))
|
||||||
|
|
||||||
|
db.session.delete(m)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return '', 204
|
||||||
|
|
||||||
|
# builtin file proxy
|
||||||
|
@route('<_id>/file')
|
||||||
|
def get_file(self, _id: str):
|
||||||
|
|
||||||
|
if _id == "$default":
|
||||||
|
default = Default.query.filter_by(type=AIModelType.svm).first_or_404()
|
||||||
|
m = default.aimodel
|
||||||
|
else:
|
||||||
|
m = AIModel.query.filter_by(type=AIModelType.svm, id=_id).first_or_404()
|
||||||
|
|
||||||
if "means" in request.args:
|
if "means" in request.args:
|
||||||
path = "means/" + str(m.id)
|
path = "means/" + str(m.id)
|
||||||
@ -121,50 +133,3 @@ class SVMView(FlaskView):
|
|||||||
abort(500, "The ID is stored in the database but not int the Object Store")
|
abort(500, "The ID is stored in the database but not int the Object Store")
|
||||||
|
|
||||||
return Response(data.stream(), mimetype=data.headers['Content-type'])
|
return Response(data.stream(), mimetype=data.headers['Content-type'])
|
||||||
|
|
||||||
@route('<_id>/details')
|
|
||||||
def get_details(self, _id: str):
|
|
||||||
|
|
||||||
if _id == "$default":
|
|
||||||
# TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
|
||||||
default = Default.query.filter_by(type=AIModelType.SVM).first_or_404()
|
|
||||||
m = default.aimodel
|
|
||||||
else:
|
|
||||||
m = AIModel.query.filter_by(type=AIModelType.SVM, id=_id).first_or_404()
|
|
||||||
|
|
||||||
return jsonify(self.aimodel_schema.dump(m))
|
|
||||||
|
|
||||||
def delete(self, _id: str):
|
|
||||||
|
|
||||||
if _id == "$default":
|
|
||||||
# TODO: Kitalálni, hogy inkább a latestestest-el térjen-e vissza
|
|
||||||
default = Default.query.filter_by(type=AIModelType.SVM).first_or_404()
|
|
||||||
m = default.aimodel
|
|
||||||
else:
|
|
||||||
m = AIModel.query.filter_by(type=AIModelType.SVM, id=_id).first_or_404()
|
|
||||||
|
|
||||||
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'], "means/" + str(m.id))
|
|
||||||
storage.connection.remove_object(current_app.config['MINIO_SVM_BUCKET_NAME'], "model/" + str(m.id))
|
|
||||||
|
|
||||||
db.session.delete(m)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return '', 204
|
|
||||||
|
|
||||||
@json_required
|
|
||||||
@route('$default', methods=['PUT'])
|
|
||||||
def put_default(self):
|
|
||||||
|
|
||||||
try:
|
|
||||||
req = self.default_schema.load(request.json)
|
|
||||||
except ValidationError as e:
|
|
||||||
abort(400, str(e))
|
|
||||||
|
|
||||||
m = AIModel.query.filter_by(type=AIModelType.SVM, id=req['id']).first_or_404()
|
|
||||||
|
|
||||||
Default.query.filter_by(type=AIModelType.SVM).delete()
|
|
||||||
new_default = Default(type=AIModelType.SVM, aimodel=m)
|
|
||||||
db.session.add(new_default)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return '', 204
|
|
||||||
|
Loading…
Reference in New Issue
Block a user