admin: Fix admin page
This commit is contained in:
parent
036ca7b21e
commit
72624a3f4e
9 changed files with 333 additions and 23 deletions
|
@ -3,12 +3,14 @@ from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_migrate import Migrate
|
from flask_migrate import Migrate
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
from flask_assets import Bundle, Environment
|
from flask_assets import Bundle, Environment
|
||||||
|
from flask_cors import CORS
|
||||||
|
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
migrate = Migrate()
|
migrate = Migrate()
|
||||||
login = LoginManager()
|
login = LoginManager()
|
||||||
environment = Environment()
|
environment = Environment()
|
||||||
|
cors = CORS()
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
@ -19,6 +21,7 @@ def create_app():
|
||||||
migrate.init_app(app, db)
|
migrate.init_app(app, db)
|
||||||
login.init_app(app)
|
login.init_app(app)
|
||||||
environment.init_app(app)
|
environment.init_app(app)
|
||||||
|
cors.init_app(app)
|
||||||
|
|
||||||
scss = Bundle('css/style.scss', filters='scss',
|
scss = Bundle('css/style.scss', filters='scss',
|
||||||
output='css/style.css')
|
output='css/style.css')
|
||||||
|
@ -28,8 +31,10 @@ def create_app():
|
||||||
|
|
||||||
from . import registration
|
from . import registration
|
||||||
from . import dashboard
|
from . import dashboard
|
||||||
|
from . import admin
|
||||||
|
|
||||||
app.register_blueprint(registration.bp)
|
app.register_blueprint(registration.bp)
|
||||||
app.register_blueprint(dashboard.bp)
|
app.register_blueprint(dashboard.bp)
|
||||||
|
app.register_blueprint(admin.bp)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
182
goathacks/admin/__init__.py
Normal file
182
goathacks/admin/__init__.py
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
from flask import Blueprint, jsonify, redirect, render_template, url_for
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
|
from goathacks.models import User
|
||||||
|
|
||||||
|
bp = Blueprint("admin", __name__, url_prefix="/admin")
|
||||||
|
|
||||||
|
from goathacks import db
|
||||||
|
|
||||||
|
@bp.route("/")
|
||||||
|
@login_required
|
||||||
|
def home():
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
male_count = 0
|
||||||
|
female_count = 0
|
||||||
|
nb_count = 0
|
||||||
|
check_in_count = 0
|
||||||
|
waitlist_count = 0
|
||||||
|
total_count = 0
|
||||||
|
shirt_count = {'XS': 0, 'S': 0, 'M': 0, 'L': 0, 'XL': 0}
|
||||||
|
hackers = db.session.execute(db.select(User)).scalars().all()
|
||||||
|
schools = {}
|
||||||
|
|
||||||
|
for h in hackers:
|
||||||
|
if h.waitlisted:
|
||||||
|
waitlist_count += 1
|
||||||
|
|
||||||
|
if h.checked_in:
|
||||||
|
check_in_count += 1
|
||||||
|
|
||||||
|
if h.gender == 'F':
|
||||||
|
female_count += 1
|
||||||
|
elif h.gender == 'M':
|
||||||
|
male_count += 1
|
||||||
|
else:
|
||||||
|
nb_count += 1
|
||||||
|
|
||||||
|
total_count += 1
|
||||||
|
|
||||||
|
if h.school not in schools:
|
||||||
|
schools[h.school] = 1
|
||||||
|
else:
|
||||||
|
schools[h.school] += 1
|
||||||
|
|
||||||
|
if h.shirt_size not in shirt_count:
|
||||||
|
shirt_count[h.shirt_size] = 1
|
||||||
|
else:
|
||||||
|
shirt_count[h.shirt_size] += 1
|
||||||
|
return render_template("admin.html", waitlist_count=waitlist_count,
|
||||||
|
total_count=total_count, shirt_count=shirt_count,
|
||||||
|
hackers=hackers, male_count=male_count,
|
||||||
|
female_count=female_count, nb_count=nb_count,
|
||||||
|
check_in_count=check_in_count, schools=schools)
|
||||||
|
|
||||||
|
@bp.route("/check_in/<int:id>")
|
||||||
|
@login_required
|
||||||
|
def check_in(id):
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
user = User.query.filter_by(id=id).one()
|
||||||
|
if user is None:
|
||||||
|
return {"status": "error", "msg": "No user found"}
|
||||||
|
user.checked_in = True
|
||||||
|
db.session.commit()
|
||||||
|
return {"status": "success"}
|
||||||
|
|
||||||
|
@bp.route("/drop/<int:id>")
|
||||||
|
@login_required
|
||||||
|
def drop(id):
|
||||||
|
if not current_user.is_admin and not current_user.id == id:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
user = User.query.filter_by(id=id).one()
|
||||||
|
if user is None:
|
||||||
|
return {"status": "error", "msg": "user not found"}
|
||||||
|
|
||||||
|
if user.checked_in:
|
||||||
|
return {"status": "error", "msg": "Hacker is already checked in"}
|
||||||
|
|
||||||
|
db.session.delete(user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return {"status": "success"}
|
||||||
|
|
||||||
|
@bp.route("/change_admin/<int:id>/<string:action>")
|
||||||
|
@login_required
|
||||||
|
def change_admin(id, action):
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
user = User.query.filter_by(id=id).one()
|
||||||
|
if user is None:
|
||||||
|
return {"status": "error", "msg": "user not found"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
valid_actions = ['promote', 'demote']
|
||||||
|
if action not in valid_actions:
|
||||||
|
return {"status": "error", "msg": "invalid action"}
|
||||||
|
|
||||||
|
if action == "promote":
|
||||||
|
user.is_admin = True
|
||||||
|
else:
|
||||||
|
user.is_admin = False
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return {"status": "success"}
|
||||||
|
|
||||||
|
@bp.route("/promote_from_waitlist/<int:id>")
|
||||||
|
@login_required
|
||||||
|
def promote_waitlist(id):
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
user = User.query.filter_by(id=id).one()
|
||||||
|
if user is None:
|
||||||
|
return {"status": "error", "msg": "user not found"}
|
||||||
|
|
||||||
|
user.waitlisted = False
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return {"status": "success"}
|
||||||
|
|
||||||
|
@bp.route("/hackers.csv")
|
||||||
|
@login_required
|
||||||
|
def hackers_csv():
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
users = User.query.all()
|
||||||
|
return json_to_csv(User.create_json_output(users))
|
||||||
|
|
||||||
|
@bp.route("/hackers")
|
||||||
|
@login_required
|
||||||
|
def hackers():
|
||||||
|
if not current_user.is_admin:
|
||||||
|
return redirect(url_for("dashboard.home"))
|
||||||
|
|
||||||
|
users = User.query.all()
|
||||||
|
return User.create_json_output(users)
|
||||||
|
|
||||||
|
import json
|
||||||
|
import csv
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
def json_to_csv(data):
|
||||||
|
# Opening JSON file and loading the data
|
||||||
|
# into the variable data
|
||||||
|
|
||||||
|
json_data=[]
|
||||||
|
if(type(data) is json):
|
||||||
|
json_data=data
|
||||||
|
elif(type(data) is str):
|
||||||
|
json_data=json.loads(data)
|
||||||
|
else:
|
||||||
|
json_data = json.loads(json.dumps(data))
|
||||||
|
# now we will open a file for writing
|
||||||
|
csv_out = StringIO("")
|
||||||
|
|
||||||
|
# create the csv writer object
|
||||||
|
csv_writer = csv.writer(csv_out)
|
||||||
|
|
||||||
|
# Counter variable used for writing
|
||||||
|
# headers to the CSV file
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
for e in json_data:
|
||||||
|
if count == 0:
|
||||||
|
|
||||||
|
# Writing headers of CSV file
|
||||||
|
header = e.keys()
|
||||||
|
csv_writer.writerow(header)
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
# Writing data of CSV file
|
||||||
|
csv_writer.writerow(e.values())
|
||||||
|
csv_out.seek(0)
|
||||||
|
return csv_out.read()
|
|
@ -17,6 +17,29 @@ class User(db.Model, UserMixin):
|
||||||
shirt_size = Column(String, nullable=True)
|
shirt_size = Column(String, nullable=True)
|
||||||
accomodations = Column(String, nullable=True)
|
accomodations = Column(String, nullable=True)
|
||||||
checked_in = Column(Boolean, nullable=False, default=False)
|
checked_in = Column(Boolean, nullable=False, default=False)
|
||||||
|
school = Column(String, nullable=True)
|
||||||
|
phone = Column(String, nullable=True)
|
||||||
|
gender = Column(String, nullable=True)
|
||||||
|
|
||||||
|
def create_json_output(lis):
|
||||||
|
hackers = []
|
||||||
|
|
||||||
|
for u in lis:
|
||||||
|
hackers.append({
|
||||||
|
'checked_in': u.checked_in,
|
||||||
|
'waitlisted': u.waitlisted,
|
||||||
|
'admin': u.is_admin,
|
||||||
|
'id': u.id,
|
||||||
|
'email': u.email,
|
||||||
|
'first_name': u.first_name,
|
||||||
|
'last_name': u.last_name,
|
||||||
|
'phone_number': u.phone,
|
||||||
|
'shirt_size': u.shirt_size,
|
||||||
|
'special_needs': u.accomodations,
|
||||||
|
'school': u.school
|
||||||
|
})
|
||||||
|
|
||||||
|
return hackers
|
||||||
|
|
||||||
|
|
||||||
@login.user_loader
|
@login.user_loader
|
||||||
|
|
|
@ -82,7 +82,7 @@ const promoteFromWaitlist = (id) => {
|
||||||
confirmButtonText: 'Yes, promote!',
|
confirmButtonText: 'Yes, promote!',
|
||||||
confirmButtonColor: successColor
|
confirmButtonColor: successColor
|
||||||
}, () => {
|
}, () => {
|
||||||
$.get('/promote_from_waitlist?mlh_id=' + id, (data) => {
|
$.get('/admin/promote_from_waitlist/' + id, (data) => {
|
||||||
let title = ''
|
let title = ''
|
||||||
let msg = ''
|
let msg = ''
|
||||||
let type = ''
|
let type = ''
|
||||||
|
@ -110,7 +110,7 @@ const changeAdmin = (id, action) => {
|
||||||
confirmButtonText: 'Yes, ' + action + '!',
|
confirmButtonText: 'Yes, ' + action + '!',
|
||||||
confirmButtonColor: errColor
|
confirmButtonColor: errColor
|
||||||
}, () => {
|
}, () => {
|
||||||
$.get('/change_admin?mlh_id=' + id + '&action=' + action, (data) => {
|
$.get('/admin/change_admin/' + id + '/' + action, (data) => {
|
||||||
let title = ''
|
let title = ''
|
||||||
let msg = ''
|
let msg = ''
|
||||||
let type = ''
|
let type = ''
|
||||||
|
@ -138,7 +138,7 @@ const drop = (id) => {
|
||||||
confirmButtonText: 'Yes, drop!',
|
confirmButtonText: 'Yes, drop!',
|
||||||
confirmButtonColor: errColor
|
confirmButtonColor: errColor
|
||||||
}, () => {
|
}, () => {
|
||||||
$.get('/drop?mlh_id=' + id, (data) => {
|
$.get('/admin/drop/' + id, (data) => {
|
||||||
let title = ''
|
let title = ''
|
||||||
let msg = ''
|
let msg = ''
|
||||||
let type = ''
|
let type = ''
|
||||||
|
@ -166,7 +166,7 @@ const checkIn = (id) => {
|
||||||
confirmButtonText: 'Yes, check in!',
|
confirmButtonText: 'Yes, check in!',
|
||||||
confirmButtonColor: successColor
|
confirmButtonColor: successColor
|
||||||
}, () => {
|
}, () => {
|
||||||
$.get('/check_in?mlh_id=' + id, (data) => {
|
$.get('/admin/check_in/' + id, (data) => {
|
||||||
let title = ''
|
let title = ''
|
||||||
let msg = ''
|
let msg = ''
|
||||||
let type = ''
|
let type = ''
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
|
||||||
<script src="../static/js/admin.js"></script>
|
<script src="{{url_for('static', filename='js/admin.js')}}"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -43,7 +43,8 @@
|
||||||
<h5>JSON object of users from MLH (Including dropped applications):</h5>
|
<h5>JSON object of users from MLH (Including dropped applications):</h5>
|
||||||
<p><a href="{{ mlh_url }}"><b>Do NOT share this URL.</b></a></p>
|
<p><a href="{{ mlh_url }}"><b>Do NOT share this URL.</b></a></p>
|
||||||
<h5>Get registered hackers only:</h5>
|
<h5>Get registered hackers only:</h5>
|
||||||
<p><a href="/hackers">JSON</a> <a href="/hackers.csv">CSV</a></p>
|
<p><a href="{{url_for('admin.hackers')}}">JSON</a> <a
|
||||||
|
href="{{url_for('admin.hackers_csv')}}">CSV</a></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
|
@ -81,10 +82,12 @@
|
||||||
<script>
|
<script>
|
||||||
let schoolNames = []
|
let schoolNames = []
|
||||||
let schoolNums = []
|
let schoolNums = []
|
||||||
|
console.log("{{schools}}")
|
||||||
|
|
||||||
{% for school in schools %}
|
{% for school in schools %}
|
||||||
schoolNames.push('{{ school }}')
|
schoolNames.push('{{ school }}')
|
||||||
schoolNums.push({{ schools[school] }})
|
schoolNums.push({{ schools[school] }})
|
||||||
|
console.log("{{school}}")
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
let schoolCtx = document.getElementById('schoolCanvas')
|
let schoolCtx = document.getElementById('schoolCanvas')
|
||||||
|
@ -186,13 +189,13 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ shirt_count['xxs'] }}</td>
|
<td>{{ shirt_count['XXS'] }}</td>
|
||||||
<td>{{ shirt_count['xs'] }}</td>
|
<td>{{ shirt_count['XS'] }}</td>
|
||||||
<td>{{ shirt_count['s'] }}</td>
|
<td>{{ shirt_count['S'] }}</td>
|
||||||
<td>{{ shirt_count['m'] }}</td>
|
<td>{{ shirt_count['M'] }}</td>
|
||||||
<td>{{ shirt_count['l'] }}</td>
|
<td>{{ shirt_count['L'] }}</td>
|
||||||
<td>{{ shirt_count['xl'] }}</td>
|
<td>{{ shirt_count['XL'] }}</td>
|
||||||
<td>{{ shirt_count['xxl'] }}</td>
|
<td>{{ shirt_count['XXL'] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -217,27 +220,27 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for hacker in hackers %}
|
{% for hacker in hackers %}
|
||||||
<tr id="{{ hacker['id'] }}-row">
|
<tr id="{{ hacker.id }}-row">
|
||||||
<td>
|
<td>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<a href="#" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"><span
|
<a href="#" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"><span
|
||||||
class="caret"></span></a>
|
class="caret"></span></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{% if not hacker['checked_in'] %}
|
{% if not hacker.checked_in %}
|
||||||
<li><a class="check_in" id="{{ hacker['id'] }}-check_in" href="#">Check In</a>
|
<li><a class="check_in" id="{{ hacker.id }}-check_in" href="#">Check In</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if hacker['waitlisted'] and not hacker['checked_in'] %}
|
{% if hacker.waitlisted and not hacker.checked_in %}
|
||||||
<li><a class="promote_from_waitlist"
|
<li><a class="promote_from_waitlist"
|
||||||
id="{{ hacker['id'] }}-promote_from_waitlist"
|
id="{{ hacker['id'] }}-promote_from_waitlist"
|
||||||
href="#">Promote From Waitlist</a></li>
|
href="#">Promote From Waitlist</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
{% if not hacker['checked_in'] %}
|
{% if not hacker.checked_in %}
|
||||||
<li><a class="drop" id="{{ hacker['id'] }}-drop" href="#">Drop Application</a>
|
<li><a class="drop" id="{{ hacker['id'] }}-drop" href="#">Drop Application</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if hacker['admin'] %}
|
{% if hacker.is_admin %}
|
||||||
<li><a class="demote_admin" id="{{ hacker['id'] }}-demote_admin" href="#">Demote
|
<li><a class="demote_admin" id="{{ hacker['id'] }}-demote_admin" href="#">Demote
|
||||||
Admin</a>
|
Admin</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -251,15 +254,15 @@
|
||||||
</td>
|
</td>
|
||||||
<td id="{{ hacker['id'] }}-checked_in">{{ hacker['checked_in'] }}</td>
|
<td id="{{ hacker['id'] }}-checked_in">{{ hacker['checked_in'] }}</td>
|
||||||
<td id="{{ hacker['id'] }}-waitlisted">{{ hacker['waitlisted'] }}</td>
|
<td id="{{ hacker['id'] }}-waitlisted">{{ hacker['waitlisted'] }}</td>
|
||||||
<td>{{ hacker['admin'] }}</td>
|
<td>{{ hacker.is_admin }}</td>
|
||||||
<td>{{ hacker['id'] }}</td>
|
<td>{{ hacker['id'] }}</td>
|
||||||
<td>{{ hacker['registration_time'] }}</td>
|
<td>{{ hacker['registration_time'] }}</td>
|
||||||
<td>{{ hacker['email'] }}</td>
|
<td>{{ hacker['email'] }}</td>
|
||||||
<td>{{ hacker['first_name'] + ' ' + hacker['last_name'] }}</td>
|
<td>{{ hacker['first_name'] + ' ' + hacker['last_name'] }}</td>
|
||||||
<td>{{ hacker['phone_number'] }}</td>
|
<td>{{ hacker['phone'] }}</td>
|
||||||
<td>{{ hacker['shirt_size'] }}</td>
|
<td>{{ hacker['shirt_size'] }}</td>
|
||||||
<td>{{ hacker['special_needs'] }}</td>
|
<td>{{ hacker['accomodations'] }}</td>
|
||||||
<td>{{ hacker['school']['name'] }}</td>
|
<td>{{ hacker['school'] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
32
migrations/versions/311c62fe5f49_.py
Normal file
32
migrations/versions/311c62fe5f49_.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 311c62fe5f49
|
||||||
|
Revises: 3f427be4ce8a
|
||||||
|
Create Date: 2022-12-06 11:08:18.571528
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '311c62fe5f49'
|
||||||
|
down_revision = '3f427be4ce8a'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('school', sa.String(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('school')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
32
migrations/versions/8d6ae751ec62_.py
Normal file
32
migrations/versions/8d6ae751ec62_.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 8d6ae751ec62
|
||||||
|
Revises: 311c62fe5f49
|
||||||
|
Create Date: 2022-12-06 11:08:55.896919
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '8d6ae751ec62'
|
||||||
|
down_revision = '311c62fe5f49'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('phone', sa.String(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('phone')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
32
migrations/versions/a14a95ec57b0_.py
Normal file
32
migrations/versions/a14a95ec57b0_.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: a14a95ec57b0
|
||||||
|
Revises: 8d6ae751ec62
|
||||||
|
Create Date: 2022-12-06 11:12:30.581556
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'a14a95ec57b0'
|
||||||
|
down_revision = '8d6ae751ec62'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('gender', sa.String(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('user', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('gender')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
|
@ -2,6 +2,7 @@ alembic==1.8.1
|
||||||
click==8.1.3
|
click==8.1.3
|
||||||
Flask==2.2.2
|
Flask==2.2.2
|
||||||
Flask-Assets
|
Flask-Assets
|
||||||
|
Flask-CORS
|
||||||
Flask-Login==0.6.2
|
Flask-Login==0.6.2
|
||||||
Flask-Migrate==4.0.0
|
Flask-Migrate==4.0.0
|
||||||
Flask-SQLAlchemy==3.0.2
|
Flask-SQLAlchemy==3.0.2
|
||||||
|
|
Loading…
Add table
Reference in a new issue