~muirrum/comortas

37056ec2f796d9cefe4fceb434330c7c4fa791cd — Cara Salter 3 years ago a056065 0.5.0
Format with black
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,
)