diff --git a/app/admin/routes.py b/app/admin/routes.py index 59f71153881950627d8c15d756d424a7a6eb1846..1b0a039a545835500f547917556f36c2261b8788 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -16,7 +16,7 @@ def home(): @bp.route('/manage_bookings') @permission_required(admin_permission) def manage_bookings(): - return render_template('admin/index.html') + return render_template('admin/manage_bookings.html') @bp.route('/manage_users') @permission_required(super_admin_permission) diff --git a/app/api/routes.py b/app/api/routes.py index 1c40f57de07b90beaedd98c2b7f704215d5163bb..ee448e573f754c83d2db28fed8acf2a5086ba7c8 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -1,8 +1,7 @@ -from flask import render_template, redirect, url_for, Flask, jsonify +from flask import jsonify, request from app.api import bp from app.models import User, Listings -from sqlalchemy import text -from app import csrf +import json @bp.route('/user_id/<int:id>', methods=['GET']) def get_user_by_id(id): @@ -87,4 +86,38 @@ def create_listing(): #If something falls over throw nice error for debugging, will change for admin only users to see errors otherwise throw generic 500 except Exception as e: - return jsonify({'error': str(e)}), 500 \ No newline at end of file + return jsonify({'error': str(e)}), 500 + +# Sample data +data = [ + {"Name": "Tiger Nixon", "Position": "System Architect", "Office": "Edinburgh", "Age": 61, "StartDate": "2011-04-25", "Salary": "$320,800"}, + {"Name": "Garrett Winters", "Position": "Accountant", "Office": "Tokyo", "Age": 63, "StartDate": "2011-07-25", "Salary": "$170,750"}, + {"Name": "Ashton Cox", "Position": "Junior Technical Author", "Office": "San Francisco", "Age": 66, "StartDate": "2009-01-12", "Salary": "$86,000"}, + {"Name": "Cedric Kelly", "Position": "Senior Javascript Developer", "Office": "Edinburgh", "Age": 22, "StartDate": "2012-03-29", "Salary": "$433,060"}, + {"Name": "Airi Satou", "Position": "Accountant", "Office": "Tokyo", "Age": 33, "StartDate": "2008-11-28", "Salary": "$162,700"}, + {"Name": "Brielle Williamson", "Position": "Integration Specialist", "Office": "New York", "Age": 61, "StartDate": "2012-12-02", "Salary": "$372,000"}, + {"Name": "Herrod Chandler", "Position": "Sales Assistant", "Office": "San Francisco", "Age": 59, "StartDate": "2012-08-06", "Salary": "$137,500"}, + {"Name": "Rhona Davidson", "Position": "Integration Specialist", "Office": "Tokyo", "Age": 55, "StartDate": "2010-10-14", "Salary": "$327,900"}, + {"Name": "Colleen Hurst", "Position": "Javascript Developer", "Office": "San Francisco", "Age": 39, "StartDate": "2009-09-15", "Salary": "$205,500"}, + {"Name": "Sonya Frost", "Position": "Software Engineer", "Office": "Edinburgh", "Age": 23, "StartDate": "2008-12-13", "Salary": "$103,600"}, + {"Name": "Jena Gaines", "Position": "Office Manager", "Office": "London", "Age": 30, "StartDate": "2008-12-19", "Salary": "$90,560"}, + {"Name": "Quinn Flynn", "Position": "Support Lead", "Office": "Edinburgh", "Age": 22, "StartDate": "2013-03-03", "Salary": "$342,000"}, + {"Name": "Charde Marshall", "Position": "Regional Director", "Office": "San Francisco", "Age": 36, "StartDate": "2008-10-16", "Salary": "$470,600"}, + {"Name": "Haley Kennedy", "Position": "Senior Marketing Designer", "Office": "London", "Age": 43, "StartDate": "2012-12-18", "Salary": "$313,500"}, + {"Name": "Tatyana Fitzpatrick", "Position": "Regional Director", "Office": "London", "Age": 19, "StartDate": "2010-03-17", "Salary": "$385,750"}, + {"Name": "Michael Silva", "Position": "Marketing Designer", "Office": "London", "Age": 66, "StartDate": "2012-11-27", "Salary": "$198,500"}, + {"Name": "Paul Byrd", "Position": "Chief Financial Officer (CFO)", "Office": "New York", "Age": 64, "StartDate": "2010-06-09", "Salary": "$725,000"}, + {"Name": "Gloria Little", "Position": "Systems Administrator", "Office": "New York", "Age": 59, "StartDate": "2009-04-10", "Salary": "$237,500"}, + {"Name": "Bradley Greer", "Position": "Software Engineer", "Office": "London", "Age": 41, "StartDate": "2012-10-13", "Salary": "$132,000"}, + {"Name": "Dai Rios", "Position": "Personnel Lead", "Office": "Edinburgh", "Age": 35, "StartDate": "2012-09-26", "Salary": "$217,500"}, + {"Name": "Jenette Caldwell", "Position": "Development Lead", "Office": "New York", "Age": 30, "StartDate": "2011-09-03", "Salary": "$345,000"}, +] + +@bp.route('/api/get_data', methods=['GET']) +def get_data(): + min_age = request.args.get('min_age', type=int, default=0) + max_age = request.args.get('max_age', type=int, default=100) + + filtered_data = [entry for entry in data if min_age <= entry['Age'] <= max_age] + return jsonify(filtered_data) + diff --git a/app/templates/admin/manage_bookings.html b/app/templates/admin/manage_bookings.html new file mode 100644 index 0000000000000000000000000000000000000000..2cfb45a4515223289bc0a64506885a3766acebef --- /dev/null +++ b/app/templates/admin/manage_bookings.html @@ -0,0 +1,122 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>DataTables with Flask</title> + <link rel="stylesheet" href="https://cdn.datatables.net/1.10.21/css/jquery.dataTables.min.css"> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <!-- Bootstrap CSS --> + <style> + .table-container { + width: 90%; + margin: auto; + overflow-x: hidden; /* Hide overflow by default */ + } + + @media (max-width: 800px) { + .table-container { + width: 100%; + padding: 0 10px; + overflow-x: auto; /* Enable horizontal scrolling */ + } + } + + @media (min-width: 801px) { + .table-container { + overflow-x: hidden; /* Ensure overflow is hidden on larger screens */ + } + } + + /* Ensure DataTable wrapper behaves responsively */ + .dataTables_wrapper { + width: 100%; + } + </style> + <script src="https://code.jquery.com/jquery-3.5.1.js"></script> + <script src="https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js"></script> + <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script> <!-- Bootstrap JS including Popper.js --> +</head> +<body> + <table class="inputs"> + <tr> + <td>Minimum age:</td> + <td><input type="text" id="min" name="min"></td> + </tr> + <tr> + <td>Maximum age:</td> + <td><input type="text" id="max" name="max"></td> + </tr> + </table> + + <div class="table-container"> + <table id="example" class="table table-striped table-bordered display" style="width:100%"> <!-- Use Bootstrap table classes --> + <thead> + <tr> + <th>Name</th> + <th>Position</th> + <th>Office</th> + <th>Age</th> + <th>Start date</th> + <th>Salary</th> + <th>Actions</th> <!-- Add Actions column --> + </tr> + </thead> + <tbody> + </tbody> + </table> + </div> + + <script> + $(document).ready(function() { + const table = $('#example').DataTable({ + ajax: { + url: "{{ url_for('api.get_data') }}", // Updated URL + dataSrc: '', + data: function(d) { + d.min_age = $('#min').val(); + d.max_age = $('#max').val(); + } + }, + columns: [ + { data: 'Name' }, + { data: 'Position' }, + { data: 'Office' }, + { data: 'Age' }, + { data: 'StartDate' }, + { data: 'Salary' }, + { // Actions column + data: null, + className: "dt-center", + defaultContent: ` + <div class="dropdown"> + <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-expanded="false"> + Actions + </button> + <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> + <a class="dropdown-item edit-btn" href="#">Edit</a> + <a class="dropdown-item delete-btn" href="#">Delete</a> + </div> + </div>` + } + ] + }); + + $('#min, #max').on('input', function() { + table.ajax.reload(); + }); + + $('#example tbody').on('click', '.edit-btn', function() { + const data = table.row($(this).parents('tr')).data(); + // Handle edit action + alert('Edit entry for ' + data.Name); + }); + + $('#example tbody').on('click', '.delete-btn', function() { + const data = table.row($(this).parents('tr')).data(); + // Handle delete action + alert('Delete entry for ' + data.Name); + }); + }); + </script> +</body> +</html> + \ No newline at end of file diff --git a/migrations/versions/6c7070736062_add_role_id_to_users_table.py b/migrations/versions/6c7070736062_add_role_id_to_users_table.py index 06ca58762200695b2506fcaba3b348e875d99077..b08cdd25b05f0bd104793ef65c8743f4092a4236 100644 --- a/migrations/versions/6c7070736062_add_role_id_to_users_table.py +++ b/migrations/versions/6c7070736062_add_role_id_to_users_table.py @@ -1,7 +1,7 @@ """Add role_id to users table Revision ID: 6c7070736062 -Revises: ad8ca3c3dfaa +Revises: 22de5b143d05 Create Date: 2025-01-06 20:16:19.191868 """ @@ -11,7 +11,7 @@ from sqlalchemy.dialects import mysql # revision identifiers, used by Alembic. revision = '6c7070736062' -down_revision = 'ad8ca3c3dfaa' +down_revision = '22de5b143d05' branch_labels = None depends_on = None diff --git a/migrations_backup/README b/migrations_backup/README new file mode 100644 index 0000000000000000000000000000000000000000..0e048441597444a7e2850d6d7c4ce15550f79bda --- /dev/null +++ b/migrations_backup/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations_backup/alembic.ini b/migrations_backup/alembic.ini new file mode 100644 index 0000000000000000000000000000000000000000..ec9d45c26a6bb54e833fd4e6ce2de29343894f4b --- /dev/null +++ b/migrations_backup/alembic.ini @@ -0,0 +1,50 @@ +# 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,flask_migrate + +[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 + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[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_backup/env.py b/migrations_backup/env.py new file mode 100644 index 0000000000000000000000000000000000000000..4c9709271b2ff28271b13c29bba5c50b80fea0ac --- /dev/null +++ b/migrations_backup/env.py @@ -0,0 +1,113 @@ +import logging +from logging.config import fileConfig + +from flask import current_app + +from alembic import context + +# 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') + + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except (TypeError, AttributeError): + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option('sqlalchemy.url', get_engine_url()) +target_db = current_app.extensions['migrate'].db + +# 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 get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + +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, target_metadata=get_metadata(), literal_binds=True + ) + + 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.') + + conf_args = current_app.extensions['migrate'].configure_args + if conf_args.get("process_revision_directives") is None: + conf_args["process_revision_directives"] = process_revision_directives + + connectable = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_metadata(), + **conf_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations_backup/script.py.mako b/migrations_backup/script.py.mako new file mode 100644 index 0000000000000000000000000000000000000000..2c0156303a8df3ffdc9de87765bf801bf6bea4a5 --- /dev/null +++ b/migrations_backup/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_backup/versions/22de5b143d05_create_user_roles.py b/migrations_backup/versions/22de5b143d05_create_user_roles.py new file mode 100644 index 0000000000000000000000000000000000000000..5e6a5869f16c6b4644fa9222896cee3d46486009 --- /dev/null +++ b/migrations_backup/versions/22de5b143d05_create_user_roles.py @@ -0,0 +1,34 @@ +"""Create user roles + +Revision ID: 22de5b143d05 +Revises: 9a8cc1906445 +Create Date: 2025-01-06 13:40:11.307880 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.sql import table, column + +# revision identifiers, used by Alembic. +revision = '22de5b143d05' +down_revision = '9a8cc1906445' +branch_labels = None +depends_on = None + +roles_table = table('roles', + column('id', sa.Integer), + column('name', sa.String), + column('description', sa.String) +) + +def upgrade(): + roles = [ + {'name': 'super-admin', 'description': 'Super Admin, all admin perms and can create new admins'}, + {'name': 'admin', 'description': 'Can create/delete and modify bookings'}, + {'name': 'user', 'description': 'Standard user'} + ] + + op.bulk_insert(roles_table, roles) + +def downgrade(): + op.execute('DELETE FROM roles WHERE name IN ("super-admin", "admin", "user")') diff --git a/migrations_backup/versions/489bab9aaf4f_add_listing_images_table.py b/migrations_backup/versions/489bab9aaf4f_add_listing_images_table.py new file mode 100644 index 0000000000000000000000000000000000000000..bc5cb29abb12b34fcdfa29903271b8e2f7f59e8c --- /dev/null +++ b/migrations_backup/versions/489bab9aaf4f_add_listing_images_table.py @@ -0,0 +1,26 @@ +"""Add listing images table + +Revision ID: 489bab9aaf4f +Revises: 6791cbf31235 +Create Date: 2024-11-05 11:13:50.215159 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '489bab9aaf4f' +down_revision = '6791cbf31235' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + 'listing_images', + sa.Column('id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True), + sa.Column('listing_id', sa.Integer(), nullable=False), + sa.Column('image_location', sa.String(255), nullable=False), + sa.Column('image_description', sa.String(255), nullable=True) + ) diff --git a/migrations_backup/versions/6791cbf31235_add_listing_table.py b/migrations_backup/versions/6791cbf31235_add_listing_table.py new file mode 100644 index 0000000000000000000000000000000000000000..cb5beb74f5b8be4595388be5d89f65568bc1d345 --- /dev/null +++ b/migrations_backup/versions/6791cbf31235_add_listing_table.py @@ -0,0 +1,31 @@ +"""Add listing table + +Revision ID: 6791cbf31235 +Revises: ac9d4555724d +Create Date: 2024-11-05 10:36:32.872815 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6791cbf31235' +down_revision = 'ac9d4555724d' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + 'listings', + sa.Column('id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True), + sa.Column('depart_location', sa.String(255), nullable=False), + sa.Column('depart_time', sa.DateTime(), nullable=False), + sa.Column('destination_location', sa.String(255), nullable=False), + sa.Column('destination_time', sa.DateTime(), nullable=False), + sa.Column('fair_cost', sa.Float(2), nullable=False), + sa.Column('transport_type', sa.String(255), nullable=False), + sa.Column('business_tickets', sa.Integer(), nullable=False), + sa.Column('economy_tickets', sa.Integer(), nullable=False) + ) \ No newline at end of file diff --git a/migrations_backup/versions/68d89ef13132_add_main_listing_image_column.py b/migrations_backup/versions/68d89ef13132_add_main_listing_image_column.py new file mode 100644 index 0000000000000000000000000000000000000000..9ccafd816ab4ff5c783ae3c73477b67d5737efa0 --- /dev/null +++ b/migrations_backup/versions/68d89ef13132_add_main_listing_image_column.py @@ -0,0 +1,23 @@ +"""Add main Listing Image Column + +Revision ID: 68d89ef13132 +Revises: 489bab9aaf4f +Create Date: 2024-11-29 10:29:38.126811 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '68d89ef13132' +down_revision = '489bab9aaf4f' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column( + 'listing_images', + sa.Column('main_image', sa.Boolean(), nullable=False, server_default=sa.sql.expression.false()) + ) \ No newline at end of file diff --git a/migrations_backup/versions/6c7070736062_add_role_id_to_users_table.py b/migrations_backup/versions/6c7070736062_add_role_id_to_users_table.py new file mode 100644 index 0000000000000000000000000000000000000000..b08cdd25b05f0bd104793ef65c8743f4092a4236 --- /dev/null +++ b/migrations_backup/versions/6c7070736062_add_role_id_to_users_table.py @@ -0,0 +1,26 @@ +"""Add role_id to users table + +Revision ID: 6c7070736062 +Revises: 22de5b143d05 +Create Date: 2025-01-06 20:16:19.191868 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = '6c7070736062' +down_revision = '22de5b143d05' +branch_labels = None +depends_on = None + +def upgrade(): + # Add column role_id to users table + op.add_column('users', sa.Column('role_id', sa.Integer(), nullable=True)) + op.create_foreign_key(None, 'users', 'roles', ['role_id'], ['id']) + +def downgrade(): + # Remove column role_id from users table + op.drop_constraint(None, 'users', type_='foreignkey') + op.drop_column('users', 'role_id') diff --git a/migrations_backup/versions/9a8cc1906445_add_fs_uniquifier_field_to_user_model.py b/migrations_backup/versions/9a8cc1906445_add_fs_uniquifier_field_to_user_model.py new file mode 100644 index 0000000000000000000000000000000000000000..7bacfee528a6aff984243944fedccc2f36974923 --- /dev/null +++ b/migrations_backup/versions/9a8cc1906445_add_fs_uniquifier_field_to_user_model.py @@ -0,0 +1,89 @@ +"""Add fs_uniquifier field to User model + +Revision ID: 9a8cc1906445 +Revises: 68d89ef13132 +Create Date: 2025-01-06 12:52:57.272220 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql +import os + +# revision identifiers, used by Alembic. +revision = '9a8cc1906445' +down_revision = '68d89ef13132' +branch_labels = None +depends_on = None + +def column_exists(table_name, column_name): + inspector = sa.inspect(op.get_bind()) + return column_name in [col['name'] for col in inspector.get_columns(table_name)] + +def index_exists(table_name, index_name): + inspector = sa.inspect(op.get_bind()) + indexes = inspector.get_indexes(table_name) + return any(index['name'] == index_name for index in indexes) + +def upgrade(): + # Conditionally create roles table + if not op.get_bind().dialect.has_table(op.get_bind(), "roles"): + op.create_table('roles', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=80), nullable=True), + sa.Column('description', sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + + # Conditionally create roles_users table + if not op.get_bind().dialect.has_table(op.get_bind(), "roles_users"): + op.create_table('roles_users', + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('role_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['role_id'], ['roles.id']), + sa.ForeignKeyConstraint(['user_id'], ['users.id']) + ) + + with op.batch_alter_table('listing_images', schema=None) as batch_op: + batch_op.alter_column('main_image', + existing_type=mysql.TINYINT(display_width=1), + type_=sa.SmallInteger(), + existing_nullable=False, + existing_server_default=sa.text("'0'")) + + # Assign unique values to fs_uniquifier for existing users before adding the unique constraint + conn = op.get_bind() + users = conn.execute(sa.text("SELECT id FROM users WHERE fs_uniquifier IS NULL OR fs_uniquifier = ''")).fetchall() + for user in users: + conn.execute(sa.text("UPDATE users SET fs_uniquifier = :fs_uniquifier WHERE id = :id"), {'fs_uniquifier': os.urandom(32).hex(), 'id': user.id}) + + with op.batch_alter_table('users', schema=None) as batch_op: + if index_exists('users', 'api_token'): + batch_op.drop_index('api_token') + batch_op.create_unique_constraint(None, ['fs_uniquifier']) + if column_exists('users', 'token_expiry'): + batch_op.drop_column('token_expiry') + if column_exists('users', 'api_token'): + batch_op.drop_column('api_token') + if column_exists('users', 'role_id'): + batch_op.drop_column('role_id') + +def downgrade(): + with op.batch_alter_table('users', schema=None) as batch_op: + batch_op.add_column(sa.Column('role_id', mysql.SMALLINT(), server_default=sa.text("'1'"), autoincrement=False, nullable=False)) + batch_op.add_column(sa.Column('api_token', mysql.VARCHAR(length=255), nullable=True)) + batch_op.add_column(sa.Column('token_expiry', mysql.DATETIME(), nullable=True)) + batch_op.drop_constraint(None, type_='unique') + batch_op.create_index('api_token', ['api_token'], unique=True) + batch_op.drop_column('fs_uniquifier') + + with op.batch_alter_table('listing_images', schema=None) as batch_op: + batch_op.alter_column('main_image', + existing_type=sa.SmallInteger(), + type_=mysql.TINYINT(display_width=1), + existing_nullable=False, + existing_server_default=sa.text("'0'")) + + op.drop_table('roles_users') + op.drop_table('roles') diff --git a/migrations_backup/versions/ac9d4555724d_add_api_token_and_expiry.py b/migrations_backup/versions/ac9d4555724d_add_api_token_and_expiry.py new file mode 100644 index 0000000000000000000000000000000000000000..59f4c9a590293c9ba96b7d83bef1228cdce3a182 --- /dev/null +++ b/migrations_backup/versions/ac9d4555724d_add_api_token_and_expiry.py @@ -0,0 +1,27 @@ +"""Add api token and expiry + +Revision ID: ac9d4555724d +Revises: +Create Date: 2024-11-01 10:56:05.827705 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'ac9d4555724d' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + 'users', + sa.Column('id', sa.Integer(), nullable=False, autoincrement=True, primary_key=True), + sa.Column('username', sa.String(255), nullable=False, unique=True), + sa.Column('email', sa.String(255), nullable=False, unique=True), + sa.Column('password', sa.String(255), nullable=False), + sa.Column('role_id', sa.SmallInteger(), nullable=False, server_default='3') + ) \ No newline at end of file