diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..45d444e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.pythonPath": "venv/bin/python3.7" +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 674fad4..9b1724d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,9 +23,14 @@ The scope should be the name of the npm package affected (as perceived by the pe The following is the list of supported scopes: - - **config** + - **core** - **common** + - **app** + - **tests** + - **models** + - **views** + - **manager** - **vcs** - - **core** - - **routes** + - **deps** - **migrations** + - **config** diff --git a/app/__init__.py b/app/__init__.py index cdbfbba..eed71a8 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -17,16 +17,26 @@ def create_app(config_name): app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # disabling sqlalchemy event system CONFIG[config_name].init_app(app) + root = CONFIG[config_name].APPLICATION_ROOT + # flask migrate doesn't recognize the tables without this import + from app.models import Application, Genre, ApplicationType, Session, Questionnaire + # Set up extensions db.init_app(app) # Create app blueprints - from .main import main as main_blueprint + from app.routes.v1 import main as main_blueprint app.register_blueprint(main_blueprint, url_prefix=root + '/') - from .applications import applications as applications_blueprint - app.register_blueprint(applications_blueprint, url_prefix=root + '/applications') + from app.routes.v1 import application as application_blueprint + app.register_blueprint(application_blueprint, url_prefix=root + '/application') + + from app.routes.v1 import session as session_blueprint + app.register_blueprint(session_blueprint, url_prefix=root + '/session') + + from app.routes.v1 import questionnaire as questionnaire_blueprint + app.register_blueprint(questionnaire_blueprint, url_prefix=root + '/questionnaire') return app diff --git a/app/applications/__init__.py b/app/applications/__init__.py deleted file mode 100644 index ff34c6c..0000000 --- a/app/applications/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from app.applications.views import applications # noqa diff --git a/app/applications/views.py b/app/applications/views.py deleted file mode 100644 index 054ff53..0000000 --- a/app/applications/views.py +++ /dev/null @@ -1,13 +0,0 @@ -from flask import Blueprint, jsonify - -applications = Blueprint('applications', __name__) - - -@applications.route('/', methods=['GET']) -def get_applications(): - return jsonify({"message": "Get Application New API"}) - - -@applications.route('/', methods=['POST']) -def create_application(): - return jsonify({"message": "Created Application New API"}) diff --git a/app/main/__init__.py b/app/main/__init__.py deleted file mode 100644 index 59dee14..0000000 --- a/app/main/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from app.main.views import main # noqa diff --git a/app/models/__init__.py b/app/models/__init__.py index e405ef7..2316650 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -4,3 +4,7 @@ """ from .application import * # noqa +from .application_type import * # noqa +from .genre import * # noqa +from .session import * # noqa +from .questionnaire import * # noqa diff --git a/app/models/application.py b/app/models/application.py index d6d7d0f..50b2754 100644 --- a/app/models/application.py +++ b/app/models/application.py @@ -1,18 +1,40 @@ +from marshmallow import fields, validate +from .application_type import ApplicationTypeSchema +from .genre import GenreSchema from .. import db, ma class Application(db.Model): - __tablename__ = 'applications' + __tablename__ = 'application' + id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(250), nullable=False) + name = db.Column(db.String(100), nullable=False) + identifier = db.Column(db.String(100), nullable=False) + developer = db.Column(db.String(100), nullable=False) + type_id = db.Column(db.Integer, db.ForeignKey('application_type.id', use_alter=True, name='fk_type_id'), nullable=False) + description = db.Column(db.String(250), nullable=False) creation_date = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), nullable=False) - genre = db.Column(db.String(250), nullable=False) + genre_id = db.Column(db.Integer, db.ForeignKey('genre.id', use_alter=True, name='fk_genre_id'), nullable=False) + sessions = db.relationship('Session', backref='app', lazy='dynamic') - def __init__(self, name, genre): + def __init__(self, name, identifier, developer, type, description, genre): self.name = name + self.identifier = identifier + self.developer = developer + self.type = type + self.description = description self.genre = genre + def __repr__(self): + return '' % self.id + class ApplicationSchema(ma.Schema): - class Meta: - fields = ('id', 'name', 'creation_date', 'genre') + id = fields.Integer(dump_only=True) + name = fields.String(required=True, validate=validate.Length(1, 100)) + identifier = fields.String() + developer = fields.String(required=True, validate=validate.Length(1, 100)) + type = fields.Nested(ApplicationTypeSchema, dump_only=True) + description = fields.String(required=True, validate=validate.Length(1, 250)) + creation_date = fields.DateTime() + genre = fields.Nested(GenreSchema, dump_only=True) diff --git a/app/models/application_type.py b/app/models/application_type.py new file mode 100644 index 0000000..c93015a --- /dev/null +++ b/app/models/application_type.py @@ -0,0 +1,56 @@ +import os +import json +from marshmallow import fields, validate +from .. import db, ma + +APPLICATION_TYPE_META_FILE_PATH = 'meta/application_type.meta.json' + + +class ApplicationType(db.Model): + __tablename__ = 'application_type' + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(50), nullable=False) + display_name = db.Column(db.String(100), nullable=False) + display_name_full = db.Column(db.String(250), nullable=False) + applications = db.relationship('Application', backref='type', lazy='dynamic') + + def __init__(self, name, display_name, display_name_full): + self.name = name + self.display_name = display_name + self.display_name_full = display_name_full + + @classmethod + def seed(cls): + if cls.is_table_empty(cls): + if os.path.exists(APPLICATION_TYPE_META_FILE_PATH): + with open(APPLICATION_TYPE_META_FILE_PATH) as app_type_meta_json: + data = json.load(app_type_meta_json) + for item in data['types']: + app_type = ApplicationType(name=item['name'], display_name=item['display_name'], display_name_full=item['display_name_full']) + app_type.save() + print("Adding application type metadata: {}".format(app_type)) + else: + # TODO: Add exception + print("Couldn't locate meta file") + else: + print('Table is already filled') + + def save(self): + db.session.add(self) + db.session.commit() + + def is_table_empty(self): + if not self.query.all(): + return True + return False + + def __repr__(self): + return '' % self.id + + +class ApplicationTypeSchema(ma.Schema): + id = fields.Integer() + name = fields.String(required=True) + display_name = fields.String(required=True) + display_name_full = fields.String(required=True) diff --git a/app/models/genre.py b/app/models/genre.py new file mode 100644 index 0000000..89a0ecf --- /dev/null +++ b/app/models/genre.py @@ -0,0 +1,53 @@ +import os +import json +from marshmallow import fields +from .. import db, ma + +GENRE_META_FILE_PATH = 'meta/genre.meta.json' + + +class Genre(db.Model): + __tablename__ = 'genre' + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + display_name = db.Column(db.String(250), nullable=False) + applications = db.relationship('Application', backref='genre', lazy='dynamic') + + def __init__(self, name, display_name): + self.name = name + self.display_name = display_name + + @classmethod + def seed(cls): + if cls.is_table_empty(cls): + if os.path.exists(GENRE_META_FILE_PATH): + with open(GENRE_META_FILE_PATH) as genre_meta_json: + data = json.load(genre_meta_json) + for item in data['genre']: + genre = Genre(name=item['name'], display_name=item['display_name']) + genre.save() + print("Adding genre metadata: {}".format(genre)) + else: + # TODO: Add exception + print("Couldn't locate meta file") + else: + print('Table is already filled') + + def save(self): + db.session.add(self) + db.session.commit() + + def is_table_empty(self): + if not self.query.all(): + return True + return False + + def __repr__(self): + return '' % self.id + + +class GenreSchema(ma.Schema): + id = fields.Integer() + name = fields.String(required=True) + display_name = fields.String(required=True) diff --git a/app/models/questionnaire.py b/app/models/questionnaire.py new file mode 100644 index 0000000..3ae3522 --- /dev/null +++ b/app/models/questionnaire.py @@ -0,0 +1,32 @@ +from marshmallow import fields, validate +from .. import db, ma + + +class Questionnaire(db.Model): + __tablename__ = 'questionnaire' + + id = db.Column(db.Integer, primary_key=True) + pre = db.Column(db.JSON, nullable=False) + post = db.Column(db.JSON, nullable=False, default={}) + creation_date = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), nullable=False) + session = db.relationship("Session", uselist=False, backref="questionnaire") + + def __init__(self, pre, post): + self.pre = pre + self.post = post + + def __repr__(self): + return '' % self.id + + +class SymptomSchema(ma.Schema): + name = fields.String(required=False) + display_name = fields.String(required=False) + score = fields.String(required=False) + + +class QuestionnaireSchema(ma.Schema): + id = fields.Integer(dump_only=True) + pre = fields.List(fields.Nested(SymptomSchema), dump_only=True) + post = fields.List(fields.Nested(SymptomSchema), dump_only=True) + creation_date = fields.DateTime() diff --git a/app/models/session.py b/app/models/session.py new file mode 100644 index 0000000..d71089b --- /dev/null +++ b/app/models/session.py @@ -0,0 +1,37 @@ +from marshmallow import fields, validate +from .. import db, ma +from .application import ApplicationSchema +from .questionnaire import QuestionnaireSchema + + +class Session(db.Model): + __tablename__ = 'session' + + id = db.Column(db.Integer, primary_key=True) + app_id = db.Column(db.Integer, db.ForeignKey('application.id', use_alter=True, name='fk_app_id'), nullable=False) + creation_date = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), nullable=False) + expected_emotions = db.Column(db.JSON, nullable=False) + questionnaire_id = db.Column(db.Integer, db.ForeignKey('questionnaire.id', use_alter=True, name='fk_questionnaire_id'), nullable=False) + cssi_score = db.Column(db.Float, nullable=False, default=0) + latency_scores = db.Column(db.JSON, nullable=False, default={}) + total_latency_score = db.Column(db.Float, nullable=False, default=0) + sentiment_scores = db.Column(db.JSON, nullable=False, default={}) + total_sentiment_score = db.Column(db.Float, nullable=False, default=0) + questionnaire_scores = db.Column(db.JSON, nullable=True, default={}) + total_questionnaire_score = db.Column(db.Float, nullable=False, default=0) + + def __init__(self, app, expected_emotions, questionnaire): + self.app = app + self.expected_emotions = expected_emotions + self.questionnaire = questionnaire + + def __repr__(self): + return '' % self.id + + +class SessionSchema(ma.Schema): + id = fields.Integer(dump_only=True) + creation_date = fields.DateTime() + expected_emotions = fields.List(fields.String(), dump_only=True) + app = fields.Nested(ApplicationSchema, dump_only=True) + questionnaire = fields.Nested(QuestionnaireSchema, dump_only=True) diff --git a/app/routes/__init__.py b/app/routes/__init__.py new file mode 100644 index 0000000..8d55024 --- /dev/null +++ b/app/routes/__init__.py @@ -0,0 +1 @@ +from app.routes.v1 import * # noqa diff --git a/app/routes/v1/__init__.py b/app/routes/v1/__init__.py new file mode 100644 index 0000000..ebfd221 --- /dev/null +++ b/app/routes/v1/__init__.py @@ -0,0 +1,4 @@ +from app.routes.v1.application import application # noqa +from app.routes.v1.main import main # noqa +from app.routes.v1.session import session # noqa +from app.routes.v1.questionnaire import questionnaire # noqa diff --git a/app/routes/v1/application.py b/app/routes/v1/application.py new file mode 100644 index 0000000..f1f60dc --- /dev/null +++ b/app/routes/v1/application.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# (c) Copyright 2019 Brion Mario. +# (c) This file is part of the CSSI REST API and is made available under MIT license. +# (c) For more information, see https://github.com/brionmario/cssi-api/blob/master/LICENSE.txt +# (c) Please forward any queries to the given email address. email: brion@apareciumlabs.com + +"""Application routes module + +This modules contains all the different routes to interact with applications. + +Authors: + Brion Mario + +""" + +import uuid +from flask import Blueprint, jsonify, request +from app.models import Application, ApplicationType, ApplicationSchema, Genre +from app import db + +application = Blueprint('application', __name__) + +application_schema = ApplicationSchema(strict=True) +applications_schema = ApplicationSchema(many=True, strict=True) + + +@application.route('/', methods=['GET']) +def get_applications(): + """Get a list of all the Applications""" + applications = Application.query.all() + result = applications_schema.dump(applications).data + return jsonify({'status': 'success', 'message': None, 'data': result}), 200 + + +@application.route('/', methods=['GET']) +def get_application(id): + """Get info on an Applications when an id is passed in""" + application = Application.query.get(id) + result = application_schema.dump(application).data + return jsonify({'status': 'success', 'message': None, 'data': result}), 200 + + +@application.route('/', methods=['POST']) +def create_application(): + """Create a new Application""" + + json_data = request.get_json(force=True) + + if not json_data: + return jsonify({'status': 'error', 'message': 'No input was provided.'}), 400 + + # Validate and deserialize input + data, errors = application_schema.load(json_data) + if errors: + return jsonify({'status': 'error', 'message': 'Incorrect format of data provided.', 'data': errors}), 422 + + name = request.json['name'] + identifier = str(uuid.uuid4().hex) + developer = request.json['developer'] + type = ApplicationType.query.filter_by(id=request.json['type']).first() + description = request.json['description'] + genre = Genre.query.filter_by(id=request.json['genre']).first() + + # validate application type + if not type: + return {'status': 'error', 'message': 'Invalid Application Type'}, 400 + + # validate genre + if not genre: + return {'status': 'error', 'message': 'Invalid Genre Type'}, 400 + + new_application = Application(name=name, identifier=identifier, developer=developer, type=type, description=description, genre=genre) + + db.session.add(new_application) + db.session.commit() + + result = application_schema.dump(new_application).data + + return jsonify({'status': 'success', 'message': 'Created new application {}.'.format(name), 'data': result}), 201 diff --git a/app/main/views.py b/app/routes/v1/main.py similarity index 100% rename from app/main/views.py rename to app/routes/v1/main.py diff --git a/app/routes/v1/questionnaire.py b/app/routes/v1/questionnaire.py new file mode 100644 index 0000000..4acb097 --- /dev/null +++ b/app/routes/v1/questionnaire.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# (c) Copyright 2019 Brion Mario. +# (c) This file is part of the CSSI REST API and is made available under MIT license. +# (c) For more information, see https://github.com/brionmario/cssi-api/blob/master/LICENSE.txt +# (c) Please forward any queries to the given email address. email: brion@apareciumlabs.com + +"""Application routes module + +This modules contains all the different routes to interact with applications. + +Authors: + Brion Mario + +""" + +import uuid +from flask import Blueprint, jsonify, request +from app.models import Questionnaire, ApplicationType, QuestionnaireSchema, Genre +from app import db + +questionnaire = Blueprint('questionnaire', __name__) + +questionnaire_schema = QuestionnaireSchema(strict=True) +questionnaires_schema = QuestionnaireSchema(many=True, strict=True) + + +@questionnaire.route('/', methods=['GET']) +def get_questionnaire_list(): + """Get a list of all the Questionnaire""" + questionnaires = Questionnaire.query.all() + result = questionnaires_schema.dump(questionnaires).data + return jsonify({'status': 'success', 'message': None, 'data': result}), 200 + + +@questionnaire.route('/', methods=['GET']) +def get_questionnaire(id): + """Get questionnaire when an id is passed in""" + questionnaire = Questionnaire.query.get(id) + result = questionnaire_schema.dump(questionnaire).data + return jsonify({'status': 'success', 'message': None, 'data': result}), 200 + + +@questionnaire.route('/', methods=['POST']) +def create_questionnaire(): + """Create a new Questionnaire""" + pre = request.json['pre'] + post = request.json['post'] + + new_questionnaire = Questionnaire(pre=pre, post=post) + + db.session.add(new_questionnaire) + db.session.commit() + + result = questionnaire_schema.dump(new_questionnaire).data + + return jsonify({'status': 'success', 'message': 'Created new questionnaire', 'data': result}), 201 diff --git a/app/routes/v1/session.py b/app/routes/v1/session.py new file mode 100644 index 0000000..0930393 --- /dev/null +++ b/app/routes/v1/session.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# (c) Copyright 2019 Brion Mario. +# (c) This file is part of the CSSI REST API and is made available under MIT license. +# (c) For more information, see https://github.com/brionmario/cssi-api/blob/master/LICENSE.txt +# (c) Please forward any queries to the given email address. email: brion@apareciumlabs.com + +"""Session routes module + +This modules contains all the different routes to interact with Sessions. + +Authors: + Brion Mario + +""" + +from flask import Blueprint, jsonify, request +from app.models import Session, SessionSchema, Application, Questionnaire +from app import db + +session = Blueprint('session', __name__) + +session_schema = SessionSchema(strict=True) +sessions_schema = SessionSchema(many=True, strict=True) + + +@session.route('/', methods=['GET']) +def get_sessions_list(): + """Get a list of all the sessions""" + sessions = Session.query.all() + result = sessions_schema.dump(sessions).data + return jsonify({'status': 'success', 'message': None, 'data': result}), 200 + + +@session.route('/', methods=['GET']) +def get_session(id): + """Get info on a session when an id is passed in""" + session = Session.query.get(id) + result = sessions_schema.dump(session).data + return jsonify({'status': 'success', 'message': None, 'data': result}), 200 + + +@session.route('/', methods=['POST']) +def create_session(): + """Create a new Session""" + + app = Application.query.filter_by(id=request.json['app']).first() + questionnaire = Questionnaire.query.filter_by(id=request.json['questionnaire_id']).first() + expected_emotions = request.json['expected_emotions'] + + print(questionnaire) + + # validate application type + if not app: + return {'status': 'error', 'message': 'Invalid application.'}, 400 + + new_session = Session(app=app, expected_emotions=expected_emotions, questionnaire=questionnaire) + + db.session.add(new_session) + db.session.commit() + + result = session_schema.dump(new_session).data + + return jsonify({'status': 'success', 'message': 'Created new session for application with id of {}.'.format(request.json['app']), 'data': result}), 201 diff --git a/manage.py b/manage.py index a5097f5..e7c3b85 100644 --- a/manage.py +++ b/manage.py @@ -26,10 +26,24 @@ manager.add_command('db', MigrateCommand) +@manager.command +def create_metadata(): + """Create the table metadata. + + Application types and Genres need to be added to the database + in order for new applications to be added. + """ + from app.models import Genre, ApplicationType + + Genre.seed() + ApplicationType.seed() @manager.command def test(): - """Run the unit tests.""" + """Run the unit tests. + + This function will run the unit tests in the tests package. + """ import unittest tests = unittest.TestLoader().discover('tests') @@ -38,9 +52,9 @@ def test(): @manager.command def recreate_db(): - """ - Recreates a local database. You probably should not use this on - production. + """Recreates a local database + + Not safe to use in production. """ db.drop_all() db.create_all() diff --git a/meta/application_type.meta.json b/meta/application_type.meta.json new file mode 100644 index 0000000..8aa8220 --- /dev/null +++ b/meta/application_type.meta.json @@ -0,0 +1,19 @@ +{ + "types": [ + { + "name": "vr", + "display_name": "VR", + "display_name_full": "Virtual Reality" + }, + { + "name": "ar", + "display_name": "AR", + "display_name_full": "Augmented Reality" + }, + { + "name": "mr", + "display_name": "VR", + "display_name_full": "Mixed Reality" + } + ] +} \ No newline at end of file diff --git a/meta/genre.meta.json b/meta/genre.meta.json new file mode 100644 index 0000000..23dcf17 --- /dev/null +++ b/meta/genre.meta.json @@ -0,0 +1,48 @@ +{ + "genre": [ + { + "name": "puzzle", + "display_name": "Puzzle" + }, + { + "name": "arcade", + "display_name": "Arcade" + }, + { + "name": "action", + "display_name": "Action" + }, + { + "name": "family", + "display_name": "Family" + }, + { + "name": "educational", + "display_name": "Educational" + }, + { + "name": "adventure", + "display_name": "Adventure" + }, + { + "name": "strategy", + "display_name": "Strategy" + }, + { + "name": "board", + "display_name": "Board" + }, + { + "name": "simulation", + "display_name": "Simulation" + }, + { + "name": "trivia", + "display_name": "Trivia" + }, + { + "name": "other", + "display_name": "Other" + } + ] +} \ No newline at end of file diff --git a/migrations/versions/2b5013b1ff2e_.py b/migrations/versions/2b5013b1ff2e_.py deleted file mode 100644 index 48a3cf6..0000000 --- a/migrations/versions/2b5013b1ff2e_.py +++ /dev/null @@ -1,34 +0,0 @@ -"""empty message - -Revision ID: 2b5013b1ff2e -Revises: -Create Date: 2019-04-14 16:51:50.295795 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '2b5013b1ff2e' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('applications', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('name', sa.String(length=250), nullable=False), - sa.Column('creation_date', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), - sa.Column('genre', sa.String(length=250), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('applications') - # ### end Alembic commands ### diff --git a/migrations/versions/89ee48680e79_.py b/migrations/versions/89ee48680e79_.py new file mode 100644 index 0000000..4f39656 --- /dev/null +++ b/migrations/versions/89ee48680e79_.py @@ -0,0 +1,42 @@ +"""empty message + +Revision ID: 89ee48680e79 +Revises: cb018c771a53 +Create Date: 2019-04-16 04:22:10.614121 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '89ee48680e79' +down_revision = 'cb018c771a53' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_foreign_key('fk_type_id', 'application', 'application_type', ['type_id'], ['id'], use_alter=True) + op.create_foreign_key('fk_genre_id', 'application', 'genre', ['genre_id'], ['id'], use_alter=True) + op.alter_column('questionnaire', 'post', + existing_type=mysql.TEXT(), + nullable=True) + op.drop_column('questionnaire', 'session_id') + op.create_foreign_key('fk_app_id', 'session', 'application', ['app_id'], ['id'], use_alter=True) + op.create_foreign_key('fk_questionnaire_id', 'session', 'questionnaire', ['questionnaire_id'], ['id'], use_alter=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('fk_questionnaire_id', 'session', type_='foreignkey') + op.drop_constraint('fk_app_id', 'session', type_='foreignkey') + op.add_column('questionnaire', sa.Column('session_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=False)) + op.alter_column('questionnaire', 'post', + existing_type=mysql.TEXT(), + nullable=False) + op.drop_constraint('fk_genre_id', 'application', type_='foreignkey') + op.drop_constraint('fk_type_id', 'application', type_='foreignkey') + # ### end Alembic commands ### diff --git a/migrations/versions/cb018c771a53_.py b/migrations/versions/cb018c771a53_.py new file mode 100644 index 0000000..bd2e305 --- /dev/null +++ b/migrations/versions/cb018c771a53_.py @@ -0,0 +1,83 @@ +"""empty message + +Revision ID: cb018c771a53 +Revises: +Create Date: 2019-04-16 01:05:44.197617 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'cb018c771a53' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('application', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('identifier', sa.String(length=100), nullable=False), + sa.Column('developer', sa.String(length=100), nullable=False), + sa.Column('type_id', sa.Integer(), nullable=False), + sa.Column('description', sa.String(length=250), nullable=False), + sa.Column('creation_date', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), + sa.Column('genre_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['genre_id'], ['genre.id'], name='fk_genre_id', use_alter=True), + sa.ForeignKeyConstraint(['type_id'], ['application_type.id'], name='fk_type_id', use_alter=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('application_type', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=50), nullable=False), + sa.Column('display_name', sa.String(length=100), nullable=False), + sa.Column('display_name_full', sa.String(length=250), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('genre', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=100), nullable=False), + sa.Column('display_name', sa.String(length=250), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('questionnaire', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('pre', sa.TEXT(), nullable=False), + sa.Column('post', sa.TEXT(), nullable=False), + sa.Column('creation_date', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), + sa.Column('session_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['session_id'], ['session.id'], name='fk_session_id', use_alter=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('session', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('app_id', sa.Integer(), nullable=False), + sa.Column('creation_date', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), + sa.Column('expected_emotions', sa.TEXT(), nullable=False), + sa.Column('cssi_score', sa.Float(), nullable=False), + sa.Column('latency_scores', sa.TEXT(), nullable=False), + sa.Column('total_latency_score', sa.Float(), nullable=False), + sa.Column('sentiment_scores', sa.TEXT(), nullable=False), + sa.Column('total_sentiment_score', sa.Float(), nullable=False), + sa.Column('questionnaire_id', sa.Integer(), nullable=False), + sa.Column('questionnaire_scores', sa.TEXT(), nullable=True), + sa.Column('total_questionnaire_score', sa.Float(), nullable=False), + sa.ForeignKeyConstraint(['app_id'], ['application.id'], name='fk_app_id', use_alter=True), + sa.ForeignKeyConstraint(['questionnaire_id'], ['questionnaire.id'], name='fk_questionnaire_id', use_alter=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('session') + op.drop_table('questionnaire') + op.drop_table('genre') + op.drop_table('application_type') + op.drop_table('application') + # ### end Alembic commands ###