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
|
||||
- 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
|
||||
|
||||
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
|
||||
from __future__ import annotations
|
||||
|
||||
import click
|
||||
import os
|
||||
from flask import Flask, render_template
|
||||
from boker.app import app, create_app
|
||||
|
||||
from auth import current_user_id, is_logged_in, is_site_admin, normalize_email
|
||||
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
|
||||
__all__ = ["app", "create_app"]
|
||||
|
||||
|
||||
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__":
|
||||
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:
|
||||
return False
|
||||
|
||||
from db import database_extensions_available
|
||||
from boker.db import database_extensions_available
|
||||
|
||||
if not database_extensions_available():
|
||||
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()
|
||||
return bool(user and user.is_site_admin)
|
||||
@@ -4,8 +4,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from models import SessionEntry, SessionSummary
|
||||
from utils import net_result_bucket, session_chart_label, session_sort_key
|
||||
from boker.models import SessionEntry, SessionSummary
|
||||
from boker.utils import net_result_bucket, session_chart_label, session_sort_key
|
||||
|
||||
PLAYER_PALETTE = [
|
||||
"#9b8cf0", # --line-1 violet
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
DATA_PATH = BASE_DIR / "data" / "entries.csv"
|
||||
DEFAULT_DATABASE_URL = f"sqlite:///{BASE_DIR / 'data' / 'boker-dev.sqlite3'}"
|
||||
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
|
||||
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)
|
||||
migrate.init_app(app, db)
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
import uuid
|
||||
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.
|
||||
raise RuntimeError("Database dependencies are not installed.")
|
||||
@@ -7,7 +7,7 @@ import socket
|
||||
from flask import current_app
|
||||
from flask_mail import Message
|
||||
|
||||
from extensions import mail
|
||||
from boker.extensions import mail
|
||||
|
||||
|
||||
def _mail_send_suppressed() -> bool:
|
||||
File renamed without changes.
@@ -5,13 +5,13 @@ import secrets
|
||||
import string
|
||||
from datetime import date
|
||||
|
||||
from auth import hash_password, normalize_email
|
||||
from db import db
|
||||
from boker.auth import hash_password, normalize_email
|
||||
from boker.db import db
|
||||
|
||||
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
||||
raise RuntimeError("Database dependencies are not installed.")
|
||||
|
||||
from db_models import ( # noqa: E402
|
||||
from boker.db_models import ( # noqa: E402
|
||||
LedgerEvent,
|
||||
League,
|
||||
LeagueMembership,
|
||||
@@ -24,7 +24,7 @@ from db_models import ( # noqa: E402
|
||||
make_user,
|
||||
normalize_lookup,
|
||||
)
|
||||
from utils import slugify
|
||||
from boker.utils import slugify
|
||||
|
||||
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"}:
|
||||
raise ValueError(f"Unsupported session status: {status}")
|
||||
|
||||
from db_models import utc_now
|
||||
from boker.db_models import utc_now
|
||||
|
||||
session.status = status
|
||||
if status == "open":
|
||||
@@ -323,7 +323,7 @@ def add_league_member(
|
||||
|
||||
|
||||
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(
|
||||
league_id=league_id,
|
||||
@@ -421,7 +421,7 @@ def delete_season(season: Season) -> 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)
|
||||
PokerSession.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
|
||||
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.
|
||||
raise RuntimeError("Database dependencies are not installed.")
|
||||
|
||||
from db_models import ( # noqa: E402
|
||||
from boker.db_models import ( # noqa: E402
|
||||
CANONICAL_EVENT_TYPES,
|
||||
LedgerEvent,
|
||||
Player,
|
||||
@@ -14,7 +14,7 @@ from db_models import ( # noqa: E402
|
||||
User,
|
||||
canonical_event_type,
|
||||
)
|
||||
from storage import EventRow # noqa: E402
|
||||
from boker.storage import EventRow # noqa: E402
|
||||
|
||||
|
||||
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 auth import (
|
||||
from boker.auth import (
|
||||
current_user_id,
|
||||
generate_invite_token,
|
||||
generate_reset_token,
|
||||
@@ -20,8 +20,8 @@ from auth import (
|
||||
verify_password,
|
||||
verify_reset_token,
|
||||
)
|
||||
from db import database_extensions_available, db
|
||||
from extensions import limiter
|
||||
from boker.db import database_extensions_available, db
|
||||
from boker.extensions import limiter
|
||||
|
||||
account_bp = Blueprint("account", __name__, url_prefix="/account")
|
||||
EMAIL_VERIFICATION_TTL = timedelta(minutes=15)
|
||||
@@ -64,7 +64,7 @@ def _new_verification_code() -> str:
|
||||
|
||||
|
||||
def _issue_verification_code(user) -> str:
|
||||
from db_models import utc_now
|
||||
from boker.db_models import utc_now
|
||||
|
||||
code = _new_verification_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:
|
||||
from emails import send_email_verification_code
|
||||
from boker.emails import send_email_verification_code
|
||||
|
||||
code = _issue_verification_code(user)
|
||||
db.session.commit()
|
||||
@@ -112,7 +112,7 @@ def register():
|
||||
}
|
||||
|
||||
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"])
|
||||
password = request.form.get("password", "")
|
||||
@@ -158,7 +158,7 @@ def login():
|
||||
}
|
||||
|
||||
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"])
|
||||
password = request.form.get("password", "")
|
||||
@@ -195,7 +195,7 @@ def verify_email():
|
||||
flash("Account database is not available.", "error")
|
||||
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 = 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")
|
||||
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 = db.session.get(User, user_id) if user_id else None
|
||||
@@ -275,7 +275,7 @@ def settings():
|
||||
flash("Account database is not available.", "error")
|
||||
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())
|
||||
if user is None:
|
||||
@@ -292,7 +292,7 @@ def update_email():
|
||||
flash("Account database is not available.", "error")
|
||||
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())
|
||||
if user is None:
|
||||
@@ -307,7 +307,7 @@ def update_email():
|
||||
elif not verify_password(user.password_hash, current_password):
|
||||
flash("Current password is incorrect.", "error")
|
||||
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)
|
||||
if existing and existing.id != user.id:
|
||||
@@ -327,7 +327,7 @@ def update_password():
|
||||
flash("Account database is not available.", "error")
|
||||
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())
|
||||
if user is None:
|
||||
@@ -359,7 +359,7 @@ def disable_account():
|
||||
flash("Account database is not available.", "error")
|
||||
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())
|
||||
if user is None:
|
||||
@@ -395,8 +395,8 @@ def delete_account():
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.settings"))
|
||||
|
||||
from db_models import League, LeagueMembership, User, utc_now
|
||||
from league_repositories import delete_league
|
||||
from boker.db_models import League, LeagueMembership, User, utc_now
|
||||
from boker.league_repositories import delete_league
|
||||
|
||||
user = db.session.get(User, current_user_id())
|
||||
if user is None:
|
||||
@@ -436,9 +436,9 @@ def forgot_password():
|
||||
flash("Account database is not available.", "error")
|
||||
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 league_repositories import find_user_by_email
|
||||
from boker.league_repositories import find_user_by_email
|
||||
|
||||
email = normalize_email(request.form.get("email", ""))
|
||||
user = find_user_by_email(email)
|
||||
@@ -472,7 +472,7 @@ def reset_password(token):
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.login"))
|
||||
|
||||
from db_models import User
|
||||
from boker.db_models import User
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
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")
|
||||
return redirect(url_for("leagues.index"))
|
||||
|
||||
from db_models import User
|
||||
from league_repositories import add_league_member, find_league_by_id, find_membership, find_user_by_email
|
||||
from boker.db_models import User
|
||||
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_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.exc import IntegrityError
|
||||
|
||||
from auth import (
|
||||
from boker.auth import (
|
||||
current_user_id,
|
||||
generate_reset_token,
|
||||
hash_password,
|
||||
normalize_email,
|
||||
site_admin_required,
|
||||
)
|
||||
from db import db
|
||||
from db_models import LedgerEvent, League, LeagueMembership, Player, PokerSession, User, utc_now
|
||||
from league_repositories import delete_league, transfer_league_ownership
|
||||
from utils import slugify
|
||||
from boker.db import db
|
||||
from boker.db_models import LedgerEvent, League, LeagueMembership, Player, PokerSession, User, utc_now
|
||||
from boker.league_repositories import delete_league, transfer_league_ownership
|
||||
from boker.utils import slugify
|
||||
|
||||
|
||||
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")
|
||||
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)
|
||||
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")
|
||||
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()
|
||||
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 auth import current_user_id, login_required, normalize_email
|
||||
from charts import cumulative_profit_series, player_session_series
|
||||
from db import database_extensions_available, db
|
||||
from models import SessionSummary
|
||||
from 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.auth import current_user_id, login_required, normalize_email
|
||||
from boker.charts import cumulative_profit_series, player_session_series
|
||||
from boker.db import database_extensions_available, db
|
||||
from boker.models import SessionSummary
|
||||
from boker.services import apply_rank_changes, build_leaderboard, build_session_summaries, session_events
|
||||
from boker.utils import cents_to_dollars, session_label, session_sort_key
|
||||
|
||||
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]):
|
||||
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)
|
||||
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):
|
||||
"""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)
|
||||
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]:
|
||||
from ledger_repositories import session_event_ref
|
||||
from league_repositories import list_sessions_for_league
|
||||
from boker.ledger_repositories import session_event_ref
|
||||
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)}
|
||||
|
||||
@@ -112,8 +112,8 @@ def index():
|
||||
flash("League database is not available.", "error")
|
||||
return render_template("leagues_index.html", leagues=[])
|
||||
|
||||
from league_repositories import list_leagues_for_user
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from boker.league_repositories import list_leagues_for_user
|
||||
from boker.ledger_repositories import list_event_rows_for_league
|
||||
|
||||
memberships = list_leagues_for_user(current_user_id() or "")
|
||||
league_summaries = []
|
||||
@@ -190,8 +190,8 @@ def new():
|
||||
}
|
||||
|
||||
if request.method == "POST":
|
||||
from db_models import User
|
||||
from league_repositories import create_league, unique_league_slug
|
||||
from boker.db_models import User
|
||||
from boker.league_repositories import create_league, unique_league_slug
|
||||
|
||||
owner = db.session.get(User, current_user_id())
|
||||
if owner is None:
|
||||
@@ -221,8 +221,8 @@ def dashboard(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from league_repositories import league_counts, user_has_league_role
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from boker.league_repositories import league_counts, user_has_league_role
|
||||
from boker.ledger_repositories import list_event_rows_for_league
|
||||
|
||||
league, resp = get_league_with_visibility_gate(league_ref)
|
||||
if resp:
|
||||
@@ -289,7 +289,7 @@ def legacy_dashboard_redirect(league_id: str, league_slug: str):
|
||||
flash("League database is not available.", "error")
|
||||
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)
|
||||
if league is None:
|
||||
@@ -306,8 +306,8 @@ def leaderboard(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from league_repositories import (
|
||||
from boker.ledger_repositories import list_event_rows_for_league
|
||||
from boker.league_repositories import (
|
||||
list_players_for_league,
|
||||
list_seasons_for_league,
|
||||
user_has_league_role,
|
||||
@@ -328,10 +328,10 @@ def leaderboard(league_ref: str):
|
||||
selected_season_id = request.args.get("season", "").strip()
|
||||
selected_season = None
|
||||
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)
|
||||
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_refs = {
|
||||
session_event_ref(s)
|
||||
@@ -442,12 +442,12 @@ def ledger(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import (
|
||||
from boker.ledger_repositories import (
|
||||
append_ledger_event,
|
||||
list_all_event_rows_for_league,
|
||||
list_event_rows_for_league,
|
||||
)
|
||||
from league_repositories import (
|
||||
from boker.league_repositories import (
|
||||
find_player_for_league,
|
||||
find_session_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")
|
||||
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"})
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from league_repositories import find_player_for_league, user_has_league_role
|
||||
from boker.ledger_repositories import list_event_rows_for_league
|
||||
from boker.league_repositories import find_player_for_league, user_has_league_role
|
||||
|
||||
league, resp = get_league_with_visibility_gate(league_ref)
|
||||
if resp:
|
||||
@@ -661,7 +661,7 @@ def players(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from league_repositories import (
|
||||
from boker.league_repositories import (
|
||||
create_player,
|
||||
list_players_for_league,
|
||||
player_name_exists,
|
||||
@@ -700,7 +700,7 @@ def players(league_ref: str):
|
||||
flash("Player added.", "success")
|
||||
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))
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import append_ledger_event
|
||||
from league_repositories import (
|
||||
from boker.ledger_repositories import append_ledger_event
|
||||
from boker.league_repositories import (
|
||||
create_poker_session,
|
||||
list_seasons_for_league,
|
||||
list_sessions_for_league,
|
||||
@@ -816,10 +816,10 @@ def sessions(league_ref: str):
|
||||
status = "closed" if form["status"] == "closed" else "open"
|
||||
season_id = form["season_id"] or 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
|
||||
# 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 = [
|
||||
s for s in _ls(league.id, include_archived=False)
|
||||
if s.start_date and s.end_date
|
||||
@@ -836,7 +836,7 @@ def sessions(league_ref: str):
|
||||
session.label = form["label"] or None
|
||||
session.notes = form["notes"] or None
|
||||
if status == "open":
|
||||
from league_repositories import set_session_status
|
||||
from boker.league_repositories import set_session_status
|
||||
|
||||
set_session_status(session, "open")
|
||||
|
||||
@@ -854,8 +854,8 @@ def sessions(league_ref: str):
|
||||
flash(f"Created {session.display_label}.", "success")
|
||||
return redirect(url_for("leagues.sessions", **league_url_values(league)))
|
||||
|
||||
from db_models import LedgerEvent
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from boker.db_models import LedgerEvent
|
||||
from boker.ledger_repositories import list_event_rows_for_league
|
||||
|
||||
all_sessions = list_sessions_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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from db_models import LedgerEvent, PokerSession
|
||||
from league_repositories import list_sessions_for_league
|
||||
from boker.db_models import LedgerEvent, PokerSession
|
||||
from boker.league_repositories import list_sessions_for_league
|
||||
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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))
|
||||
|
||||
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(
|
||||
_PS.league_id == league.id,
|
||||
_PS.session_date == new_date,
|
||||
@@ -993,7 +993,7 @@ def edit_session(league_ref: str, session_id: str):
|
||||
session.session_date = new_date
|
||||
|
||||
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)
|
||||
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")
|
||||
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 league_repositories import (
|
||||
from boker.ledger_repositories import append_ledger_event, list_all_event_rows_for_session, list_event_rows_for_session
|
||||
from boker.league_repositories import (
|
||||
find_session_for_league,
|
||||
list_players_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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from charts import session_breakdown_series
|
||||
from 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.charts import session_breakdown_series
|
||||
from boker.ledger_repositories import list_event_rows_for_league, list_event_rows_for_session
|
||||
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)
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import append_ledger_event
|
||||
from league_repositories import find_session_for_league, set_session_status
|
||||
from boker.ledger_repositories import append_ledger_event
|
||||
from boker.league_repositories import find_session_for_league, set_session_status
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from league_repositories import (
|
||||
from boker.league_repositories import (
|
||||
create_season,
|
||||
list_seasons_for_league,
|
||||
user_has_league_role,
|
||||
@@ -1283,7 +1283,7 @@ def auto_assign_seasons(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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:
|
||||
flash("Break-even threshold must be between $0.00 and $100.00.", "error")
|
||||
else:
|
||||
from utils import slugify
|
||||
from boker.utils import slugify
|
||||
|
||||
league.name = form["name"]
|
||||
league.slug = slugify(form["name"])
|
||||
@@ -1449,7 +1449,7 @@ def league_settings(league_ref: str):
|
||||
flash("League settings saved.", "success")
|
||||
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)
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from auth import generate_invite_token
|
||||
from emails import send_league_invite
|
||||
from boker.auth import generate_invite_token
|
||||
from boker.emails import send_league_invite
|
||||
from flask import current_app
|
||||
from db_models import User
|
||||
from league_repositories import find_membership, find_user_by_email
|
||||
from boker.db_models import User
|
||||
from boker.league_repositories import find_membership, find_user_by_email
|
||||
|
||||
league = require_league(league_ref, {"owner"})
|
||||
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")
|
||||
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"})
|
||||
|
||||
@@ -1530,7 +1530,7 @@ def update_member_role(league_ref: str, user_id: str):
|
||||
flash("League database is not available.", "error")
|
||||
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"})
|
||||
role = request.form.get("role", "").strip()
|
||||
@@ -1560,7 +1560,7 @@ def transfer_ownership(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
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"})
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from storage import CSV_HEADERS
|
||||
from boker.ledger_repositories import list_event_rows_for_league
|
||||
from boker.storage import CSV_HEADERS
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
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")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from db_models import CANONICAL_EVENT_TYPES, canonical_event_type
|
||||
from ledger_repositories import (
|
||||
from boker.db_models import CANONICAL_EVENT_TYPES, canonical_event_type
|
||||
from boker.ledger_repositories import (
|
||||
append_ledger_event,
|
||||
list_ledger_events_for_league,
|
||||
session_event_ref,
|
||||
)
|
||||
from league_repositories import create_player, list_players_for_league, list_sessions_for_league, unique_player_slug
|
||||
from storage import CSV_HEADERS
|
||||
from boker.league_repositories import create_player, list_players_for_league, list_sessions_for_league, unique_player_slug
|
||||
from boker.storage import CSV_HEADERS
|
||||
|
||||
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")
|
||||
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)
|
||||
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 current_app
|
||||
|
||||
from charts import cumulative_profit_series, player_session_series, session_breakdown_series
|
||||
from config import ELIGIBLE_MIN_SESSIONS
|
||||
from services import (
|
||||
from boker.charts import cumulative_profit_series, player_session_series, session_breakdown_series
|
||||
from boker.config import ELIGIBLE_MIN_SESSIONS
|
||||
from boker.services import (
|
||||
apply_rank_changes,
|
||||
build_leaderboard,
|
||||
build_session_summaries,
|
||||
session_events,
|
||||
)
|
||||
from storage import load_events
|
||||
from utils import session_label, session_sort_key
|
||||
from boker.storage import load_events
|
||||
from boker.utils import session_label, session_sort_key
|
||||
|
||||
public_bp = Blueprint("public", __name__)
|
||||
|
||||
@@ -40,9 +40,9 @@ def terms():
|
||||
|
||||
@public_bp.get("/explore")
|
||||
def explore():
|
||||
from auth import is_logged_in
|
||||
from db import database_extensions_available
|
||||
from league_repositories import league_counts, list_public_leagues
|
||||
from boker.auth import is_logged_in
|
||||
from boker.db import database_extensions_available
|
||||
from boker.league_repositories import league_counts, list_public_leagues
|
||||
|
||||
q = request.args.get("q", "").strip()
|
||||
leagues = list_public_leagues(q) if database_extensions_available() else []
|
||||
@@ -4,9 +4,9 @@ from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from models import PlayerStats, SessionEntry, SessionSummary
|
||||
from storage import EventRow
|
||||
from utils import entry_sort_key, net_result_bucket, session_sort_key
|
||||
from boker.models import PlayerStats, SessionEntry, SessionSummary
|
||||
from boker.storage import EventRow
|
||||
from boker.utils import entry_sort_key, net_result_bucket, session_sort_key
|
||||
|
||||
SESSION_MARKER_TYPES = {"session_open", "session_close"}
|
||||
|
||||
File renamed without changes.
@@ -7,7 +7,7 @@ import re
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from models import SessionEntry, SessionSummary
|
||||
from boker.models import SessionEntry, SessionSummary
|
||||
|
||||
BREAK_EVEN_TOLERANCE_CENTS = 100
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import unittest
|
||||
|
||||
from charts import player_session_series
|
||||
from services import (
|
||||
from boker.charts import player_session_series
|
||||
from boker.services import (
|
||||
build_leaderboard,
|
||||
build_session_summaries,
|
||||
pending_payout_carry_items,
|
||||
prunable_empty_session_ids,
|
||||
)
|
||||
from utils import session_sort_key
|
||||
from boker.utils import session_sort_key
|
||||
|
||||
|
||||
def cents(amount: float) -> int:
|
||||
|
||||
+15
-15
@@ -4,8 +4,8 @@ from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from app import create_app
|
||||
from auth import hash_password
|
||||
from db import db
|
||||
from boker.auth import hash_password
|
||||
from boker.db import db
|
||||
|
||||
|
||||
class InternalAdminAccessTests(unittest.TestCase):
|
||||
@@ -22,7 +22,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
self.client = self.app.test_client()
|
||||
|
||||
with self.app.app_context():
|
||||
from db_models import League, LeagueMembership, User
|
||||
from boker.db_models import League, LeagueMembership, User
|
||||
|
||||
db.create_all()
|
||||
self.user = User(
|
||||
@@ -118,7 +118,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
def test_admin_users_page_is_paginated(self):
|
||||
with self.app.app_context():
|
||||
from db_models import User
|
||||
from boker.db_models import User
|
||||
|
||||
users = [
|
||||
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):
|
||||
with self.app.app_context():
|
||||
from db_models import League
|
||||
from boker.db_models import League
|
||||
|
||||
leagues = [
|
||||
League(
|
||||
@@ -160,8 +160,8 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
def test_session_growth_uses_session_date_not_created_at(self):
|
||||
with self.app.app_context():
|
||||
from db_models import PokerSession
|
||||
from routes.internal import _growth_data
|
||||
from boker.db_models import PokerSession
|
||||
from boker.routes.internal import _growth_data
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
old_session = PokerSession(
|
||||
@@ -196,7 +196,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
with self.app.app_context():
|
||||
from db_models import User
|
||||
from boker.db_models import User
|
||||
|
||||
user = db.session.get(User, self.user_id)
|
||||
self.assertEqual(user.email, "viewer@example.com")
|
||||
@@ -212,7 +212,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
with self.app.app_context():
|
||||
from db_models import User
|
||||
from boker.db_models import User
|
||||
|
||||
user = db.session.get(User, self.user_id)
|
||||
self.assertEqual(user.email, "changed@example.com")
|
||||
@@ -220,7 +220,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
def test_admin_delete_user_blocks_owned_leagues(self):
|
||||
self.login_as(self.admin_id)
|
||||
with self.app.app_context():
|
||||
from db_models import League, LeagueMembership
|
||||
from boker.db_models import League, LeagueMembership
|
||||
|
||||
owned_league = League(
|
||||
name="Manager Owned Poker",
|
||||
@@ -241,7 +241,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
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))
|
||||
|
||||
@@ -251,7 +251,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
archive_response = self.client.post(f"/internal/leagues/{self.league_id}/archive")
|
||||
self.assertEqual(archive_response.status_code, 302)
|
||||
with self.app.app_context():
|
||||
from db_models import League
|
||||
from boker.db_models import League
|
||||
|
||||
league = db.session.get(League, self.league_id)
|
||||
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")
|
||||
self.assertEqual(restore_response.status_code, 302)
|
||||
with self.app.app_context():
|
||||
from db_models import League
|
||||
from boker.db_models import League
|
||||
|
||||
league = db.session.get(League, self.league_id)
|
||||
self.assertIsNone(league.archived_at)
|
||||
@@ -280,7 +280,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
with self.app.app_context():
|
||||
from db_models import League
|
||||
from boker.db_models import League
|
||||
|
||||
league = db.session.get(League, self.league_id)
|
||||
self.assertEqual(league.name, "Saturday Poker")
|
||||
@@ -300,7 +300,7 @@ class InternalAdminAccessTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
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)
|
||||
self.assertEqual(league.created_by_user_id, self.manager_id)
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import unittest
|
||||
|
||||
from models import SessionEntry, SessionSummary
|
||||
from services import build_leaderboard
|
||||
from boker.models import SessionEntry, SessionSummary
|
||||
from boker.services import build_leaderboard
|
||||
|
||||
|
||||
class CashInAccountingTest(unittest.TestCase):
|
||||
|
||||
Reference in new issue
Block a user