libs.configuration
libs/configuration.py
1""" 2libs/configuration.py 3""" 4 5import argparse 6import logging 7import os 8import sys 9from functools import partial 10from typing import TYPE_CHECKING, cast 11 12import libs.commands.graph.entry 13import libs.commands.ranking.entry 14import libs.commands.report.entry 15import libs.commands.results.entry 16import libs.global_value as g 17from cls.config import AppConfig 18from integrations import factory 19from libs.data import lookup 20from libs.functions import compose 21from libs.registry import member, team 22from libs.types import StyleOptions 23 24if TYPE_CHECKING: 25 from cls.config import SubCommand 26 from integrations.protocols import MessageParserProtocol 27 28 29def set_loglevel(): 30 """ログレベル追加""" 31 32 # DEBUG : 10 33 # INFO : 20 34 # WARNING : 30 35 # ERROR : 40 36 # CRITICAL : 50 37 38 # TRACE 39 logging.TRACE = 5 # type: ignore 40 logging.trace = partial(logging.log, logging.TRACE) # type: ignore 41 logging.addLevelName(logging.TRACE, "TRACE") # type: ignore 42 43 44def arg_parser() -> argparse.Namespace: 45 """コマンドライン解析 46 47 Returns: 48 argparse.Namespace: オブジェクト 49 """ 50 51 p = argparse.ArgumentParser( 52 formatter_class=argparse.RawTextHelpFormatter, 53 add_help=True, 54 ) 55 56 p.add_argument( 57 "-c", "--config", 58 default="config.ini", 59 help="設定ファイル(default: %(default)s)", 60 ) 61 p.add_argument( 62 "--profile", 63 help=argparse.SUPPRESS, 64 ) 65 p.add_argument( 66 "--service", 67 choices=[ 68 "slack", 69 "discord", 70 "standard_io", "std", 71 "web", "flask", 72 ], 73 default="slack", 74 help="連携先サービス", 75 ) 76 77 logging_group = p.add_argument_group("logging options") 78 logging_group.add_argument( 79 "--debug", 80 action="store_true", 81 help="デバッグ情報表示", 82 ) 83 logging_group.add_argument( 84 "--verbose", "--trace", 85 dest="verbose", 86 action="store_true", 87 help="詳細デバッグ情報表示", 88 ) 89 logging_group.add_argument( 90 "--moderate", 91 action="store_true", 92 help="ログレベルがエラー以下のもを非表示", 93 ) 94 logging_group.add_argument( 95 "--notime", 96 action="store_true", 97 help="ログフォーマットから日時を削除", 98 ) 99 100 match os.path.basename(sys.argv[0]): 101 case "app.py": 102 service_stdio = p.add_argument_group("Only allowed when --service=standard_io") 103 service_stdio.add_argument( 104 "--text", 105 type=str, 106 help="input text strings", 107 ) 108 service_web = p.add_argument_group("Only allowed when --service=web") 109 service_web.add_argument( 110 "--host", 111 type=str, 112 default="127.0.0.1", 113 help="listen address(default: %(default)s)", 114 ) 115 service_web.add_argument( 116 "--port", 117 type=int, 118 default=8000, 119 help="bind port(default: %(default)s)", 120 ) 121 case "dbtools.py": # dbtools専用オプション 122 required = p.add_argument_group("Required options(amutually exclusive)") 123 exclusive = required.add_mutually_exclusive_group() 124 exclusive.add_argument( 125 "--compar", 126 action="store_true", 127 help="データ突合", 128 ) 129 exclusive.add_argument( 130 "--unification", 131 nargs="?", 132 const="rename.ini", 133 help="ファイルの内容に従って記録済みのメンバー名を修正する(default: %(const)s)", 134 ) 135 exclusive.add_argument( 136 "--recalculation", 137 action="store_true", 138 help="ポイント再計算", 139 ) 140 exclusive.add_argument( 141 "--export", 142 dest="export_data", 143 nargs="?", 144 const="export", 145 metavar="PREFIX", 146 help="メンバー設定情報をエクスポート(default prefix: %(const)s)", 147 ) 148 exclusive.add_argument( 149 "--import", 150 nargs="?", 151 dest="import_data", 152 const="export", 153 metavar="PREFIX", 154 help="メンバー設定情報をインポート(default prefix: %(const)s)", 155 ) 156 exclusive.add_argument( 157 "--vacuum", 158 action="store_true", 159 help="database vacuum", 160 ) 161 exclusive.add_argument( 162 "--gen-test-data", 163 type=int, 164 dest="gen_test_data", 165 nargs="?", 166 const=1, 167 default=None, 168 metavar="count", 169 help="テスト用サンプルデータ生成(count=生成回数, default: %(const)s)", 170 ) 171 case "test.py": # 動作テスト用オプション 172 p.add_argument( 173 "-t", "--testcase", 174 dest="testcase", 175 ) 176 177 return p.parse_args() 178 179 180def setup(): 181 """設定ファイル読み込み""" 182 183 set_loglevel() 184 185 g.args = arg_parser() 186 187 # 連携サービス 188 match g.args.service: 189 case "slack": 190 g.selected_service = "slack" 191 case "discord": 192 g.selected_service = "discord" 193 case "standard_io" | "std": 194 g.selected_service = "standard_io" 195 case "web" | "flask": 196 g.selected_service = "web" 197 case _: 198 sys.exit() 199 200 if not hasattr(g.args, "testcase"): 201 g.args.testcase = False 202 else: 203 g.selected_service = "standard_io" 204 205 # ログフォーマット 206 if g.args.notime: 207 fmt = "" 208 else: 209 fmt = "[%(asctime)s]" 210 if g.args.debug or g.args.verbose: 211 fmt += "[%(levelname)s][%(name)s:%(module)s:%(funcName)s] %(message)s" 212 else: 213 fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s" 214 215 if g.args.debug: 216 if g.args.verbose: 217 logging.basicConfig(level=logging.TRACE, format=fmt) # type: ignore 218 logging.info("DEBUG MODE(verbose)") 219 else: 220 logging.basicConfig(level=logging.DEBUG, format=fmt) 221 logging.info("DEBUG MODE") 222 else: 223 if g.args.moderate: 224 logging.basicConfig(level=logging.WARNING, format=fmt) 225 else: 226 logging.basicConfig(level=logging.INFO, format=fmt) 227 228 g.cfg = AppConfig(g.args.config) 229 g.adapter = factory.select_adapter(g.selected_service, g.cfg) 230 register() 231 232 # 設定内容のロギング 233 logging.info("conf: %s", os.path.join(g.cfg.config_dir, g.args.config)) 234 logging.info("font: %s", g.cfg.setting.font_file) 235 logging.info("database: %s", g.cfg.setting.database_file) 236 logging.info("service: %s, graph_library: %s", g.selected_service, g.adapter.conf.plotting_backend) 237 logging.info( 238 "rule_version: %s, origin_point: %s, return_point: %s, time_adjust: %sh", 239 g.cfg.mahjong.rule_version, g.cfg.mahjong.origin_point, g.cfg.mahjong.return_point, g.cfg.setting.time_adjust 240 ) 241 242 243def read_memberslist(log=True): 244 """メンバー/チームリスト読み込み 245 246 Args: 247 log (bool, optional): 読み込み時に内容をログに出力する. Defaults to True. 248 """ 249 250 g.cfg.member.guest_name = lookup.db.get_guest() 251 g.member_list = lookup.db.get_member_list() 252 g.team_list = lookup.db.get_team_list() 253 254 if log: 255 logging.info("guest_name: %s", g.cfg.member.guest_name) 256 logging.info("member_list: %s", sorted(set(g.member_list.values()))) 257 logging.info("team_list: %s", [x["team"] for x in g.team_list]) 258 259 260def register(): 261 """ディスパッチテーブル登録""" 262 263 def dispatch_help(m: "MessageParserProtocol"): 264 m.set_data("ヘルプ", compose.msg_help.event_message(), StyleOptions()) 265 m.post.ts = m.data.event_ts 266 267 def dispatch_download(m: "MessageParserProtocol"): 268 m.set_data("成績記録DB", g.cfg.setting.database_file, StyleOptions()) 269 270 def dispatch_members_list(m: "MessageParserProtocol"): 271 m.set_data("登録済みメンバー", lookup.textdata.get_members_list(), StyleOptions(codeblock=True)) 272 m.post.ts = m.data.event_ts 273 274 def dispatch_team_list(m: "MessageParserProtocol"): 275 m.set_data("登録済みチーム", lookup.textdata.get_team_list(), StyleOptions(codeblock=True)) 276 m.post.ts = m.data.event_ts 277 278 def dispatch_member_append(m: "MessageParserProtocol"): 279 m.set_data("メンバー追加", member.append(m.argument), StyleOptions(key_title=False)) 280 281 def dispatch_member_remove(m: "MessageParserProtocol"): 282 m.set_data("メンバー削除", member.remove(m.argument), StyleOptions(key_title=False)) 283 284 def dispatch_team_create(m: "MessageParserProtocol"): 285 m.set_data("チーム作成", team.create(m.argument), StyleOptions(key_title=False)) 286 287 def dispatch_team_delete(m: "MessageParserProtocol"): 288 m.set_data("チーム削除", team.delete(m.argument), StyleOptions(key_title=False)) 289 290 def dispatch_team_append(m: "MessageParserProtocol"): 291 m.set_data("チーム所属", team.append(m.argument), StyleOptions(key_title=False)) 292 293 def dispatch_team_remove(m: "MessageParserProtocol"): 294 m.set_data("チーム脱退", team.remove(m.argument), StyleOptions(key_title=False)) 295 296 def dispatch_team_clear(m: "MessageParserProtocol"): 297 m.set_data("全チーム削除", team.clear(), StyleOptions(key_title=False)) 298 299 dispatch_table: dict = { 300 "results": libs.commands.results.entry.main, 301 "graph": libs.commands.graph.entry.main, 302 "ranking": libs.commands.ranking.entry.main, 303 "report": libs.commands.report.entry.main, 304 "member": dispatch_members_list, 305 "team_list": dispatch_team_list, 306 "download": dispatch_download, 307 "add": dispatch_member_append, 308 "delete": dispatch_member_remove, 309 "team_create": dispatch_team_create, 310 "team_del": dispatch_team_delete, 311 "team_add": dispatch_team_append, 312 "team_remove": dispatch_team_remove, 313 "team_clear": dispatch_team_clear, 314 } 315 316 # ヘルプ 317 g.keyword_dispatcher.update({g.cfg.setting.help: dispatch_help}) 318 319 for command, ep in dispatch_table.items(): 320 # 呼び出しキーワード登録 321 if hasattr(g.cfg, command): 322 sub_command = cast("SubCommand", getattr(g.cfg, command)) 323 for alias in sub_command.commandword: 324 g.keyword_dispatcher.update({alias: ep}) 325 # スラッシュコマンド登録 326 for alias in cast(list, getattr(g.cfg.alias, command)): 327 g.command_dispatcher.update({alias: ep}) 328 329 # サービス別コマンド登録 330 g.command_dispatcher.update(g.adapter.conf.command_dispatcher) 331 g.keyword_dispatcher.update(g.adapter.conf.keyword_dispatcher) 332 333 logging.debug("keyword_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.keyword_dispatcher.items()])) 334 logging.debug("command_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.command_dispatcher.items()]))
def
set_loglevel():
30def set_loglevel(): 31 """ログレベル追加""" 32 33 # DEBUG : 10 34 # INFO : 20 35 # WARNING : 30 36 # ERROR : 40 37 # CRITICAL : 50 38 39 # TRACE 40 logging.TRACE = 5 # type: ignore 41 logging.trace = partial(logging.log, logging.TRACE) # type: ignore 42 logging.addLevelName(logging.TRACE, "TRACE") # type: ignore
ログレベル追加
def
arg_parser() -> argparse.Namespace:
45def arg_parser() -> argparse.Namespace: 46 """コマンドライン解析 47 48 Returns: 49 argparse.Namespace: オブジェクト 50 """ 51 52 p = argparse.ArgumentParser( 53 formatter_class=argparse.RawTextHelpFormatter, 54 add_help=True, 55 ) 56 57 p.add_argument( 58 "-c", "--config", 59 default="config.ini", 60 help="設定ファイル(default: %(default)s)", 61 ) 62 p.add_argument( 63 "--profile", 64 help=argparse.SUPPRESS, 65 ) 66 p.add_argument( 67 "--service", 68 choices=[ 69 "slack", 70 "discord", 71 "standard_io", "std", 72 "web", "flask", 73 ], 74 default="slack", 75 help="連携先サービス", 76 ) 77 78 logging_group = p.add_argument_group("logging options") 79 logging_group.add_argument( 80 "--debug", 81 action="store_true", 82 help="デバッグ情報表示", 83 ) 84 logging_group.add_argument( 85 "--verbose", "--trace", 86 dest="verbose", 87 action="store_true", 88 help="詳細デバッグ情報表示", 89 ) 90 logging_group.add_argument( 91 "--moderate", 92 action="store_true", 93 help="ログレベルがエラー以下のもを非表示", 94 ) 95 logging_group.add_argument( 96 "--notime", 97 action="store_true", 98 help="ログフォーマットから日時を削除", 99 ) 100 101 match os.path.basename(sys.argv[0]): 102 case "app.py": 103 service_stdio = p.add_argument_group("Only allowed when --service=standard_io") 104 service_stdio.add_argument( 105 "--text", 106 type=str, 107 help="input text strings", 108 ) 109 service_web = p.add_argument_group("Only allowed when --service=web") 110 service_web.add_argument( 111 "--host", 112 type=str, 113 default="127.0.0.1", 114 help="listen address(default: %(default)s)", 115 ) 116 service_web.add_argument( 117 "--port", 118 type=int, 119 default=8000, 120 help="bind port(default: %(default)s)", 121 ) 122 case "dbtools.py": # dbtools専用オプション 123 required = p.add_argument_group("Required options(amutually exclusive)") 124 exclusive = required.add_mutually_exclusive_group() 125 exclusive.add_argument( 126 "--compar", 127 action="store_true", 128 help="データ突合", 129 ) 130 exclusive.add_argument( 131 "--unification", 132 nargs="?", 133 const="rename.ini", 134 help="ファイルの内容に従って記録済みのメンバー名を修正する(default: %(const)s)", 135 ) 136 exclusive.add_argument( 137 "--recalculation", 138 action="store_true", 139 help="ポイント再計算", 140 ) 141 exclusive.add_argument( 142 "--export", 143 dest="export_data", 144 nargs="?", 145 const="export", 146 metavar="PREFIX", 147 help="メンバー設定情報をエクスポート(default prefix: %(const)s)", 148 ) 149 exclusive.add_argument( 150 "--import", 151 nargs="?", 152 dest="import_data", 153 const="export", 154 metavar="PREFIX", 155 help="メンバー設定情報をインポート(default prefix: %(const)s)", 156 ) 157 exclusive.add_argument( 158 "--vacuum", 159 action="store_true", 160 help="database vacuum", 161 ) 162 exclusive.add_argument( 163 "--gen-test-data", 164 type=int, 165 dest="gen_test_data", 166 nargs="?", 167 const=1, 168 default=None, 169 metavar="count", 170 help="テスト用サンプルデータ生成(count=生成回数, default: %(const)s)", 171 ) 172 case "test.py": # 動作テスト用オプション 173 p.add_argument( 174 "-t", "--testcase", 175 dest="testcase", 176 ) 177 178 return p.parse_args()
コマンドライン解析
Returns:
argparse.Namespace: オブジェクト
def
setup():
181def setup(): 182 """設定ファイル読み込み""" 183 184 set_loglevel() 185 186 g.args = arg_parser() 187 188 # 連携サービス 189 match g.args.service: 190 case "slack": 191 g.selected_service = "slack" 192 case "discord": 193 g.selected_service = "discord" 194 case "standard_io" | "std": 195 g.selected_service = "standard_io" 196 case "web" | "flask": 197 g.selected_service = "web" 198 case _: 199 sys.exit() 200 201 if not hasattr(g.args, "testcase"): 202 g.args.testcase = False 203 else: 204 g.selected_service = "standard_io" 205 206 # ログフォーマット 207 if g.args.notime: 208 fmt = "" 209 else: 210 fmt = "[%(asctime)s]" 211 if g.args.debug or g.args.verbose: 212 fmt += "[%(levelname)s][%(name)s:%(module)s:%(funcName)s] %(message)s" 213 else: 214 fmt += "[%(levelname)s][%(module)s:%(funcName)s] %(message)s" 215 216 if g.args.debug: 217 if g.args.verbose: 218 logging.basicConfig(level=logging.TRACE, format=fmt) # type: ignore 219 logging.info("DEBUG MODE(verbose)") 220 else: 221 logging.basicConfig(level=logging.DEBUG, format=fmt) 222 logging.info("DEBUG MODE") 223 else: 224 if g.args.moderate: 225 logging.basicConfig(level=logging.WARNING, format=fmt) 226 else: 227 logging.basicConfig(level=logging.INFO, format=fmt) 228 229 g.cfg = AppConfig(g.args.config) 230 g.adapter = factory.select_adapter(g.selected_service, g.cfg) 231 register() 232 233 # 設定内容のロギング 234 logging.info("conf: %s", os.path.join(g.cfg.config_dir, g.args.config)) 235 logging.info("font: %s", g.cfg.setting.font_file) 236 logging.info("database: %s", g.cfg.setting.database_file) 237 logging.info("service: %s, graph_library: %s", g.selected_service, g.adapter.conf.plotting_backend) 238 logging.info( 239 "rule_version: %s, origin_point: %s, return_point: %s, time_adjust: %sh", 240 g.cfg.mahjong.rule_version, g.cfg.mahjong.origin_point, g.cfg.mahjong.return_point, g.cfg.setting.time_adjust 241 )
設定ファイル読み込み
def
read_memberslist(log=True):
244def read_memberslist(log=True): 245 """メンバー/チームリスト読み込み 246 247 Args: 248 log (bool, optional): 読み込み時に内容をログに出力する. Defaults to True. 249 """ 250 251 g.cfg.member.guest_name = lookup.db.get_guest() 252 g.member_list = lookup.db.get_member_list() 253 g.team_list = lookup.db.get_team_list() 254 255 if log: 256 logging.info("guest_name: %s", g.cfg.member.guest_name) 257 logging.info("member_list: %s", sorted(set(g.member_list.values()))) 258 logging.info("team_list: %s", [x["team"] for x in g.team_list])
メンバー/チームリスト読み込み
Arguments:
- log (bool, optional): 読み込み時に内容をログに出力する. Defaults to True.
def
register():
261def register(): 262 """ディスパッチテーブル登録""" 263 264 def dispatch_help(m: "MessageParserProtocol"): 265 m.set_data("ヘルプ", compose.msg_help.event_message(), StyleOptions()) 266 m.post.ts = m.data.event_ts 267 268 def dispatch_download(m: "MessageParserProtocol"): 269 m.set_data("成績記録DB", g.cfg.setting.database_file, StyleOptions()) 270 271 def dispatch_members_list(m: "MessageParserProtocol"): 272 m.set_data("登録済みメンバー", lookup.textdata.get_members_list(), StyleOptions(codeblock=True)) 273 m.post.ts = m.data.event_ts 274 275 def dispatch_team_list(m: "MessageParserProtocol"): 276 m.set_data("登録済みチーム", lookup.textdata.get_team_list(), StyleOptions(codeblock=True)) 277 m.post.ts = m.data.event_ts 278 279 def dispatch_member_append(m: "MessageParserProtocol"): 280 m.set_data("メンバー追加", member.append(m.argument), StyleOptions(key_title=False)) 281 282 def dispatch_member_remove(m: "MessageParserProtocol"): 283 m.set_data("メンバー削除", member.remove(m.argument), StyleOptions(key_title=False)) 284 285 def dispatch_team_create(m: "MessageParserProtocol"): 286 m.set_data("チーム作成", team.create(m.argument), StyleOptions(key_title=False)) 287 288 def dispatch_team_delete(m: "MessageParserProtocol"): 289 m.set_data("チーム削除", team.delete(m.argument), StyleOptions(key_title=False)) 290 291 def dispatch_team_append(m: "MessageParserProtocol"): 292 m.set_data("チーム所属", team.append(m.argument), StyleOptions(key_title=False)) 293 294 def dispatch_team_remove(m: "MessageParserProtocol"): 295 m.set_data("チーム脱退", team.remove(m.argument), StyleOptions(key_title=False)) 296 297 def dispatch_team_clear(m: "MessageParserProtocol"): 298 m.set_data("全チーム削除", team.clear(), StyleOptions(key_title=False)) 299 300 dispatch_table: dict = { 301 "results": libs.commands.results.entry.main, 302 "graph": libs.commands.graph.entry.main, 303 "ranking": libs.commands.ranking.entry.main, 304 "report": libs.commands.report.entry.main, 305 "member": dispatch_members_list, 306 "team_list": dispatch_team_list, 307 "download": dispatch_download, 308 "add": dispatch_member_append, 309 "delete": dispatch_member_remove, 310 "team_create": dispatch_team_create, 311 "team_del": dispatch_team_delete, 312 "team_add": dispatch_team_append, 313 "team_remove": dispatch_team_remove, 314 "team_clear": dispatch_team_clear, 315 } 316 317 # ヘルプ 318 g.keyword_dispatcher.update({g.cfg.setting.help: dispatch_help}) 319 320 for command, ep in dispatch_table.items(): 321 # 呼び出しキーワード登録 322 if hasattr(g.cfg, command): 323 sub_command = cast("SubCommand", getattr(g.cfg, command)) 324 for alias in sub_command.commandword: 325 g.keyword_dispatcher.update({alias: ep}) 326 # スラッシュコマンド登録 327 for alias in cast(list, getattr(g.cfg.alias, command)): 328 g.command_dispatcher.update({alias: ep}) 329 330 # サービス別コマンド登録 331 g.command_dispatcher.update(g.adapter.conf.command_dispatcher) 332 g.keyword_dispatcher.update(g.adapter.conf.keyword_dispatcher) 333 334 logging.debug("keyword_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.keyword_dispatcher.items()])) 335 logging.debug("command_dispatcher:\n%s", "\n".join([f"\t{k}: {v}" for k, v in g.command_dispatcher.items()]))
ディスパッチテーブル登録