mdbq 0.4.6__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/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 +55 -204
- {mdbq-0.4.6.dist-info → mdbq-1.0.0.dist-info}/METADATA +1 -1
- {mdbq-0.4.6.dist-info → mdbq-1.0.0.dist-info}/RECORD +9 -7
- {mdbq-0.4.6.dist-info → mdbq-1.0.0.dist-info}/WHEEL +1 -1
- {mdbq-0.4.6.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
|
@@ -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
@@ -16,7 +16,7 @@ import calendar
|
|
16
16
|
from mdbq.config import get_myconf
|
17
17
|
from mdbq.config import set_support
|
18
18
|
from mdbq.dataframe import converter
|
19
|
-
from mdbq.
|
19
|
+
from mdbq.aggregation import mysql_types
|
20
20
|
|
21
21
|
warnings.filterwarnings('ignore')
|
22
22
|
|
@@ -35,153 +35,6 @@ class MysqlUpload:
|
|
35
35
|
'charset': charset, # utf8mb4 支持存储四字节的UTF-8字符集
|
36
36
|
'cursorclass': pymysql.cursors.DictCursor,
|
37
37
|
}
|
38
|
-
self.conn = None
|
39
|
-
|
40
|
-
@staticmethod
|
41
|
-
def try_except(func): # 在类内部定义一个异常处理方法
|
42
|
-
@wraps(func)
|
43
|
-
def wrapper(*args, **kwargs):
|
44
|
-
try:
|
45
|
-
return func(*args, **kwargs)
|
46
|
-
except Exception as e:
|
47
|
-
print(f'{func.__name__}, {e}') # 将异常信息返回
|
48
|
-
|
49
|
-
return wrapper
|
50
|
-
|
51
|
-
def _conn(self):
|
52
|
-
self.config = {
|
53
|
-
'host': self.host,
|
54
|
-
'port': int(self.port),
|
55
|
-
'user': self.username,
|
56
|
-
'password': self.password,
|
57
|
-
'charset': 'utf8mb4', # utf8mb4 支持存储四字节的UTF-8字符集
|
58
|
-
'cursorclass': pymysql.cursors.DictCursor,
|
59
|
-
}
|
60
|
-
try:
|
61
|
-
self.conn = pymysql.connect(**self.config) # 连接数据库
|
62
|
-
return True
|
63
|
-
except:
|
64
|
-
return False
|
65
|
-
|
66
|
-
# @try_except
|
67
|
-
def df_to_mysql_bak(self, df, tabel_name, db_name='远程数据源'):
|
68
|
-
"""
|
69
|
-
将 df 写入数据库
|
70
|
-
db_name: 数据库名称
|
71
|
-
tabel_name: 集合/表名称
|
72
|
-
"""
|
73
|
-
db_name = re.sub(r'[\',,()()/=<>+\-*^"’\[\]~#|&% .]', '_', db_name)
|
74
|
-
tabel_name = re.sub(r'[\',,()()/=<>+\-*^"’\[\]~#|&% .]', '_', tabel_name)
|
75
|
-
cv = converter.DataFrameConverter()
|
76
|
-
df = cv.convert_df_cols(df=df) # 清理列名中的不合规字符
|
77
|
-
|
78
|
-
connection = pymysql.connect(**self.config) # 连接数据库
|
79
|
-
try:
|
80
|
-
with connection.cursor() as cursor:
|
81
|
-
cursor.execute(f"SHOW DATABASES LIKE '{db_name}'") # 检查数据库是否存在
|
82
|
-
database_exists = cursor.fetchone()
|
83
|
-
if not database_exists:
|
84
|
-
# 如果数据库不存在,则新建
|
85
|
-
if '8.138.27' in str(self.host) or platform.system() == "Linux": # 阿里云 mysql 低版本不支持 0900
|
86
|
-
cursor.execute(f"CREATE DATABASE {db_name} COLLATE utf8mb4_unicode_ci")
|
87
|
-
self.config.update({'charset': 'utf8mb4_unicode_ci'})
|
88
|
-
if '192.168.1.100' in str(self.host):
|
89
|
-
cursor.execute(f"CREATE DATABASE {db_name}")
|
90
|
-
else:
|
91
|
-
cursor.execute(f"CREATE DATABASE {db_name} COLLATE utf8mb4_0900_ai_ci")
|
92
|
-
# cursor.execute(f"CREATE DATABASE {db_name}")
|
93
|
-
connection.commit()
|
94
|
-
print(f"创建Database: {db_name}")
|
95
|
-
except Exception as e:
|
96
|
-
print(e)
|
97
|
-
return
|
98
|
-
finally:
|
99
|
-
connection.close() # 这里要断开连接
|
100
|
-
time.sleep(0.2)
|
101
|
-
|
102
|
-
self.config.update({'database': db_name}) # 添加更新 config 字段
|
103
|
-
connection = pymysql.connect(**self.config) # 重新连接数据库
|
104
|
-
try:
|
105
|
-
with connection.cursor() as cursor:
|
106
|
-
tabel_name = tabel_name.replace('-', '_')
|
107
|
-
# 1. 查询表, 不存在则创建一个空表
|
108
|
-
cursor.execute(f"SHOW TABLES LIKE '{tabel_name}'")
|
109
|
-
if not cursor.fetchone():
|
110
|
-
sql = f'CREATE TABLE IF NOT EXISTS {tabel_name} (id INT AUTO_INCREMENT PRIMARY KEY)'
|
111
|
-
cursor.execute(sql)
|
112
|
-
print(f'创建 mysql 表: {tabel_name}')
|
113
|
-
|
114
|
-
# # 2. 列数据类型转换
|
115
|
-
# cols = df.columns.tolist()
|
116
|
-
# dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
|
117
|
-
# # 转换为 mysql 的数据类型
|
118
|
-
# dtypes.update({col: self.convert_dtype_to_sql(df=df, col=col, dtype=dtypes[col]) for col in cols})
|
119
|
-
dtypes = self.convert_dtypes(df=df, db_name=db_name, tabel_name=tabel_name)
|
120
|
-
|
121
|
-
# 3. 检查列, 不存在则添加新列
|
122
|
-
cols = df.columns.tolist()
|
123
|
-
for col in cols:
|
124
|
-
sql = ('SELECT 1 FROM information_schema.columns WHERE table_schema = %s AND table_name = %s AND '
|
125
|
-
'column_name = %s')
|
126
|
-
cursor.execute(sql, (db_name, {tabel_name}, col))
|
127
|
-
if cursor.fetchone() is None: # 如果列不存在,添加新列
|
128
|
-
print(f"添加列: {col}({dtypes[col]})") # 添加列并指定数据类型
|
129
|
-
# if col == '日期':
|
130
|
-
# sql = f"ALTER TABLE {tabel_name} ADD COLUMN {col} DATE default NULL;"
|
131
|
-
# else:
|
132
|
-
# sql = f"ALTER TABLE {tabel_name} ADD COLUMN {col} mediumtext default NULL;"
|
133
|
-
sql = f"ALTER TABLE {tabel_name} ADD COLUMN {col} {dtypes[col]} default NULL;"
|
134
|
-
cursor.execute(sql)
|
135
|
-
|
136
|
-
if col == '日期':
|
137
|
-
cursor.execute(f"SHOW INDEXES FROM `{tabel_name}` WHERE Column_name = %s", ({col},))
|
138
|
-
result = cursor.fetchone() # 检查索引是否存在
|
139
|
-
if not result:
|
140
|
-
# print(f'创建索引: {col}')
|
141
|
-
cursor.execute(f"CREATE INDEX index_name ON {tabel_name}({col})")
|
142
|
-
connection.commit() # 提交事务
|
143
|
-
|
144
|
-
# # 4. 移除指定日期范围内的数据, 避免重复插入
|
145
|
-
# dates = df['日期'].values.tolist()
|
146
|
-
# start_date = pd.to_datetime(min(dates)).strftime('%Y-%m-%d')
|
147
|
-
# end_date = (pd.to_datetime(max(dates)) + datetime.timedelta(days=1)).strftime('%Y-%m-%d')
|
148
|
-
# sql = f"DELETE FROM {tabel_name} WHERE {'日期'} BETWEEN '%s' AND '%s'" % (start_date, end_date)
|
149
|
-
# cursor.execute(sql)
|
150
|
-
# connection.commit()
|
151
|
-
|
152
|
-
# 5. 更新插入数据
|
153
|
-
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
154
|
-
print(f'{now}正在更新 mysql ({self.host}:{self.port}) {db_name}/{tabel_name}')
|
155
|
-
if str(self.host) == '192.168.1.100': # 群晖服务器
|
156
|
-
try:
|
157
|
-
datas = df.to_dict('records')
|
158
|
-
for data in datas:
|
159
|
-
cols = ', '.join(data.keys())
|
160
|
-
values = ', '.join([f'"{v}"' for v in data.values()])
|
161
|
-
sql = f"INSERT INTO {tabel_name} ({cols}) VALUES ({values})"
|
162
|
-
cursor.execute(sql)
|
163
|
-
connection.commit()
|
164
|
-
except Exception as e:
|
165
|
-
print(e)
|
166
|
-
connection.rollback()
|
167
|
-
else: # 其他服务器
|
168
|
-
try:
|
169
|
-
engine = create_engine(
|
170
|
-
f'mysql+pymysql://{self.username}:{self.password}@{self.host}:{self.port}/{db_name}'
|
171
|
-
)
|
172
|
-
df.to_sql(tabel_name, con=engine, if_exists='append', index=False)
|
173
|
-
except Exception as e: # 如果异常则回滚
|
174
|
-
try:
|
175
|
-
connection.rollback()
|
176
|
-
print(f'{e}, 发生异常,正在重试...')
|
177
|
-
# df = df.replace([np.inf, -np.inf], 0)
|
178
|
-
df.to_sql(tabel_name, con=engine, if_exists='append', index=False)
|
179
|
-
except Exception as e:
|
180
|
-
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
181
|
-
print(f'{now}{db_name}/{tabel_name}数据异常, 正在回滚: {e}')
|
182
|
-
connection.rollback()
|
183
|
-
finally:
|
184
|
-
connection.close()
|
185
38
|
|
186
39
|
def df_to_mysql(self, df, tabel_name, db_name='远程数据源', drop_duplicates=False):
|
187
40
|
"""
|
@@ -221,7 +74,7 @@ class MysqlUpload:
|
|
221
74
|
cursor.execute(sql)
|
222
75
|
print(f'创建 mysql 表: {tabel_name}')
|
223
76
|
|
224
|
-
# 2.
|
77
|
+
# 2. 列数据类型转换,将 df 数据类型转换为 mysql 的数据类型
|
225
78
|
dtypes = self.convert_dtypes(df=df, db_name=db_name, tabel_name=tabel_name)
|
226
79
|
|
227
80
|
# 有特殊字符不需转义
|
@@ -292,12 +145,10 @@ class MysqlUpload:
|
|
292
145
|
2. json 文件中没有或者缺失部分列信息(利用 convert_dtype_to_sql 函数按指定规则转换缺失列)
|
293
146
|
"""
|
294
147
|
cols = df.columns.tolist()
|
295
|
-
path = set_support.SetSupport(dirname='support').dirname
|
296
|
-
|
297
|
-
# if os.path.isfile(json_file):
|
298
|
-
d = data_types.DataTypes()
|
148
|
+
# path = set_support.SetSupport(dirname='support').dirname
|
149
|
+
d = mysql_types.DataTypes()
|
299
150
|
# 从本地文件中读取 dtype 信息
|
300
|
-
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)
|
301
152
|
# 可能会因为没有 json 文件, 返回 None
|
302
153
|
if dtypes:
|
303
154
|
# 按照文件记录更新 dtypes
|
@@ -306,7 +157,7 @@ class MysqlUpload:
|
|
306
157
|
col_not_exist = [col for col in cols if col not in dtypes.keys()]
|
307
158
|
# 这些列不存在于 df 中, 必须移除
|
308
159
|
[dtypes.pop(col) for col in list(dtypes.keys()) if col not in cols]
|
309
|
-
else:
|
160
|
+
else: # 没有 json 文件时
|
310
161
|
dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
|
311
162
|
col_not_exist = cols
|
312
163
|
# 对文件不存在的列信息进行数据类型转换(按指定规则)
|
@@ -347,55 +198,6 @@ class MysqlUpload:
|
|
347
198
|
else:
|
348
199
|
return 'mediumtext'
|
349
200
|
|
350
|
-
def upload_pandas(self, update_path, db_name, days=None):
|
351
|
-
"""
|
352
|
-
专门用来上传 pandas数据源的全部文件, 跳过 '其他数据' or '京东数据集'
|
353
|
-
db_name: 数据库名: pandas数据源
|
354
|
-
update_path: pandas数据源所在路径
|
355
|
-
days: 更新近期数据,单位: 天, 不设置则全部更新
|
356
|
-
"""
|
357
|
-
if days:
|
358
|
-
today = datetime.date.today()
|
359
|
-
start_date = pd.to_datetime(today - datetime.timedelta(days=days))
|
360
|
-
else:
|
361
|
-
start_date = pd.to_datetime('2000-01-01')
|
362
|
-
|
363
|
-
root_files = os.listdir(update_path)
|
364
|
-
for root_file in root_files:
|
365
|
-
if '其他数据' in root_file or '年.csv' in root_file or '京东数据集' in root_file:
|
366
|
-
continue # 跳过的文件夹
|
367
|
-
f_path = os.path.join(update_path, root_file)
|
368
|
-
|
369
|
-
if os.path.isdir(f_path):
|
370
|
-
for root, dirs, files in os.walk(f_path, topdown=False):
|
371
|
-
for name in files:
|
372
|
-
if name.endswith('.csv') and 'baidu' not in name:
|
373
|
-
df = pd.read_csv(os.path.join(root, name), encoding='utf-8_sig', header=0, na_filter=False)
|
374
|
-
# if '日期' not in df.columns.tolist():
|
375
|
-
# now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
376
|
-
# print(f'{now}{root_file} 缺少日期列, 不支持上传 mysql')
|
377
|
-
# continue
|
378
|
-
if '日期' in df.columns.tolist():
|
379
|
-
df['日期'] = df['日期'].apply(lambda x: pd.to_datetime(x) if x else x)
|
380
|
-
df = df[df['日期'] >= start_date]
|
381
|
-
if len(df) == 0:
|
382
|
-
continue
|
383
|
-
self.df_to_mysql(df=df, db_name=db_name, tabel_name=root_file)
|
384
|
-
elif os.path.isfile(f_path):
|
385
|
-
if f_path.endswith('.csv') and 'baidu' not in f_path:
|
386
|
-
df = pd.read_csv(f_path, encoding='utf-8_sig', header=0, na_filter=False)
|
387
|
-
# if '日期' not in df.columns.tolist():
|
388
|
-
# now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
389
|
-
# print(f'{now}{root_file} 缺少日期列, 不支持上传 mysql')
|
390
|
-
# continue
|
391
|
-
if '日期' not in df.columns.tolist():
|
392
|
-
df['日期'] = df['日期'].apply(lambda x: pd.to_datetime(x) if x else x)
|
393
|
-
df = df[df['日期'] >= start_date]
|
394
|
-
if len(df) == 0:
|
395
|
-
continue
|
396
|
-
table = f'{os.path.splitext(root_file)[0]}_f' # 这里定义了文件表会加 _f 后缀
|
397
|
-
self.df_to_mysql(df=df, db_name=db_name, tabel_name=table)
|
398
|
-
|
399
201
|
# @try_except
|
400
202
|
def read_mysql(self, tabel_name, start_date, end_date, db_name='远程数据源', ):
|
401
203
|
start_date = pd.to_datetime(start_date).strftime('%Y-%m-%d')
|
@@ -446,6 +248,55 @@ class MysqlUpload:
|
|
446
248
|
print(f'{now}mysql ({self.host}) 表: {tabel_name} 获取数据长度: {len(df)}, 用时: {cost_time} 秒')
|
447
249
|
return df
|
448
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
|
+
|
449
300
|
|
450
301
|
class OptimizeDatas:
|
451
302
|
"""
|
@@ -1,7 +1,9 @@
|
|
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
8
|
mdbq/aggregation/query_data.py,sha256=5lzvEokjHuKtlaSBYjOFH8VA2MTtX8R3MwEUNs03qKg,24491
|
7
9
|
mdbq/bdup/__init__.py,sha256=AkhsGk81SkG1c8FqDH5tRq-8MZmFobVbN60DTyukYTY,28
|
@@ -22,8 +24,8 @@ 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
|