M .build.yml => .build.yml +20 -17
@@ 1,17 1,20 @@
-image: alpine/latest
-
-packages:
- - python3
- - py3-pip
-
-sources:
- - https://git.sr.ht/~muirrum/comortas
-
-tasks:
- - setup: |
- cd comortas
- pip install -r requirements.txt
- pip install -r dev-requirements.txt
- - lint: |
- cd comortas
- black --check --diff fllscoring/
+image: alpine/latest
+packages:
+ - python3
+ - python3-dev
+ - py3-pip
+ - postgresql
+ - postgresql-dev
+ - gcc
+ - musl
+sources:
+ - https://git.sr.ht/~muirrum/comortas
+tasks:
+ - setup: |
+ cd comortas
+ pip install -r requirements.txt
+ pip install -r dev-requirements.txt
+ - lint: |
+ cd comortas
+ export PATH=$PATH:$HOME/.local/bin
+ black --check --diff fllscoring/<
\ No newline at end of file
M fllscoring/__init__.py => fllscoring/__init__.py +9 -5
@@ 18,15 18,19 @@ def create_app():
app = Flask(__name__)
app.config.from_mapping(
- SQLALCHEMY_DATABASE_URI='postgresql://fllscoring:fllscoring@localhost/fllscoring',
- SECRET_KEY='badkey'
+ SQLALCHEMY_DATABASE_URI="postgresql://fllscoring:fllscoring@localhost/fllscoring",
+ SECRET_KEY="badkey",
+ SECURITY_LOGIN_URL="/auth/login",
+ SECURITY_LOGOUT_URL="/auth/logout",
+ SECURITY_REGISTER_URL="/auth/register",
+ SECURITY_POST_LOGIN_VIEW="/profile",
)
- app.config.from_object('fllscoring.config')
+ app.config.from_object("fllscoring.config")
# Health check
- @app.route('/hello')
+ @app.route("/hello")
def hello():
- return 'Hello!'
+ return "Hello!"
# Init extensions
db.init_app(app)
M fllscoring/auth/__init__.py => fllscoring/auth/__init__.py +30 -11
@@ 4,16 4,24 @@ from werkzeug.security import generate_password_hash, check_password_hash
import flask_login
from flask_login import current_user
-from flask_principal import identity_changed, Identity, AnonymousIdentity, identity_loaded, RoleNeed, UserNeed
+from flask_principal import (
+ identity_changed,
+ Identity,
+ AnonymousIdentity,
+ identity_loaded,
+ RoleNeed,
+ UserNeed,
+)
from fllscoring import login_manager, db
from fllscoring.models import Users
from fllscoring.auth import forms
-bp = Blueprint('auth', __name__, url_prefix="/auth")
+bp = Blueprint("auth", __name__, url_prefix="/auth")
-@bp.route('/login', methods=["GET", "POST"])
+
+@bp.route("/login", methods=["GET", "POST"])
def login():
form = forms.LoginForm()
@@ 23,34 31,45 @@ def login():
if check_password_hash(user.password_hash, form.password.data):
flask_login.login_user(user)
- identity_changed.send(current_app._get_current_object(), identity=Identity(user.user_id))
+ identity_changed.send(
+ current_app._get_current_object(), identity=Identity(user.id)
+ )
return redirect(url_for("profile.profile"))
else:
flash("Incorrect password")
else:
flash("Incorrect username")
- return render_template('auth/login.html', form=form, title="Login")
+ return render_template("auth/login.html", form=form, title="Login")
+
-@bp.route('/register', methods=["GET", "POST"])
+@bp.route("/register", methods=["GET", "POST"])
def register():
form = forms.RegisterForm()
if form.validate_on_submit():
- user = Users(username=form.username.data, email=form.email.data, password_hash=generate_password_hash(form.password.data))
+ user = Users(
+ username=form.username.data,
+ email=form.email.data,
+ password_hash=generate_password_hash(form.password.data),
+ )
db.session.add(user)
db.session.commit()
flask_login.login_user(user)
- identity_changed.send(current_app._get_current_object(), identity=Identity(user.user_id))
- return redirect(url_for('profile.profile'))
+ identity_changed.send(
+ current_app._get_current_object(), identity=Identity(user.id)
+ )
+ return redirect(url_for("profile.profile"))
- return render_template('auth/register.html', form=form, title="Register")
+ return render_template("auth/register.html", form=form, title="Register")
-@bp.route('/logout')
+
+@bp.route("/logout")
def logout():
flask_login.logout_user()
identity_changed.send(current_app._get_current_object(), AnonymousIdentity())
return redirect(url_for("meta.home"))
+
@login_manager.user_loader
def user_loader(user_id):
return Users.query.filter_by(id=user_id).first()
M fllscoring/auth/forms.py => fllscoring/auth/forms.py +6 -2
@@ 2,12 2,16 @@ from flask_wtf import FlaskForm
from wtforms.fields import StringField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo
+
class RegisterForm(FlaskForm):
username = StringField(label="Username", validators=[DataRequired()])
email = StringField(label="Email Address", validators=[DataRequired(), Email()])
password = PasswordField(label="Password", validators=[DataRequired()])
- password_confirm = PasswordField(label="Confirm Password", validators=[DataRequired(), EqualTo("password")])
+ password_confirm = PasswordField(
+ label="Confirm Password", validators=[DataRequired(), EqualTo("password")]
+ )
+
class LoginForm(FlaskForm):
username = StringField(label="Username", validators=[DataRequired()])
- password = PasswordField(label="Password", validators=[DataRequired()])>
\ No newline at end of file
+ password = PasswordField(label="Password", validators=[DataRequired()])
A fllscoring/auth/permissions.py => fllscoring/auth/permissions.py +30 -0
@@ 0,0 1,30 @@
+from collections import namedtuple
+from functools import partial
+
+from flask import current_app as app
+
+from flask_login import current_user
+from flask_principal import identity_loaded, Permission, RoleNeed, UserNeed
+
+
+TournamentNeed = namedtuple("tournament", ["method", "value"])
+ViewTournamentNeed = partial(TournamentNeed, "view")
+
+
+class ViewTournamentPermission(Permission):
+ def __init__(self, tournament_id):
+ need = ViewTournamentNeed(tournament_id)
+ super(ViewTournamentPermission, self).__init__(need)
+
+
+@identity_loaded.connect_via(app)
+def on_identity_loaded(sender, identity):
+ identity.user = current_user
+ if hasattr(current_user, "id"):
+ identity.provides.add(UserNeed(current_user.id))
+ if hasattr(current_user, "roles"):
+ for role in current_user.roles:
+ identity.provides.add(RoleNeed(role.name))
+ if hasattr(current_user, "tournaments"):
+ for tournament in tournaments:
+ identity.provides.add(TournamentNeed(tournament.id))
M fllscoring/config.example.py => fllscoring/config.example.py +2 -2
@@ 1,2 1,2 @@
-SQLALCHEMY_DATABASE_URI="postgresql://fllscoring:fllscoring@localhost/fllscoring"
-SECRET_KEY='badkey'>
\ No newline at end of file
+SQLALCHEMY_DATABASE_URI = "postgresql://fllscoring:fllscoring@localhost/fllscoring"
+SECRET_KEY = "badkey"
M fllscoring/meta/__init__.py => fllscoring/meta/__init__.py +7 -5
@@ 1,11 1,13 @@
from flask import Blueprint, render_template
-bp = Blueprint('meta', __name__)
+bp = Blueprint("meta", __name__)
-@bp.route('/')
+
+@bp.route("/")
def home():
- return render_template('home.html')
+ return render_template("home.html")
+
-@bp.route('/about')
+@bp.route("/about")
def about():
- return render_template('about.html')>
\ No newline at end of file
+ return render_template("about.html")
M fllscoring/models.py => fllscoring/models.py +19 -4
@@ 22,26 22,41 @@ class Users(db.Model, UserMixin):
def get_id(self):
return self.id
+
class Role(RoleMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
description = db.Column(db.String)
+
class Tournaments(db.Model):
id = db.Column(db.Integer, primary_key=True)
owner_id = db.Column(db.Integer, ForeignKey("users.id"), nullable=False, index=True)
tournament_name = db.Column(db.String, nullable=False)
+
class Team(db.Model):
id = db.Column(db.Integer, primary_key=True)
- tournament = db.Column(db.Integer, ForeignKey("tournaments.id", ondelete="CASCADE", onupdate="CASCADE"), nullable=False, index=True)
- team_number = db.Column(db.Integer, nullable=False, unique=True, info={'label': 'Team Number'})
- team_name = db.Column(db.String, nullable=False, default="No Name", info={'label': 'Team Name'})
- team_town = db.Column(db.String, nullable=False, default="No Town", info={'label': 'Team Town'})
+ tournament = db.Column(
+ db.Integer,
+ ForeignKey("tournaments.id", ondelete="CASCADE", onupdate="CASCADE"),
+ nullable=False,
+ index=True,
+ )
+ team_number = db.Column(
+ db.Integer, nullable=False, unique=True, info={"label": "Team Number"}
+ )
+ team_name = db.Column(
+ db.String, nullable=False, default="No Name", info={"label": "Team Name"}
+ )
+ team_town = db.Column(
+ db.String, nullable=False, default="No Town", info={"label": "Team Town"}
+ )
def __str__(self):
return f"{self.team_number} - {self.team_name}"
+
class MatchScore(db.Model):
id = db.Column(db.Integer, primary_key=True)
tournament = db.Column(db.Integer, ForeignKey("tournaments.id", ondelete="CASCADE"))
M fllscoring/profile/__init__.py => fllscoring/profile/__init__.py +4 -3
@@ 2,9 2,10 @@ from flask import Blueprint, render_template
from flask_login import login_required
-bp = Blueprint('profile', __name__, url_prefix="/profile")
+bp = Blueprint("profile", __name__, url_prefix="/profile")
-@bp.route('/')
+
+@bp.route("/")
@login_required
def profile():
- return render_template("profile/profile.html", title="Profile")>
\ No newline at end of file
+ return render_template("profile/profile.html", title="Profile")
M fllscoring/scoring/__init__.py => fllscoring/scoring/__init__.py +33 -4
@@ 1,14 1,43 @@
-from flask import Blueprint
+from flask import Blueprint, render_template, abort
-from flask_login import login_required
+from flask_login import login_required, current_user
+
+from fllscoring.models import MatchScore, Tournaments, Team
bp = Blueprint("scoring", __name__, url_prefix="/scoring")
-@bp.route('/home')
+
+@bp.route("/home")
@login_required
def home():
+ tournaments = Tournaments.query.filter_by(owner_id=current_user.user_id).all()
+ return render_template(
+ "scoring/home.html", title="Scoring home", tournaments=tournaments
+ )
+
+
+@bp.route("/tournament/<int:tournament_id>")
+@login_required
+def tournament_home(tournament_id):
+ tournament = Tournaments.query.filter_by(id=tournament_id).first()
+ if tournament is None:
+ abort(404)
+ if tournament.owner_id is not current_user.user_id:
+ abort(403)
+
+ mscores = MatchScore.query.filter_by(tournament=tournament.id).all()
+ teams = Team.query.filter_by(tournament=tournament_id).all()
+ return render_template(
+ "scoring/tournament_home.html", title="Scoring", mscores=mscores, teams=teams
+ )
+
+
+@bp.route("/tournament/new_score", methods=["GET", "POST"])
+@login_required
+def new_score():
return "TODO"
+
@bp.route("/leaderboard_home")
def leaderboard_home():
- return "TODO">
\ No newline at end of file
+ return "TODO"
A fllscoring/scoring/forms.py => fllscoring/scoring/forms.py +3 -0
@@ 0,0 1,3 @@
+from flask_wtf import FlaskForm
+from wtforms.fields import StringField, IntegerField
+from wtforms.validators import DataRequired
M fllscoring/setup/__init__.py => fllscoring/setup/__init__.py +53 -17
@@ 6,13 6,23 @@ from fllscoring.models import Tournaments, Team
from fllscoring.setup.forms import NewTournamentForm, NewTeamForm, EditTeamForm
from fllscoring import db
-bp = Blueprint('setup', __name__, url_prefix="/setup")
+from fllscoring.auth.permissions import (
+ TournamentNeed,
+ ViewTournamentNeed,
+ ViewTournamentPermission,
+)
+
+bp = Blueprint("setup", __name__, url_prefix="/setup")
+
@bp.route("/home")
@login_required
def home():
- tournaments = Tournaments.query.filter_by(owner_id=current_user.user_id).all()
- return render_template('setup/home.html', title="Tournament Setup", tournaments=tournaments)
+ tournaments = Tournaments.query.filter_by(owner_id=current_user.id).all()
+ return render_template(
+ "setup/home.html", title="Tournament Setup", tournaments=tournaments
+ )
+
@bp.route("/create_tournament", methods=["GET", "POST"])
def new_tournament():
@@ 67,12 77,17 @@ def delete_tournament(id):
tournament = Tournaments.query.filter_by(id=id).first()
if tournament is None:
abort(404)
- if request.method == 'POST':
+ if request.method == "POST":
db.session.delete(tournament)
db.session.commit()
- return redirect(url_for('setup.home'))
-
- return render_template("setup/delete_tournament.html", title=f"Delete tournament {tournament.tournament_name}", tournament=tournament)
+ return redirect(url_for("setup.home"))
+
+ return render_template(
+ "setup/delete_tournament.html",
+ title=f"Delete tournament {tournament.tournament_name}",
+ tournament=tournament,
+ )
+
@bp.route("/new_team/<int:tournament_id>", methods=["GET", "POST"])
@login_required
@@ 80,12 95,22 @@ def new_team(tournament_id):
form = NewTeamForm()
if form.validate_on_submit():
- team = Team(tournament=tournament_id, team_number=form.team_number.data,team_name=form.team_name.data, team_town=form.team_town.data)
+ team = Team(
+ tournament=tournament_id,
+ team_number=form.team_number.data,
+ team_name=form.team_name.data,
+ team_town=form.team_town.data,
+ )
db.session.add(team)
db.session.commit()
return redirect(url_for("setup.manage_tournament", id=tournament_id))
- return render_template("setup/new_team.html", form=form, title=f"New Team for tournament {tournament_id}")
+ return render_template(
+ "setup/new_team.html",
+ form=form,
+ title=f"New Team for tournament {tournament_id}",
+ )
+
@bp.route("/edit_team/<int:tournament_id>/<int:team_id>", methods=["GET", "POST"])
@login_required
@@ 93,18 118,24 @@ def edit_team(tournament_id, team_id):
team = Team.query.filter_by(id=team_id).first()
if team is None:
abort(404)
-
+
if request.method == "POST":
form = EditTeamForm(request.form, obj=team)
if form.validate():
form.populate_obj(team)
db.session.add(team)
db.session.commit()
- return redirect(url_for('setup.manage_tournament', id=team.tournament))
+ return redirect(url_for("setup.manage_tournament", id=team.tournament))
else:
form = EditTeamForm(obj=team)
-
- return render_template("setup/edit_team.html", form=form, title=f"Edit team {team.team_name}", team=team)
+
+ return render_template(
+ "setup/edit_team.html",
+ form=form,
+ title=f"Edit team {team.team_name}",
+ team=team,
+ )
+
@bp.route("/delete_team/<int:tournament_id>/<int:team_id>", methods=["GET", "POST"])
@login_required
@@ 115,11 146,16 @@ def delete_team(tournament_id, team_id):
abort(404)
elif tournament is None:
abort(404)
-
- if request.method is 'POST':
+
+ if request.method is "POST":
db.session.delete(team)
db.session.commit()
flash("Deleted")
- return redirect('setup.manage_tournament', id=tournament_id)
+ return redirect("setup.manage_tournament", id=tournament_id)
- return render_template('setup/delete_team.html', title=f"Delete team {team}", team=team, tournament=tournament)>
\ No newline at end of file
+ return render_template(
+ "setup/delete_team.html",
+ title=f"Delete team {team}",
+ team=team,
+ tournament=tournament,
+ )
M fllscoring/setup/forms.py => fllscoring/setup/forms.py +3 -1
@@ 10,11 10,13 @@ from fllscoring.models import Team
class NewTournamentForm(FlaskForm):
tournament_name = StringField(label="Name", validators=[DataRequired()])
+
class NewTeamForm(FlaskForm):
team_number = IntegerField(label="Team Number", validators=[DataRequired()])
team_name = StringField(label="Team Nickname")
team_town = StringField(label="Team Town")
+
class EditTeamForm(ModelForm):
class Meta:
- model = Team>
\ No newline at end of file
+ model = Team
A fllscoring/templates/scoring/home.html => fllscoring/templates/scoring/home.html +14 -0
@@ 0,0 1,14 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<h1>Your Tournaments</h1>
+{% for t in tournaments %}
+<div class="paper">
+ <span class="hbar">
+ <p>{{ t.tournament_name }}</p>
+ <a href="{{ url_for('scoring.tournament_home', tournament_id=t.id) }}" class="paper accent movable">Score</a>
+ </span>
+</div>
+<br>
+{% endfor %}
+{% endblock %}<
\ No newline at end of file
A fllscoring/templates/scoring/tournament_home.html => fllscoring/templates/scoring/tournament_home.html +21 -0
@@ 0,0 1,21 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<span class="hbar">
+ <h1>Matches</h1>
+</span>
+{% for t in teams %}
+<div>
+ <span class="hbar">
+ <h3>{{ t }}</h3>
+ <a href="" class="paper accent movable">New Score</a>
+ </span>
+ {% for m in mscores %}
+ {% if m.team == t.id %}
+ {{ m.id }}
+ {% endif %}
+ {% endfor %}
+</div>
+<br>
+{% endfor %}
+{% endblock %}<
\ No newline at end of file
M migrations/env.py => migrations/env.py +10 -11
@@ 14,16 14,17 @@ 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')
+logger = logging.getLogger("alembic.env")
# 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',
- str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%'))
-target_metadata = current_app.extensions['migrate'].db.metadata
+ "sqlalchemy.url",
+ str(current_app.extensions["migrate"].db.engine.url).replace("%", "%%"),
+)
+target_metadata = current_app.extensions["migrate"].db.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
@@ 44,9 45,7 @@ def run_migrations_offline():
"""
url = config.get_main_option("sqlalchemy.url")
- context.configure(
- url=url, target_metadata=target_metadata, literal_binds=True
- )
+ context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
@@ 64,20 63,20 @@ def run_migrations_online():
# 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):
+ if getattr(config.cmd_opts, "autogenerate", False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
- logger.info('No changes in schema detected.')
+ logger.info("No changes in schema detected.")
- connectable = current_app.extensions['migrate'].db.engine
+ connectable = current_app.extensions["migrate"].db.engine
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
- **current_app.extensions['migrate'].configure_args
+ **current_app.extensions["migrate"].configure_args
)
with context.begin_transaction():
M migrations/versions/0bc190d5b794_.py => migrations/versions/0bc190d5b794_.py +8 -7
@@ 10,20 10,21 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '0bc190d5b794'
+revision = "0bc190d5b794"
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
- op.create_table('users',
- sa.Column('user_id', sa.Integer, primary_key=True),
- sa.Column('username', sa.String, nullable=False),
- sa.Column('email', sa.String, nullable=False),
- sa.Column('password_hash', sa.String, nullable=False)
+ op.create_table(
+ "users",
+ sa.Column("user_id", sa.Integer, primary_key=True),
+ sa.Column("username", sa.String, nullable=False),
+ sa.Column("email", sa.String, nullable=False),
+ sa.Column("password_hash", sa.String, nullable=False),
)
def downgrade():
- op.drop_table('users')
+ op.drop_table("users")
A migrations/versions/107d488e1c5f_add_tournament_ids.py => migrations/versions/107d488e1c5f_add_tournament_ids.py +33 -0
@@ 0,0 1,33 @@
+"""Add tournament IDs
+
+Revision ID: 107d488e1c5f
+Revises: 3182aa2fdb75
+Create Date: 2021-05-02 15:06:44.276503
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = "107d488e1c5f"
+down_revision = "3182aa2fdb75"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column(
+ "users",
+ sa.Column(
+ "tournaments", postgresql.ARRAY(sa.Integer(), dimensions=1), nullable=True
+ ),
+ )
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column("users", "tournaments")
+ # ### end Alembic commands ###
M migrations/versions/142a0c4e80c0_.py => migrations/versions/142a0c4e80c0_.py +4 -4
@@ 10,19 10,19 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '142a0c4e80c0'
-down_revision = '0bc190d5b794'
+revision = "142a0c4e80c0"
+down_revision = "0bc190d5b794"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.add_column('users', sa.Column('is_superadmin', sa.Boolean(), nullable=True))
+ op.add_column("users", sa.Column("is_superadmin", sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_column('users', 'is_superadmin')
+ op.drop_column("users", "is_superadmin")
# ### end Alembic commands ###
M migrations/versions/2cb04c80d294_scoring.py => migrations/versions/2cb04c80d294_scoring.py +27 -16
@@ 10,33 10,44 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '2cb04c80d294'
-down_revision = '5b61d04de41e'
+revision = "2cb04c80d294"
+down_revision = "5b61d04de41e"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('match',
- sa.Column('id', sa.Integer(), nullable=False),
- sa.Column('tournament', sa.Integer(), nullable=True),
- sa.ForeignKeyConstraint(['tournament'], ['tournaments.id'], ),
- sa.PrimaryKeyConstraint('id')
+ op.create_table(
+ "match",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("tournament", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["tournament"],
+ ["tournaments.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
)
- op.create_table('match_teams',
- sa.Column('id', sa.Integer(), nullable=False),
- sa.Column('match_id', sa.Integer(), nullable=True),
- sa.Column('team_id', sa.Integer(), nullable=True),
- sa.ForeignKeyConstraint(['match_id'], ['match.id'], ),
- sa.ForeignKeyConstraint(['team_id'], ['team.id'], ),
- sa.PrimaryKeyConstraint('id')
+ op.create_table(
+ "match_teams",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("match_id", sa.Integer(), nullable=True),
+ sa.Column("team_id", sa.Integer(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["match_id"],
+ ["match.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["team_id"],
+ ["team.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_table('match_teams')
- op.drop_table('match')
+ op.drop_table("match_teams")
+ op.drop_table("match")
# ### end Alembic commands ###
A migrations/versions/3182aa2fdb75_add_active_column_to_users.py => migrations/versions/3182aa2fdb75_add_active_column_to_users.py +28 -0
@@ 0,0 1,28 @@
+"""add active column to users
+
+Revision ID: 3182aa2fdb75
+Revises: 621bb8cab38e
+Create Date: 2021-04-30 11:42:04.563500
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "3182aa2fdb75"
+down_revision = "621bb8cab38e"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column("users", sa.Column("active", sa.Boolean(), nullable=False))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column("users", "active")
+ # ### end Alembic commands ###
M migrations/versions/5b61d04de41e_teams.py => migrations/versions/5b61d04de41e_teams.py +18 -14
@@ 10,30 10,34 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '5b61d04de41e'
-down_revision = 'b7aa2e2835b4'
+revision = "5b61d04de41e"
+down_revision = "b7aa2e2835b4"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('team',
- sa.Column('id', sa.Integer(), nullable=False),
- sa.Column('tournament', sa.Integer(), nullable=False),
- sa.Column('team_number', sa.Integer(), nullable=False),
- sa.Column('team_name', sa.String(), nullable=False),
- sa.Column('team_town', sa.String(), nullable=False),
- sa.ForeignKeyConstraint(['tournament'], ['tournaments.id'], ),
- sa.PrimaryKeyConstraint('id'),
- sa.UniqueConstraint('team_number')
+ op.create_table(
+ "team",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("tournament", sa.Integer(), nullable=False),
+ sa.Column("team_number", sa.Integer(), nullable=False),
+ sa.Column("team_name", sa.String(), nullable=False),
+ sa.Column("team_town", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["tournament"],
+ ["tournaments.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ sa.UniqueConstraint("team_number"),
)
- op.create_index(op.f('ix_team_tournament'), 'team', ['tournament'], unique=False)
+ op.create_index(op.f("ix_team_tournament"), "team", ["tournament"], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_index(op.f('ix_team_tournament'), table_name='team')
- op.drop_table('team')
+ op.drop_index(op.f("ix_team_tournament"), table_name="team")
+ op.drop_table("team")
# ### end Alembic commands ###
M migrations/versions/616627625894_set_cascade.py => migrations/versions/616627625894_set_cascade.py +28 -10
@@ 10,25 10,43 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '616627625894'
-down_revision = 'bed7e505df9b'
+revision = "616627625894"
+down_revision = "bed7e505df9b"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_constraint('match_score_tournament_fkey', 'match_score', type_='foreignkey')
- op.create_foreign_key(None, 'match_score', 'tournaments', ['tournament'], ['id'], ondelete='CASCADE')
- op.drop_constraint('team_tournament_fkey', 'team', type_='foreignkey')
- op.create_foreign_key(None, 'team', 'tournaments', ['tournament'], ['id'], onupdate='CASCADE', ondelete='CASCADE')
+ op.drop_constraint("match_score_tournament_fkey", "match_score", type_="foreignkey")
+ op.create_foreign_key(
+ None, "match_score", "tournaments", ["tournament"], ["id"], ondelete="CASCADE"
+ )
+ op.drop_constraint("team_tournament_fkey", "team", type_="foreignkey")
+ op.create_foreign_key(
+ None,
+ "team",
+ "tournaments",
+ ["tournament"],
+ ["id"],
+ onupdate="CASCADE",
+ ondelete="CASCADE",
+ )
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_constraint(None, 'team', type_='foreignkey')
- op.create_foreign_key('team_tournament_fkey', 'team', 'tournaments', ['tournament'], ['id'])
- op.drop_constraint(None, 'match_score', type_='foreignkey')
- op.create_foreign_key('match_score_tournament_fkey', 'match_score', 'tournaments', ['tournament'], ['id'])
+ op.drop_constraint(None, "team", type_="foreignkey")
+ op.create_foreign_key(
+ "team_tournament_fkey", "team", "tournaments", ["tournament"], ["id"]
+ )
+ op.drop_constraint(None, "match_score", type_="foreignkey")
+ op.create_foreign_key(
+ "match_score_tournament_fkey",
+ "match_score",
+ "tournaments",
+ ["tournament"],
+ ["id"],
+ )
# ### end Alembic commands ###
M migrations/versions/621bb8cab38e_flask_security.py => migrations/versions/621bb8cab38e_flask_security.py +10 -8
@@ 10,23 10,25 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '621bb8cab38e'
-down_revision = '616627625894'
+revision = "621bb8cab38e"
+down_revision = "616627625894"
branch_labels = None
depends_on = None
def upgrade():
- op.alter_column('users', 'user_id', new_column_name='id')
+ op.alter_column("users", "user_id", new_column_name="id")
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_constraint('tournaments_owner_id_fkey', 'tournaments', type_='foreignkey')
- op.create_foreign_key(None, 'tournaments', 'users', ['owner_id'], ['id'])
+ op.drop_constraint("tournaments_owner_id_fkey", "tournaments", type_="foreignkey")
+ op.create_foreign_key(None, "tournaments", "users", ["owner_id"], ["id"])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('users', 'id', new_column_name='user_id')
- op.drop_constraint(None, 'tournaments', type_='foreignkey')
- op.create_foreign_key('tournaments_owner_id_fkey', 'tournaments', 'users', ['owner_id'], ['user_id'])
+ op.alter_column("users", "id", new_column_name="user_id")
+ op.drop_constraint(None, "tournaments", type_="foreignkey")
+ op.create_foreign_key(
+ "tournaments_owner_id_fkey", "tournaments", "users", ["owner_id"], ["user_id"]
+ )
# ### end Alembic commands ###
M migrations/versions/8cba3105ef08_.py => migrations/versions/8cba3105ef08_.py +8 -10
@@ 10,25 10,23 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = '8cba3105ef08'
-down_revision = '142a0c4e80c0'
+revision = "8cba3105ef08"
+down_revision = "142a0c4e80c0"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('users', 'is_superadmin',
- existing_type=sa.BOOLEAN(),
- nullable=False)
- op.create_unique_constraint(None, 'users', ['username'])
+ op.alter_column(
+ "users", "is_superadmin", existing_type=sa.BOOLEAN(), nullable=False
+ )
+ op.create_unique_constraint(None, "users", ["username"])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_constraint(None, 'users', type_='unique')
- op.alter_column('users', 'is_superadmin',
- existing_type=sa.BOOLEAN(),
- nullable=True)
+ op.drop_constraint(None, "users", type_="unique")
+ op.alter_column("users", "is_superadmin", existing_type=sa.BOOLEAN(), nullable=True)
# ### end Alembic commands ###
M migrations/versions/b7aa2e2835b4_tournaments.py => migrations/versions/b7aa2e2835b4_tournaments.py +17 -11
@@ 10,27 10,33 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
-revision = 'b7aa2e2835b4'
-down_revision = '8cba3105ef08'
+revision = "b7aa2e2835b4"
+down_revision = "8cba3105ef08"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('tournaments',
- sa.Column('id', sa.Integer(), nullable=False),
- sa.Column('owner_id', sa.Integer(), nullable=False),
- sa.Column('tournament_name', sa.String(), nullable=False),
- sa.ForeignKeyConstraint(['owner_id'], ['users.user_id'], ),
- sa.PrimaryKeyConstraint('id')
+ op.create_table(
+ "tournaments",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("owner_id", sa.Integer(), nullable=False),
+ sa.Column("tournament_name", sa.String(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["owner_id"],
+ ["users.user_id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_index(
+ op.f("ix_tournaments_owner_id"), "tournaments", ["owner_id"], unique=False
)
- op.create_index(op.f('ix_tournaments_owner_id'), 'tournaments', ['owner_id'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.drop_index(op.f('ix_tournaments_owner_id'), table_name='tournaments')
- op.drop_table('tournaments')
+ op.drop_index(op.f("ix_tournaments_owner_id"), table_name="tournaments")
+ op.drop_table("tournaments")
# ### end Alembic commands ###
M migrations/versions/bed7e505df9b_add_match_scores.py => migrations/versions/bed7e505df9b_add_match_scores.py +47 -26
@@ 10,44 10,65 @@ import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
-revision = 'bed7e505df9b'
-down_revision = '2cb04c80d294'
+revision = "bed7e505df9b"
+down_revision = "2cb04c80d294"
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('match_score',
- sa.Column('id', sa.Integer(), nullable=False),
- sa.Column('tournament', sa.Integer(), nullable=True),
- sa.Column('team', sa.Integer(), nullable=True),
- sa.Column('score', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
- sa.ForeignKeyConstraint(['team'], ['team.id'], ),
- sa.ForeignKeyConstraint(['tournament'], ['tournaments.id'], ),
- sa.PrimaryKeyConstraint('id')
+ op.create_table(
+ "match_score",
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("tournament", sa.Integer(), nullable=True),
+ sa.Column("team", sa.Integer(), nullable=True),
+ sa.Column("score", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["team"],
+ ["team.id"],
+ ),
+ sa.ForeignKeyConstraint(
+ ["tournament"],
+ ["tournaments.id"],
+ ),
+ sa.PrimaryKeyConstraint("id"),
)
- op.drop_table('match_teams')
- op.drop_table('match')
+ op.drop_table("match_teams")
+ op.drop_table("match")
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
- op.create_table('match',
- sa.Column('id', sa.INTEGER(), server_default=sa.text("nextval('match_id_seq'::regclass)"), autoincrement=True, nullable=False),
- sa.Column('tournament', sa.INTEGER(), autoincrement=False, nullable=True),
- sa.ForeignKeyConstraint(['tournament'], ['tournaments.id'], name='match_tournament_fkey'),
- sa.PrimaryKeyConstraint('id', name='match_pkey'),
- postgresql_ignore_search_path=False
+ op.create_table(
+ "match",
+ sa.Column(
+ "id",
+ sa.INTEGER(),
+ server_default=sa.text("nextval('match_id_seq'::regclass)"),
+ autoincrement=True,
+ nullable=False,
+ ),
+ sa.Column("tournament", sa.INTEGER(), autoincrement=False, nullable=True),
+ sa.ForeignKeyConstraint(
+ ["tournament"], ["tournaments.id"], name="match_tournament_fkey"
+ ),
+ sa.PrimaryKeyConstraint("id", name="match_pkey"),
+ postgresql_ignore_search_path=False,
)
- op.create_table('match_teams',
- sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
- sa.Column('match_id', sa.INTEGER(), autoincrement=False, nullable=True),
- sa.Column('team_id', sa.INTEGER(), autoincrement=False, nullable=True),
- sa.ForeignKeyConstraint(['match_id'], ['match.id'], name='match_teams_match_id_fkey'),
- sa.ForeignKeyConstraint(['team_id'], ['team.id'], name='match_teams_team_id_fkey'),
- sa.PrimaryKeyConstraint('id', name='match_teams_pkey')
+ op.create_table(
+ "match_teams",
+ sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
+ sa.Column("match_id", sa.INTEGER(), autoincrement=False, nullable=True),
+ sa.Column("team_id", sa.INTEGER(), autoincrement=False, nullable=True),
+ sa.ForeignKeyConstraint(
+ ["match_id"], ["match.id"], name="match_teams_match_id_fkey"
+ ),
+ sa.ForeignKeyConstraint(
+ ["team_id"], ["team.id"], name="match_teams_team_id_fkey"
+ ),
+ sa.PrimaryKeyConstraint("id", name="match_teams_pkey"),
)
- op.drop_table('match_score')
+ op.drop_table("match_score")
# ### end Alembic commands ###
M setup.py => setup.py +2 -2
@@ 1,4 1,4 @@
-from setuptools import setup,find_packages
+from setuptools import setup, find_packages
setup(
name="fllscoring",
@@ 7,5 7,5 @@ setup(
author_email="cara@devcara.com",
packages=find_packages(),
include_package_data=True,
- zip_safe=False
+ zip_safe=False,
)