mdbq 0.4.5__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mdbq/aggregation/aggregation.py +27 -140
- mdbq/aggregation/df_types.py +180 -0
- mdbq/aggregation/mysql_types.py +231 -0
- mdbq/aggregation/query_data.py +1 -1
- mdbq/dataframe/converter.py +45 -0
- mdbq/mysql/{data_types.py → data_types_/345/215/263/345/260/206/345/210/240/351/231/244.py} +19 -8
- mdbq/mysql/mysql.py +148 -191
- {mdbq-0.4.5.dist-info → mdbq-1.0.0.dist-info}/METADATA +1 -1
- {mdbq-0.4.5.dist-info → mdbq-1.0.0.dist-info}/RECORD +11 -9
- {mdbq-0.4.5.dist-info → mdbq-1.0.0.dist-info}/WHEEL +1 -1
- {mdbq-0.4.5.dist-info → mdbq-1.0.0.dist-info}/top_level.txt +0 -0
mdbq/aggregation/aggregation.py
CHANGED
@@ -11,6 +11,7 @@ import platform
|
|
11
11
|
import json
|
12
12
|
from mdbq.mongo import mongo
|
13
13
|
from mdbq.mysql import mysql
|
14
|
+
from mdbq.aggregation import df_types
|
14
15
|
from mdbq.config import get_myconf
|
15
16
|
from mdbq.config import set_support
|
16
17
|
from mdbq.dataframe import converter
|
@@ -25,109 +26,15 @@ warnings.filterwarnings('ignore')
|
|
25
26
|
1. DatabaseUpdate: 程序用于对爬虫下载的原始数据进行清洗并入库;
|
26
27
|
数据清洗主要包括对字段名的非法字符处理,对 df 中的非法值进行预处理;
|
27
28
|
数据入库时会较检并更新本地 json 文件的 dtypes 信息;
|
28
|
-
若 json 缺失 dtypes 信息, 可用
|
29
|
+
若 json 缺失 dtypes 信息, 可用 update_df_types_to_json 先更新, 或者手动修改添加本地 json 信息;
|
29
30
|
2. DataTypes: 类用于将某个csv文件的 dtypes 信息存入本地 json 文件, 会调用 converter 对 df 预处理;
|
30
31
|
作用于完善某个数据库 dtypes 信息,可以使用本函数更新;
|
31
|
-
3.
|
32
|
+
3. update_df_types_to_json: 函数将一个 csv 文件的 dtypes 信息更新至本地 json 文件;
|
32
33
|
4. upload: 函数将一个文件夹上传至数据库;
|
33
|
-
如果本地 json 中确实这个数据库的 dtypes 信息, 请用
|
34
|
+
如果本地 json 中确实这个数据库的 dtypes 信息, 请用 update_df_types_to_json 更新 json 文件再执行数据上传;
|
34
35
|
"""
|
35
36
|
|
36
37
|
|
37
|
-
class DataTypes:
|
38
|
-
"""
|
39
|
-
将某表的列信息添加到 json 示例:
|
40
|
-
file = '/Users/xigua/Downloads/天猫直通车旧报表(未排重版本).csv'
|
41
|
-
df = pd.read_csv(file, encoding='utf-8_sig', header=0, na_filter=False)
|
42
|
-
d = DataTypes()
|
43
|
-
d.read_dtypes(
|
44
|
-
df=df,
|
45
|
-
db_name='天猫数据2',
|
46
|
-
collection_name='旧版报表',
|
47
|
-
is_file_dtype=False, # 关闭文件优先
|
48
|
-
)
|
49
|
-
d.dtypes_to_file()
|
50
|
-
"""
|
51
|
-
def __init__(self):
|
52
|
-
self.path = set_support.SetSupport(dirname='support').dirname
|
53
|
-
if not os.path.exists(self.path):
|
54
|
-
os.mkdir(self.path)
|
55
|
-
self.json_file = os.path.join(self.path, 'data_types.json')
|
56
|
-
# self.datas = json.loads('{}') # 等待写入 json 文件的 dtypes 数据
|
57
|
-
self.datas = {'json统计': {'数据库量': 0, '集合数量': 0, '字段量': 0}}
|
58
|
-
self.json_before()
|
59
|
-
|
60
|
-
def json_before(self):
|
61
|
-
""" 本地 json 文件的 dtypes 信息, 初始化更新给 self.datas """
|
62
|
-
if os.path.isfile(self.json_file):
|
63
|
-
with open(self.json_file, 'r', encoding='utf-8_sig') as json_file:
|
64
|
-
json_ = json.load(json_file)
|
65
|
-
self.datas.update(json_)
|
66
|
-
|
67
|
-
def load_dtypes(self, db_name, collection_name, ):
|
68
|
-
return self.datas[db_name][collection_name]
|
69
|
-
|
70
|
-
|
71
|
-
def read_dtypes(self, db_name, collection_name, df=pd.DataFrame(), is_file_dtype=True):
|
72
|
-
"""
|
73
|
-
读取 df 的 dtypes, 并更新本地 json 文件
|
74
|
-
期间会 清理不合规的列名, 并对数据类型进行转换(尝试将 object 类型转为 int 或 float)
|
75
|
-
返回: df 的 dtypes, 后续使用示例: df = df.astype(dtypes, errors='ignore')
|
76
|
-
is_file_dtype=True: 默认情况下以旧 json 优先, 即允许手动指定 json 文件里面的数据类型
|
77
|
-
"""
|
78
|
-
if len(df) == 0:
|
79
|
-
return
|
80
|
-
cv = converter.DataFrameConverter()
|
81
|
-
df = cv.convert_df_cols(df=df) # 清理 dataframe 列名的不合规字符
|
82
|
-
dtypes = df.dtypes.apply(str).to_dict()
|
83
|
-
dtypes = {db_name: {collection_name: dtypes}}
|
84
|
-
|
85
|
-
if not self.datas: # 如果不存在本地 json 文件, 直接返回即可
|
86
|
-
self.datas.update(dtypes)
|
87
|
-
return self.datas[db_name][collection_name]
|
88
|
-
else: # 存在则读取,并更新 df 的 dtypes
|
89
|
-
if db_name in list(self.datas.keys()): # ['京东数据2', '天猫数据2', '生意参谋数据2', '生意经2']
|
90
|
-
if collection_name in list(self.datas[db_name].keys()):
|
91
|
-
if is_file_dtype: # 旧数据优先
|
92
|
-
# # 用 dtypes 更新, 允许手动指定 json 文件里面的数据类型
|
93
|
-
dtypes[db_name][collection_name].update(self.datas[db_name][collection_name])
|
94
|
-
# 将 dtypes 更新进去,使 self.datas 包含新旧信息
|
95
|
-
self.datas[db_name][collection_name].update(dtypes[db_name][collection_name])
|
96
|
-
else: # 新数据优先
|
97
|
-
self.datas[db_name][collection_name].update(dtypes[db_name][collection_name])
|
98
|
-
else:
|
99
|
-
if is_file_dtype: # 旧数据优先
|
100
|
-
dtypes[db_name].update(self.datas[db_name])
|
101
|
-
self.datas[db_name].update(dtypes[db_name])
|
102
|
-
else:
|
103
|
-
self.datas[db_name].update(dtypes[db_name])
|
104
|
-
else:
|
105
|
-
# dtypes.update(self.datas) # 可以注释掉, 因为旧数据 self.datas 是空的
|
106
|
-
self.datas.update(dtypes)
|
107
|
-
dbs = 0
|
108
|
-
collections = 0
|
109
|
-
cols = 0
|
110
|
-
# self.datas.pop('json统计')
|
111
|
-
for k, v in self.datas.items():
|
112
|
-
if k == 'json统计':
|
113
|
-
continue
|
114
|
-
dbs += 1
|
115
|
-
for d, j in v.items():
|
116
|
-
collections += 1
|
117
|
-
for t, p in j.items():
|
118
|
-
cols += 1
|
119
|
-
tips = {'json统计': {'数据库量': dbs, '集合数量': collections, '字段量': cols}}
|
120
|
-
self.datas.update(tips)
|
121
|
-
return self.datas[db_name][collection_name] # 返回 df 的 dtypes
|
122
|
-
|
123
|
-
def dtypes_to_file(self):
|
124
|
-
""" 保存为本地 json 文件 """
|
125
|
-
# print(self.datas)
|
126
|
-
with open(self.json_file, 'w', encoding='utf-8_sig') as f:
|
127
|
-
json.dump(self.datas, f, ensure_ascii=False, sort_keys=True, indent=4)
|
128
|
-
time.sleep(1)
|
129
|
-
|
130
|
-
|
131
38
|
class DatabaseUpdate:
|
132
39
|
def __init__(self, path):
|
133
40
|
self.path = path # 数据所在目录, 即: 下载文件夹
|
@@ -142,7 +49,6 @@ class DatabaseUpdate:
|
|
142
49
|
print(f'1.1.0 初始化时传入了不存在的目录: {self.path}')
|
143
50
|
return
|
144
51
|
|
145
|
-
json_data = DataTypes() # json 文件, 包含数据的 dtypes 信息
|
146
52
|
for root, dirs, files in os.walk(self.path, topdown=False):
|
147
53
|
for name in files:
|
148
54
|
if '~$' in name or '.DS' in name or '.localized' in name or '.ini' in name or '$RECYCLE.BIN' in name or 'Icon' in name:
|
@@ -680,13 +586,6 @@ class DatabaseUpdate:
|
|
680
586
|
except Exception as e:
|
681
587
|
print(f'{name}, {e}')
|
682
588
|
if len(df) > 0:
|
683
|
-
# 创建包含 dtypes 信息的 json 文件
|
684
|
-
json_data.read_dtypes(
|
685
|
-
df=df,
|
686
|
-
db_name=db_name,
|
687
|
-
collection_name=collection_name,
|
688
|
-
is_file_dtype=True, # 默认本地文件优先: True
|
689
|
-
)
|
690
589
|
# 将数据传入 self.datas 等待更新进数据库
|
691
590
|
self.datas.append(
|
692
591
|
{
|
@@ -695,10 +594,8 @@ class DatabaseUpdate:
|
|
695
594
|
'数据主体': df,
|
696
595
|
}
|
697
596
|
)
|
698
|
-
json_data.dtypes_to_file() # 写入 json 文件, 包含数据的 dtypes 信息
|
699
597
|
|
700
598
|
# 品销宝一个表格里面包含多个 sheet, 最好是单独处理
|
701
|
-
json_data = DataTypes() # json 文件, 包含数据的 dtypes 信息
|
702
599
|
for root, dirs, files in os.walk(self.path, topdown=False):
|
703
600
|
for name in files:
|
704
601
|
if '~$' in name or '.DS' in name or '.localized' in name or '.jpg' in name or '.png' in name:
|
@@ -720,12 +617,6 @@ class DatabaseUpdate:
|
|
720
617
|
df.insert(loc=1, column='报表类型', value=sheet4)
|
721
618
|
db_name = '天猫数据2'
|
722
619
|
collection_name = f'推广数据_品销宝_{sheet4}'
|
723
|
-
json_data.read_dtypes(
|
724
|
-
df=df,
|
725
|
-
db_name=db_name,
|
726
|
-
collection_name=collection_name,
|
727
|
-
is_file_dtype=False,
|
728
|
-
)
|
729
620
|
self.datas.append(
|
730
621
|
{
|
731
622
|
'数据库名': db_name,
|
@@ -735,7 +626,6 @@ class DatabaseUpdate:
|
|
735
626
|
)
|
736
627
|
if is_move:
|
737
628
|
os.remove(os.path.join(root, name))
|
738
|
-
json_data.dtypes_to_file() # 写入 json 文件, 包含数据的 dtypes 信息
|
739
629
|
|
740
630
|
df = self.date_table() # 创建一个日期表
|
741
631
|
self.datas.append(
|
@@ -750,6 +640,7 @@ class DatabaseUpdate:
|
|
750
640
|
"""
|
751
641
|
将清洗后的 df 上传数据库
|
752
642
|
"""
|
643
|
+
df_to_json = dtypes.DataTypes() # json 文件, 包含数据的 dtypes 信息
|
753
644
|
for service_database in service_databases:
|
754
645
|
for service_name, database in service_database.items():
|
755
646
|
# print(service_name, database)
|
@@ -766,7 +657,13 @@ class DatabaseUpdate:
|
|
766
657
|
drop_duplicates=False,
|
767
658
|
)
|
768
659
|
for data in self.datas:
|
769
|
-
|
660
|
+
db_name, collection_name, df = data['数据库名'], data['集合名称'], data['数据主体']
|
661
|
+
df_to_json.get_df_types(
|
662
|
+
df=df,
|
663
|
+
db_name=db_name,
|
664
|
+
collection_name=collection_name,
|
665
|
+
is_file_dtype=True, # 默认本地文件优先: True
|
666
|
+
)
|
770
667
|
d.df_to_mongo(df=df, db_name=db_name, collection_name=collection_name)
|
771
668
|
|
772
669
|
elif database == 'mysql':
|
@@ -782,7 +679,14 @@ class DatabaseUpdate:
|
|
782
679
|
)
|
783
680
|
for data in self.datas:
|
784
681
|
df, db_name, collection_name = data['数据主体'], data['数据库名'], data['集合名称']
|
682
|
+
df_to_json.get_df_types(
|
683
|
+
df=df,
|
684
|
+
db_name=db_name,
|
685
|
+
collection_name=collection_name,
|
686
|
+
is_file_dtype=True, # 默认本地文件优先: True
|
687
|
+
)
|
785
688
|
m.df_to_mysql(df=df, db_name=db_name, tabel_name=collection_name)
|
689
|
+
df_to_json.as_json_file() # 写入 json 文件, 包含数据的 dtypes 信息
|
786
690
|
|
787
691
|
def new_unzip(self, path=None, is_move=None):
|
788
692
|
"""
|
@@ -929,26 +833,9 @@ class DatabaseUpdate:
|
|
929
833
|
df.sort_values('日期', ascending=False, ignore_index=True, inplace=True)
|
930
834
|
return df
|
931
835
|
|
932
|
-
def update_dtypte():
|
933
|
-
""" 更新一个文件的 dtype 信息到 json 文件 """
|
934
|
-
file = '/Users/xigua/数据中心/原始文件2/月数据/流量来源/【生意参谋平台】无线店铺流量来源-2023-04-01_2023-04-30.csv'
|
935
|
-
df = pd.read_csv(file, encoding='utf-8_sig', header=0, na_filter=False)
|
936
|
-
d = DataTypes()
|
937
|
-
d.read_dtypes(
|
938
|
-
df=df,
|
939
|
-
db_name='生意参谋数据2',
|
940
|
-
collection_name='店铺来源_月数据',
|
941
|
-
is_file_dtype=True, # 日常需开启文件优先, 正常不要让新文件修改 json 已有的类型
|
942
|
-
)
|
943
|
-
d.dtypes_to_file()
|
944
|
-
|
945
836
|
|
946
|
-
def upload():
|
837
|
+
def upload(path, db_name, collection_name):
|
947
838
|
""" 上传一个文件夹到数据库 """
|
948
|
-
path = '/Users/xigua/数据中心/原始文件2/生意经/店铺指标'
|
949
|
-
db_name = '生意经2'
|
950
|
-
collection_name = '店铺指标'
|
951
|
-
|
952
839
|
username, password, host, port = get_myconf.select_config_values(
|
953
840
|
target_service='home_lx',
|
954
841
|
database='mongodb',
|
@@ -981,8 +868,8 @@ def upload():
|
|
981
868
|
port=port,
|
982
869
|
)
|
983
870
|
|
984
|
-
|
985
|
-
dtypes =
|
871
|
+
df_to_json = df_types.DataTypes()
|
872
|
+
dtypes = df_to_json.load_dtypes(
|
986
873
|
db_name=db_name,
|
987
874
|
collection_name=collection_name,
|
988
875
|
)
|
@@ -1007,8 +894,6 @@ def upload():
|
|
1007
894
|
intersection_keys = dtypes.keys() & old_dt.keys() # 获取两个字典键的交集
|
1008
895
|
dtypes = {k: dtypes[k] for k in intersection_keys} # 使用交集的键创建新字典
|
1009
896
|
df = df.astype(dtypes)
|
1010
|
-
# print(intersection_dict)
|
1011
|
-
# print(df)
|
1012
897
|
|
1013
898
|
d.df_to_mongo(df=df, db_name=db_name, collection_name=collection_name)
|
1014
899
|
m.df_to_mysql(df=df, db_name=db_name, tabel_name=collection_name)
|
@@ -1034,6 +919,8 @@ def main():
|
|
1034
919
|
if __name__ == '__main__':
|
1035
920
|
# username, password, host, port = get_myconf.select_config_values(target_service='nas', database='mysql')
|
1036
921
|
# print(username, password, host, port)
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
922
|
+
upload(
|
923
|
+
path='/Users/xigua/数据中心/原始文件2/生意经/地域分布',
|
924
|
+
db_name = '生意经2',
|
925
|
+
collection_name = '省份城市分析',
|
926
|
+
)
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# -*- coding:utf-8 -*-
|
2
|
+
import warnings
|
3
|
+
import pandas as pd
|
4
|
+
import numpy as np
|
5
|
+
import chardet
|
6
|
+
import zipfile
|
7
|
+
|
8
|
+
from numpy import dtype
|
9
|
+
from pandas.tseries.holiday import next_monday
|
10
|
+
from pyzipper import PyZipFile
|
11
|
+
import os
|
12
|
+
import platform
|
13
|
+
import json
|
14
|
+
import pymysql
|
15
|
+
from mdbq.mongo import mongo
|
16
|
+
from mdbq.mysql import mysql
|
17
|
+
from mdbq.mysql import s_query
|
18
|
+
from mdbq.config import get_myconf
|
19
|
+
from mdbq.config import set_support
|
20
|
+
from mdbq.dataframe import converter
|
21
|
+
import datetime
|
22
|
+
import time
|
23
|
+
import re
|
24
|
+
import shutil
|
25
|
+
import getpass
|
26
|
+
|
27
|
+
from sqlalchemy.dialects.postgresql.pg_catalog import pg_get_serial_sequence
|
28
|
+
|
29
|
+
warnings.filterwarnings('ignore')
|
30
|
+
"""
|
31
|
+
1. 记录 dataframe 或者数据库的列信息(dtypes)
|
32
|
+
2. 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
33
|
+
"""
|
34
|
+
|
35
|
+
|
36
|
+
class DataTypes:
|
37
|
+
"""
|
38
|
+
数据简介: 记录 dataframe 或者数据库的列信息(dtypes),可以记录其信息或者加载相关信息用于入库使用,
|
39
|
+
第一字段为分类(如 dataframe/mysql),第二字段为数据库名,第三字段为集合名,第四段列名及其数据类型
|
40
|
+
"""
|
41
|
+
def __init__(self):
|
42
|
+
self.datas = {
|
43
|
+
"json统计":
|
44
|
+
{
|
45
|
+
"字段量": 0,
|
46
|
+
"数据库量": 0,
|
47
|
+
"集合数量": 0
|
48
|
+
}
|
49
|
+
}
|
50
|
+
self.path = set_support.SetSupport(dirname='support').dirname
|
51
|
+
self.json_file = os.path.join(self.path, 'df_types.json')
|
52
|
+
if not os.path.isdir(self.path):
|
53
|
+
os.makedirs(self.path)
|
54
|
+
if not os.path.isfile(self.json_file):
|
55
|
+
with open(self.json_file, 'w', encoding='utf-8_sig') as f:
|
56
|
+
json.dump(self.datas, f, ensure_ascii=False, sort_keys=True, indent=4)
|
57
|
+
self.json_before()
|
58
|
+
|
59
|
+
def json_before(self):
|
60
|
+
""" 本地 json 文件的 dtypes 信息, 初始化更新给 self.datas """
|
61
|
+
with open(self.json_file, 'r', encoding='utf-8_sig') as f:
|
62
|
+
json_ = json.load(f)
|
63
|
+
self.datas.update(json_)
|
64
|
+
|
65
|
+
def get_df_types(self, db_name, collection_name, df=pd.DataFrame(), is_file_dtype=True):
|
66
|
+
"""
|
67
|
+
读取 df 的 dtypes, 并更新本地 json 文件
|
68
|
+
期间会 清理不合规的列名, 并对数据类型进行转换(尝试将 object 类型转为 int 或 float)
|
69
|
+
返回: df 的 dtypes, 后续使用示例: df = df.astype(dtypes, errors='ignore')
|
70
|
+
is_file_dtype=True: 默认情况下以旧 json 优先, 即允许手动指定 json 文件里面的数据类型
|
71
|
+
"""
|
72
|
+
if len(df) == 0:
|
73
|
+
return
|
74
|
+
cv = converter.DataFrameConverter()
|
75
|
+
df = cv.convert_df_cols(df=df) # 清理 dataframe 非法值
|
76
|
+
dtypes = df.dtypes.apply(str).to_dict()
|
77
|
+
dtypes = {db_name: {collection_name: dtypes}}
|
78
|
+
|
79
|
+
if not self.datas: # 如果不存在本地 json 文件, 直接返回即可
|
80
|
+
self.datas.update(dtypes)
|
81
|
+
return self.datas[db_name][collection_name]
|
82
|
+
else: # 存在则读取,并更新 df 的 dtypes
|
83
|
+
if db_name in list(self.datas.keys()): # ['京东数据2', '天猫数据2', '生意参谋数据2', '生意经2']
|
84
|
+
if collection_name in list(self.datas[db_name].keys()):
|
85
|
+
if is_file_dtype: # 旧数据优先
|
86
|
+
# # 用 dtypes 更新, 允许手动指定 json 文件里面的数据类型
|
87
|
+
dtypes[db_name][collection_name].update(self.datas[db_name][collection_name])
|
88
|
+
# 将 dtypes 更新进去,使 self.datas 包含新旧信息
|
89
|
+
self.datas[db_name][collection_name].update(dtypes[db_name][collection_name])
|
90
|
+
else: # 新数据优先
|
91
|
+
self.datas[db_name][collection_name].update(dtypes[db_name][collection_name])
|
92
|
+
else:
|
93
|
+
if is_file_dtype: # 旧数据优先
|
94
|
+
dtypes[db_name].update(self.datas[db_name])
|
95
|
+
self.datas[db_name].update(dtypes[db_name])
|
96
|
+
else:
|
97
|
+
self.datas[db_name].update(dtypes[db_name])
|
98
|
+
else:
|
99
|
+
# dtypes.update(self.datas) # 可以注释掉, 因为旧数据 self.datas 是空的
|
100
|
+
self.datas.update(dtypes)
|
101
|
+
dbs = 0
|
102
|
+
collections = 0
|
103
|
+
cols = 0
|
104
|
+
# self.datas.pop('json统计')
|
105
|
+
for k, v in self.datas.items():
|
106
|
+
if k == 'json统计':
|
107
|
+
continue
|
108
|
+
dbs += 1
|
109
|
+
for d, j in v.items():
|
110
|
+
collections += 1
|
111
|
+
for t, p in j.items():
|
112
|
+
cols += 1
|
113
|
+
tips = {'json统计': {'数据库量': dbs, '集合数量': collections, '字段量': cols}}
|
114
|
+
self.datas.update(tips)
|
115
|
+
return self.datas[db_name][collection_name] # 返回 df 的 dtypes
|
116
|
+
|
117
|
+
def as_json_file(self):
|
118
|
+
""" 保存为本地 json 文件 """
|
119
|
+
with open(self.json_file, 'w', encoding='utf-8_sig') as f:
|
120
|
+
json.dump(
|
121
|
+
self.datas,
|
122
|
+
f,
|
123
|
+
ensure_ascii=False, # 默认True,非ASCII字符将被转义。如为False,则非ASCII字符会以\uXXXX输出
|
124
|
+
sort_keys=True, # 默认为False。如果为True,则字典的输出将按键排序。
|
125
|
+
indent=4,
|
126
|
+
)
|
127
|
+
time.sleep(1)
|
128
|
+
|
129
|
+
def df_dtypes_to_json(self, db_name, collection_name, path, df=pd.DataFrame(), is_file_dtype=True):
|
130
|
+
if len(df) == 0:
|
131
|
+
return
|
132
|
+
cv = converter.DataFrameConverter()
|
133
|
+
df = cv.convert_df_cols(df=df) # 清理 dataframe 列名的不合规字符
|
134
|
+
dtypes = df.dtypes.apply(str).to_dict()
|
135
|
+
dtypes = {'dataframe': {db_name: {collection_name: dtypes}}}
|
136
|
+
self.dtypes_to_json(dtypes=dtypes, cl='dataframe', db_name=db_name, collection_name=collection_name, path=path, is_file_dtype=is_file_dtype)
|
137
|
+
|
138
|
+
def load_dtypes(self, db_name, collection_name):
|
139
|
+
if db_name in list(self.datas.keys()):
|
140
|
+
if collection_name in list(self.datas[db_name].keys()):
|
141
|
+
return self.datas[db_name][collection_name]
|
142
|
+
else:
|
143
|
+
print(f'不存在的集合名信息: {collection_name}, 文件位置: {self.json_file}')
|
144
|
+
return {}
|
145
|
+
else:
|
146
|
+
print(f'不存在的数据库信息: {db_name}, 文件位置: {self.json_file}')
|
147
|
+
return {}
|
148
|
+
|
149
|
+
|
150
|
+
def update_df_types_to_json(file, db_name, collection_name, is_file_dtype=True):
|
151
|
+
""" 更新一个文件的 dtype 信息到 json 文件 """
|
152
|
+
df = pd.read_csv(file, encoding='utf-8_sig', header=0, na_filter=False)
|
153
|
+
df_to_json = DataTypes()
|
154
|
+
df_to_json.get_df_types(
|
155
|
+
df=df,
|
156
|
+
db_name=db_name,
|
157
|
+
collection_name=collection_name,
|
158
|
+
is_file_dtype=is_file_dtype, # 日常需开启文件优先, 正常不要让新文件修改 json 已有的类型
|
159
|
+
)
|
160
|
+
df_to_json.as_json_file()
|
161
|
+
print(f'json文件已存储: {df_to_json.json_file}')
|
162
|
+
|
163
|
+
|
164
|
+
def test_load_dtypes(db_name, collection_name):
|
165
|
+
d = DataTypes()
|
166
|
+
res = d.load_dtypes(db_name=db_name, collection_name=collection_name)
|
167
|
+
print(res)
|
168
|
+
|
169
|
+
|
170
|
+
if __name__ == '__main__':
|
171
|
+
file = '/Users/xigua/数据中心/pandas数据源/店铺日报.csv'
|
172
|
+
update_df_types_to_json(
|
173
|
+
file=file,
|
174
|
+
db_name='pandas数据源',
|
175
|
+
collection_name='店铺日报',
|
176
|
+
is_file_dtype=True,
|
177
|
+
)
|
178
|
+
# test_load_dtypes(db_name='pandas数据源', collection_name='店铺日报')
|
179
|
+
|
180
|
+
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# -*- coding:utf-8 -*-
|
2
|
+
import warnings
|
3
|
+
import pandas as pd
|
4
|
+
import numpy as np
|
5
|
+
import chardet
|
6
|
+
import zipfile
|
7
|
+
|
8
|
+
from numpy import dtype
|
9
|
+
from pandas.tseries.holiday import next_monday
|
10
|
+
from pyzipper import PyZipFile
|
11
|
+
import os
|
12
|
+
import platform
|
13
|
+
import json
|
14
|
+
import pymysql
|
15
|
+
from mdbq.mongo import mongo
|
16
|
+
from mdbq.mysql import mysql
|
17
|
+
from mdbq.mysql import s_query
|
18
|
+
from mdbq.config import get_myconf
|
19
|
+
from mdbq.config import set_support
|
20
|
+
from mdbq.dataframe import converter
|
21
|
+
import datetime
|
22
|
+
import time
|
23
|
+
import re
|
24
|
+
import shutil
|
25
|
+
import getpass
|
26
|
+
|
27
|
+
from sqlalchemy.dialects.postgresql.pg_catalog import pg_get_serial_sequence
|
28
|
+
|
29
|
+
warnings.filterwarnings('ignore')
|
30
|
+
"""
|
31
|
+
1. 记录 dataframe 或者数据库的列信息(dtypes)
|
32
|
+
2. 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
33
|
+
"""
|
34
|
+
|
35
|
+
|
36
|
+
class DataTypes:
|
37
|
+
"""
|
38
|
+
数据简介: 记录 dataframe 或者数据库的列信息(dtypes),可以记录其信息或者加载相关信息用于入库使用,
|
39
|
+
第一字段为分类(如 dataframe/mysql),第二字段为数据库名,第三字段为集合名,第四段列名及其数据类型
|
40
|
+
"""
|
41
|
+
def __init__(self):
|
42
|
+
self.datas = {
|
43
|
+
'_json统计':
|
44
|
+
{
|
45
|
+
'分类': 0,
|
46
|
+
'数据库量': 0,
|
47
|
+
'集合数量': 0,
|
48
|
+
'字段量': 0,
|
49
|
+
'数据简介': '记录 dataframe 或者数据库的列信息(dtypes)',
|
50
|
+
}
|
51
|
+
}
|
52
|
+
self.path = set_support.SetSupport(dirname='support').dirname
|
53
|
+
self.json_file = os.path.join(self.path, 'mysql_types.json')
|
54
|
+
if not os.path.isdir(self.path):
|
55
|
+
os.makedirs(self.path)
|
56
|
+
if not os.path.isfile(self.json_file):
|
57
|
+
with open(self.json_file, 'w', encoding='utf-8_sig') as f:
|
58
|
+
json.dump(self.datas, f, ensure_ascii=False, sort_keys=True, indent=4)
|
59
|
+
self.json_before()
|
60
|
+
|
61
|
+
def json_before(self):
|
62
|
+
""" 本地 json 文件的 dtypes 信息, 初始化更新给 self.datas """
|
63
|
+
with open(self.json_file, 'r', encoding='utf-8_sig') as f:
|
64
|
+
json_ = json.load(f)
|
65
|
+
self.datas.update(json_)
|
66
|
+
|
67
|
+
def get_mysql_types(self, cl, dtypes, db_name, tabel_name, is_file_dtype=True):
|
68
|
+
""" 更新 mysql 的 types 信息到 json 文件 """
|
69
|
+
if cl in self.datas.keys():
|
70
|
+
if db_name in list(self.datas[cl].keys()): # ['京东数据2', '天猫数据2', '生意参谋数据2', '生意经2']
|
71
|
+
if tabel_name in list(self.datas[cl][db_name].keys()):
|
72
|
+
if is_file_dtype: # 旧数据优先
|
73
|
+
# # 用 dtypes 更新, 允许手动指定 json 文件里面的数据类型
|
74
|
+
dtypes[cl][db_name][tabel_name].update(self.datas[cl][db_name][tabel_name])
|
75
|
+
# 将 dtypes 更新进去,使 self.datas 包含新旧信息
|
76
|
+
self.datas[cl][db_name][tabel_name].update(dtypes[cl][db_name][tabel_name])
|
77
|
+
else: # 新数据优先
|
78
|
+
self.datas[cl][db_name][tabel_name].update(dtypes[cl][db_name][tabel_name])
|
79
|
+
else:
|
80
|
+
if is_file_dtype: # 旧数据优先
|
81
|
+
dtypes[cl][db_name].update(self.datas[cl][db_name])
|
82
|
+
self.datas[cl][db_name].update(dtypes[cl][db_name])
|
83
|
+
else:
|
84
|
+
self.datas[cl][db_name].update(dtypes[cl][db_name])
|
85
|
+
else:
|
86
|
+
# dtypes.update(self.datas) # 可以注释掉, 因为旧数据 self.datas 是空的
|
87
|
+
self.datas[cl].update(dtypes[cl])
|
88
|
+
else:
|
89
|
+
self.datas.update(dtypes)
|
90
|
+
|
91
|
+
cif = 0 # 分类
|
92
|
+
dbs = 0 # 数据库
|
93
|
+
collections = 0 # 集合
|
94
|
+
cols = 0 # 字段
|
95
|
+
for k, v in self.datas.items():
|
96
|
+
if k == '_json统计':
|
97
|
+
continue # 不统计头信息
|
98
|
+
cif += 1
|
99
|
+
for t, g in v.items():
|
100
|
+
dbs += 1
|
101
|
+
for d, j in g.items():
|
102
|
+
collections += 1
|
103
|
+
for t, p in j.items():
|
104
|
+
cols += 1
|
105
|
+
tips = {'分类': cif, '数据库量': dbs, '集合数量': collections, '字段量': cols}
|
106
|
+
self.datas['_json统计'].update(tips)
|
107
|
+
# with open(json_file, 'w', encoding='utf-8_sig') as f:
|
108
|
+
# json.dump(
|
109
|
+
# self.datas,
|
110
|
+
# f,
|
111
|
+
# ensure_ascii=False, # 默认True,非ASCII字符将被转义。如为False,则非ASCII字符会以\uXXXX输出
|
112
|
+
# sort_keys=True, # 默认为False。如果为True,则字典的输出将按键排序。
|
113
|
+
# indent=4,
|
114
|
+
# )
|
115
|
+
|
116
|
+
def as_json_file(self):
|
117
|
+
""" 保存为本地 json 文件 """
|
118
|
+
with open(self.json_file, 'w', encoding='utf-8_sig') as f:
|
119
|
+
json.dump(
|
120
|
+
self.datas,
|
121
|
+
f,
|
122
|
+
ensure_ascii=False, # 默认True,非ASCII字符将被转义。如为False,则非ASCII字符会以\uXXXX输出
|
123
|
+
sort_keys=True, # 默认为False。如果为True,则字典的输出将按键排序。
|
124
|
+
indent=4,
|
125
|
+
)
|
126
|
+
time.sleep(1)
|
127
|
+
|
128
|
+
def load_dtypes(self, db_name, tabel_name, cl='mysql', ):
|
129
|
+
"""
|
130
|
+
mysql.py 程序从本地文件中读取 dtype 信息
|
131
|
+
如果缺失 dtypes 信息,则执行 mysql_all_dtypes 以便更新所有数据库 dtypes 信息到 json 文件
|
132
|
+
"""
|
133
|
+
if cl in self.datas.keys():
|
134
|
+
if db_name in list(self.datas[cl].keys()):
|
135
|
+
if tabel_name in list(self.datas[cl][db_name].keys()):
|
136
|
+
return self.datas[cl][db_name][tabel_name]
|
137
|
+
else:
|
138
|
+
print(f'不存在的集合名信息: {tabel_name}, 文件位置: {self.json_file}')
|
139
|
+
mysql_all_dtypes() # 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
140
|
+
return {}
|
141
|
+
else:
|
142
|
+
print(f'不存在的数据库信息: {db_name}, 文件位置: {self.json_file}')
|
143
|
+
mysql_all_dtypes() # 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
144
|
+
return {}
|
145
|
+
else:
|
146
|
+
print(f'不存在的数据分类: {cl}, 文件位置: {self.json_file}')
|
147
|
+
mysql_all_dtypes() # 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
148
|
+
return {}
|
149
|
+
|
150
|
+
|
151
|
+
def mysql_all_dtypes(path=None):
|
152
|
+
"""
|
153
|
+
更新笔记本 mysql 中所有数据库的 dtypes 信息到本地 json
|
154
|
+
"""
|
155
|
+
if not path:
|
156
|
+
path = set_support.SetSupport(dirname='support').dirname
|
157
|
+
|
158
|
+
username, password, host, port = get_myconf.select_config_values(target_service='home_lx', database='mysql')
|
159
|
+
config = {
|
160
|
+
'host': host,
|
161
|
+
'port': port,
|
162
|
+
'user': username,
|
163
|
+
'password': password,
|
164
|
+
'charset': 'utf8mb4', # utf8mb4 支持存储四字节的UTF-8字符集
|
165
|
+
'cursorclass': pymysql.cursors.DictCursor,
|
166
|
+
}
|
167
|
+
|
168
|
+
connection = pymysql.connect(**config) # 连接数据库
|
169
|
+
with connection.cursor() as cursor:
|
170
|
+
sql = "SHOW DATABASES;"
|
171
|
+
cursor.execute(sql)
|
172
|
+
db_name_lists = cursor.fetchall()
|
173
|
+
db_name_lists = [item['Database'] for item in db_name_lists]
|
174
|
+
connection.close()
|
175
|
+
|
176
|
+
sys_lists = ['information_schema', 'mysql', 'performance_schema', 'sakila', 'sys']
|
177
|
+
db_name_lists = [item for item in db_name_lists if item not in sys_lists]
|
178
|
+
|
179
|
+
# db_name_lists = [
|
180
|
+
# '京东数据2',
|
181
|
+
# '天猫数据2',
|
182
|
+
# '市场数据2',
|
183
|
+
# '生意参谋数据2',
|
184
|
+
# '生意经2',
|
185
|
+
# '属性设置2',
|
186
|
+
# '聚合数据',
|
187
|
+
# ]
|
188
|
+
results = []
|
189
|
+
for db_name in db_name_lists:
|
190
|
+
config.update({'database': db_name}) # 添加更新 config 字段
|
191
|
+
connection = pymysql.connect(**config) # 连接数据库
|
192
|
+
try:
|
193
|
+
with connection.cursor() as cursor:
|
194
|
+
sql = f"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{db_name}';"
|
195
|
+
sql = "SHOW TABLES;"
|
196
|
+
cursor.execute(sql)
|
197
|
+
table_name = cursor.fetchall()
|
198
|
+
for table in table_name:
|
199
|
+
for k, v in table.items():
|
200
|
+
results.append({db_name: v})
|
201
|
+
except:
|
202
|
+
pass
|
203
|
+
finally:
|
204
|
+
connection.close()
|
205
|
+
time.sleep(0.5)
|
206
|
+
|
207
|
+
d = DataTypes()
|
208
|
+
for result in results:
|
209
|
+
for db_name, tabel_name in result.items():
|
210
|
+
print(f'获取列信息 数据库: < {db_name} >, 数据表: < {tabel_name} >')
|
211
|
+
# d.mysql_dtypes_to_json(db_name=db_name, tabel_name=tabel_name, path=path)
|
212
|
+
sq = s_query.QueryDatas(username=username, password=password, host=host, port=port)
|
213
|
+
# 获取数据表的指定列, 返回列表
|
214
|
+
# [{'视频bv号': 'BV1Dm4y1S7BU', '下载进度': 1}, {'视频bv号': 'BV1ov411c7US', '下载进度': 1}]
|
215
|
+
name_type = sq.dtypes_to_list(db_name=db_name, tabel_name=tabel_name)
|
216
|
+
if name_type:
|
217
|
+
dtypes = {item['COLUMN_NAME']: item['COLUMN_TYPE'] for item in name_type}
|
218
|
+
dtypes = {'mysql': {db_name: {tabel_name: dtypes}}}
|
219
|
+
d.get_mysql_types(
|
220
|
+
dtypes=dtypes,
|
221
|
+
cl='mysql',
|
222
|
+
db_name=db_name,
|
223
|
+
tabel_name=tabel_name,
|
224
|
+
is_file_dtype=True
|
225
|
+
)
|
226
|
+
else:
|
227
|
+
print(f'数据库回传数据(name_type)为空')
|
228
|
+
d.as_json_file()
|
229
|
+
|
230
|
+
if __name__ == '__main__':
|
231
|
+
mysql_all_dtypes() # 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
mdbq/aggregation/query_data.py
CHANGED
@@ -525,5 +525,5 @@ def data_aggregation(service_databases=[{}]):
|
|
525
525
|
|
526
526
|
|
527
527
|
if __name__ == '__main__':
|
528
|
-
data_aggregation(service_databases=[{'
|
528
|
+
data_aggregation(service_databases=[{'company': 'mysql'}])
|
529
529
|
# optimize_data.op_data(service_databases=[{'company': 'mysql'}], days=3650) # 立即启动对聚合数据的清理工作
|
mdbq/dataframe/converter.py
CHANGED
@@ -9,6 +9,51 @@ class DataFrameConverter(object):
|
|
9
9
|
self.df = df
|
10
10
|
|
11
11
|
def convert_df_cols(self, df=pd.DataFrame({})):
|
12
|
+
"""
|
13
|
+
清理 dataframe 非法值
|
14
|
+
对数据类型进行转换(尝试将 object 类型转为 int 或 float)
|
15
|
+
"""
|
16
|
+
if len(df) == 0:
|
17
|
+
df = self.df
|
18
|
+
if len(df) == 0:
|
19
|
+
return
|
20
|
+
# dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
|
21
|
+
df.replace([np.inf, -np.inf], 0, inplace=True) # 清理一些非法值
|
22
|
+
df.replace(to_replace=['\\N', '-', '--', '', 'nan'], value=0, regex=False, inplace=True) # 替换掉特殊字符
|
23
|
+
df.replace(to_replace=[','], value='', regex=True, inplace=True)
|
24
|
+
df.replace(to_replace=['="'], value='', regex=True, inplace=True) # ="和"不可以放在一起清洗, 因为有: id=86785565
|
25
|
+
df.replace(to_replace=['"'], value='', regex=True, inplace=True)
|
26
|
+
cols = df.columns.tolist()
|
27
|
+
|
28
|
+
for col in cols:
|
29
|
+
# df[col] = df[col].apply(lambda x: re.sub('[="]', '', str(x)) if '="' in str(x) else x)
|
30
|
+
# 百分比在某些数据库中不兼容, 转换百分比为小数
|
31
|
+
df[col] = df[col].apply(lambda x: float(float((str(x).rstrip("%"))) / 100) if str(x).endswith('%') and '~' not in str(x) else x)
|
32
|
+
# 尝试转换合适的数据类型
|
33
|
+
if df[col].dtype == 'object':
|
34
|
+
try:
|
35
|
+
# df[col] = df[col].astype(int) # 尝试转换 int
|
36
|
+
df[col] = df[col].apply(lambda x: int(x) if '_' not in str(x) else x)
|
37
|
+
except:
|
38
|
+
# df[col] = df[col].astype('float64', errors='ignore') # 尝试转换 float, 报错则忽略
|
39
|
+
try:
|
40
|
+
df[col] = df[col].apply(lambda x: float(x) if '_' not in str(x) else x)
|
41
|
+
except:
|
42
|
+
pass
|
43
|
+
if df[col].dtype == 'float': # 对于小数类型, 保留 6 位小数
|
44
|
+
df[col] = df[col].apply(lambda x: round(float(x), 6) if x != 0 else x)
|
45
|
+
# 清理列名, 在 mysql 里面列名不能含有某些特殊字符
|
46
|
+
if '日期' in col or '时间' in col:
|
47
|
+
try:
|
48
|
+
df[col] = df[col].apply(lambda x: pd.to_datetime(x))
|
49
|
+
except:
|
50
|
+
pass
|
51
|
+
new_col = col.lower()
|
52
|
+
df.rename(columns={col: new_col}, inplace=True)
|
53
|
+
df.fillna(0, inplace=True)
|
54
|
+
return df
|
55
|
+
|
56
|
+
def convert_df_cols_bak(self, df=pd.DataFrame({})):
|
12
57
|
"""
|
13
58
|
清理 dataframe 列名的不合规字符(mysql)
|
14
59
|
对数据类型进行转换(尝试将 object 类型转为 int 或 float)
|
@@ -49,13 +49,19 @@ class DataTypes:
|
|
49
49
|
'数据简介': '记录 dataframe 或者数据库的列信息(dtypes)',
|
50
50
|
}
|
51
51
|
}
|
52
|
+
self.path = set_support.SetSupport(dirname='support').dirname
|
53
|
+
self.json_file = os.path.join(self.path, 'mysql_types.json')
|
54
|
+
if not os.path.isdir(self.path):
|
55
|
+
os.makedirs(self.path)
|
56
|
+
if not os.path.isfile(self.json_file):
|
57
|
+
with open(self.json_file, 'w', encoding='utf-8_sig') as f:
|
58
|
+
json.dump(self.datas, f, ensure_ascii=False, sort_keys=True, indent=4)
|
52
59
|
|
53
|
-
def json_before(self
|
60
|
+
def json_before(self):
|
54
61
|
""" 本地 json 文件的 dtypes 信息, 初始化更新给 self.datas """
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
self.datas.update(json_)
|
62
|
+
with open(self.json_file, 'r', encoding='utf-8_sig') as f:
|
63
|
+
json_ = json.load(f)
|
64
|
+
self.datas.update(json_)
|
59
65
|
|
60
66
|
def df_dtypes_to_json(self, db_name, collection_name, path, df=pd.DataFrame(), is_file_dtype=True):
|
61
67
|
if len(df) == 0:
|
@@ -148,7 +154,10 @@ class DataTypes:
|
|
148
154
|
if os.path.isfile(json_file):
|
149
155
|
self.json_before(json_file=json_file)
|
150
156
|
else:
|
151
|
-
|
157
|
+
# 如果不存在,则新建文件
|
158
|
+
with open(json_file, 'w', encoding='utf-8_sig') as f:
|
159
|
+
json.dump(self.datas, f, ensure_ascii=False, sort_keys=True, indent=4)
|
160
|
+
# print(f'不存在的文件: {json_file}')
|
152
161
|
return
|
153
162
|
|
154
163
|
if cl in self.datas.keys():
|
@@ -170,7 +179,7 @@ def mysql_all_dtypes(path=None):
|
|
170
179
|
"""
|
171
180
|
更新笔记本 mysql 中所有数据库的 dtypes 信息到本地 json
|
172
181
|
"""
|
173
|
-
if not
|
182
|
+
if not path:
|
174
183
|
path = set_support.SetSupport(dirname='support').dirname
|
175
184
|
|
176
185
|
username, password, host, port = get_myconf.select_config_values(target_service='home_lx', database='mysql')
|
@@ -229,4 +238,6 @@ def mysql_all_dtypes(path=None):
|
|
229
238
|
|
230
239
|
|
231
240
|
if __name__ == '__main__':
|
232
|
-
mysql_all_dtypes() # 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
241
|
+
# mysql_all_dtypes() # 更新 mysql 中所有数据库的 dtypes 信息到本地 json
|
242
|
+
d = DataTypes()
|
243
|
+
|
mdbq/mysql/mysql.py
CHANGED
@@ -9,13 +9,14 @@ import warnings
|
|
9
9
|
import pymysql
|
10
10
|
import numpy as np
|
11
11
|
import pandas as pd
|
12
|
+
from more_itertools.more import iequals
|
12
13
|
from sqlalchemy import create_engine
|
13
14
|
import os
|
14
15
|
import calendar
|
15
16
|
from mdbq.config import get_myconf
|
16
17
|
from mdbq.config import set_support
|
17
18
|
from mdbq.dataframe import converter
|
18
|
-
from mdbq.
|
19
|
+
from mdbq.aggregation import mysql_types
|
19
20
|
|
20
21
|
warnings.filterwarnings('ignore')
|
21
22
|
|
@@ -34,153 +35,106 @@ class MysqlUpload:
|
|
34
35
|
'charset': charset, # utf8mb4 支持存储四字节的UTF-8字符集
|
35
36
|
'cursorclass': pymysql.cursors.DictCursor,
|
36
37
|
}
|
37
|
-
self.conn = None
|
38
38
|
|
39
|
-
|
40
|
-
def try_except(func): # 在类内部定义一个异常处理方法
|
41
|
-
@wraps(func)
|
42
|
-
def wrapper(*args, **kwargs):
|
43
|
-
try:
|
44
|
-
return func(*args, **kwargs)
|
45
|
-
except Exception as e:
|
46
|
-
print(f'{func.__name__}, {e}') # 将异常信息返回
|
47
|
-
|
48
|
-
return wrapper
|
49
|
-
|
50
|
-
def _conn(self):
|
51
|
-
self.config = {
|
52
|
-
'host': self.host,
|
53
|
-
'port': int(self.port),
|
54
|
-
'user': self.username,
|
55
|
-
'password': self.password,
|
56
|
-
'charset': 'utf8mb4', # utf8mb4 支持存储四字节的UTF-8字符集
|
57
|
-
'cursorclass': pymysql.cursors.DictCursor,
|
58
|
-
}
|
59
|
-
try:
|
60
|
-
self.conn = pymysql.connect(**self.config) # 连接数据库
|
61
|
-
return True
|
62
|
-
except:
|
63
|
-
return False
|
64
|
-
|
65
|
-
# @try_except
|
66
|
-
def df_to_mysql(self, df, tabel_name, db_name='远程数据源'):
|
39
|
+
def df_to_mysql(self, df, tabel_name, db_name='远程数据源', drop_duplicates=False):
|
67
40
|
"""
|
68
41
|
将 df 写入数据库
|
69
42
|
db_name: 数据库名称
|
70
43
|
tabel_name: 集合/表名称
|
44
|
+
drop_duplicates:仅限于聚合数据使用,其他情况不要设置
|
71
45
|
"""
|
72
|
-
db_name = re.sub(r'[\',,()()/=<>+\-*^"’\[\]~#|&% .]', '_', db_name)
|
73
|
-
tabel_name = re.sub(r'[\',,()()/=<>+\-*^"’\[\]~#|&% .]', '_', tabel_name)
|
74
46
|
cv = converter.DataFrameConverter()
|
75
|
-
df = cv.convert_df_cols(df=df) #
|
47
|
+
df = cv.convert_df_cols(df=df) # 清理 dataframe 非法值
|
76
48
|
|
77
49
|
connection = pymysql.connect(**self.config) # 连接数据库
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
print(f"创建Database: {db_name}")
|
94
|
-
except Exception as e:
|
95
|
-
print(e)
|
96
|
-
return
|
97
|
-
finally:
|
98
|
-
connection.close() # 这里要断开连接
|
99
|
-
time.sleep(0.2)
|
50
|
+
with connection.cursor() as cursor:
|
51
|
+
cursor.execute(f"SHOW DATABASES LIKE '{db_name}'") # 检查数据库是否存在
|
52
|
+
database_exists = cursor.fetchone()
|
53
|
+
if not database_exists:
|
54
|
+
# 如果数据库不存在,则新建
|
55
|
+
if '8.138.27' in str(self.host) or platform.system() == "Linux": # 阿里云 mysql 低版本不支持 0900
|
56
|
+
sql = f"CREATE DATABASE `{db_name}` COLLATE utf8mb4_unicode_ci"
|
57
|
+
self.config.update({'charset': 'utf8mb4_unicode_ci'})
|
58
|
+
if '192.168.1.100' in str(self.host):
|
59
|
+
sql = f"CREATE DATABASE `{db_name}`"
|
60
|
+
else:
|
61
|
+
sql = f"CREATE DATABASE `{db_name}` COLLATE utf8mb4_0900_ai_ci"
|
62
|
+
cursor.execute(sql)
|
63
|
+
connection.commit()
|
64
|
+
print(f"创建Database: {db_name}")
|
100
65
|
|
101
66
|
self.config.update({'database': db_name}) # 添加更新 config 字段
|
102
67
|
connection = pymysql.connect(**self.config) # 重新连接数据库
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
print(f'创建 mysql 表: {tabel_name}')
|
112
|
-
|
113
|
-
# # 2. 列数据类型转换
|
114
|
-
# cols = df.columns.tolist()
|
115
|
-
# dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
|
116
|
-
# # 转换为 mysql 的数据类型
|
117
|
-
# dtypes.update({col: self.convert_dtype_to_sql(df=df, col=col, dtype=dtypes[col]) for col in cols})
|
118
|
-
dtypes = self.convert_dtypes(df=df, db_name=db_name, tabel_name=tabel_name)
|
119
|
-
|
120
|
-
# 3. 检查列, 不存在则添加新列
|
121
|
-
cols = df.columns.tolist()
|
122
|
-
for col in cols:
|
123
|
-
sql = ('SELECT 1 FROM information_schema.columns WHERE table_schema = %s AND table_name = %s AND '
|
124
|
-
'column_name = %s')
|
125
|
-
cursor.execute(sql, (db_name, {tabel_name}, col))
|
126
|
-
if cursor.fetchone() is None: # 如果列不存在,添加新列
|
127
|
-
print(f"添加列: {col}({dtypes[col]})") # 添加列并指定数据类型
|
128
|
-
# if col == '日期':
|
129
|
-
# sql = f"ALTER TABLE {tabel_name} ADD COLUMN {col} DATE default NULL;"
|
130
|
-
# else:
|
131
|
-
# sql = f"ALTER TABLE {tabel_name} ADD COLUMN {col} mediumtext default NULL;"
|
132
|
-
sql = f"ALTER TABLE {tabel_name} ADD COLUMN {col} {dtypes[col]} default NULL;"
|
133
|
-
cursor.execute(sql)
|
68
|
+
with connection.cursor() as cursor:
|
69
|
+
# 1. 查询表, 不存在则创建一个空表
|
70
|
+
sql = f"SHOW TABLES LIKE '{tabel_name}';" # 有特殊字符不需转义
|
71
|
+
cursor.execute(sql)
|
72
|
+
if not cursor.fetchone():
|
73
|
+
sql = f"CREATE TABLE IF NOT EXISTS `{tabel_name}` (id INT AUTO_INCREMENT PRIMARY KEY)"
|
74
|
+
cursor.execute(sql)
|
75
|
+
print(f'创建 mysql 表: {tabel_name}')
|
134
76
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
# sql = f"DELETE FROM {tabel_name} WHERE {'日期'} BETWEEN '%s' AND '%s'" % (start_date, end_date)
|
148
|
-
# cursor.execute(sql)
|
149
|
-
# connection.commit()
|
150
|
-
|
151
|
-
# 5. 更新插入数据
|
152
|
-
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
153
|
-
print(f'{now}正在更新 mysql ({self.host}:{self.port}) {db_name}/{tabel_name}')
|
154
|
-
if str(self.host) == '192.168.1.100': # 群晖服务器
|
155
|
-
try:
|
156
|
-
datas = df.to_dict('records')
|
157
|
-
for data in datas:
|
158
|
-
cols = ', '.join(data.keys())
|
159
|
-
values = ', '.join([f'"{v}"' for v in data.values()])
|
160
|
-
sql = f"INSERT INTO {tabel_name} ({cols}) VALUES ({values})"
|
161
|
-
cursor.execute(sql)
|
162
|
-
connection.commit()
|
163
|
-
except Exception as e:
|
164
|
-
print(e)
|
165
|
-
connection.rollback()
|
166
|
-
else: # 其他服务器
|
77
|
+
# 2. 列数据类型转换,将 df 数据类型转换为 mysql 的数据类型
|
78
|
+
dtypes = self.convert_dtypes(df=df, db_name=db_name, tabel_name=tabel_name)
|
79
|
+
|
80
|
+
# 有特殊字符不需转义
|
81
|
+
sql = f"SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '{db_name}' AND TABLE_NAME = '{tabel_name}';"
|
82
|
+
cursor.execute(sql)
|
83
|
+
col_exist = [item['COLUMN_NAME'] for item in cursor.fetchall()]
|
84
|
+
cols = df.columns.tolist()
|
85
|
+
col_not_exist = [col for col in cols if col not in col_exist]
|
86
|
+
# 检查列,不存在则新建列
|
87
|
+
if col_not_exist: # 数据表中不存在的列
|
88
|
+
for col in col_not_exist:
|
167
89
|
try:
|
168
|
-
|
169
|
-
|
170
|
-
)
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
90
|
+
# 创建列,需转义
|
91
|
+
sql = f"ALTER TABLE `{tabel_name}` ADD COLUMN `{col}` {dtypes[col]} DEFAULT NULL;"
|
92
|
+
cursor.execute(sql)
|
93
|
+
print(f"添加列: {col}({dtypes[col]})") # 添加列并指定数据类型
|
94
|
+
|
95
|
+
# 创建索引
|
96
|
+
if col == '日期':
|
97
|
+
cursor.execute(f"SHOW INDEXES FROM `{tabel_name}` WHERE `Column_name` = '{col}'")
|
98
|
+
result = cursor.fetchone() # 检查索引是否存在
|
99
|
+
if not result:
|
100
|
+
cursor.execute(f"CREATE INDEX index_name ON `{tabel_name}`(`{col}`)")
|
101
|
+
except:
|
102
|
+
pass
|
103
|
+
connection.commit() # 提交事务
|
104
|
+
|
105
|
+
# 4. 移除指定日期范围内的数据,仅限于聚合数据使用,其他情况不要设置
|
106
|
+
if drop_duplicates and '日期' in df.columns.tolist():
|
107
|
+
dates = df['日期'].values.tolist()
|
108
|
+
start_date = pd.to_datetime(min(dates)).strftime('%Y-%m-%d')
|
109
|
+
end_date = (pd.to_datetime(max(dates)) + datetime.timedelta(days=1)).strftime('%Y-%m-%d')
|
110
|
+
sql = f"DELETE FROM `{tabel_name}` WHERE {'日期'} BETWEEN '%s' AND '%s'" % (start_date, end_date)
|
111
|
+
cursor.execute(sql)
|
112
|
+
connection.commit()
|
113
|
+
|
114
|
+
# 5. 更新插入数据
|
115
|
+
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
116
|
+
print(f'{now}正在更新 mysql ({self.host}:{self.port}) {db_name}/{tabel_name}')
|
117
|
+
datas = df.to_dict(orient='records')
|
118
|
+
for data in datas:
|
119
|
+
try:
|
120
|
+
cols = ', '.join(f"`{item}`" for item in data.keys()) # 列名转义
|
121
|
+
# data.update({item: f"{data[item]}" for item in data.keys()}) # 全部值转字符, 不是必须的
|
122
|
+
values = ', '.join([f"'{item}'" for item in data.values()]) # 值要加单引号 ''
|
123
|
+
condition = []
|
124
|
+
for k, v in data.items():
|
125
|
+
condition += [f"`{k}` = '{v}'"]
|
126
|
+
condition = ' AND '.join(condition) # 构建查询条件
|
127
|
+
# print(condition)
|
128
|
+
|
129
|
+
sql = f"SELECT {cols} FROM `{tabel_name}` WHERE {condition}"
|
130
|
+
cursor.execute(sql)
|
131
|
+
result = cursor.fetchall() # 获取查询结果, 如果有结果返回 list,没有则返回空元组 tuple
|
132
|
+
if not result: # 数据不存在则插入
|
133
|
+
sql = f"INSERT INTO `{tabel_name}` ({cols}) VALUES ({values});"
|
134
|
+
cursor.execute(sql)
|
135
|
+
except:
|
136
|
+
pass
|
137
|
+
connection.commit() # 提交事务
|
184
138
|
|
185
139
|
def convert_dtypes(self, df, db_name, tabel_name):
|
186
140
|
"""
|
@@ -191,12 +145,10 @@ class MysqlUpload:
|
|
191
145
|
2. json 文件中没有或者缺失部分列信息(利用 convert_dtype_to_sql 函数按指定规则转换缺失列)
|
192
146
|
"""
|
193
147
|
cols = df.columns.tolist()
|
194
|
-
path = set_support.SetSupport(dirname='support').dirname
|
195
|
-
|
196
|
-
# if os.path.isfile(json_file):
|
197
|
-
d = data_types.DataTypes()
|
148
|
+
# path = set_support.SetSupport(dirname='support').dirname
|
149
|
+
d = mysql_types.DataTypes()
|
198
150
|
# 从本地文件中读取 dtype 信息
|
199
|
-
dtypes = d.load_dtypes(cl='mysql', db_name=db_name,
|
151
|
+
dtypes = d.load_dtypes(cl='mysql', db_name=db_name, tabel_name=tabel_name)
|
200
152
|
# 可能会因为没有 json 文件, 返回 None
|
201
153
|
if dtypes:
|
202
154
|
# 按照文件记录更新 dtypes
|
@@ -205,7 +157,7 @@ class MysqlUpload:
|
|
205
157
|
col_not_exist = [col for col in cols if col not in dtypes.keys()]
|
206
158
|
# 这些列不存在于 df 中, 必须移除
|
207
159
|
[dtypes.pop(col) for col in list(dtypes.keys()) if col not in cols]
|
208
|
-
else:
|
160
|
+
else: # 没有 json 文件时
|
209
161
|
dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
|
210
162
|
col_not_exist = cols
|
211
163
|
# 对文件不存在的列信息进行数据类型转换(按指定规则)
|
@@ -240,61 +192,12 @@ class MysqlUpload:
|
|
240
192
|
return 'mediumtext'
|
241
193
|
return 'INT'
|
242
194
|
elif dtype == 'float64':
|
243
|
-
return '
|
195
|
+
return 'double' # mysql 中不要使用 float 类型,会影响计算结果
|
244
196
|
elif dtype == 'object':
|
245
197
|
return 'mediumtext'
|
246
198
|
else:
|
247
199
|
return 'mediumtext'
|
248
200
|
|
249
|
-
def upload_pandas(self, update_path, db_name, days=None):
|
250
|
-
"""
|
251
|
-
专门用来上传 pandas数据源的全部文件, 跳过 '其他数据' or '京东数据集'
|
252
|
-
db_name: 数据库名: pandas数据源
|
253
|
-
update_path: pandas数据源所在路径
|
254
|
-
days: 更新近期数据,单位: 天, 不设置则全部更新
|
255
|
-
"""
|
256
|
-
if days:
|
257
|
-
today = datetime.date.today()
|
258
|
-
start_date = pd.to_datetime(today - datetime.timedelta(days=days))
|
259
|
-
else:
|
260
|
-
start_date = pd.to_datetime('2000-01-01')
|
261
|
-
|
262
|
-
root_files = os.listdir(update_path)
|
263
|
-
for root_file in root_files:
|
264
|
-
if '其他数据' in root_file or '年.csv' in root_file or '京东数据集' in root_file:
|
265
|
-
continue # 跳过的文件夹
|
266
|
-
f_path = os.path.join(update_path, root_file)
|
267
|
-
|
268
|
-
if os.path.isdir(f_path):
|
269
|
-
for root, dirs, files in os.walk(f_path, topdown=False):
|
270
|
-
for name in files:
|
271
|
-
if name.endswith('.csv') and 'baidu' not in name:
|
272
|
-
df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
|
273
|
-
# if '日期' not in df.columns.tolist():
|
274
|
-
# now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
275
|
-
# print(f'{now}{root_file} 缺少日期列, 不支持上传 mysql')
|
276
|
-
# continue
|
277
|
-
if '日期' in df.columns.tolist():
|
278
|
-
df['日期'] = df['日期'].apply(lambda x: pd.to_datetime(x) if x else x)
|
279
|
-
df = df[df['日期'] >= start_date]
|
280
|
-
if len(df) == 0:
|
281
|
-
continue
|
282
|
-
self.df_to_mysql(df=df, db_name=db_name, tabel_name=root_file)
|
283
|
-
elif os.path.isfile(f_path):
|
284
|
-
if f_path.endswith('.csv') and 'baidu' not in f_path:
|
285
|
-
df = pd.read_csv(f_path, encoding='utf-8_sig', header=0, na_filter=False)
|
286
|
-
# if '日期' not in df.columns.tolist():
|
287
|
-
# now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
288
|
-
# print(f'{now}{root_file} 缺少日期列, 不支持上传 mysql')
|
289
|
-
# continue
|
290
|
-
if '日期' not in df.columns.tolist():
|
291
|
-
df['日期'] = df['日期'].apply(lambda x: pd.to_datetime(x) if x else x)
|
292
|
-
df = df[df['日期'] >= start_date]
|
293
|
-
if len(df) == 0:
|
294
|
-
continue
|
295
|
-
table = f'{os.path.splitext(root_file)[0]}_f' # 这里定义了文件表会加 _f 后缀
|
296
|
-
self.df_to_mysql(df=df, db_name=db_name, tabel_name=table)
|
297
|
-
|
298
201
|
# @try_except
|
299
202
|
def read_mysql(self, tabel_name, start_date, end_date, db_name='远程数据源', ):
|
300
203
|
start_date = pd.to_datetime(start_date).strftime('%Y-%m-%d')
|
@@ -345,6 +248,55 @@ class MysqlUpload:
|
|
345
248
|
print(f'{now}mysql ({self.host}) 表: {tabel_name} 获取数据长度: {len(df)}, 用时: {cost_time} 秒')
|
346
249
|
return df
|
347
250
|
|
251
|
+
def upload_pandas(self, update_path, db_name, days=None):
|
252
|
+
"""
|
253
|
+
专门用来上传 pandas数据源的全部文件, 跳过 '其他数据' or '京东数据集'
|
254
|
+
db_name: 数据库名: pandas数据源
|
255
|
+
update_path: pandas数据源所在路径
|
256
|
+
days: 更新近期数据,单位: 天, 不设置则全部更新
|
257
|
+
"""
|
258
|
+
if days:
|
259
|
+
today = datetime.date.today()
|
260
|
+
start_date = pd.to_datetime(today - datetime.timedelta(days=days))
|
261
|
+
else:
|
262
|
+
start_date = pd.to_datetime('2000-01-01')
|
263
|
+
|
264
|
+
root_files = os.listdir(update_path)
|
265
|
+
for root_file in root_files:
|
266
|
+
if '其他数据' in root_file or '年.csv' in root_file or '京东数据集' in root_file:
|
267
|
+
continue # 跳过的文件夹
|
268
|
+
f_path = os.path.join(update_path, root_file)
|
269
|
+
|
270
|
+
if os.path.isdir(f_path):
|
271
|
+
for root, dirs, files in os.walk(f_path, topdown=False):
|
272
|
+
for name in files:
|
273
|
+
if name.endswith('.csv') and 'baidu' not in name:
|
274
|
+
df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
|
275
|
+
# if '日期' not in df.columns.tolist():
|
276
|
+
# now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
277
|
+
# print(f'{now}{root_file} 缺少日期列, 不支持上传 mysql')
|
278
|
+
# continue
|
279
|
+
if '日期' in df.columns.tolist():
|
280
|
+
df['日期'] = df['日期'].apply(lambda x: pd.to_datetime(x) if x else x)
|
281
|
+
df = df[df['日期'] >= start_date]
|
282
|
+
if len(df) == 0:
|
283
|
+
continue
|
284
|
+
self.df_to_mysql(df=df, db_name=db_name, tabel_name=root_file)
|
285
|
+
elif os.path.isfile(f_path):
|
286
|
+
if f_path.endswith('.csv') and 'baidu' not in f_path:
|
287
|
+
df = pd.read_csv(f_path, encoding='utf-8_sig', header=0, na_filter=False)
|
288
|
+
# if '日期' not in df.columns.tolist():
|
289
|
+
# now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
290
|
+
# print(f'{now}{root_file} 缺少日期列, 不支持上传 mysql')
|
291
|
+
# continue
|
292
|
+
if '日期' not in df.columns.tolist():
|
293
|
+
df['日期'] = df['日期'].apply(lambda x: pd.to_datetime(x) if x else x)
|
294
|
+
df = df[df['日期'] >= start_date]
|
295
|
+
if len(df) == 0:
|
296
|
+
continue
|
297
|
+
table = f'{os.path.splitext(root_file)[0]}_f' # 这里定义了文件表会加 _f 后缀
|
298
|
+
self.df_to_mysql(df=df, db_name=db_name, tabel_name=table)
|
299
|
+
|
348
300
|
|
349
301
|
class OptimizeDatas:
|
350
302
|
"""
|
@@ -689,8 +641,13 @@ def download_datas(tabel_name, save_path, start_date):
|
|
689
641
|
|
690
642
|
|
691
643
|
if __name__ == '__main__':
|
692
|
-
username, password, host, port = get_myconf.select_config_values(target_service='
|
644
|
+
username, password, host, port = get_myconf.select_config_values(target_service='company', database='mysql')
|
693
645
|
print(username, password, host, port)
|
694
646
|
|
647
|
+
df = pd.read_csv('/Users/xigua/Downloads/余额查询.csv', encoding='utf-8_sig', header=0, na_filter=False)
|
648
|
+
# df = df.to_dict(orient='records')
|
649
|
+
m = MysqlUpload(username=username, password=password, host=host, port=port)
|
650
|
+
m.df_to_mysql_new(df=df, db_name='te2- %s t', tabel_name='测 -sdf @%试 表')
|
651
|
+
|
695
652
|
|
696
653
|
|
@@ -1,9 +1,11 @@
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
2
2
|
mdbq/__version__.py,sha256=y9Mp_8x0BCZSHsdLT_q5tX9wZwd5QgqrSIENLrb6vXA,62
|
3
3
|
mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
|
4
|
-
mdbq/aggregation/aggregation.py,sha256=
|
4
|
+
mdbq/aggregation/aggregation.py,sha256=1myTHs_OwFngpf1_qFfC9l9CJKpLAVg8X9fO6at-3XY,53064
|
5
|
+
mdbq/aggregation/df_types.py,sha256=T35KML0sdch8GzIwo7CxSIrt72YVElBeCrsKQx4dX_0,7531
|
6
|
+
mdbq/aggregation/mysql_types.py,sha256=-dJbc7FJ0L0czjb2M55x11oy0AnwlKnrSkArUjoIFo0,9705
|
5
7
|
mdbq/aggregation/optimize_data.py,sha256=jLAWtxPUuhpo4XTVrhKtT4xK3grs7r73ePQfLhxlu1I,779
|
6
|
-
mdbq/aggregation/query_data.py,sha256=
|
8
|
+
mdbq/aggregation/query_data.py,sha256=5lzvEokjHuKtlaSBYjOFH8VA2MTtX8R3MwEUNs03qKg,24491
|
7
9
|
mdbq/bdup/__init__.py,sha256=AkhsGk81SkG1c8FqDH5tRq-8MZmFobVbN60DTyukYTY,28
|
8
10
|
mdbq/bdup/bdup.py,sha256=LAV0TgnQpc-LB-YuJthxb0U42_VkPidzQzAagan46lU,4234
|
9
11
|
mdbq/clean/__init__.py,sha256=A1d6x3L27j4NtLgiFV5TANwEkLuaDfPHDQNrPBbNWtU,41
|
@@ -16,14 +18,14 @@ mdbq/config/products.py,sha256=tFqSfFSXyZXcof0gAeHq0Ftn4F5i9ucoMyIqZ1H_D2Q,4260
|
|
16
18
|
mdbq/config/set_support.py,sha256=LJLEbUFrv8y-GVskiwOI8A9uRaCEAUa0Yfjugt4yLp0,768
|
17
19
|
mdbq/config/update_conf.py,sha256=taL3ZqKgiVWwUrDFuaYhim9a72Hm4BHRhhDscJTziR8,4535
|
18
20
|
mdbq/dataframe/__init__.py,sha256=2HtCN8AdRj53teXDqzysC1h8aPL-mMFy561ESmhehGQ,22
|
19
|
-
mdbq/dataframe/converter.py,sha256=
|
21
|
+
mdbq/dataframe/converter.py,sha256=cD9u9eaDkOcxMaiZH6Wq_0Jp9PLsoPJOmrys7yZpGvI,5535
|
20
22
|
mdbq/log/__init__.py,sha256=Mpbrav0s0ifLL7lVDAuePEi1hJKiSHhxcv1byBKDl5E,15
|
21
23
|
mdbq/log/mylogger.py,sha256=oaT7Bp-Hb9jZt52seP3ISUuxVcI19s4UiqTeouScBO0,3258
|
22
24
|
mdbq/mongo/__init__.py,sha256=SILt7xMtQIQl_m-ik9WLtJSXIVf424iYgCfE_tnQFbw,13
|
23
25
|
mdbq/mongo/mongo.py,sha256=q0B4wXDSTtXg_vMN7MPh6zdxl6tT68tM74LmdVNQQek,31892
|
24
26
|
mdbq/mysql/__init__.py,sha256=A_DPJyAoEvTSFojiI2e94zP0FKtCkkwKP1kYUCSyQzo,11
|
25
|
-
mdbq/mysql/
|
26
|
-
mdbq/mysql/mysql.py,sha256=
|
27
|
+
mdbq/mysql/data_types_即将删除.py,sha256=sjBBDKr9674LdjM5N_dwyJACdZPbdB8Beli59jGdgnQ,10378
|
28
|
+
mdbq/mysql/mysql.py,sha256=51VBrxoIJo7z2Pw0SN-u1oJp8oWvRj87ULFmH0kJXN0,31914
|
27
29
|
mdbq/mysql/s_query.py,sha256=6-8O9MHhi3-7n3isJ7t2kTCYL2mSBC_HrxSQmXM5UtI,7901
|
28
30
|
mdbq/mysql/year_month_day.py,sha256=VgewoE2pJxK7ErjfviL_SMTN77ki8GVbTUcao3vFUCE,1523
|
29
31
|
mdbq/other/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
|
@@ -34,7 +36,7 @@ mdbq/pbix/__init__.py,sha256=Trtfaynu9RjoTyLLYBN2xdRxTvm_zhCniUkVTAYwcjo,24
|
|
34
36
|
mdbq/pbix/pbix_refresh.py,sha256=JUjKW3bNEyoMVfVfo77UhguvS5AWkixvVhDbw4_MHco,2396
|
35
37
|
mdbq/pbix/refresh_all.py,sha256=tgy762608HMaXWynbOURIf2UVMuSPybzrDXQnOOcnZU,6102
|
36
38
|
mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
|
37
|
-
mdbq-0.
|
38
|
-
mdbq-0.
|
39
|
-
mdbq-0.
|
40
|
-
mdbq-0.
|
39
|
+
mdbq-1.0.0.dist-info/METADATA,sha256=AcCveIBgct8lUzgMAxu2w5wVpfraRoiM4fECYC0QF30,245
|
40
|
+
mdbq-1.0.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
|
41
|
+
mdbq-1.0.0.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
|
42
|
+
mdbq-1.0.0.dist-info/RECORD,,
|
File without changes
|