libs.data.loader
libs/data/loader.py
1""" 2libs/data/loader.py 3""" 4 5import logging 6import re 7from datetime import datetime 8 9import pandas as pd 10 11import libs.global_value as g 12from libs.utils import dbutil 13 14 15def read_data(keyword: str) -> pd.DataFrame: 16 """データベースからデータを取得する 17 18 Args: 19 keyword (str): SQL選択キーワード 20 21 Returns: 22 pd.DataFrame: 集計結果 23 """ 24 25 # デバッグ用 26 pd.set_option("display.max_rows", None) 27 pd.set_option("display.max_columns", None) 28 29 if "starttime" in g.params: 30 g.params.update(starttime=g.params["starttime"].format("sql")) 31 if "endtime" in g.params: 32 g.params.update(endtime=g.params["endtime"].format("sql")) 33 if "rule_version" not in g.params: 34 g.params.update(rule_version=g.cfg.mahjong.rule_version) 35 36 sql = query_modification(dbutil.query(keyword)) 37 logging.debug("prm: %s", g.params) 38 logging.debug("sql: %s", named_query(sql)) 39 40 df = pd.read_sql( 41 sql=sql, 42 con=dbutil.connection(g.cfg.setting.database_file), 43 params=g.params, 44 ) 45 logging.trace(df) # type: ignore 46 47 return df 48 49 50def query_modification(sql: str) -> str: 51 """クエリをオプションの内容で修正する 52 53 Args: 54 sql (str): 修正するクエリ 55 56 Returns: 57 str: 修正後のクエリ 58 """ 59 60 if g.params.get("individual"): # 個人集計 61 sql = sql.replace("--[individual] ", "") 62 # ゲスト関連フラグ 63 if g.params.get("unregistered_replace"): 64 sql = sql.replace("--[unregistered_replace] ", "") 65 if g.params.get("guest_skip"): 66 sql = sql.replace("--[guest_not_skip] ", "") 67 else: 68 sql = sql.replace("--[guest_skip] ", "") 69 else: 70 sql = sql.replace("--[unregistered_not_replace] ", "") 71 else: # チーム集計 72 g.params.update(unregistered_replace=False) 73 g.params.update(guest_skip=True) 74 sql = sql.replace("--[team] ", "") 75 if not g.params.get("friendly_fire"): 76 sql = sql.replace("--[friendly_fire] ", "") 77 78 # 集約集計 79 match g.params.get("collection"): 80 case "daily": 81 sql = sql.replace("--[collection_daily] ", "") 82 sql = sql.replace("--[collection] ", "") 83 case "monthly": 84 sql = sql.replace("--[collection_monthly] ", "") 85 sql = sql.replace("--[collection] ", "") 86 case "yearly": 87 sql = sql.replace("--[collection_yearly] ", "") 88 sql = sql.replace("--[collection] ", "") 89 case "all": 90 sql = sql.replace("--[collection_all] ", "") 91 sql = sql.replace("--[collection] ", "") 92 case _: 93 sql = sql.replace("--[not_collection] ", "") 94 95 # ルール横断集計 96 if g.params.get("mixed"): 97 sql = sql.replace("game_info.rule_version = :rule_version", "1 = 1") 98 sql = sql.replace("results.rule_version = :rule_version", "1 = 1") 99 sql = sql.replace("rule_version = :rule_version", "1 = 1") 100 101 # コメント検索 102 if g.params.get("search_word") or g.params.get("group_length"): 103 sql = sql.replace("--[group_by] ", "") 104 else: 105 sql = sql.replace("--[not_group_by] ", "") 106 107 if g.params.get("search_word"): 108 sql = sql.replace("--[search_word] ", "") 109 else: 110 sql = sql.replace("--[not_search_word] ", "") 111 112 if g.params.get("group_length"): 113 sql = sql.replace("--[group_length] ", "") 114 else: 115 sql = sql.replace("--[not_group_length] ", "") 116 if g.params.get("search_word"): 117 sql = sql.replace("--[comment] ", "") 118 else: 119 sql = sql.replace("--[not_comment] ", "") 120 121 # 直近N検索用(全範囲取得してから絞る) 122 if g.params.get("target_count") != 0: 123 sql = sql.replace( 124 "and my.playtime between", 125 "-- and my.playtime between" 126 ) 127 128 # プレイヤーリスト 129 if g.params.get("player_name"): 130 sql = sql.replace("--[player_name] ", "") 131 sql = sql.replace( 132 "<<player_list>>", 133 ":" + ", :".join(g.params["player_list"]) 134 ) 135 sql = sql.replace("<<guest_mark>>", g.cfg.setting.guest_mark) 136 137 # フラグの処理 138 match g.cfg.aggregate_unit: 139 case "M": 140 sql = sql.replace("<<collection>>", "substr(collection_daily, 1, 7) as 集計") 141 sql = sql.replace("<<group by>>", "group by 集計") 142 case "Y": 143 sql = sql.replace("<<collection>>", "substr(collection_daily, 1, 4) as 集計") 144 sql = sql.replace("<<group by>>", "group by 集計") 145 case "A": 146 sql = sql.replace("<<collection>>", "'合計' as 集計") 147 sql = sql.replace("<<group by>>", "") 148 149 if g.params.get("interval") is not None: 150 if g.params.get("interval") == 0: 151 sql = sql.replace("<<Calculation Formula>>", ":interval") 152 else: 153 sql = sql.replace( 154 "<<Calculation Formula>>", 155 "(row_number() over (order by total_count desc) - 1) / :interval" 156 ) 157 if g.params.get("kind") is not None: 158 if g.params.get("kind") == "grandslam": 159 if g.cfg.undefined_word == 0: 160 sql = sql.replace("<<where_string>>", "and (words.type is null or words.type = 0)") 161 else: 162 sql = sql.replace("<<where_string>>", "and words.type = 0") 163 else: 164 match g.cfg.undefined_word: 165 case 1: 166 sql = sql.replace("<<where_string>>", "and (words.type is null or words.type = 1)") 167 case 2: 168 sql = sql.replace("<<where_string>>", "and (words.type is null or words.type = 2)") 169 case _: 170 sql = sql.replace("<<where_string>>", "and (words.type = 1 or words.type = 2)") 171 172 # SQLコメント削除 173 sql = re.sub(r"^ *--\[.*$", "", sql, flags=re.MULTILINE) 174 sql = re.sub(r"\n+", "\n", sql, flags=re.MULTILINE) 175 176 return sql 177 178 179def named_query(query: str) -> str: 180 """クエリにパラメータをバインドして返す 181 182 Args: 183 query (str): SQL 184 185 Returns: 186 str: バインド済みSQL 187 """ 188 189 for k, v in g.params.items(): 190 if isinstance(v, datetime): 191 g.params[k] = v.strftime("%Y-%m-%d %H:%M:%S") 192 193 return re.sub(r":(\w+)", lambda m: repr(g.params.get(m.group(1), m.group(0))), query)
def
read_data(keyword: str) -> pandas.core.frame.DataFrame:
16def read_data(keyword: str) -> pd.DataFrame: 17 """データベースからデータを取得する 18 19 Args: 20 keyword (str): SQL選択キーワード 21 22 Returns: 23 pd.DataFrame: 集計結果 24 """ 25 26 # デバッグ用 27 pd.set_option("display.max_rows", None) 28 pd.set_option("display.max_columns", None) 29 30 if "starttime" in g.params: 31 g.params.update(starttime=g.params["starttime"].format("sql")) 32 if "endtime" in g.params: 33 g.params.update(endtime=g.params["endtime"].format("sql")) 34 if "rule_version" not in g.params: 35 g.params.update(rule_version=g.cfg.mahjong.rule_version) 36 37 sql = query_modification(dbutil.query(keyword)) 38 logging.debug("prm: %s", g.params) 39 logging.debug("sql: %s", named_query(sql)) 40 41 df = pd.read_sql( 42 sql=sql, 43 con=dbutil.connection(g.cfg.setting.database_file), 44 params=g.params, 45 ) 46 logging.trace(df) # type: ignore 47 48 return df
データベースからデータを取得する
Arguments:
- keyword (str): SQL選択キーワード
Returns:
pd.DataFrame: 集計結果
def
query_modification(sql: str) -> str:
51def query_modification(sql: str) -> str: 52 """クエリをオプションの内容で修正する 53 54 Args: 55 sql (str): 修正するクエリ 56 57 Returns: 58 str: 修正後のクエリ 59 """ 60 61 if g.params.get("individual"): # 個人集計 62 sql = sql.replace("--[individual] ", "") 63 # ゲスト関連フラグ 64 if g.params.get("unregistered_replace"): 65 sql = sql.replace("--[unregistered_replace] ", "") 66 if g.params.get("guest_skip"): 67 sql = sql.replace("--[guest_not_skip] ", "") 68 else: 69 sql = sql.replace("--[guest_skip] ", "") 70 else: 71 sql = sql.replace("--[unregistered_not_replace] ", "") 72 else: # チーム集計 73 g.params.update(unregistered_replace=False) 74 g.params.update(guest_skip=True) 75 sql = sql.replace("--[team] ", "") 76 if not g.params.get("friendly_fire"): 77 sql = sql.replace("--[friendly_fire] ", "") 78 79 # 集約集計 80 match g.params.get("collection"): 81 case "daily": 82 sql = sql.replace("--[collection_daily] ", "") 83 sql = sql.replace("--[collection] ", "") 84 case "monthly": 85 sql = sql.replace("--[collection_monthly] ", "") 86 sql = sql.replace("--[collection] ", "") 87 case "yearly": 88 sql = sql.replace("--[collection_yearly] ", "") 89 sql = sql.replace("--[collection] ", "") 90 case "all": 91 sql = sql.replace("--[collection_all] ", "") 92 sql = sql.replace("--[collection] ", "") 93 case _: 94 sql = sql.replace("--[not_collection] ", "") 95 96 # ルール横断集計 97 if g.params.get("mixed"): 98 sql = sql.replace("game_info.rule_version = :rule_version", "1 = 1") 99 sql = sql.replace("results.rule_version = :rule_version", "1 = 1") 100 sql = sql.replace("rule_version = :rule_version", "1 = 1") 101 102 # コメント検索 103 if g.params.get("search_word") or g.params.get("group_length"): 104 sql = sql.replace("--[group_by] ", "") 105 else: 106 sql = sql.replace("--[not_group_by] ", "") 107 108 if g.params.get("search_word"): 109 sql = sql.replace("--[search_word] ", "") 110 else: 111 sql = sql.replace("--[not_search_word] ", "") 112 113 if g.params.get("group_length"): 114 sql = sql.replace("--[group_length] ", "") 115 else: 116 sql = sql.replace("--[not_group_length] ", "") 117 if g.params.get("search_word"): 118 sql = sql.replace("--[comment] ", "") 119 else: 120 sql = sql.replace("--[not_comment] ", "") 121 122 # 直近N検索用(全範囲取得してから絞る) 123 if g.params.get("target_count") != 0: 124 sql = sql.replace( 125 "and my.playtime between", 126 "-- and my.playtime between" 127 ) 128 129 # プレイヤーリスト 130 if g.params.get("player_name"): 131 sql = sql.replace("--[player_name] ", "") 132 sql = sql.replace( 133 "<<player_list>>", 134 ":" + ", :".join(g.params["player_list"]) 135 ) 136 sql = sql.replace("<<guest_mark>>", g.cfg.setting.guest_mark) 137 138 # フラグの処理 139 match g.cfg.aggregate_unit: 140 case "M": 141 sql = sql.replace("<<collection>>", "substr(collection_daily, 1, 7) as 集計") 142 sql = sql.replace("<<group by>>", "group by 集計") 143 case "Y": 144 sql = sql.replace("<<collection>>", "substr(collection_daily, 1, 4) as 集計") 145 sql = sql.replace("<<group by>>", "group by 集計") 146 case "A": 147 sql = sql.replace("<<collection>>", "'合計' as 集計") 148 sql = sql.replace("<<group by>>", "") 149 150 if g.params.get("interval") is not None: 151 if g.params.get("interval") == 0: 152 sql = sql.replace("<<Calculation Formula>>", ":interval") 153 else: 154 sql = sql.replace( 155 "<<Calculation Formula>>", 156 "(row_number() over (order by total_count desc) - 1) / :interval" 157 ) 158 if g.params.get("kind") is not None: 159 if g.params.get("kind") == "grandslam": 160 if g.cfg.undefined_word == 0: 161 sql = sql.replace("<<where_string>>", "and (words.type is null or words.type = 0)") 162 else: 163 sql = sql.replace("<<where_string>>", "and words.type = 0") 164 else: 165 match g.cfg.undefined_word: 166 case 1: 167 sql = sql.replace("<<where_string>>", "and (words.type is null or words.type = 1)") 168 case 2: 169 sql = sql.replace("<<where_string>>", "and (words.type is null or words.type = 2)") 170 case _: 171 sql = sql.replace("<<where_string>>", "and (words.type = 1 or words.type = 2)") 172 173 # SQLコメント削除 174 sql = re.sub(r"^ *--\[.*$", "", sql, flags=re.MULTILINE) 175 sql = re.sub(r"\n+", "\n", sql, flags=re.MULTILINE) 176 177 return sql
クエリをオプションの内容で修正する
Arguments:
- sql (str): 修正するクエリ
Returns:
str: 修正後のクエリ
def
named_query(query: str) -> str:
180def named_query(query: str) -> str: 181 """クエリにパラメータをバインドして返す 182 183 Args: 184 query (str): SQL 185 186 Returns: 187 str: バインド済みSQL 188 """ 189 190 for k, v in g.params.items(): 191 if isinstance(v, datetime): 192 g.params[k] = v.strftime("%Y-%m-%d %H:%M:%S") 193 194 return re.sub(r":(\w+)", lambda m: repr(g.params.get(m.group(1), m.group(0))), query)
クエリにパラメータをバインドして返す
Arguments:
- query (str): SQL
Returns:
str: バインド済みSQL