integrations.web.events.create_bp
ルート設定
1""" 2ルート設定 3""" 4 5from integrations.web.events.create_bp.detail import detail_bp 6from integrations.web.events.create_bp.graph import graph_bp 7from integrations.web.events.create_bp.index import index_bp 8from integrations.web.events.create_bp.member import member_bp 9from integrations.web.events.create_bp.ranking import ranking_bp 10from integrations.web.events.create_bp.report import report_bp 11from integrations.web.events.create_bp.score import score_bp 12from integrations.web.events.create_bp.summary import summary_bp 13from integrations.web.events.create_bp.user_assets import user_assets_bp 14 15__all__ = [ 16 "detail_bp", 17 "graph_bp", 18 "index_bp", 19 "member_bp", 20 "ranking_bp", 21 "report_bp", 22 "score_bp", 23 "summary_bp", 24 "user_assets_bp", 25]
21def detail_bp(adapter: "ServiceAdapter") -> Blueprint: 22 """ 23 個人成績詳細ページ用Blueprint 24 25 Args: 26 adapter (ServiceAdapter): web用アダプタ 27 28 Returns: 29 Blueprint: Blueprint 30 31 """ 32 bp = Blueprint("detail", __name__, url_prefix="/detail") 33 34 @bp.route("/", methods=["GET", "POST"]) 35 def detail() -> "Response": 36 if not adapter.conf.view_summary: 37 abort(403) 38 39 padding = current_app.config["padding"] 40 players = g.cfg.member.lists 41 42 m = adapter.parser() 43 cookie_data = cast(dict[str, Any], adapter.functions.get_cookie(request)) 44 text = " ".join(cookie_data.values()) 45 m.data.text = f"{g.cfg.results.commandword[0]} {text}" 46 libs.dispatcher.by_keyword(m) 47 48 _, message = adapter.functions.header_message(m) 49 50 for data, options in m.post.message: 51 if options.title: 52 message += f"<h2>{options.title}</h2>\n" 53 54 if isinstance(data, pd.DataFrame): 55 show_index = options.show_index 56 if options.title == "戦績" and g.params.verbose: 57 padding = "0.25em 0.75em" 58 data = _conv_verbose(data) 59 message += adapter.functions.to_styled_html(data, padding, show_index) 60 message = message.replace(f">{g.params.player_name}<", f"><div class='player_name'>{g.params.player_name}</div><") 61 62 if isinstance(data, str): 63 message += adapter.functions.to_text_html(data) 64 65 cookie_data.update({"body": message, "players": players, **asdict(adapter.conf)}) 66 page = adapter.functions.set_cookie("detail.html", request, cookie_data) 67 68 return page 69 70 return bp
個人成績詳細ページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
22def graph_bp(adapter: "ServiceAdapter") -> Blueprint: 23 """ 24 グラフ表示ページ用Blueprint 25 26 Args: 27 adapter (ServiceAdapter): web用アダプタ 28 29 Returns: 30 Blueprint: Blueprint 31 32 """ 33 bp = Blueprint("graph", __name__, url_prefix="/graph") 34 35 @bp.route("/", methods=["GET", "POST"]) 36 def graph() -> "Response": 37 if not adapter.conf.view_graph: 38 abort(403) 39 40 padding = current_app.config["padding"] 41 42 m = adapter.parser() 43 cookie_data = adapter.functions.get_cookie(request) 44 text = " ".join(cookie_data.values()) 45 m.data.text = f"{g.cfg.graph.commandword[0]} {text}" 46 libs.dispatcher.by_keyword(m) 47 48 _, message = adapter.functions.header_message(m) 49 50 for data, options in m.post.message: 51 if isinstance(data, PosixPath) and data.exists(): 52 message += f"<p>\n{data.read_text(encoding='utf-8')}\n</p>\n" 53 54 if isinstance(data, pd.DataFrame) and options.title == "素点情報": 55 show_index = options.show_index 56 data["ゲーム数"] = data["ゲーム数"].astype("float") 57 data.rename(columns={"平均値(x)": "平均値", "中央値(|)": "中央値"}, inplace=True) 58 message += f"<h2>{options.title}</h2>\n" 59 message += adapter.functions.to_styled_html(data, padding, show_index) 60 61 if isinstance(data, pd.DataFrame) and options.title == "順位/ポイント情報": 62 show_index = options.show_index 63 data["ゲーム数"] = data["ゲーム数"].astype("float") 64 multi = [ 65 ("", "ゲーム数"), 66 ("1位", "獲得数"), 67 ("1位", "獲得率"), 68 ("2位", "獲得数"), 69 ("2位", "獲得率"), 70 ("3位", "獲得数"), 71 ("3位", "獲得率"), 72 ("4位", "獲得数"), 73 ("4位", "獲得率"), 74 ("", "平均順位"), 75 ("区間成績", "区間ポイント"), 76 ("区間成績", "区間平均"), 77 ("", "通算ポイント"), 78 ] 79 data.columns = pd.MultiIndex.from_tuples(multi) 80 message += f"<h2>{options.title}</h2>\n" 81 message += adapter.functions.to_styled_html(data, padding, show_index) 82 83 cookie_data.update(body=message, **asdict(adapter.conf)) 84 page = adapter.functions.set_cookie("graph.html", request, cookie_data) 85 86 return page 87 88 return bp
グラフ表示ページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
15def index_bp(adapter: "ServiceAdapter") -> Blueprint: 16 """ 17 index用Blueprint 18 19 Args: 20 adapter (ServiceAdapter): web用アダプタ 21 22 Returns: 23 Blueprint: Blueprint 24 25 """ 26 bp = Blueprint("index", __name__, url_prefix="/") 27 28 @bp.route("/", methods=["GET", "POST"]) 29 def index() -> str: 30 return render_template("index.html", **asdict(adapter.conf)) 31 32 return bp
index用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
20def member_bp(adapter: "ServiceAdapter") -> Blueprint: 21 """ 22 メンバー管理ページ用Blueprint 23 24 Args: 25 adapter (ServiceAdapter): web用アダプタ 26 27 Returns: 28 Blueprint: Blueprint 29 30 """ 31 bp = Blueprint("member", __name__, url_prefix="/member") 32 33 @bp.route("/", methods=["GET", "POST"]) 34 def mgt_member() -> str: 35 if not adapter.conf.management_member: 36 abort(403) 37 38 m = adapter.parser() 39 g.params = dictutil.placeholder(g.cfg.help, m) 40 41 padding = current_app.config["padding"] 42 data: dict[str, Any] = asdict(adapter.conf) 43 44 if request.method == "POST": 45 match request.form.get("action"): 46 case "add_member": 47 if name := request.form.get("member", "").strip(): 48 ret = member.append(name.split()[0:2]) 49 data.update(result_msg=ret) 50 case "del_member": 51 if name := request.form.get("member", "").strip(): 52 ret = member.remove(name.split()[0:2]) 53 data.update(result_msg=ret) 54 case "add_team": 55 if team_name := request.form.get("team", "").strip(): 56 ret = team.append(team_name.split()[0:2]) 57 data.update(result_msg=ret) 58 case "del_team": 59 if team_name := request.form.get("team", "").strip(): 60 ret = team.remove(team_name.split()[0:2]) 61 data.update(result_msg=ret) 62 case "delete_all_team": 63 ret = team.clear() 64 data.update(result_msg=ret) 65 66 lookup.read_memberslist() 67 68 member_df = g.params.read_data("MEMBER_INFO") 69 if member_df.empty: 70 data.update(member_table="<p>登録済みメンバーはいません。</p>") 71 else: 72 data.update(member_table=adapter.functions.to_styled_html(member_df.drop(columns=["id"]), padding)) 73 74 team_df = g.params.read_data("TEAM_INFO") 75 if team_df.empty: 76 data.update(team_table="<p>登録済みチームはありません。</p>") 77 else: 78 data.update(team_table=adapter.functions.to_styled_html(team_df.drop(columns=["id"]), padding)) 79 80 return render_template("registry.html", **data) 81 82 return bp
メンバー管理ページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
21def ranking_bp(adapter: "ServiceAdapter") -> Blueprint: 22 """ 23 ランキングページ用Blueprint 24 25 Args: 26 adapter (ServiceAdapter): web用アダプタ 27 28 Returns: 29 Blueprint: Blueprint 30 31 """ 32 bp = Blueprint("ranking", __name__, url_prefix="/ranking") 33 34 @bp.route("/", methods=["GET", "POST"]) 35 def ranking() -> "Response": 36 if not adapter.conf.view_ranking: 37 abort(403) 38 39 padding = current_app.config["padding"] 40 41 m = adapter.parser() 42 cookie_data = adapter.functions.get_cookie(request) 43 text = " ".join(cookie_data.values()) 44 m.data.text = f"{g.cfg.ranking.commandword[0]} {text}" 45 libs.dispatcher.by_keyword(m) 46 47 _, message = adapter.functions.header_message(m) 48 49 for data, options in m.post.message: 50 if options.title: 51 message += f"<h2>{options.title}</h2>\n" 52 53 if isinstance(data, pd.DataFrame): 54 show_index = options.show_index 55 message += adapter.functions.to_styled_html(data, padding, show_index) 56 57 if isinstance(data, str): 58 message += adapter.functions.to_text_html(data) 59 60 cookie_data.update(body=message, **asdict(adapter.conf)) 61 page = adapter.functions.set_cookie("ranking.html", request, cookie_data) 62 63 return page 64 65 return bp
ランキングページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
21def report_bp(adapter: "ServiceAdapter") -> Blueprint: 22 """ 23 レポートページ用Blueprint 24 25 Args: 26 adapter (ServiceAdapter): web用アダプタ 27 28 Returns: 29 Blueprint: Blueprint 30 31 """ 32 bp = Blueprint("report", __name__, url_prefix="/report") 33 34 @bp.route("/", methods=["GET", "POST"]) 35 def report() -> "Response": 36 if not adapter.conf.view_report: 37 abort(403) 38 39 padding = current_app.config["padding"] 40 41 m = adapter.parser() 42 cookie_data = adapter.functions.get_cookie(request) 43 text = " ".join(cookie_data.values()) 44 m.data.text = f"{g.cfg.report.commandword[0]} {text}" 45 libs.dispatcher.by_keyword(m) 46 47 headline_title, message = adapter.functions.header_message(m) 48 49 for data, options in m.post.message: 50 if not options.title.isnumeric() and options.title: 51 message += f"<h2>{options.title}</h2>\n" 52 53 if isinstance(data, pd.DataFrame): 54 show_index = options.show_index 55 if {"個人成績一覧", "チーム成績一覧"} & set(headline_title): 56 check_column = data.columns.to_list() 57 multi = [ 58 ("", "プレイヤー名" if g.params.individual else "チーム名"), 59 ("", "ゲーム数"), 60 ("ポイント", "通算") if {"通算ポイント", "平均ポイント"}.issubset(check_column) else None, 61 ("ポイント", "平均") if {"通算ポイント", "平均ポイント"}.issubset(check_column) else None, 62 ("1位", "獲得数") if {"1位数", "1位率"}.issubset(check_column) else None, 63 ("1位", "獲得率") if {"1位数", "1位率"}.issubset(check_column) else None, 64 ("2位", "獲得数") if {"2位数", "2位率"}.issubset(check_column) else None, 65 ("2位", "獲得率") if {"2位数", "2位率"}.issubset(check_column) else None, 66 ("3位", "獲得数") if {"3位数", "3位率"}.issubset(check_column) else None, 67 ("3位", "獲得率") if {"3位数", "3位率"}.issubset(check_column) else None, 68 ("4位", "獲得数") if {"4位数", "4位率"}.issubset(check_column) else None, 69 ("4位", "獲得率") if {"4位数", "4位率"}.issubset(check_column) else None, 70 ("平均順位", "") if {"平均順位", "平順"} & set(check_column) else None, 71 ("トビ", "回数") if {"トビ数", "トビ率"}.issubset(check_column) else None, 72 ("トビ", "率") if {"トビ数", "トビ率"}.issubset(check_column) else None, 73 ("役満", "和了数") if {"役満和了数", "役満和了率"}.issubset(check_column) else None, 74 ("役満", "和了率") if {"役満和了数", "役満和了率"}.issubset(check_column) else None, 75 ] 76 data.columns = pd.MultiIndex.from_tuples([x for x in multi if x is not None]) 77 elif "成績上位者" in headline_title: 78 name = "名前" if g.params.individual else "チーム" 79 check_column = data.columns.to_list() 80 multi = [ 81 ("", "集計月"), 82 ("1位", name), 83 ("1位", "獲得ポイント"), 84 ("2位", name), 85 ("2位", "獲得ポイント"), 86 ("3位", name), 87 ("3位", "獲得ポイント"), 88 ("4位", name), 89 ("4位", "獲得ポイント"), 90 ("5位", name), 91 ("5位", "獲得ポイント"), 92 ] 93 data.columns = pd.MultiIndex.from_tuples([x for x in multi if x is not None]) 94 message += adapter.functions.to_styled_html(data, padding, show_index) 95 96 if isinstance(data, str): 97 message += adapter.functions.to_text_html(data) 98 99 cookie_data.update(body=message, **asdict(adapter.conf)) 100 page = adapter.functions.set_cookie("report.html", request, cookie_data) 101 102 return page 103 104 return bp
レポートページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
23def score_bp(adapter: "ServiceAdapter") -> Blueprint: 24 """ 25 スコア管理ページ用Blueprint 26 27 Args: 28 adapter (ServiceAdapter): web用アダプタ 29 30 Returns: 31 Blueprint: Blueprint 32 33 """ 34 bp = Blueprint("score", __name__, url_prefix="/score") 35 36 @bp.route("/", methods=["GET", "POST"]) 37 def mgt_score() -> str: 38 if not adapter.conf.management_score: 39 abort(403) 40 41 padding = current_app.config["padding"] 42 players = g.cfg.member.lists 43 m = adapter.parser() 44 45 def score_table() -> str: 46 df = formatter.df_rename( 47 pd.read_sql( 48 sql=""" 49 select 50 '<input type="radio" name="ts" value="' || ts || '">' as '#', 51 playtime, 52 p1_name, p1_str, 53 p2_name, p2_str, 54 p3_name, p3_str, 55 p4_name, p4_str, 56 comment, source 57 from 58 result 59 order by 60 ts desc 61 limit 0, 10 62 ; 63 """, 64 con=dbutil.connection(g.cfg.setting.database_file), 65 ), 66 options=StyleOptions(), 67 ) 68 69 if not isinstance(df.columns, pd.MultiIndex): 70 new_columns = [tuple(col.split(" ")) if " " in col else ("", col) for col in df.columns] 71 df.columns = pd.MultiIndex.from_tuples(new_columns, names=["座席", "項目"]) 72 73 return adapter.functions.to_styled_html(df, padding) 74 75 data: dict[str, Any] = asdict(adapter.conf) 76 data.update(players=players) 77 78 if request.method == "POST": 79 data.update(request.form.to_dict()) 80 data.update(mode="update") 81 data.update(g.cfg.setting.to_dict()) 82 83 if "ts" in data: 84 match request.form.get("action"): 85 case "modify": 86 sql = "select * from result where ts = :ts;" 87 df = pd.read_sql(sql=sql, con=dbutil.connection(g.cfg.setting.database_file), params=data) 88 data.update(next(iter(df.T.to_dict().values()))) 89 return render_template("score_input.html", **data) 90 case "delete": 91 m.data.event_ts = request.form.get("ts", "") 92 modify.db_delete(m) 93 data.update(table=score_table()) 94 return render_template("score_list.html", **data) 95 case "update": 96 g.params.unregistered_replace = False 97 data.update(request.form.to_dict(), players=players) 98 if p1_name := request.form.get("p1_other"): 99 data.update(p1_name=formatter.name_replace(p1_name)) 100 if p2_name := request.form.get("p2_other"): 101 data.update(p2_name=formatter.name_replace(p2_name)) 102 if p3_name := request.form.get("p3_other"): 103 data.update(p3_name=formatter.name_replace(p3_name)) 104 if p4_name := request.form.get("p4_other"): 105 data.update(p4_name=formatter.name_replace(p4_name)) 106 if not request.form.get("comment"): 107 data.update(comment=None) 108 109 detection = GameResult(**data) 110 detection.source = "web" 111 m.status.source = "web" 112 113 if data.get("mode") == "insert": 114 modify.db_insert(detection, m) 115 else: 116 modify.db_update(detection, m) 117 118 data.update(table=score_table()) 119 return render_template("score_list.html", **data) 120 elif request.form.get("action") == "modify": # 新規登録 121 playtime = ExtDt() 122 data.update( 123 mode="insert", 124 playtime=playtime.format(ExtDt.FMT.SQL), 125 ts=playtime.format(ExtDt.FMT.TS), 126 ) 127 return render_template("score_input.html", **data) 128 129 data.update(table=score_table()) 130 return render_template("score_list.html", **data) 131 132 return bp
スコア管理ページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
21def summary_bp(adapter: "ServiceAdapter") -> Blueprint: 22 """ 23 成績サマリページ用Blueprint 24 25 Args: 26 adapter (ServiceAdapter): web用アダプタ 27 28 Returns: 29 Blueprint: Blueprint 30 31 """ 32 bp = Blueprint("summary", __name__, url_prefix="/summary") 33 34 @bp.route("/", methods=["GET", "POST"]) 35 def summary() -> "Response": 36 if not adapter.conf.view_summary: 37 abort(403) 38 39 padding = current_app.config["padding"] 40 41 m = adapter.parser() 42 cookie_data = adapter.functions.get_cookie(request) 43 text = " ".join(cookie_data.values()) 44 m.data.text = f"{g.cfg.results.commandword[0]} {text}" 45 libs.dispatcher.by_keyword(m) 46 47 _, message = adapter.functions.header_message(m) 48 49 for data, options in m.post.message: 50 if options.title: 51 message += f"<h2>{options.title}</h2>\n" 52 53 if isinstance(data, pd.DataFrame): 54 show_index = options.show_index 55 if options.title == "戦績" and g.params.verbose: 56 padding = "0.25em 0.75em" 57 data = _conv_verbose(data) 58 59 message += adapter.functions.to_styled_html(data, padding, show_index) 60 61 if isinstance(data, str): 62 message += adapter.functions.to_text_html(data) 63 64 cookie_data.update(body=message, **asdict(adapter.conf)) 65 page = adapter.functions.set_cookie("summary.html", request, cookie_data) 66 67 return page 68 69 return bp
成績サマリページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
def
user_assets_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
17def user_assets_bp(adapter: "ServiceAdapter") -> Blueprint: 18 """ 19 ユーザー指定CSS用Blueprint 20 21 Args: 22 adapter (ServiceAdapter): web用アダプタ 23 24 Returns: 25 Blueprint: Blueprint 26 27 """ 28 bp = Blueprint( 29 "user_assets", 30 __name__, 31 static_folder=os.path.dirname(os.path.join(g.cfg.config_dir, adapter.conf.custom_css)), 32 static_url_path="/user_static", 33 ) 34 35 @bp.before_request 36 def restrict_static() -> None: 37 if not os.path.basename(request.path) == adapter.conf.custom_css: 38 abort(403) 39 40 return bp
ユーザー指定CSS用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint