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