major file restrucutre
This commit is contained in:
1 parent
ac278065d2
commit
6da4c3e379
53 files changed
+188
-180
No files matched your search
@@ -1,66 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import click
|
||||
from flask import Flask
|
||||
|
||||
from auth import current_user_id, is_logged_in
|
||||
from config import 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.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 create_app(config_overrides: dict | None = None) -> Flask:
|
||||
app = Flask(__name__)
|
||||
cfg = ProductionConfig if os.getenv("FLASK_ENV") == "production" else Config
|
||||
app.config.from_object(cfg)
|
||||
if config_overrides:
|
||||
app.config.update(config_overrides)
|
||||
|
||||
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(),
|
||||
}
|
||||
|
||||
app.register_blueprint(public_bp)
|
||||
app.register_blueprint(account_bp)
|
||||
app.register_blueprint(leagues_bp)
|
||||
|
||||
@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.")
|
||||
|
||||
return app
|
||||
|
||||
from boker import create_app
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
app.run(debug=os.getenv("FLASK_DEBUG", "0") == "1")
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
import click
|
||||
from flask import Flask
|
||||
|
||||
from .auth import current_user_id, is_logged_in
|
||||
from .config import 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.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 create_app(config_overrides: dict | None = None) -> Flask:
|
||||
app = Flask(__name__)
|
||||
cfg = ProductionConfig if os.getenv("FLASK_ENV") == "production" else Config
|
||||
app.config.from_object(cfg)
|
||||
if config_overrides:
|
||||
app.config.update(config_overrides)
|
||||
|
||||
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(),
|
||||
}
|
||||
|
||||
app.register_blueprint(public_bp)
|
||||
app.register_blueprint(account_bp)
|
||||
app.register_blueprint(leagues_bp)
|
||||
|
||||
@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.")
|
||||
|
||||
return app
|
||||
File renamed without changes.
@@ -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 .models import SessionEntry, SessionSummary
|
||||
from .utils import net_result_bucket, session_chart_label, session_sort_key
|
||||
|
||||
PLAYER_PALETTE = [
|
||||
"#9b8cf0", # --line-1 violet
|
||||
@@ -4,12 +4,12 @@ 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'}"
|
||||
|
||||
ELIGIBLE_MIN_SESSIONS = 3
|
||||
APP_VERSION = "2.3.0"
|
||||
APP_VERSION = "2.3.1"
|
||||
|
||||
|
||||
def load_local_env(env_path: Path) -> None:
|
||||
+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.
|
||||
from . import 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 .db import db
|
||||
|
||||
if db is None: # pragma: no cover - imported only when DB dependencies exist.
|
||||
raise RuntimeError("Database dependencies are not installed.")
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
from flask import current_app
|
||||
from flask_mail import Message
|
||||
|
||||
from extensions import mail
|
||||
from .extensions import mail
|
||||
|
||||
|
||||
def send_password_reset(to_email: str, reset_url: str) -> None:
|
||||
File renamed without changes.
File renamed without changes.
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 ..auth import hash_password, normalize_email
|
||||
from ..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 ..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 ..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 ..db_models import utc_now
|
||||
|
||||
session.status = status
|
||||
if status == "open":
|
||||
@@ -313,7 +313,7 @@ def add_league_member(
|
||||
|
||||
|
||||
def remove_league_member(league_id: str, user_id: str) -> None:
|
||||
from db_models import utc_now
|
||||
from ..db_models import utc_now
|
||||
|
||||
membership = LeagueMembership.query.filter_by(
|
||||
league_id=league_id,
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
from db import db
|
||||
from ..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 ..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 ..storage import EventRow # noqa: E402
|
||||
|
||||
|
||||
def session_event_ref(session: PokerSession) -> str:
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -5,7 +5,7 @@ from urllib.parse import urlparse
|
||||
|
||||
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
||||
|
||||
from auth import (
|
||||
from ..auth import (
|
||||
current_user_id,
|
||||
generate_invite_token,
|
||||
generate_reset_token,
|
||||
@@ -18,8 +18,8 @@ from auth import (
|
||||
verify_password,
|
||||
verify_reset_token,
|
||||
)
|
||||
from db import database_extensions_available, db
|
||||
from extensions import limiter
|
||||
from ..db import database_extensions_available, db
|
||||
from ..extensions import limiter
|
||||
|
||||
account_bp = Blueprint("account", __name__, url_prefix="/account")
|
||||
|
||||
@@ -49,7 +49,7 @@ def register():
|
||||
}
|
||||
|
||||
if request.method == "POST":
|
||||
from league_repositories import create_user, find_user_by_email
|
||||
from ..repositories.leagues import create_user, find_user_by_email
|
||||
|
||||
email = normalize_email(form["email"])
|
||||
password = request.form.get("password", "")
|
||||
@@ -88,7 +88,7 @@ def login():
|
||||
}
|
||||
|
||||
if request.method == "POST":
|
||||
from league_repositories import find_user_by_email
|
||||
from ..repositories.leagues import find_user_by_email
|
||||
|
||||
user = find_user_by_email(form["email"])
|
||||
password = request.form.get("password", "")
|
||||
@@ -122,7 +122,7 @@ def settings():
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("leagues.index"))
|
||||
|
||||
from db_models import User
|
||||
from ..db_models import User
|
||||
|
||||
user = db.session.get(User, current_user_id())
|
||||
if user is None:
|
||||
@@ -139,7 +139,7 @@ def update_email():
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.settings"))
|
||||
|
||||
from db_models import User
|
||||
from ..db_models import User
|
||||
|
||||
user = db.session.get(User, current_user_id())
|
||||
if user is None:
|
||||
@@ -154,7 +154,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 ..repositories.leagues import find_user_by_email
|
||||
|
||||
existing = find_user_by_email(new_email)
|
||||
if existing and existing.id != user.id:
|
||||
@@ -174,7 +174,7 @@ def update_password():
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.settings"))
|
||||
|
||||
from db_models import User
|
||||
from ..db_models import User
|
||||
|
||||
user = db.session.get(User, current_user_id())
|
||||
if user is None:
|
||||
@@ -206,7 +206,7 @@ def delete_account():
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.settings"))
|
||||
|
||||
from db_models import League, User, utc_now
|
||||
from ..db_models import League, User, utc_now
|
||||
|
||||
user = db.session.get(User, current_user_id())
|
||||
if user is None:
|
||||
@@ -246,9 +246,10 @@ def forgot_password():
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.forgot_password"))
|
||||
|
||||
from emails import send_password_reset
|
||||
from flask import current_app
|
||||
from league_repositories import find_user_by_email
|
||||
|
||||
from ..emails import send_password_reset
|
||||
from ..repositories.leagues import find_user_by_email
|
||||
|
||||
email = normalize_email(request.form.get("email", ""))
|
||||
user = find_user_by_email(email)
|
||||
@@ -282,7 +283,7 @@ def reset_password(token):
|
||||
flash("Account database is not available.", "error")
|
||||
return redirect(url_for("account.login"))
|
||||
|
||||
from db_models import User
|
||||
from ..db_models import User
|
||||
|
||||
user = db.session.get(User, user_id)
|
||||
if user is None or user.disabled_at is not None:
|
||||
@@ -320,8 +321,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
|
||||
from ..db_models import User
|
||||
from ..repositories.leagues import add_league_member, find_league_by_id, find_membership
|
||||
|
||||
user = db.session.get(User, current_user_id())
|
||||
if user is None or user.email != data.get("email"):
|
||||
@@ -16,16 +16,16 @@ from flask import (
|
||||
url_for,
|
||||
)
|
||||
|
||||
from auth import is_admin
|
||||
from services import (
|
||||
from ..auth import is_admin
|
||||
from ..services import (
|
||||
build_session_summaries,
|
||||
next_session_id,
|
||||
pending_payout_carry_items,
|
||||
prunable_empty_session_ids,
|
||||
unique_player_names,
|
||||
)
|
||||
from storage import CSV_HEADERS, append_event, load_events, write_events
|
||||
from utils import cents_to_dollars, session_label
|
||||
from ..storage import CSV_HEADERS, append_event, load_events, write_events
|
||||
from ..utils import cents_to_dollars, session_label
|
||||
|
||||
admin_bp = Blueprint("admin", __name__)
|
||||
|
||||
@@ -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 ..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
|
||||
|
||||
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 ..repositories.leagues 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 ..repositories.leagues 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 ..repositories.ledger import session_event_ref
|
||||
from ..repositories.leagues 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 ..repositories.leagues import list_leagues_for_user
|
||||
from ..repositories.ledger 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 ..db_models import User
|
||||
from ..repositories.leagues import create_league, unique_league_slug
|
||||
|
||||
owner = db.session.get(User, current_user_id())
|
||||
if owner is None:
|
||||
@@ -221,7 +221,7 @@ 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 ..repositories.leagues import league_counts, user_has_league_role
|
||||
|
||||
league, resp = get_league_with_visibility_gate(league_ref)
|
||||
if resp:
|
||||
@@ -242,7 +242,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 ..repositories.leagues import find_league_by_id, user_has_league_role
|
||||
|
||||
league = find_league_by_id(league_id)
|
||||
if league is None:
|
||||
@@ -259,9 +259,9 @@ def leaderboard(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from config import ELIGIBLE_MIN_SESSIONS
|
||||
from ledger_repositories import list_event_rows_for_league
|
||||
from league_repositories import list_players_for_league, user_has_league_role
|
||||
from ..config import ELIGIBLE_MIN_SESSIONS
|
||||
from ..repositories.ledger import list_event_rows_for_league
|
||||
from ..repositories.leagues import list_players_for_league, user_has_league_role
|
||||
|
||||
league, resp = get_league_with_visibility_gate(league_ref)
|
||||
if resp:
|
||||
@@ -366,12 +366,12 @@ def ledger(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from ledger_repositories import (
|
||||
from ..repositories.ledger import (
|
||||
append_ledger_event,
|
||||
list_all_event_rows_for_league,
|
||||
list_event_rows_for_league,
|
||||
)
|
||||
from league_repositories import (
|
||||
from ..repositories.leagues import (
|
||||
find_player_for_league,
|
||||
find_session_for_league,
|
||||
list_players_for_league,
|
||||
@@ -506,7 +506,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 ..repositories.ledger import void_ledger_event
|
||||
|
||||
league = require_league(league_ref, {"owner"})
|
||||
reason = request.form.get("void_reason", "").strip()
|
||||
@@ -529,8 +529,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 ..repositories.ledger import list_event_rows_for_league
|
||||
from ..repositories.leagues import find_player_for_league, user_has_league_role
|
||||
|
||||
league, resp = get_league_with_visibility_gate(league_ref)
|
||||
if resp:
|
||||
@@ -588,7 +588,7 @@ def players(league_ref: str):
|
||||
flash("League database is not available.", "error")
|
||||
return redirect(url_for("public.home"))
|
||||
|
||||
from league_repositories import (
|
||||
from ..repositories.leagues import (
|
||||
create_player,
|
||||
list_players_for_league,
|
||||
player_name_exists,
|
||||
@@ -627,7 +627,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 ..repositories.ledger import list_event_rows_for_league
|
||||
|
||||
all_sessions = build_session_summaries(list_event_rows_for_league(league.id))
|
||||
board = build_leaderboard(all_sessions)
|
||||
@@ -661,7 +661,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 ..repositories.leagues import find_player_for_league, set_player_status
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
player = find_player_for_league(league.id, player_id)
|
||||
@@ -681,7 +681,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 ..repositories.leagues 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)
|
||||
@@ -709,8 +709,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 ..repositories.ledger import append_ledger_event
|
||||
from ..repositories.leagues import (
|
||||
create_poker_session,
|
||||
list_sessions_for_league,
|
||||
user_has_league_role,
|
||||
@@ -747,7 +747,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 ..repositories.leagues import set_session_status
|
||||
|
||||
set_session_status(session, "open")
|
||||
|
||||
@@ -765,8 +765,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 ..db_models import LedgerEvent
|
||||
from ..repositories.ledger 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))
|
||||
@@ -803,8 +803,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 ..db_models import LedgerEvent, PokerSession
|
||||
from ..repositories.leagues import list_sessions_for_league
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
|
||||
@@ -839,7 +839,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 ..repositories.leagues import find_session_for_league
|
||||
|
||||
league = require_league(league_ref, {"owner"})
|
||||
session = find_session_for_league(league.id, session_id)
|
||||
@@ -859,7 +859,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 ..repositories.leagues import find_session_for_league
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
session = find_session_for_league(league.id, session_id)
|
||||
@@ -877,7 +877,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 ..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,
|
||||
@@ -900,8 +900,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 ..repositories.ledger import append_ledger_event, list_all_event_rows_for_session, list_event_rows_for_session
|
||||
from ..repositories.leagues import (
|
||||
find_session_for_league,
|
||||
list_players_for_league,
|
||||
user_has_league_role,
|
||||
@@ -989,9 +989,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_session_for_league, list_sessions_for_league, user_has_league_role
|
||||
from ..charts import session_breakdown_series
|
||||
from ..repositories.ledger import list_event_rows_for_league, list_event_rows_for_session
|
||||
from ..repositories.leagues import find_league_by_public_key, 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)
|
||||
@@ -1062,8 +1062,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 ..repositories.ledger import append_ledger_event
|
||||
from ..repositories.leagues import find_session_for_league, set_session_status
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
session = find_session_for_league(league.id, session_id)
|
||||
@@ -1114,7 +1114,7 @@ def league_settings(league_ref: str):
|
||||
elif form["visibility"] not in ("private", "public"):
|
||||
flash("Invalid visibility value.", "error")
|
||||
else:
|
||||
from utils import slugify
|
||||
from ..utils import slugify
|
||||
|
||||
league.name = form["name"]
|
||||
league.slug = slugify(form["name"])
|
||||
@@ -1124,7 +1124,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 ..repositories.leagues 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, members=members)
|
||||
@@ -1137,10 +1137,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 flask import current_app
|
||||
from league_repositories import find_membership, find_user_by_email
|
||||
|
||||
from ..auth import generate_invite_token
|
||||
from ..emails import send_league_invite
|
||||
from ..repositories.leagues import find_membership, find_user_by_email
|
||||
|
||||
league = require_league(league_ref, {"owner"})
|
||||
email = normalize_email(request.form.get("email", ""))
|
||||
@@ -1181,7 +1182,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 ..repositories.leagues import remove_league_member
|
||||
|
||||
league = require_league(league_ref, {"owner"})
|
||||
|
||||
@@ -1202,7 +1203,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 ..db_models import utc_now
|
||||
|
||||
league = require_league(league_ref, {"owner"})
|
||||
confirm_name = request.form.get("confirm_name", "").strip()
|
||||
@@ -1224,8 +1225,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 ..repositories.ledger import list_event_rows_for_league
|
||||
from ..storage import CSV_HEADERS
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager", "viewer"})
|
||||
rows = list_event_rows_for_league(league.id)
|
||||
@@ -1250,14 +1251,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 ..db_models import CANONICAL_EVENT_TYPES, canonical_event_type
|
||||
from ..repositories.ledger import (
|
||||
append_ledger_event,
|
||||
list_ledger_events_for_league,
|
||||
session_event_ref,
|
||||
)
|
||||
from league_repositories import list_players_for_league, list_sessions_for_league
|
||||
from storage import CSV_HEADERS
|
||||
from ..repositories.leagues import list_players_for_league, list_sessions_for_league
|
||||
from ..storage import CSV_HEADERS
|
||||
|
||||
league = require_league(league_ref, {"owner", "manager"})
|
||||
|
||||
@@ -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 ..charts import cumulative_profit_series, player_session_series, session_breakdown_series
|
||||
from ..config import ELIGIBLE_MIN_SESSIONS
|
||||
from ..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 ..storage import load_events
|
||||
from ..utils import session_label, session_sort_key
|
||||
|
||||
public_bp = Blueprint("public", __name__)
|
||||
|
||||
@@ -30,9 +30,9 @@ def help():
|
||||
|
||||
@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 ..auth import is_logged_in
|
||||
from ..db import database_extensions_available
|
||||
from ..repositories.leagues 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 .models import PlayerStats, SessionEntry, SessionSummary
|
||||
from .storage import EventRow
|
||||
from .utils import entry_sort_key, net_result_bucket, session_sort_key
|
||||
|
||||
SESSION_MARKER_TYPES = {"session_open", "session_close"}
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -7,7 +7,7 @@ import re
|
||||
|
||||
from markupsafe import Markup
|
||||
|
||||
from models import SessionEntry, SessionSummary
|
||||
from .models import SessionEntry, SessionSummary
|
||||
|
||||
BREAK_EVEN_TOLERANCE_CENTS = 100
|
||||
|
||||
+15
-13
@@ -2,15 +2,17 @@ from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import app as app_module
|
||||
from charts import player_session_series
|
||||
from services import (
|
||||
from boker import create_app
|
||||
from boker.charts import player_session_series
|
||||
from boker.services import (
|
||||
build_leaderboard,
|
||||
build_session_summaries,
|
||||
pending_payout_carry_items,
|
||||
)
|
||||
from storage import load_events, write_events
|
||||
from utils import session_sort_key
|
||||
from boker.storage import load_events, write_events
|
||||
from boker.utils import session_sort_key
|
||||
|
||||
_app = create_app()
|
||||
|
||||
|
||||
def cents(amount: float) -> int:
|
||||
@@ -203,8 +205,8 @@ class AccountingTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
data_path = Path(tmpdir) / "entries.csv"
|
||||
write_events(data_path, rows)
|
||||
original_path = app_module.app.config["DATA_PATH"]
|
||||
app_module.app.config["DATA_PATH"] = data_path
|
||||
original_path = _app.config["DATA_PATH"]
|
||||
_app.config["DATA_PATH"] = data_path
|
||||
|
||||
try:
|
||||
sessions = build_session_summaries(load_events(data_path))
|
||||
@@ -213,7 +215,7 @@ class AccountingTests(unittest.TestCase):
|
||||
[{"player_name": "A", "amount_cents": cents(5)}],
|
||||
)
|
||||
|
||||
with app_module.app.test_client() as client:
|
||||
with _app.test_client() as client:
|
||||
with client.session_transaction() as flask_session:
|
||||
flask_session["is_admin"] = True
|
||||
|
||||
@@ -222,7 +224,7 @@ class AccountingTests(unittest.TestCase):
|
||||
data={"session_id": "s2", "player_name": "A"},
|
||||
)
|
||||
finally:
|
||||
app_module.app.config["DATA_PATH"] = original_path
|
||||
_app.config["DATA_PATH"] = original_path
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
updated_sessions = build_session_summaries(load_events(data_path))
|
||||
@@ -261,17 +263,17 @@ class AccountingTests(unittest.TestCase):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
data_path = Path(tmpdir) / "entries.csv"
|
||||
write_events(data_path, rows)
|
||||
original_path = app_module.app.config["DATA_PATH"]
|
||||
app_module.app.config["DATA_PATH"] = data_path
|
||||
original_path = _app.config["DATA_PATH"]
|
||||
_app.config["DATA_PATH"] = data_path
|
||||
|
||||
try:
|
||||
with app_module.app.test_client() as client:
|
||||
with _app.test_client() as client:
|
||||
with client.session_transaction() as flask_session:
|
||||
flask_session["is_admin"] = True
|
||||
|
||||
response = client.post("/admin/prune-empty-sessions")
|
||||
finally:
|
||||
app_module.app.config["DATA_PATH"] = original_path
|
||||
_app.config["DATA_PATH"] = original_path
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
remaining = load_events(data_path)
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
import unittest
|
||||
|
||||
from stats import SessionEntry, SessionSummary, build_leaderboard
|
||||
from boker.models import SessionEntry, SessionSummary
|
||||
from boker.services import build_leaderboard
|
||||
|
||||
|
||||
class CashInAccountingTest(unittest.TestCase):
|
||||
@@ -78,7 +79,7 @@ class CashInAccountingTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(player.total_buy_in_cents, 2500)
|
||||
self.assertEqual(player.total_front_collected_cents, 0)
|
||||
self.assertEqual(player.total_cash_in_cents, 3000)
|
||||
self.assertEqual(player.total_real_cash_in_cents, 2500)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user