From 1b4f72820d7d8213f5488cde48e68a47e0a9fe6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Heikkil=C3=A4?= <timo.t.heikkila@utu.fi> Date: Tue, 6 Nov 2018 11:05:59 +0200 Subject: [PATCH] add fixed language selctor --- .gitignore | 2 - app.db | Bin 0 -> 94208 bytes app/routes.py | 38 +++++++- app/templates/base.html | 48 +++++++++- app/templates/consent.html | 9 +- app/templates/create_experiment.html | 2 +- app/templates/index.html | 86 +++++++++++++++-- config.py | 16 ++++ migrations/README | 1 + migrations/alembic.ini | 45 +++++++++ migrations/env.py | 87 +++++++++++++++++ migrations/script.py.mako | 24 +++++ migrations/versions/75ace1b8b1e0_.py | 137 +++++++++++++++++++++++++++ 13 files changed, 473 insertions(+), 22 deletions(-) create mode 100644 app.db create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/75ace1b8b1e0_.py diff --git a/.gitignore b/.gitignore index 2e28ad5..e11292b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ __pycache__/ -/migrations /venv -app.db cmd.txt DB_inserts.txt dumb.sql diff --git a/app.db b/app.db new file mode 100644 index 0000000000000000000000000000000000000000..bf4690f3e45b7cc6ab8cc1930c9254191e8060ef GIT binary patch literal 94208 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 C^iC)M literal 0 HcmV?d00001 diff --git a/app/routes.py b/app/routes.py index b29fcd3..0153d1c 100644 --- a/app/routes.py +++ b/app/routes.py @@ -49,16 +49,21 @@ APP_ROOT = os.path.dirname(os.path.abspath(__file__)) - - - - - @app.route('/') @app.route('/index') def index(): experiments = experiment.query.all() + + if session: + + flash("") + + else: + + flash("set lang") + session['language'] = "All" + return render_template('index.html', title='Home', experiments=experiments) @@ -69,6 +74,29 @@ def consent(): return render_template('consent.html', exp_id=exp_id, experiments=experiments) +@app.route('/set_language') +def set_language(): + + session['language'] = request.args.get('language', None) + + + + + return redirect(url_for('index')) + +@app.route('/remove_language') +def remove_language(): + + + experiments = experiment.query.all() + + + return render_template('index.html', title='Home', experiments=experiments) + + + + + @app.route('/session') def participant_session(): diff --git a/app/templates/base.html b/app/templates/base.html index aa3814a..3675532 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -29,7 +29,11 @@ {% if pages %} <a class="navbar-brand pl-5 font-weight-light">Participant ID: <span class="text-success font-weight-bold">{{ session['user']}}</span></a> {% else %} - <a class="navbar-brand pl-3 text-success font-weight-light" href="{{ url_for('index') }}" class="nav-link">MEGA-fMRI Stimulus Rating Tool</a> + + + <a class="navbar-brand pl-3 text-success font-weight-light" href="{{ url_for('index') }}" class="nav-link">Onni</a> + + {% endif %} </div> <div class="col-6 navbar-nav"> @@ -48,11 +52,47 @@ {% endwith %} {% endblock %} {% if current_user.is_authenticated %} - <a class="nav-item" href="{{ url_for('researcher_info') }}" class="nav-link">Data preparation info |</a> - <a class="nav-item" href="{{ url_for('create_experiment') }}" class="nav-link">Create experiment |</a> + <a class="nav-item" href="{{ url_for('researcher_info') }}" class="nav-link">Info |</a> + + + + <a class="nav-item dropdown"> + <a class="nav-item dropdown-toggle text-align-right" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + Language + </a> + <div class="dropdown-menu" aria-labelledby="navbarDropdown"> + + <a class="dropdown-item" href="{{ url_for('set_language', language='Finnish') }}">Finnish</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='English') }}">English</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Persian') }}">Persian</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Greek') }}">Greek</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Italian') }}">Italian</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Chinese') }}">Chinese</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='All') }}">Show all languages</a> + </div> + + <a class="nav-item" href="{{ url_for('create_experiment') }}" class="nav-link">| Create experiment |</a> + {% endif %} {% if current_user.is_anonymous %} - <a class="nav-item" href="{{ url_for('login') }}" class="nav-link">Researcher login</a> + <a class="nav-item" href="{{ url_for('login') }}" class="nav-link">Researcher login |</a> + + <a class="nav-item dropdown"> + <a class="nav-item dropdown-toggle text-align-right" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + Language + </a> + <div class="dropdown-menu" aria-labelledby="navbarDropdown"> + + <a class="dropdown-item" href="{{ url_for('set_language', language='Finnish') }}">Finnish</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='English') }}">English</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Persian') }}">Persian</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Greek') }}">Greek</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Italian') }}">Italian</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='Chinese') }}">Chinese</a> + <a class="dropdown-item" href="{{ url_for('set_language', language='All') }}">Show all languages</a> + </div> + + {% else %} <a class="nav-item" href="{{ url_for('logout') }}" class="nav-link">Logout</a> {% endif %} diff --git a/app/templates/consent.html b/app/templates/consent.html index ee2743f..c6b5b6a 100644 --- a/app/templates/consent.html +++ b/app/templates/consent.html @@ -5,11 +5,14 @@ <br> <h4>Lue tutkimustiedote <a href="tiedote.pdf">tästä.</a></h4> <br/> -<p class="lead">Ennen tämän suostumuksen antamista olen lukenut ja ymmärtänyt saamani tutkimustiedotteen, sekä saanut riittävästi tietoa tutkimuksen kulusta. Minulle on selvitetty, että minusta kerättäviä tutkimustietoja tullaan käsittelemään luottamuksellisina siten, että niistä ei voida tunnistaa henkilöllisyyttäni. Ymmärrän, että osallistumiseni tutkimukseen on täysin vapaaehtoista ja että voin missä tutkimuksen vaiheessa tahansa keskeyttää tutkimuksen antamatta perustetta. Minulle on lisäksi selvitetty, että halutessani saan tutkimustiedotteessa nimetyltä tutkijalta lisätietoja tutkimuksen yleisistä periaatteista ja edistymisestä. Ymmärrän, että aineistoa kerätään pelkästään tieteellistä tutkimusta varten eikä sitä luovuteta osittainkaan koehenkilölle itselleen, ja että tietokoneeni IP-osoitetta tai muita tunnistetietoja ei tallenneta. +<p class="lead">Prior to giving the consent I have read and understood the Experiment/ Research Bulletin I received, and I have received sufficient information about the course of the experiment. I have been clarified that the experiment data collected from me will be treated confidentially in such way that my identity can not be identified. I understand that my participation in the study is completely voluntary and that I can stop the experiment at any stage of the experiment without giving any reason. Furthermore, I have been clarified that I will receive further information about the general principles and the progress of the experiment from the researcher mentioned at the Experiment/ Research Bulletin, if I wish to do so. I understand that the data is collected only for purposes of the scientific research and it is not even partially disclosed to the participant himself/ herself and that my IP address or other identifying information will not be saved. + </p> -<p class="lead">Klikkaamalla alla olevaa painiketta annan suostumukseni tutkimuksen yhteydessä tapahtuvaan tietojen keräämiseen ja niiden käsittelyyn tiedotteessa kuvatulla tavalla. +<p class="lead">By clicking on the button below, I give my consent to the collection and processing of the data in this experiment, as described in the Experiment/ Research Bulletin. + </p> -<p class="lead">Tästä elektronisesta suostumuksesta säilytetään digitaalinen merkintä tutkimuksesta vastaavan tutkijan tiedostoissa. +<p class="lead">Of this electronical consent, there will be a digital entry saved in the files of the researcher responsible for the experiment. + </p> <p> <a class="btn btn-primary" href="{{ url_for('participant_session', exp_id=exp_id, agree='true') }}" role="button">Agree</a> diff --git a/app/templates/create_experiment.html b/app/templates/create_experiment.html index d8ca0c7..97c3275 100644 --- a/app/templates/create_experiment.html +++ b/app/templates/create_experiment.html @@ -31,7 +31,7 @@ <option value="Bulgarian">Bulgarian</option> <option value="Catalan">Catalan</option> <option value="Cambodian">Cambodian</option> - <option value="Chinese (Mandarin)">Chinese (Mandarin)</option> + <option value="Chinese">Chinese</option> <option value="Croation">Croation</option> <option value="Czech">Czech</option> <option value="Danish">Danish</option> diff --git a/app/templates/index.html b/app/templates/index.html index 55823d0..5aa7c8c 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -2,17 +2,22 @@ {% block content %} - <h1 class="container mt-5 display-4 text-center"><br>{{ _('Welcome') }}</h1> + <h1 class="container mt-5 display-4 text-center"><br>{{ _('Welcome to Onni') }}</h1> <br> - <p class="lead">This is the Human Emotion Systems laboratorys stimulus rating tool. If you have previously started a rating task you can continue that task on this page. If you are a researcher you can create new rating tasks by <a href="{{ url_for('login') }}">logging in.</a> - Or you can start a new rating task and start rating by selecting a rating task from the database list below.</p> + <br> + <p class="lead text-center">This is the Human Emotion Systems laboratorys stimulus rating tool. If you have previously started a rating task you can continue that task on this page. If you are a researcher you can create new rating tasks by <a href="{{ url_for('login') }}">logging in.</a> + Or you can start a new rating task and start rating by selecting a task from the database list below. + <br> + <br> + You can choose the language suitable for you from the language menu in the upper right corner + </p> <div class="row"> <div class="col mt-5"> <h3>List of experiments in database:</h3> {% block attributes %} {% for exp in experiments %} - {% if exp.status == 'Public' %} + {% if exp.status == 'Public' and session['language'] == exp.language %} <ul class="list-group mb-4"> <li class="list-group-item active"><span class="font-weight-bold">Name:</span> {{ exp.name }} </li> @@ -39,14 +44,50 @@ <a class="btn btn-outline-info" href="{{ url_for('view_experiment', exp_id=exp.idexperiment) }}" role="button">View / Edit</a> {% endif %} - + </ul> <br> <br> {% endif %} - {% if (exp.status == 'Hidden') and (current_user.is_authenticated) %} + + {% if exp.status == 'Public' and session['language'] == "All" %} + + <ul class="list-group mb-4"> + <li class="list-group-item active"><span class="font-weight-bold">Name:</span> {{ exp.name }} </li> + <li class="list-group-item"><span class="font-weight-bold">Instruction:</span> {{ exp.instruction }}</li> + {% if current_user.is_authenticated %} + <li class="list-group-item"><span class="font-weight-bold">ID number:</span> {{ exp.idexperiment }} </li> + <li class="list-group-item"><span class="font-weight-bold">Language:</span> {{ exp.language }}</li> + <li class="list-group-item"><span class="font-weight-bold">Status:</span> {{ exp.status }}</li> + {% endif %} + <li class="list-group-item"> + <button class="btn btn-outline-success dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + Begin task + </button> + <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> + <a class="dropdown-item" href="{{ url_for('consent', exp_id=exp.idexperiment) }}">As a new participant</a> + <a class="dropdown-item" href="{{ url_for('begin_with_id', exp_id=exp.idexperiment) }}">I have received an ID to use for this task</a> + </div> + <a class="btn btn-outline-success" href="{{ url_for('continue_task', exp_id=exp.idexperiment) }}" role="button">Continue task</a> + + {% if current_user.is_authenticated %} + + + <a class="btn btn-outline-info" href="{{ url_for('experiment_statistics', exp_id=exp.idexperiment) }}" role="button">Statistics</a> + <a class="btn btn-outline-info" href="{{ url_for('view_experiment', exp_id=exp.idexperiment) }}" role="button">View / Edit</a> + + {% endif %} + + + </ul> + <br> + <br> + {% endif %} + + + {% if (exp.status == 'Hidden') and (current_user.is_authenticated) and session['language'] == exp.language %} <br> <h3>Unpublished experiment:</h3> <ul class="list-group mb-4"> @@ -80,7 +121,38 @@ {% endif %} - + {% if (exp.status == 'Hidden') and (current_user.is_authenticated) and session['language'] == "All" %} + <br> + <h3>Unpublished experiment:</h3> + <ul class="list-group mb-4"> + <li class="list-group-item list-group-item-dark"><span class="font-weight-bold">Name:</span> {{ exp.name }} </li> + <li class="list-group-item"><span class="font-weight-bold">Instruction:</span> {{ exp.instruction }}</li> + {% if current_user.is_authenticated %} + <li class="list-group-item"><span class="font-weight-bold">ID number:</span> {{ exp.idexperiment }} </li> + <li class="list-group-item"><span class="font-weight-bold">Language:</span> {{ exp.language }}</li> + <li class="list-group-item"><span class="font-weight-bold">Status:</span> {{ exp.status }}</li> + {% endif %} + <li class="list-group-item"> + <button class="btn btn-outline-success dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + Begin task + </button> + <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> + <a class="dropdown-item" href="{{ url_for('consent', exp_id=exp.idexperiment) }}">As a new participant</a> + <a class="dropdown-item" href="{{ url_for('begin_with_id', exp_id=exp.idexperiment) }}">I have received an ID to use for this task</a> + </div> + <a class="btn btn-outline-success" href="{{ url_for('continue_task', exp_id=exp.idexperiment) }}" role="button">Continue task</a> + {% if current_user.is_authenticated %} + + + <a class="btn btn-outline-info" href="{{ url_for('experiment_statistics', exp_id=exp.idexperiment) }}" role="button">Statistics</a> + <a class="btn btn-outline-info" href="{{ url_for('view_experiment', exp_id=exp.idexperiment) }}" role="button">View / Edit</a> + + + {% endif %} + </li> + </ul> + + {% endif %} {% endfor %} {% endblock %} diff --git a/config.py b/config.py index 1b0d935..ce6f05c 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,23 @@ class Config(object): #seret key is set in __ini__.py #SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess' + + + + + #SQLITE3 connection settings: + SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'app.db') SQLALCHEMY_TRACK_MODIFICATIONS = False + + #MariaDB mysql database settings + """ + 'mysql+pymysql://'+MYSQL_USER+':'+MYSQL_PASSWORD+'@'+MYSQL_SERVER+'/'+MYSQL_DB+'?charset=utf8mb4' + + MYSQL_USER rating + MYSQL_PASSWORD timotimo + MYSQL_SERVER localhost + MYSQL_DB rating_tool_db + """ \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000..f8ed480 --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000..23663ff --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,87 @@ +from __future__ import with_statement +from alembic import context +from sqlalchemy import engine_from_config, pool +from logging.config import fileConfig +import logging + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from flask import current_app +config.set_main_option('sqlalchemy.url', + current_app.config.get('SQLALCHEMY_DATABASE_URI')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure(url=url) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + engine = engine_from_config(config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool) + + connection = engine.connect() + context.configure(connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/75ace1b8b1e0_.py b/migrations/versions/75ace1b8b1e0_.py new file mode 100644 index 0000000..d34fc9a --- /dev/null +++ b/migrations/versions/75ace1b8b1e0_.py @@ -0,0 +1,137 @@ +"""empty message + +Revision ID: 75ace1b8b1e0 +Revises: +Create Date: 2018-11-04 18:40:37.722652 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '75ace1b8b1e0' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('background_question', + sa.Column('idbackground_question', sa.Integer(), nullable=False), + sa.Column('background_question', sa.String(length=120), nullable=True), + sa.Column('experiment_idexperiment', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('idbackground_question') + ) + op.create_table('experiment', + sa.Column('idexperiment', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=120), nullable=True), + sa.Column('instruction', sa.String(length=120), nullable=True), + sa.Column('directoryname', sa.String(length=120), nullable=True), + sa.Column('language', sa.String(length=120), nullable=True), + sa.Column('status', sa.String(length=120), nullable=True), + sa.Column('randomization', sa.String(length=120), nullable=True), + sa.PrimaryKeyConstraint('idexperiment') + ) + op.create_index(op.f('ix_experiment_directoryname'), 'experiment', ['directoryname'], unique=True) + op.create_index(op.f('ix_experiment_instruction'), 'experiment', ['instruction'], unique=False) + op.create_index(op.f('ix_experiment_name'), 'experiment', ['name'], unique=False) + op.create_table('trial_randomization', + sa.Column('idtrial_randomization', sa.Integer(), nullable=False), + sa.Column('page_idpage', sa.Integer(), nullable=True), + sa.Column('randomized_idpage', sa.Integer(), nullable=True), + sa.Column('answer_set_idanswer_set', sa.Integer(), nullable=True), + sa.Column('experiment_idexperiment', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('idtrial_randomization') + ) + op.create_table('user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=64), nullable=True), + sa.Column('email', sa.String(length=120), nullable=True), + sa.Column('password_hash', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True) + op.create_table('answer_set', + sa.Column('idanswer_set', sa.Integer(), nullable=False), + sa.Column('experiment_idexperiment', sa.Integer(), nullable=True), + sa.Column('session', sa.String(length=120), nullable=True), + sa.Column('agreement', sa.String(length=120), nullable=True), + sa.Column('answer_counter', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['experiment_idexperiment'], ['experiment.idexperiment'], ), + sa.PrimaryKeyConstraint('idanswer_set') + ) + op.create_table('background_question_option', + sa.Column('idbackground_question_option', sa.Integer(), nullable=False), + sa.Column('background_question_idbackground_question', sa.Integer(), nullable=True), + sa.Column('option', sa.String(length=120), nullable=True), + sa.ForeignKeyConstraint(['background_question_idbackground_question'], ['background_question.idbackground_question'], ), + sa.PrimaryKeyConstraint('idbackground_question_option') + ) + op.create_table('page', + sa.Column('idpage', sa.Integer(), nullable=False), + sa.Column('experiment_idexperiment', sa.Integer(), nullable=True), + sa.Column('type', sa.String(length=120), nullable=True), + sa.Column('text', sa.String(length=120), nullable=True), + sa.Column('media', sa.String(length=120), nullable=True), + sa.ForeignKeyConstraint(['experiment_idexperiment'], ['experiment.idexperiment'], ), + sa.PrimaryKeyConstraint('idpage') + ) + op.create_index(op.f('ix_page_media'), 'page', ['media'], unique=False) + op.create_index(op.f('ix_page_text'), 'page', ['text'], unique=False) + op.create_index(op.f('ix_page_type'), 'page', ['type'], unique=False) + op.create_table('question', + sa.Column('idquestion', sa.Integer(), nullable=False), + sa.Column('experiment_idexperiment', sa.Integer(), nullable=True), + sa.Column('question', sa.String(length=120), nullable=True), + sa.Column('left', sa.String(length=120), nullable=True), + sa.Column('right', sa.String(length=120), nullable=True), + sa.ForeignKeyConstraint(['experiment_idexperiment'], ['experiment.idexperiment'], ), + sa.PrimaryKeyConstraint('idquestion') + ) + op.create_table('answer', + sa.Column('idanswer', sa.Integer(), nullable=False), + sa.Column('question_idquestion', sa.Integer(), nullable=True), + sa.Column('answer_set_idanswer_set', sa.Integer(), nullable=True), + sa.Column('answer', sa.String(length=120), nullable=True), + sa.Column('page_idpage', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['answer_set_idanswer_set'], ['answer_set.idanswer_set'], ), + sa.ForeignKeyConstraint(['page_idpage'], ['page.idpage'], ), + sa.ForeignKeyConstraint(['question_idquestion'], ['question.idquestion'], ), + sa.PrimaryKeyConstraint('idanswer') + ) + op.create_table('background_question_answer', + sa.Column('idbackground_question_answer', sa.Integer(), nullable=False), + sa.Column('answer_set_idanswer_set', sa.Integer(), nullable=True), + sa.Column('answer', sa.String(length=120), nullable=True), + sa.Column('background_question_idbackground_question', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['answer_set_idanswer_set'], ['answer_set.idanswer_set'], ), + sa.ForeignKeyConstraint(['background_question_idbackground_question'], ['background_question.idbackground_question'], ), + sa.PrimaryKeyConstraint('idbackground_question_answer') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('background_question_answer') + op.drop_table('answer') + op.drop_table('question') + op.drop_index(op.f('ix_page_type'), table_name='page') + op.drop_index(op.f('ix_page_text'), table_name='page') + op.drop_index(op.f('ix_page_media'), table_name='page') + op.drop_table('page') + op.drop_table('background_question_option') + op.drop_table('answer_set') + op.drop_index(op.f('ix_user_username'), table_name='user') + op.drop_index(op.f('ix_user_email'), table_name='user') + op.drop_table('user') + op.drop_table('trial_randomization') + op.drop_index(op.f('ix_experiment_name'), table_name='experiment') + op.drop_index(op.f('ix_experiment_instruction'), table_name='experiment') + op.drop_index(op.f('ix_experiment_directoryname'), table_name='experiment') + op.drop_table('experiment') + op.drop_table('background_question') + # ### end Alembic commands ### -- GitLab