Move application code into package
This commit is contained in:
25 files changed
+285
-269
No files matched your search
@@ -23,6 +23,14 @@ The current app is account-based and league-based. League owners/managers record
|
|||||||
- Chart.js
|
- Chart.js
|
||||||
- plain CSS
|
- plain CSS
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
|
||||||
|
- `app.py` is the deployment entrypoint for `flask --app app` and Gunicorn.
|
||||||
|
- `boker/` contains the Flask application package, routes, models, services, repositories, and config.
|
||||||
|
- `templates/` and `static/` contain Jinja views and public assets.
|
||||||
|
- `migrations/` contains Alembic migrations for database deploys.
|
||||||
|
- `tests/` contains the committed regression suite.
|
||||||
|
|
||||||
## Ledger Model
|
## Ledger Model
|
||||||
|
|
||||||
Each ledger row is an event, not a final snapshot. Corrections are made by appending or voiding events, so the history remains auditable.
|
Each ledger row is an event, not a final snapshot. Corrections are made by appending or voiding events, so the history remains auditable.
|
||||||
|
|||||||
@@ -1,125 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import click
|
from boker.app import app, create_app
|
||||||
import os
|
|
||||||
from flask import Flask, render_template
|
|
||||||
|
|
||||||
from auth import current_user_id, is_logged_in, is_site_admin, normalize_email
|
__all__ = ["app", "create_app"]
|
||||||
from config import DEFAULT_SECRET_KEY, Config, ProductionConfig
|
|
||||||
from db import database_extensions_available, db, init_database
|
|
||||||
from extensions import csrf, limiter, mail
|
|
||||||
from routes.account import account_bp
|
|
||||||
from routes.internal import internal_bp
|
|
||||||
from routes.leagues import leagues_bp
|
|
||||||
from routes.public import public_bp
|
|
||||||
from storage import ensure_data_file
|
|
||||||
from utils import cents_to_dollars, safe_date_label
|
|
||||||
|
|
||||||
|
|
||||||
def _config_for_environment():
|
|
||||||
app_env = os.getenv("APP_ENV", os.getenv("FLASK_ENV", "")).lower()
|
|
||||||
if app_env in {"prod", "production"}:
|
|
||||||
return ProductionConfig
|
|
||||||
return Config
|
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_overrides: dict | None = None) -> Flask:
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config.from_object(_config_for_environment())
|
|
||||||
if config_overrides:
|
|
||||||
app.config.update(config_overrides)
|
|
||||||
|
|
||||||
if app.config["SESSION_COOKIE_SECURE"] and app.config["SECRET_KEY"] == DEFAULT_SECRET_KEY:
|
|
||||||
raise RuntimeError("Set SECRET_KEY before running in production.")
|
|
||||||
|
|
||||||
ensure_data_file(app.config["DATA_PATH"])
|
|
||||||
init_database(app)
|
|
||||||
csrf.init_app(app)
|
|
||||||
limiter.init_app(app)
|
|
||||||
mail.init_app(app)
|
|
||||||
|
|
||||||
app.jinja_env.filters["money"] = cents_to_dollars
|
|
||||||
app.jinja_env.filters["pretty_date"] = safe_date_label
|
|
||||||
|
|
||||||
@app.context_processor
|
|
||||||
def inject_globals() -> dict:
|
|
||||||
return {
|
|
||||||
"app_version": app.config["APP_VERSION"],
|
|
||||||
"current_user_id": current_user_id(),
|
|
||||||
"is_logged_in": is_logged_in(),
|
|
||||||
"current_user_is_site_admin": is_site_admin(),
|
|
||||||
}
|
|
||||||
|
|
||||||
app.register_blueprint(public_bp)
|
|
||||||
app.register_blueprint(account_bp)
|
|
||||||
app.register_blueprint(internal_bp)
|
|
||||||
app.register_blueprint(leagues_bp)
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
|
||||||
def not_found(e):
|
|
||||||
return render_template("404.html"), 404
|
|
||||||
|
|
||||||
@app.errorhandler(403)
|
|
||||||
def forbidden(e):
|
|
||||||
return render_template("403.html"), 403
|
|
||||||
|
|
||||||
@app.cli.command("init-db")
|
|
||||||
def init_db_command() -> None:
|
|
||||||
if not database_extensions_available() or db is None:
|
|
||||||
raise click.ClickException(
|
|
||||||
"Database dependencies are not installed. Run pip install -r requirements.txt."
|
|
||||||
)
|
|
||||||
|
|
||||||
with app.app_context():
|
|
||||||
db.create_all()
|
|
||||||
|
|
||||||
click.echo("Initialized database tables.")
|
|
||||||
|
|
||||||
@app.cli.command("grant-site-admin")
|
|
||||||
@click.argument("email")
|
|
||||||
def grant_site_admin_command(email: str) -> None:
|
|
||||||
if not database_extensions_available() or db is None:
|
|
||||||
raise click.ClickException(
|
|
||||||
"Database dependencies are not installed. Run pip install -r requirements.txt."
|
|
||||||
)
|
|
||||||
|
|
||||||
from db_models import User
|
|
||||||
|
|
||||||
normalized_email = normalize_email(email)
|
|
||||||
with app.app_context():
|
|
||||||
user = User.query.filter_by(email=normalized_email, disabled_at=None).one_or_none()
|
|
||||||
if user is None:
|
|
||||||
raise click.ClickException(f"No active user found for {normalized_email}.")
|
|
||||||
user.is_site_admin = True
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
click.echo(f"Granted site admin access to {normalized_email}.")
|
|
||||||
|
|
||||||
@app.cli.command("revoke-site-admin")
|
|
||||||
@click.argument("email")
|
|
||||||
def revoke_site_admin_command(email: str) -> None:
|
|
||||||
if not database_extensions_available() or db is None:
|
|
||||||
raise click.ClickException(
|
|
||||||
"Database dependencies are not installed. Run pip install -r requirements.txt."
|
|
||||||
)
|
|
||||||
|
|
||||||
from db_models import User
|
|
||||||
|
|
||||||
normalized_email = normalize_email(email)
|
|
||||||
with app.app_context():
|
|
||||||
user = User.query.filter_by(email=normalized_email).one_or_none()
|
|
||||||
if user is None:
|
|
||||||
raise click.ClickException(f"No user found for {normalized_email}.")
|
|
||||||
user.is_site_admin = False
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
click.echo(f"Revoked site admin access from {normalized_email}.")
|
|
||||||
|
|
||||||
return app
|
|
||||||
|
|
||||||
|
|
||||||
app = create_app()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(debug=True)
|
app.run(debug=True)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Application package for myboker.org."""
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import click
|
||||||
|
import os
|
||||||
|
from flask import Flask, render_template
|
||||||
|
|
||||||
|
from boker.auth import current_user_id, is_logged_in, is_site_admin, normalize_email
|
||||||
|
from boker.config import DEFAULT_SECRET_KEY, Config, ProductionConfig
|
||||||
|
from boker.db import database_extensions_available, db, init_database
|
||||||
|
from boker.extensions import csrf, limiter, mail
|
||||||
|
from boker.routes.account import account_bp
|
||||||
|
from boker.routes.internal import internal_bp
|
||||||
|
from boker.routes.leagues import leagues_bp
|
||||||
|
from boker.routes.public import public_bp
|
||||||
|
from boker.storage import ensure_data_file
|
||||||
|
from boker.utils import cents_to_dollars, safe_date_label
|
||||||
|
|
||||||
|
|
||||||
|
def _config_for_environment():
|
||||||
|
app_env = os.getenv("APP_ENV", os.getenv("FLASK_ENV", "")).lower()
|
||||||
|
if app_env in {"prod", "production"}:
|
||||||
|
return ProductionConfig
|
||||||
|
return Config
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(config_overrides: dict | None = None) -> Flask:
|
||||||
|
app = Flask(__name__, template_folder="../templates", static_folder="../static")
|
||||||
|
app.config.from_object(_config_for_environment())
|
||||||
|
if config_overrides:
|
||||||
|
app.config.update(config_overrides)
|
||||||
|
|
||||||
|
if app.config["SESSION_COOKIE_SECURE"] and app.config["SECRET_KEY"] == DEFAULT_SECRET_KEY:
|
||||||
|
raise RuntimeError("Set SECRET_KEY before running in production.")
|
||||||
|
|
||||||
|
ensure_data_file(app.config["DATA_PATH"])
|
||||||
|
init_database(app)
|
||||||
|
csrf.init_app(app)
|
||||||
|
limiter.init_app(app)
|
||||||
|
mail.init_app(app)
|
||||||
|
|
||||||
|
app.jinja_env.filters["money"] = cents_to_dollars
|
||||||
|
app.jinja_env.filters["pretty_date"] = safe_date_label
|
||||||
|
|
||||||
|
@app.context_processor
|
||||||
|
def inject_globals() -> dict:
|
||||||
|
return {
|
||||||
|
"app_version": app.config["APP_VERSION"],
|
||||||
|
"current_user_id": current_user_id(),
|
||||||
|
"is_logged_in": is_logged_in(),
|
||||||
|
"current_user_is_site_admin": is_site_admin(),
|
||||||
|
}
|
||||||
|
|
||||||
|
app.register_blueprint(public_bp)
|
||||||
|
app.register_blueprint(account_bp)
|
||||||
|
app.register_blueprint(internal_bp)
|
||||||
|
app.register_blueprint(leagues_bp)
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
def not_found(e):
|
||||||
|
return render_template("404.html"), 404
|
||||||
|
|
||||||
|
@app.errorhandler(403)
|
||||||
|
def forbidden(e):
|
||||||
|
return render_template("403.html"), 403
|
||||||
|
|
||||||
|
@app.cli.command("init-db")
|
||||||
|
def init_db_command() -> None:
|
||||||
|
if not database_extensions_available() or db is None:
|
||||||
|
raise click.ClickException(
|
||||||
|
"Database dependencies are not installed. Run pip install -r requirements.txt."
|
||||||
|
)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
click.echo("Initialized database tables.")
|
||||||
|
|
||||||
|
@app.cli.command("grant-site-admin")
|
||||||
|
@click.argument("email")
|
||||||
|
def grant_site_admin_command(email: str) -> None:
|
||||||
|
if not database_extensions_available() or db is None:
|
||||||
|
raise click.ClickException(
|
||||||
|
"Database dependencies are not installed. Run pip install -r requirements.txt."
|
||||||
|
)
|
||||||
|
|
||||||
|
from boker.db_models import User
|
||||||
|
|
||||||
|
normalized_email = normalize_email(email)
|
||||||
|
with app.app_context():
|
||||||
|
user = User.query.filter_by(email=normalized_email, disabled_at=None).one_or_none()
|
||||||
|
if user is None:
|
||||||
|
raise click.ClickException(f"No active user found for {normalized_email}.")
|
||||||
|
user.is_site_admin = True
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
click.echo(f"Granted site admin access to {normalized_email}.")
|
||||||
|
|
||||||
|
@app.cli.command("revoke-site-admin")
|
||||||
|
@click.argument("email")
|
||||||
|
def revoke_site_admin_command(email: str) -> None:
|
||||||
|
if not database_extensions_available() or db is None:
|
||||||
|
raise click.ClickException(
|
||||||
|
"Database dependencies are not installed. Run pip install -r requirements.txt."
|
||||||
|
)
|
||||||
|
|
||||||
|
from boker.db_models import User
|
||||||
|
|
||||||
|
normalized_email = normalize_email(email)
|
||||||
|
with app.app_context():
|
||||||
|
user = User.query.filter_by(email=normalized_email).one_or_none()
|
||||||
|
if user is None:
|
||||||
|
raise click.ClickException(f"No user found for {normalized_email}.")
|
||||||
|
user.is_site_admin = False
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
click.echo(f"Revoked site admin access from {normalized_email}.")
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
@@ -72,12 +72,12 @@ def is_site_admin() -> bool:
|
|||||||
if not user_id:
|
if not user_id:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
from db import database_extensions_available
|
from boker.db import database_extensions_available
|
||||||
|
|
||||||
if not database_extensions_available():
|
if not database_extensions_available():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = User.query.filter_by(id=user_id, disabled_at=None).one_or_none()
|
user = User.query.filter_by(id=user_id, disabled_at=None).one_or_none()
|
||||||
return bool(user and user.is_site_admin)
|
return bool(user and user.is_site_admin)
|
||||||
@@ -4,8 +4,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from models import SessionEntry, SessionSummary
|
from boker.models import SessionEntry, SessionSummary
|
||||||
from utils import net_result_bucket, session_chart_label, session_sort_key
|
from boker.utils import net_result_bucket, session_chart_label, session_sort_key
|
||||||
|
|
||||||
PLAYER_PALETTE = [
|
PLAYER_PALETTE = [
|
||||||
"#9b8cf0", # --line-1 violet
|
"#9b8cf0", # --line-1 violet
|
||||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
DATA_PATH = BASE_DIR / "data" / "entries.csv"
|
DATA_PATH = BASE_DIR / "data" / "entries.csv"
|
||||||
DEFAULT_DATABASE_URL = f"sqlite:///{BASE_DIR / 'data' / 'boker-dev.sqlite3'}"
|
DEFAULT_DATABASE_URL = f"sqlite:///{BASE_DIR / 'data' / 'boker-dev.sqlite3'}"
|
||||||
DEFAULT_SECRET_KEY = "change-this-before-deploying"
|
DEFAULT_SECRET_KEY = "change-this-before-deploying"
|
||||||
+1
-1
@@ -24,7 +24,7 @@ def init_database(app: Any) -> bool:
|
|||||||
app.config["DATABASE_EXTENSIONS_AVAILABLE"] = False
|
app.config["DATABASE_EXTENSIONS_AVAILABLE"] = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
import db_models # noqa: F401 - registers SQLAlchemy models with metadata.
|
import boker.db_models # noqa: F401 - registers SQLAlchemy models with metadata.
|
||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
migrate.init_app(app, db)
|
migrate.init_app(app, db)
|
||||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import date, datetime, timezone
|
from datetime import date, datetime, timezone
|
||||||
|
|
||||||
from db import db
|
from boker.db import db
|
||||||
|
|
||||||
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
||||||
raise RuntimeError("Database dependencies are not installed.")
|
raise RuntimeError("Database dependencies are not installed.")
|
||||||
@@ -7,7 +7,7 @@ import socket
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask_mail import Message
|
from flask_mail import Message
|
||||||
|
|
||||||
from extensions import mail
|
from boker.extensions import mail
|
||||||
|
|
||||||
|
|
||||||
def _mail_send_suppressed() -> bool:
|
def _mail_send_suppressed() -> bool:
|
||||||
File renamed without changes.
@@ -5,13 +5,13 @@ import secrets
|
|||||||
import string
|
import string
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from auth import hash_password, normalize_email
|
from boker.auth import hash_password, normalize_email
|
||||||
from db import db
|
from boker.db import db
|
||||||
|
|
||||||
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
||||||
raise RuntimeError("Database dependencies are not installed.")
|
raise RuntimeError("Database dependencies are not installed.")
|
||||||
|
|
||||||
from db_models import ( # noqa: E402
|
from boker.db_models import ( # noqa: E402
|
||||||
LedgerEvent,
|
LedgerEvent,
|
||||||
League,
|
League,
|
||||||
LeagueMembership,
|
LeagueMembership,
|
||||||
@@ -24,7 +24,7 @@ from db_models import ( # noqa: E402
|
|||||||
make_user,
|
make_user,
|
||||||
normalize_lookup,
|
normalize_lookup,
|
||||||
)
|
)
|
||||||
from utils import slugify
|
from boker.utils import slugify
|
||||||
|
|
||||||
PUBLIC_KEY_ALPHABET = string.ascii_lowercase + string.digits
|
PUBLIC_KEY_ALPHABET = string.ascii_lowercase + string.digits
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ def set_session_status(session: PokerSession, status: str) -> PokerSession:
|
|||||||
if status not in {"open", "closed"}:
|
if status not in {"open", "closed"}:
|
||||||
raise ValueError(f"Unsupported session status: {status}")
|
raise ValueError(f"Unsupported session status: {status}")
|
||||||
|
|
||||||
from db_models import utc_now
|
from boker.db_models import utc_now
|
||||||
|
|
||||||
session.status = status
|
session.status = status
|
||||||
if status == "open":
|
if status == "open":
|
||||||
@@ -323,7 +323,7 @@ def add_league_member(
|
|||||||
|
|
||||||
|
|
||||||
def remove_league_member(league_id: str, user_id: str) -> None:
|
def remove_league_member(league_id: str, user_id: str) -> None:
|
||||||
from db_models import utc_now
|
from boker.db_models import utc_now
|
||||||
|
|
||||||
membership = LeagueMembership.query.filter_by(
|
membership = LeagueMembership.query.filter_by(
|
||||||
league_id=league_id,
|
league_id=league_id,
|
||||||
@@ -421,7 +421,7 @@ def delete_season(season: Season) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def delete_league(league: League) -> None:
|
def delete_league(league: League) -> None:
|
||||||
from db_models import LedgerEvent, LeagueMembership, Player, PokerSession, Season
|
from boker.db_models import LedgerEvent, LeagueMembership, Player, PokerSession, Season
|
||||||
LedgerEvent.query.filter_by(league_id=league.id).delete(synchronize_session=False)
|
LedgerEvent.query.filter_by(league_id=league.id).delete(synchronize_session=False)
|
||||||
PokerSession.query.filter_by(league_id=league.id).delete(synchronize_session=False)
|
PokerSession.query.filter_by(league_id=league.id).delete(synchronize_session=False)
|
||||||
Player.query.filter_by(league_id=league.id).delete(synchronize_session=False)
|
Player.query.filter_by(league_id=league.id).delete(synchronize_session=False)
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from db import db
|
from boker.db import db
|
||||||
|
|
||||||
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
||||||
raise RuntimeError("Database dependencies are not installed.")
|
raise RuntimeError("Database dependencies are not installed.")
|
||||||
|
|
||||||
from db_models import ( # noqa: E402
|
from boker.db_models import ( # noqa: E402
|
||||||
CANONICAL_EVENT_TYPES,
|
CANONICAL_EVENT_TYPES,
|
||||||
LedgerEvent,
|
LedgerEvent,
|
||||||
Player,
|
Player,
|
||||||
@@ -14,7 +14,7 @@ from db_models import ( # noqa: E402
|
|||||||
User,
|
User,
|
||||||
canonical_event_type,
|
canonical_event_type,
|
||||||
)
|
)
|
||||||
from storage import EventRow # noqa: E402
|
from boker.storage import EventRow # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
def session_event_ref(session: PokerSession) -> str:
|
def session_event_ref(session: PokerSession) -> str:
|
||||||
File renamed without changes.
File renamed without changes.
@@ -7,7 +7,7 @@ from urllib.parse import urlsplit
|
|||||||
|
|
||||||
from flask import Blueprint, flash, redirect, render_template, request, session as flask_session, url_for
|
from flask import Blueprint, flash, redirect, render_template, request, session as flask_session, url_for
|
||||||
|
|
||||||
from auth import (
|
from boker.auth import (
|
||||||
current_user_id,
|
current_user_id,
|
||||||
generate_invite_token,
|
generate_invite_token,
|
||||||
generate_reset_token,
|
generate_reset_token,
|
||||||
@@ -20,8 +20,8 @@ from auth import (
|
|||||||
verify_password,
|
verify_password,
|
||||||
verify_reset_token,
|
verify_reset_token,
|
||||||
)
|
)
|
||||||
from db import database_extensions_available, db
|
from boker.db import database_extensions_available, db
|
||||||
from extensions import limiter
|
from boker.extensions import limiter
|
||||||
|
|
||||||
account_bp = Blueprint("account", __name__, url_prefix="/account")
|
account_bp = Blueprint("account", __name__, url_prefix="/account")
|
||||||
EMAIL_VERIFICATION_TTL = timedelta(minutes=15)
|
EMAIL_VERIFICATION_TTL = timedelta(minutes=15)
|
||||||
@@ -64,7 +64,7 @@ def _new_verification_code() -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _issue_verification_code(user) -> str:
|
def _issue_verification_code(user) -> str:
|
||||||
from db_models import utc_now
|
from boker.db_models import utc_now
|
||||||
|
|
||||||
code = _new_verification_code()
|
code = _new_verification_code()
|
||||||
user.email_verification_code_hash = hash_password(code)
|
user.email_verification_code_hash = hash_password(code)
|
||||||
@@ -73,7 +73,7 @@ def _issue_verification_code(user) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _send_verification_code(user) -> None:
|
def _send_verification_code(user) -> None:
|
||||||
from emails import send_email_verification_code
|
from boker.emails import send_email_verification_code
|
||||||
|
|
||||||
code = _issue_verification_code(user)
|
code = _issue_verification_code(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@@ -112,7 +112,7 @@ def register():
|
|||||||
}
|
}
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
from league_repositories import create_user, find_user_by_email
|
from boker.league_repositories import create_user, find_user_by_email
|
||||||
|
|
||||||
email = normalize_email(form["email"])
|
email = normalize_email(form["email"])
|
||||||
password = request.form.get("password", "")
|
password = request.form.get("password", "")
|
||||||
@@ -158,7 +158,7 @@ def login():
|
|||||||
}
|
}
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
from league_repositories import find_user_by_email
|
from boker.league_repositories import find_user_by_email
|
||||||
|
|
||||||
user = find_user_by_email(form["email"])
|
user = find_user_by_email(form["email"])
|
||||||
password = request.form.get("password", "")
|
password = request.form.get("password", "")
|
||||||
@@ -195,7 +195,7 @@ def verify_email():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.login"))
|
return redirect(url_for("account.login"))
|
||||||
|
|
||||||
from db_models import User, utc_now
|
from boker.db_models import User, utc_now
|
||||||
|
|
||||||
user_id = flask_session.get("pending_verification_user_id")
|
user_id = flask_session.get("pending_verification_user_id")
|
||||||
user = db.session.get(User, user_id) if user_id else None
|
user = db.session.get(User, user_id) if user_id else None
|
||||||
@@ -245,7 +245,7 @@ def resend_verification_code():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.login"))
|
return redirect(url_for("account.login"))
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user_id = flask_session.get("pending_verification_user_id")
|
user_id = flask_session.get("pending_verification_user_id")
|
||||||
user = db.session.get(User, user_id) if user_id else None
|
user = db.session.get(User, user_id) if user_id else None
|
||||||
@@ -275,7 +275,7 @@ def settings():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("leagues.index"))
|
return redirect(url_for("leagues.index"))
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = db.session.get(User, current_user_id())
|
user = db.session.get(User, current_user_id())
|
||||||
if user is None:
|
if user is None:
|
||||||
@@ -292,7 +292,7 @@ def update_email():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.settings"))
|
return redirect(url_for("account.settings"))
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = db.session.get(User, current_user_id())
|
user = db.session.get(User, current_user_id())
|
||||||
if user is None:
|
if user is None:
|
||||||
@@ -307,7 +307,7 @@ def update_email():
|
|||||||
elif not verify_password(user.password_hash, current_password):
|
elif not verify_password(user.password_hash, current_password):
|
||||||
flash("Current password is incorrect.", "error")
|
flash("Current password is incorrect.", "error")
|
||||||
else:
|
else:
|
||||||
from league_repositories import find_user_by_email
|
from boker.league_repositories import find_user_by_email
|
||||||
|
|
||||||
existing = find_user_by_email(new_email)
|
existing = find_user_by_email(new_email)
|
||||||
if existing and existing.id != user.id:
|
if existing and existing.id != user.id:
|
||||||
@@ -327,7 +327,7 @@ def update_password():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.settings"))
|
return redirect(url_for("account.settings"))
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = db.session.get(User, current_user_id())
|
user = db.session.get(User, current_user_id())
|
||||||
if user is None:
|
if user is None:
|
||||||
@@ -359,7 +359,7 @@ def disable_account():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.settings"))
|
return redirect(url_for("account.settings"))
|
||||||
|
|
||||||
from db_models import League, User, utc_now
|
from boker.db_models import League, User, utc_now
|
||||||
|
|
||||||
user = db.session.get(User, current_user_id())
|
user = db.session.get(User, current_user_id())
|
||||||
if user is None:
|
if user is None:
|
||||||
@@ -395,8 +395,8 @@ def delete_account():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.settings"))
|
return redirect(url_for("account.settings"))
|
||||||
|
|
||||||
from db_models import League, LeagueMembership, User, utc_now
|
from boker.db_models import League, LeagueMembership, User, utc_now
|
||||||
from league_repositories import delete_league
|
from boker.league_repositories import delete_league
|
||||||
|
|
||||||
user = db.session.get(User, current_user_id())
|
user = db.session.get(User, current_user_id())
|
||||||
if user is None:
|
if user is None:
|
||||||
@@ -436,9 +436,9 @@ def forgot_password():
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.forgot_password"))
|
return redirect(url_for("account.forgot_password"))
|
||||||
|
|
||||||
from emails import send_password_reset
|
from boker.emails import send_password_reset
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from league_repositories import find_user_by_email
|
from boker.league_repositories import find_user_by_email
|
||||||
|
|
||||||
email = normalize_email(request.form.get("email", ""))
|
email = normalize_email(request.form.get("email", ""))
|
||||||
user = find_user_by_email(email)
|
user = find_user_by_email(email)
|
||||||
@@ -472,7 +472,7 @@ def reset_password(token):
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("account.login"))
|
return redirect(url_for("account.login"))
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = db.session.get(User, user_id)
|
user = db.session.get(User, user_id)
|
||||||
if user is None or user.disabled_at is not None:
|
if user is None or user.disabled_at is not None:
|
||||||
@@ -507,8 +507,8 @@ def accept_invite(token):
|
|||||||
flash("Account database is not available.", "error")
|
flash("Account database is not available.", "error")
|
||||||
return redirect(url_for("leagues.index"))
|
return redirect(url_for("leagues.index"))
|
||||||
|
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
from league_repositories import add_league_member, find_league_by_id, find_membership, find_user_by_email
|
from boker.league_repositories import add_league_member, find_league_by_id, find_membership, find_user_by_email
|
||||||
|
|
||||||
invite_email = normalize_email(data.get("email", ""))
|
invite_email = normalize_email(data.get("email", ""))
|
||||||
invite_next = url_for("account.accept_invite", token=token)
|
invite_next = url_for("account.accept_invite", token=token)
|
||||||
@@ -9,17 +9,17 @@ from flask import Blueprint, current_app, flash, redirect, render_template, requ
|
|||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from auth import (
|
from boker.auth import (
|
||||||
current_user_id,
|
current_user_id,
|
||||||
generate_reset_token,
|
generate_reset_token,
|
||||||
hash_password,
|
hash_password,
|
||||||
normalize_email,
|
normalize_email,
|
||||||
site_admin_required,
|
site_admin_required,
|
||||||
)
|
)
|
||||||
from db import db
|
from boker.db import db
|
||||||
from db_models import LedgerEvent, League, LeagueMembership, Player, PokerSession, User, utc_now
|
from boker.db_models import LedgerEvent, League, LeagueMembership, Player, PokerSession, User, utc_now
|
||||||
from league_repositories import delete_league, transfer_league_ownership
|
from boker.league_repositories import delete_league, transfer_league_ownership
|
||||||
from utils import slugify
|
from boker.utils import slugify
|
||||||
|
|
||||||
|
|
||||||
internal_bp = Blueprint("internal", __name__, url_prefix="/internal")
|
internal_bp = Blueprint("internal", __name__, url_prefix="/internal")
|
||||||
@@ -415,7 +415,7 @@ def send_user_password_reset(user_id: str):
|
|||||||
flash("Disabled users cannot receive password resets.", "error")
|
flash("Disabled users cannot receive password resets.", "error")
|
||||||
return redirect(url_for("internal.user_detail", user_id=user.id))
|
return redirect(url_for("internal.user_detail", user_id=user.id))
|
||||||
|
|
||||||
from emails import send_password_reset
|
from boker.emails import send_password_reset
|
||||||
|
|
||||||
token = generate_reset_token(user.id)
|
token = generate_reset_token(user.id)
|
||||||
base_url = current_app.config.get("APP_BASE_URL", "").rstrip("/")
|
base_url = current_app.config.get("APP_BASE_URL", "").rstrip("/")
|
||||||
@@ -436,7 +436,7 @@ def create_user_temporary_password(user_id: str):
|
|||||||
flash("Disabled users cannot receive temporary passwords.", "error")
|
flash("Disabled users cannot receive temporary passwords.", "error")
|
||||||
return redirect(url_for("internal.user_detail", user_id=user.id))
|
return redirect(url_for("internal.user_detail", user_id=user.id))
|
||||||
|
|
||||||
from emails import send_temporary_password
|
from boker.emails import send_temporary_password
|
||||||
|
|
||||||
temporary_password = _temporary_password()
|
temporary_password = _temporary_password()
|
||||||
user.password_hash = hash_password(temporary_password)
|
user.password_hash = hash_password(temporary_password)
|
||||||
@@ -9,12 +9,12 @@ from datetime import datetime, timezone
|
|||||||
|
|
||||||
from flask import Blueprint, abort, flash, make_response, redirect, render_template, request, url_for
|
from flask import Blueprint, abort, flash, make_response, redirect, render_template, request, url_for
|
||||||
|
|
||||||
from auth import current_user_id, login_required, normalize_email
|
from boker.auth import current_user_id, login_required, normalize_email
|
||||||
from charts import cumulative_profit_series, player_session_series
|
from boker.charts import cumulative_profit_series, player_session_series
|
||||||
from db import database_extensions_available, db
|
from boker.db import database_extensions_available, db
|
||||||
from models import SessionSummary
|
from boker.models import SessionSummary
|
||||||
from services import apply_rank_changes, build_leaderboard, build_session_summaries, session_events
|
from boker.services import apply_rank_changes, build_leaderboard, build_session_summaries, session_events
|
||||||
from utils import cents_to_dollars, session_label, session_sort_key
|
from boker.utils import cents_to_dollars, session_label, session_sort_key
|
||||||
|
|
||||||
leagues_bp = Blueprint("leagues", __name__)
|
leagues_bp = Blueprint("leagues", __name__)
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ def split_league_ref(league_ref: str) -> tuple[str, str]:
|
|||||||
|
|
||||||
|
|
||||||
def require_league(league_ref: str, allowed_roles: set[str]):
|
def require_league(league_ref: str, allowed_roles: set[str]):
|
||||||
from league_repositories import find_league_by_public_key, user_has_league_role
|
from boker.league_repositories import find_league_by_public_key, user_has_league_role
|
||||||
|
|
||||||
_slug, public_key = split_league_ref(league_ref)
|
_slug, public_key = split_league_ref(league_ref)
|
||||||
league = find_league_by_public_key(public_key)
|
league = find_league_by_public_key(public_key)
|
||||||
@@ -64,7 +64,7 @@ def require_league(league_ref: str, allowed_roles: set[str]):
|
|||||||
|
|
||||||
def get_league_with_visibility_gate(league_ref: str):
|
def get_league_with_visibility_gate(league_ref: str):
|
||||||
"""Load league; if private, enforce login + membership. Returns (league, None) or (None, redirect)."""
|
"""Load league; if private, enforce login + membership. Returns (league, None) or (None, redirect)."""
|
||||||
from league_repositories import find_league_by_public_key, user_has_league_role
|
from boker.league_repositories import find_league_by_public_key, user_has_league_role
|
||||||
|
|
||||||
_slug, public_key = split_league_ref(league_ref)
|
_slug, public_key = split_league_ref(league_ref)
|
||||||
league = find_league_by_public_key(public_key)
|
league = find_league_by_public_key(public_key)
|
||||||
@@ -99,8 +99,8 @@ def empty_session_summary(session) -> SessionSummary:
|
|||||||
|
|
||||||
|
|
||||||
def session_ref_map(league_id: str) -> dict[str, str]:
|
def session_ref_map(league_id: str) -> dict[str, str]:
|
||||||
from ledger_repositories import session_event_ref
|
from boker.ledger_repositories import session_event_ref
|
||||||
from league_repositories import list_sessions_for_league
|
from boker.league_repositories import list_sessions_for_league
|
||||||
|
|
||||||
return {session_event_ref(session): session.id for session in list_sessions_for_league(league_id)}
|
return {session_event_ref(session): session.id for session in list_sessions_for_league(league_id)}
|
||||||
|
|
||||||
@@ -112,8 +112,8 @@ def index():
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return render_template("leagues_index.html", leagues=[])
|
return render_template("leagues_index.html", leagues=[])
|
||||||
|
|
||||||
from league_repositories import list_leagues_for_user
|
from boker.league_repositories import list_leagues_for_user
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
|
|
||||||
memberships = list_leagues_for_user(current_user_id() or "")
|
memberships = list_leagues_for_user(current_user_id() or "")
|
||||||
league_summaries = []
|
league_summaries = []
|
||||||
@@ -190,8 +190,8 @@ def new():
|
|||||||
}
|
}
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
from league_repositories import create_league, unique_league_slug
|
from boker.league_repositories import create_league, unique_league_slug
|
||||||
|
|
||||||
owner = db.session.get(User, current_user_id())
|
owner = db.session.get(User, current_user_id())
|
||||||
if owner is None:
|
if owner is None:
|
||||||
@@ -221,8 +221,8 @@ def dashboard(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import league_counts, user_has_league_role
|
from boker.league_repositories import league_counts, user_has_league_role
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
|
|
||||||
league, resp = get_league_with_visibility_gate(league_ref)
|
league, resp = get_league_with_visibility_gate(league_ref)
|
||||||
if resp:
|
if resp:
|
||||||
@@ -289,7 +289,7 @@ def legacy_dashboard_redirect(league_id: str, league_slug: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_league_by_id, user_has_league_role
|
from boker.league_repositories import find_league_by_id, user_has_league_role
|
||||||
|
|
||||||
league = find_league_by_id(league_id)
|
league = find_league_by_id(league_id)
|
||||||
if league is None:
|
if league is None:
|
||||||
@@ -306,8 +306,8 @@ def leaderboard(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
from league_repositories import (
|
from boker.league_repositories import (
|
||||||
list_players_for_league,
|
list_players_for_league,
|
||||||
list_seasons_for_league,
|
list_seasons_for_league,
|
||||||
user_has_league_role,
|
user_has_league_role,
|
||||||
@@ -328,10 +328,10 @@ def leaderboard(league_ref: str):
|
|||||||
selected_season_id = request.args.get("season", "").strip()
|
selected_season_id = request.args.get("season", "").strip()
|
||||||
selected_season = None
|
selected_season = None
|
||||||
if selected_season_id:
|
if selected_season_id:
|
||||||
from league_repositories import find_season, list_sessions_for_league
|
from boker.league_repositories import find_season, list_sessions_for_league
|
||||||
selected_season = find_season(league.id, selected_season_id)
|
selected_season = find_season(league.id, selected_season_id)
|
||||||
if selected_season:
|
if selected_season:
|
||||||
from ledger_repositories import session_event_ref
|
from boker.ledger_repositories import session_event_ref
|
||||||
season_db_sessions = list_sessions_for_league(league.id)
|
season_db_sessions = list_sessions_for_league(league.id)
|
||||||
season_refs = {
|
season_refs = {
|
||||||
session_event_ref(s)
|
session_event_ref(s)
|
||||||
@@ -442,12 +442,12 @@ def ledger(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import (
|
from boker.ledger_repositories import (
|
||||||
append_ledger_event,
|
append_ledger_event,
|
||||||
list_all_event_rows_for_league,
|
list_all_event_rows_for_league,
|
||||||
list_event_rows_for_league,
|
list_event_rows_for_league,
|
||||||
)
|
)
|
||||||
from league_repositories import (
|
from boker.league_repositories import (
|
||||||
find_player_for_league,
|
find_player_for_league,
|
||||||
find_session_for_league,
|
find_session_for_league,
|
||||||
list_players_for_league,
|
list_players_for_league,
|
||||||
@@ -579,7 +579,7 @@ def void_event(league_ref: str, event_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import void_ledger_event
|
from boker.ledger_repositories import void_ledger_event
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
reason = request.form.get("void_reason", "").strip()
|
reason = request.form.get("void_reason", "").strip()
|
||||||
@@ -602,8 +602,8 @@ def player_detail(league_ref: str, player_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
from league_repositories import find_player_for_league, user_has_league_role
|
from boker.league_repositories import find_player_for_league, user_has_league_role
|
||||||
|
|
||||||
league, resp = get_league_with_visibility_gate(league_ref)
|
league, resp = get_league_with_visibility_gate(league_ref)
|
||||||
if resp:
|
if resp:
|
||||||
@@ -661,7 +661,7 @@ def players(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import (
|
from boker.league_repositories import (
|
||||||
create_player,
|
create_player,
|
||||||
list_players_for_league,
|
list_players_for_league,
|
||||||
player_name_exists,
|
player_name_exists,
|
||||||
@@ -700,7 +700,7 @@ def players(league_ref: str):
|
|||||||
flash("Player added.", "success")
|
flash("Player added.", "success")
|
||||||
return redirect(url_for("leagues.players", **league_url_values(league)))
|
return redirect(url_for("leagues.players", **league_url_values(league)))
|
||||||
|
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
|
|
||||||
all_sessions = build_session_summaries(list_event_rows_for_league(league.id))
|
all_sessions = build_session_summaries(list_event_rows_for_league(league.id))
|
||||||
board = build_leaderboard(all_sessions, league.break_even_cents)
|
board = build_leaderboard(all_sessions, league.break_even_cents)
|
||||||
@@ -734,7 +734,7 @@ def update_player_status(league_ref: str, player_id: str, status: str, message:
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_player_for_league, set_player_status
|
from boker.league_repositories import find_player_for_league, set_player_status
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
player = find_player_for_league(league.id, player_id)
|
player = find_player_for_league(league.id, player_id)
|
||||||
@@ -754,7 +754,7 @@ def edit_player(league_ref: str, player_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_player_for_league, player_name_taken, update_player
|
from boker.league_repositories import find_player_for_league, player_name_taken, update_player
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
player = find_player_for_league(league.id, player_id)
|
player = find_player_for_league(league.id, player_id)
|
||||||
@@ -782,8 +782,8 @@ def sessions(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import append_ledger_event
|
from boker.ledger_repositories import append_ledger_event
|
||||||
from league_repositories import (
|
from boker.league_repositories import (
|
||||||
create_poker_session,
|
create_poker_session,
|
||||||
list_seasons_for_league,
|
list_seasons_for_league,
|
||||||
list_sessions_for_league,
|
list_sessions_for_league,
|
||||||
@@ -816,10 +816,10 @@ def sessions(league_ref: str):
|
|||||||
status = "closed" if form["status"] == "closed" else "open"
|
status = "closed" if form["status"] == "closed" else "open"
|
||||||
season_id = form["season_id"] or None
|
season_id = form["season_id"] or None
|
||||||
if season_id is None:
|
if season_id is None:
|
||||||
from league_repositories import auto_assign_sessions_to_seasons as _auto
|
from boker.league_repositories import auto_assign_sessions_to_seasons as _auto
|
||||||
# try auto-assign: create the session first, then let the
|
# try auto-assign: create the session first, then let the
|
||||||
# function match it; we pass a temporary session date check inline
|
# function match it; we pass a temporary session date check inline
|
||||||
from league_repositories import list_seasons_for_league as _ls
|
from boker.league_repositories import list_seasons_for_league as _ls
|
||||||
eligible = [
|
eligible = [
|
||||||
s for s in _ls(league.id, include_archived=False)
|
s for s in _ls(league.id, include_archived=False)
|
||||||
if s.start_date and s.end_date
|
if s.start_date and s.end_date
|
||||||
@@ -836,7 +836,7 @@ def sessions(league_ref: str):
|
|||||||
session.label = form["label"] or None
|
session.label = form["label"] or None
|
||||||
session.notes = form["notes"] or None
|
session.notes = form["notes"] or None
|
||||||
if status == "open":
|
if status == "open":
|
||||||
from league_repositories import set_session_status
|
from boker.league_repositories import set_session_status
|
||||||
|
|
||||||
set_session_status(session, "open")
|
set_session_status(session, "open")
|
||||||
|
|
||||||
@@ -854,8 +854,8 @@ def sessions(league_ref: str):
|
|||||||
flash(f"Created {session.display_label}.", "success")
|
flash(f"Created {session.display_label}.", "success")
|
||||||
return redirect(url_for("leagues.sessions", **league_url_values(league)))
|
return redirect(url_for("leagues.sessions", **league_url_values(league)))
|
||||||
|
|
||||||
from db_models import LedgerEvent
|
from boker.db_models import LedgerEvent
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
|
|
||||||
all_sessions = list_sessions_for_league(league.id)
|
all_sessions = list_sessions_for_league(league.id)
|
||||||
summaries = build_session_summaries(list_event_rows_for_league(league.id))
|
summaries = build_session_summaries(list_event_rows_for_league(league.id))
|
||||||
@@ -908,8 +908,8 @@ def prune_empty_sessions(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from db_models import LedgerEvent, PokerSession
|
from boker.db_models import LedgerEvent, PokerSession
|
||||||
from league_repositories import list_sessions_for_league
|
from boker.league_repositories import list_sessions_for_league
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
|
|
||||||
@@ -944,7 +944,7 @@ def delete_session(league_ref: str, session_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_session_for_league
|
from boker.league_repositories import find_session_for_league
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
session = find_session_for_league(league.id, session_id)
|
session = find_session_for_league(league.id, session_id)
|
||||||
@@ -964,7 +964,7 @@ def edit_session(league_ref: str, session_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_session_for_league
|
from boker.league_repositories import find_session_for_league
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
session = find_session_for_league(league.id, session_id)
|
session = find_session_for_league(league.id, session_id)
|
||||||
@@ -983,7 +983,7 @@ def edit_session(league_ref: str, session_id: str):
|
|||||||
return redirect(url_for("leagues.session_detail", league_ref=league.url_ref, session_id=session_id))
|
return redirect(url_for("leagues.session_detail", league_ref=league.url_ref, session_id=session_id))
|
||||||
|
|
||||||
if new_date != session.session_date:
|
if new_date != session.session_date:
|
||||||
from db_models import PokerSession as _PS
|
from boker.db_models import PokerSession as _PS
|
||||||
max_seq = db.session.query(db.func.max(_PS.sequence_on_date)).filter(
|
max_seq = db.session.query(db.func.max(_PS.sequence_on_date)).filter(
|
||||||
_PS.league_id == league.id,
|
_PS.league_id == league.id,
|
||||||
_PS.session_date == new_date,
|
_PS.session_date == new_date,
|
||||||
@@ -993,7 +993,7 @@ def edit_session(league_ref: str, session_id: str):
|
|||||||
session.session_date = new_date
|
session.session_date = new_date
|
||||||
|
|
||||||
if new_season_id is not None:
|
if new_season_id is not None:
|
||||||
from league_repositories import find_season
|
from boker.league_repositories import find_season
|
||||||
valid = find_season(league.id, new_season_id)
|
valid = find_season(league.id, new_season_id)
|
||||||
new_season_id = valid.id if valid else None
|
new_season_id = valid.id if valid else None
|
||||||
|
|
||||||
@@ -1012,8 +1012,8 @@ def session_detail(league_ref: str, session_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import append_ledger_event, list_all_event_rows_for_session, list_event_rows_for_session
|
from boker.ledger_repositories import append_ledger_event, list_all_event_rows_for_session, list_event_rows_for_session
|
||||||
from league_repositories import (
|
from boker.league_repositories import (
|
||||||
find_session_for_league,
|
find_session_for_league,
|
||||||
list_players_for_league,
|
list_players_for_league,
|
||||||
list_seasons_for_league,
|
list_seasons_for_league,
|
||||||
@@ -1101,9 +1101,9 @@ def session_public_view(league_ref: str, session_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from charts import session_breakdown_series
|
from boker.charts import session_breakdown_series
|
||||||
from ledger_repositories import list_event_rows_for_league, list_event_rows_for_session
|
from boker.ledger_repositories import list_event_rows_for_league, list_event_rows_for_session
|
||||||
from league_repositories import find_league_by_public_key, find_season, find_session_for_league, list_sessions_for_league, user_has_league_role
|
from boker.league_repositories import find_league_by_public_key, find_season, find_session_for_league, list_sessions_for_league, user_has_league_role
|
||||||
|
|
||||||
_slug, public_key = split_league_ref(league_ref)
|
_slug, public_key = split_league_ref(league_ref)
|
||||||
league = find_league_by_public_key(public_key)
|
league = find_league_by_public_key(public_key)
|
||||||
@@ -1182,8 +1182,8 @@ def update_session_status(league_ref: str, session_id: str, status: str, message
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import append_ledger_event
|
from boker.ledger_repositories import append_ledger_event
|
||||||
from league_repositories import find_session_for_league, set_session_status
|
from boker.league_repositories import find_session_for_league, set_session_status
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
session = find_session_for_league(league.id, session_id)
|
session = find_session_for_league(league.id, session_id)
|
||||||
@@ -1214,7 +1214,7 @@ def seasons(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import (
|
from boker.league_repositories import (
|
||||||
create_season,
|
create_season,
|
||||||
list_seasons_for_league,
|
list_seasons_for_league,
|
||||||
user_has_league_role,
|
user_has_league_role,
|
||||||
@@ -1283,7 +1283,7 @@ def auto_assign_seasons(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import auto_assign_sessions_to_seasons
|
from boker.league_repositories import auto_assign_sessions_to_seasons
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
count = auto_assign_sessions_to_seasons(league.id)
|
count = auto_assign_sessions_to_seasons(league.id)
|
||||||
@@ -1302,7 +1302,7 @@ def update_season(league_ref: str, season_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_season, update_season as repo_update_season
|
from boker.league_repositories import find_season, update_season as repo_update_season
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
season = find_season(league.id, season_id)
|
season = find_season(league.id, season_id)
|
||||||
@@ -1338,7 +1338,7 @@ def archive_season(league_ref: str, season_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import archive_season as repo_archive, find_season
|
from boker.league_repositories import archive_season as repo_archive, find_season
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
season = find_season(league.id, season_id)
|
season = find_season(league.id, season_id)
|
||||||
@@ -1359,7 +1359,7 @@ def unarchive_season(league_ref: str, season_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import find_season, unarchive_season as repo_unarchive
|
from boker.league_repositories import find_season, unarchive_season as repo_unarchive
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
season = find_season(league.id, season_id)
|
season = find_season(league.id, season_id)
|
||||||
@@ -1380,7 +1380,7 @@ def delete_season(league_ref: str, season_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import delete_season as repo_delete, find_season
|
from boker.league_repositories import delete_season as repo_delete, find_season
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
season = find_season(league.id, season_id)
|
season = find_season(league.id, season_id)
|
||||||
@@ -1437,7 +1437,7 @@ def league_settings(league_ref: str):
|
|||||||
elif break_even_cents < 0 or break_even_cents > 10000:
|
elif break_even_cents < 0 or break_even_cents > 10000:
|
||||||
flash("Break-even threshold must be between $0.00 and $100.00.", "error")
|
flash("Break-even threshold must be between $0.00 and $100.00.", "error")
|
||||||
else:
|
else:
|
||||||
from utils import slugify
|
from boker.utils import slugify
|
||||||
|
|
||||||
league.name = form["name"]
|
league.name = form["name"]
|
||||||
league.slug = slugify(form["name"])
|
league.slug = slugify(form["name"])
|
||||||
@@ -1449,7 +1449,7 @@ def league_settings(league_ref: str):
|
|||||||
flash("League settings saved.", "success")
|
flash("League settings saved.", "success")
|
||||||
return redirect(url_for("leagues.league_settings", league_ref=league.url_ref))
|
return redirect(url_for("leagues.league_settings", league_ref=league.url_ref))
|
||||||
|
|
||||||
from league_repositories import list_members_for_league
|
from boker.league_repositories import list_members_for_league
|
||||||
|
|
||||||
members = list_members_for_league(league.id)
|
members = list_members_for_league(league.id)
|
||||||
return render_template("league_settings.html", league=league, form=form, is_owner=True, can_manage=True, members=members)
|
return render_template("league_settings.html", league=league, form=form, is_owner=True, can_manage=True, members=members)
|
||||||
@@ -1462,11 +1462,11 @@ def invite_member(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from auth import generate_invite_token
|
from boker.auth import generate_invite_token
|
||||||
from emails import send_league_invite
|
from boker.emails import send_league_invite
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
from league_repositories import find_membership, find_user_by_email
|
from boker.league_repositories import find_membership, find_user_by_email
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
email = normalize_email(request.form.get("email", ""))
|
email = normalize_email(request.form.get("email", ""))
|
||||||
@@ -1509,7 +1509,7 @@ def remove_member(league_ref: str, user_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import remove_league_member
|
from boker.league_repositories import remove_league_member
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
|
|
||||||
@@ -1530,7 +1530,7 @@ def update_member_role(league_ref: str, user_id: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import set_league_member_role
|
from boker.league_repositories import set_league_member_role
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
role = request.form.get("role", "").strip()
|
role = request.form.get("role", "").strip()
|
||||||
@@ -1560,7 +1560,7 @@ def transfer_ownership(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import transfer_league_ownership
|
from boker.league_repositories import transfer_league_ownership
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
new_owner_user_id = request.form.get("new_owner_user_id", "").strip()
|
new_owner_user_id = request.form.get("new_owner_user_id", "").strip()
|
||||||
@@ -1588,7 +1588,7 @@ def archive_league(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from db_models import utc_now
|
from boker.db_models import utc_now
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
confirm_name = request.form.get("confirm_name", "").strip()
|
confirm_name = request.form.get("confirm_name", "").strip()
|
||||||
@@ -1610,7 +1610,7 @@ def delete_league_route(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from league_repositories import delete_league
|
from boker.league_repositories import delete_league
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner"})
|
league = require_league(league_ref, {"owner"})
|
||||||
confirm_name = request.form.get("confirm_name", "").strip()
|
confirm_name = request.form.get("confirm_name", "").strip()
|
||||||
@@ -1633,8 +1633,8 @@ def export_ledger_csv(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from ledger_repositories import list_event_rows_for_league
|
from boker.ledger_repositories import list_event_rows_for_league
|
||||||
from storage import CSV_HEADERS
|
from boker.storage import CSV_HEADERS
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
rows = list_event_rows_for_league(league.id)
|
rows = list_event_rows_for_league(league.id)
|
||||||
@@ -1659,14 +1659,14 @@ def import_ledger_csv(league_ref: str):
|
|||||||
flash("League database is not available.", "error")
|
flash("League database is not available.", "error")
|
||||||
return redirect(url_for("public.home"))
|
return redirect(url_for("public.home"))
|
||||||
|
|
||||||
from db_models import CANONICAL_EVENT_TYPES, canonical_event_type
|
from boker.db_models import CANONICAL_EVENT_TYPES, canonical_event_type
|
||||||
from ledger_repositories import (
|
from boker.ledger_repositories import (
|
||||||
append_ledger_event,
|
append_ledger_event,
|
||||||
list_ledger_events_for_league,
|
list_ledger_events_for_league,
|
||||||
session_event_ref,
|
session_event_ref,
|
||||||
)
|
)
|
||||||
from league_repositories import create_player, list_players_for_league, list_sessions_for_league, unique_player_slug
|
from boker.league_repositories import create_player, list_players_for_league, list_sessions_for_league, unique_player_slug
|
||||||
from storage import CSV_HEADERS
|
from boker.storage import CSV_HEADERS
|
||||||
|
|
||||||
league = require_league(league_ref, {"owner", "manager"})
|
league = require_league(league_ref, {"owner", "manager"})
|
||||||
|
|
||||||
@@ -1700,7 +1700,7 @@ def import_ledger_csv(league_ref: str):
|
|||||||
flash(f"CSV header mismatch. Expected: {', '.join(CSV_HEADERS)}", "error")
|
flash(f"CSV header mismatch. Expected: {', '.join(CSV_HEADERS)}", "error")
|
||||||
return redirect(url_for("leagues.ledger", **league_url_values(league)))
|
return redirect(url_for("leagues.ledger", **league_url_values(league)))
|
||||||
|
|
||||||
from db_models import PokerSession, make_session
|
from boker.db_models import PokerSession, make_session
|
||||||
|
|
||||||
sessions = list_sessions_for_league(league.id)
|
sessions = list_sessions_for_league(league.id)
|
||||||
session_by_ref = {session_event_ref(s): s for s in sessions}
|
session_by_ref = {session_event_ref(s): s for s in sessions}
|
||||||
@@ -4,16 +4,16 @@ from __future__ import annotations
|
|||||||
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
||||||
from charts import cumulative_profit_series, player_session_series, session_breakdown_series
|
from boker.charts import cumulative_profit_series, player_session_series, session_breakdown_series
|
||||||
from config import ELIGIBLE_MIN_SESSIONS
|
from boker.config import ELIGIBLE_MIN_SESSIONS
|
||||||
from services import (
|
from boker.services import (
|
||||||
apply_rank_changes,
|
apply_rank_changes,
|
||||||
build_leaderboard,
|
build_leaderboard,
|
||||||
build_session_summaries,
|
build_session_summaries,
|
||||||
session_events,
|
session_events,
|
||||||
)
|
)
|
||||||
from storage import load_events
|
from boker.storage import load_events
|
||||||
from utils import session_label, session_sort_key
|
from boker.utils import session_label, session_sort_key
|
||||||
|
|
||||||
public_bp = Blueprint("public", __name__)
|
public_bp = Blueprint("public", __name__)
|
||||||
|
|
||||||
@@ -40,9 +40,9 @@ def terms():
|
|||||||
|
|
||||||
@public_bp.get("/explore")
|
@public_bp.get("/explore")
|
||||||
def explore():
|
def explore():
|
||||||
from auth import is_logged_in
|
from boker.auth import is_logged_in
|
||||||
from db import database_extensions_available
|
from boker.db import database_extensions_available
|
||||||
from league_repositories import league_counts, list_public_leagues
|
from boker.league_repositories import league_counts, list_public_leagues
|
||||||
|
|
||||||
q = request.args.get("q", "").strip()
|
q = request.args.get("q", "").strip()
|
||||||
leagues = list_public_leagues(q) if database_extensions_available() else []
|
leagues = list_public_leagues(q) if database_extensions_available() else []
|
||||||
@@ -4,9 +4,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from models import PlayerStats, SessionEntry, SessionSummary
|
from boker.models import PlayerStats, SessionEntry, SessionSummary
|
||||||
from storage import EventRow
|
from boker.storage import EventRow
|
||||||
from utils import entry_sort_key, net_result_bucket, session_sort_key
|
from boker.utils import entry_sort_key, net_result_bucket, session_sort_key
|
||||||
|
|
||||||
SESSION_MARKER_TYPES = {"session_open", "session_close"}
|
SESSION_MARKER_TYPES = {"session_open", "session_close"}
|
||||||
|
|
||||||
File renamed without changes.
@@ -7,7 +7,7 @@ import re
|
|||||||
|
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
|
|
||||||
from models import SessionEntry, SessionSummary
|
from boker.models import SessionEntry, SessionSummary
|
||||||
|
|
||||||
BREAK_EVEN_TOLERANCE_CENTS = 100
|
BREAK_EVEN_TOLERANCE_CENTS = 100
|
||||||
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from charts import player_session_series
|
from boker.charts import player_session_series
|
||||||
from services import (
|
from boker.services import (
|
||||||
build_leaderboard,
|
build_leaderboard,
|
||||||
build_session_summaries,
|
build_session_summaries,
|
||||||
pending_payout_carry_items,
|
pending_payout_carry_items,
|
||||||
prunable_empty_session_ids,
|
prunable_empty_session_ids,
|
||||||
)
|
)
|
||||||
from utils import session_sort_key
|
from boker.utils import session_sort_key
|
||||||
|
|
||||||
|
|
||||||
def cents(amount: float) -> int:
|
def cents(amount: float) -> int:
|
||||||
|
|||||||
+15
-15
@@ -4,8 +4,8 @@ from datetime import datetime, timedelta, timezone
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from app import create_app
|
from app import create_app
|
||||||
from auth import hash_password
|
from boker.auth import hash_password
|
||||||
from db import db
|
from boker.db import db
|
||||||
|
|
||||||
|
|
||||||
class InternalAdminAccessTests(unittest.TestCase):
|
class InternalAdminAccessTests(unittest.TestCase):
|
||||||
@@ -22,7 +22,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
self.client = self.app.test_client()
|
self.client = self.app.test_client()
|
||||||
|
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League, LeagueMembership, User
|
from boker.db_models import League, LeagueMembership, User
|
||||||
|
|
||||||
db.create_all()
|
db.create_all()
|
||||||
self.user = User(
|
self.user = User(
|
||||||
@@ -118,7 +118,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_admin_users_page_is_paginated(self):
|
def test_admin_users_page_is_paginated(self):
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
users = [
|
users = [
|
||||||
User(email=f"bulk{i:02d}@example.com", password_hash=hash_password("password123"))
|
User(email=f"bulk{i:02d}@example.com", password_hash=hash_password("password123"))
|
||||||
@@ -136,7 +136,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_admin_leagues_page_is_paginated(self):
|
def test_admin_leagues_page_is_paginated(self):
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League
|
from boker.db_models import League
|
||||||
|
|
||||||
leagues = [
|
leagues = [
|
||||||
League(
|
League(
|
||||||
@@ -160,8 +160,8 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_session_growth_uses_session_date_not_created_at(self):
|
def test_session_growth_uses_session_date_not_created_at(self):
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import PokerSession
|
from boker.db_models import PokerSession
|
||||||
from routes.internal import _growth_data
|
from boker.routes.internal import _growth_data
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
old_session = PokerSession(
|
old_session = PokerSession(
|
||||||
@@ -196,7 +196,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = db.session.get(User, self.user_id)
|
user = db.session.get(User, self.user_id)
|
||||||
self.assertEqual(user.email, "viewer@example.com")
|
self.assertEqual(user.email, "viewer@example.com")
|
||||||
@@ -212,7 +212,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
user = db.session.get(User, self.user_id)
|
user = db.session.get(User, self.user_id)
|
||||||
self.assertEqual(user.email, "changed@example.com")
|
self.assertEqual(user.email, "changed@example.com")
|
||||||
@@ -220,7 +220,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
def test_admin_delete_user_blocks_owned_leagues(self):
|
def test_admin_delete_user_blocks_owned_leagues(self):
|
||||||
self.login_as(self.admin_id)
|
self.login_as(self.admin_id)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League, LeagueMembership
|
from boker.db_models import League, LeagueMembership
|
||||||
|
|
||||||
owned_league = League(
|
owned_league = League(
|
||||||
name="Manager Owned Poker",
|
name="Manager Owned Poker",
|
||||||
@@ -241,7 +241,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import User
|
from boker.db_models import User
|
||||||
|
|
||||||
self.assertIsNotNone(db.session.get(User, self.manager_id))
|
self.assertIsNotNone(db.session.get(User, self.manager_id))
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
archive_response = self.client.post(f"/internal/leagues/{self.league_id}/archive")
|
archive_response = self.client.post(f"/internal/leagues/{self.league_id}/archive")
|
||||||
self.assertEqual(archive_response.status_code, 302)
|
self.assertEqual(archive_response.status_code, 302)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League
|
from boker.db_models import League
|
||||||
|
|
||||||
league = db.session.get(League, self.league_id)
|
league = db.session.get(League, self.league_id)
|
||||||
self.assertIsNotNone(league.archived_at)
|
self.assertIsNotNone(league.archived_at)
|
||||||
@@ -259,7 +259,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
restore_response = self.client.post(f"/internal/leagues/{self.league_id}/restore")
|
restore_response = self.client.post(f"/internal/leagues/{self.league_id}/restore")
|
||||||
self.assertEqual(restore_response.status_code, 302)
|
self.assertEqual(restore_response.status_code, 302)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League
|
from boker.db_models import League
|
||||||
|
|
||||||
league = db.session.get(League, self.league_id)
|
league = db.session.get(League, self.league_id)
|
||||||
self.assertIsNone(league.archived_at)
|
self.assertIsNone(league.archived_at)
|
||||||
@@ -280,7 +280,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League
|
from boker.db_models import League
|
||||||
|
|
||||||
league = db.session.get(League, self.league_id)
|
league = db.session.get(League, self.league_id)
|
||||||
self.assertEqual(league.name, "Saturday Poker")
|
self.assertEqual(league.name, "Saturday Poker")
|
||||||
@@ -300,7 +300,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
with self.app.app_context():
|
with self.app.app_context():
|
||||||
from db_models import League, LeagueMembership
|
from boker.db_models import League, LeagueMembership
|
||||||
|
|
||||||
league = db.session.get(League, self.league_id)
|
league = db.session.get(League, self.league_id)
|
||||||
self.assertEqual(league.created_by_user_id, self.manager_id)
|
self.assertEqual(league.created_by_user_id, self.manager_id)
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from models import SessionEntry, SessionSummary
|
from boker.models import SessionEntry, SessionSummary
|
||||||
from services import build_leaderboard
|
from boker.services import build_leaderboard
|
||||||
|
|
||||||
|
|
||||||
class CashInAccountingTest(unittest.TestCase):
|
class CashInAccountingTest(unittest.TestCase):
|
||||||
|
|||||||
Reference in new issue
Block a user