mdbq 4.1.10__py3-none-any.whl → 4.1.12__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.
Potentially problematic release.
This version of mdbq might be problematic. Click here for more details.
- mdbq/__version__.py +1 -1
- mdbq/mysql/uploader.py +281 -30
- {mdbq-4.1.10.dist-info → mdbq-4.1.12.dist-info}/METADATA +2 -2
- {mdbq-4.1.10.dist-info → mdbq-4.1.12.dist-info}/RECORD +6 -6
- {mdbq-4.1.10.dist-info → mdbq-4.1.12.dist-info}/WHEEL +1 -1
- {mdbq-4.1.10.dist-info → mdbq-4.1.12.dist-info}/top_level.txt +0 -0
mdbq/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '4.1.
|
|
1
|
+
VERSION = '4.1.12'
|
mdbq/mysql/uploader.py
CHANGED
|
@@ -111,6 +111,7 @@ class MySQLUploader:
|
|
|
111
111
|
:param read_timeout: 读取超时(秒),默认为30
|
|
112
112
|
:param write_timeout: 写入超时(秒),默认为30
|
|
113
113
|
:param ssl: SSL配置字典,默认为None
|
|
114
|
+
:param auto_creat_missing_cols: 自动添加缺失列,默认为False,建议手动维护表结构
|
|
114
115
|
"""
|
|
115
116
|
self.username = username
|
|
116
117
|
self.password = password
|
|
@@ -134,6 +135,7 @@ class MySQLUploader:
|
|
|
134
135
|
self._table_metadata_cache = {}
|
|
135
136
|
self.metadata_cache_ttl = 300 # 5分钟缓存时间
|
|
136
137
|
self.pool = self._create_connection_pool() # 创建连接池
|
|
138
|
+
self.auto_creat_missing_cols = False # 自动添加缺失列,正常不要自动添加,建议手动维护表结构
|
|
137
139
|
|
|
138
140
|
def _create_connection_pool(self) -> PooledDB:
|
|
139
141
|
"""
|
|
@@ -695,15 +697,16 @@ class MySQLUploader:
|
|
|
695
697
|
break
|
|
696
698
|
|
|
697
699
|
if not allow_null:
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
700
|
+
# 注释掉,这里可能会产生大量日志
|
|
701
|
+
# logger.debug("该列不允许为空值", {
|
|
702
|
+
# "库": db_name,
|
|
703
|
+
# "表": table_name,
|
|
704
|
+
# "allow_null": allow_null,
|
|
705
|
+
# "列": col_name,
|
|
706
|
+
# "值": original_value,
|
|
707
|
+
# "兜底值": fallback
|
|
708
|
+
# })
|
|
709
|
+
return fallback # 直接返回兜底值
|
|
707
710
|
|
|
708
711
|
return None # 允许空值时返回None
|
|
709
712
|
|
|
@@ -799,7 +802,13 @@ class MySQLUploader:
|
|
|
799
802
|
|
|
800
803
|
# 统一处理空值
|
|
801
804
|
if is_empty_value:
|
|
802
|
-
|
|
805
|
+
fallback_value = self._get_fallback_value(column_type_lower, allow_null, db_name, table_name, col_name, value)
|
|
806
|
+
# 如果返回了兜底值(非None),直接返回,不再进行后续验证
|
|
807
|
+
# 因为兜底值已经是根据列类型设计的合适值
|
|
808
|
+
if fallback_value is not None:
|
|
809
|
+
return fallback_value
|
|
810
|
+
# 如果返回None(允许空值的情况),继续后续处理
|
|
811
|
+
return None
|
|
803
812
|
|
|
804
813
|
# JSON类型验证和转换
|
|
805
814
|
if 'json' in column_type_lower:
|
|
@@ -1072,14 +1081,41 @@ class MySQLUploader:
|
|
|
1072
1081
|
'列': self._shorten_for_log(table_columns),
|
|
1073
1082
|
})
|
|
1074
1083
|
raise ValueError(f"获取列失败 `{db_name}`.`{table_name}`")
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1084
|
+
# 检查并自动添加缺失的列
|
|
1085
|
+
missing_columns = [col for col in set_typ if col not in table_columns]
|
|
1086
|
+
if missing_columns:
|
|
1087
|
+
if not self.auto_creat_missing_cols:
|
|
1088
|
+
logger.error('列不存在且不支持自动添加,请手动维护表结构,并补齐缺失列', {
|
|
1078
1089
|
'库': db_name,
|
|
1079
1090
|
'表': table_name,
|
|
1080
|
-
'
|
|
1091
|
+
'缺失列数': len(missing_columns),
|
|
1092
|
+
'缺失列': missing_columns,
|
|
1081
1093
|
})
|
|
1082
|
-
raise ValueError(f"列不存在: `{
|
|
1094
|
+
raise ValueError(f"列不存在: `{missing_columns}` -> `{db_name}`.`{table_name}`")
|
|
1095
|
+
else:
|
|
1096
|
+
# 表有缺失列时报错,建议不允许自动添加,手动检查数据一致性,以免产生不必要的表错误
|
|
1097
|
+
# 自动添加缺失的列
|
|
1098
|
+
for col in missing_columns:
|
|
1099
|
+
try:
|
|
1100
|
+
self._add_column_to_table(db_name, table_name, col, set_typ[col], allow_null)
|
|
1101
|
+
logger.info('自动添加缺失列', {
|
|
1102
|
+
'库': db_name,
|
|
1103
|
+
'表': table_name,
|
|
1104
|
+
'列': col,
|
|
1105
|
+
'类型': set_typ[col]
|
|
1106
|
+
})
|
|
1107
|
+
except Exception as e:
|
|
1108
|
+
logger.error('添加列失败', {
|
|
1109
|
+
'库': db_name,
|
|
1110
|
+
'表': table_name,
|
|
1111
|
+
'列': col,
|
|
1112
|
+
'类型': set_typ[col],
|
|
1113
|
+
'错误': str(e)
|
|
1114
|
+
})
|
|
1115
|
+
raise ValueError(f"添加列失败: `{col}` -> `{db_name}`.`{table_name}`: {str(e)}")
|
|
1116
|
+
|
|
1117
|
+
# 重新获取表列信息
|
|
1118
|
+
table_columns = self._get_table_columns(db_name, table_name)
|
|
1083
1119
|
if date_column and date_column in table_columns:
|
|
1084
1120
|
try:
|
|
1085
1121
|
self._ensure_index(db_name, table_name, date_column)
|
|
@@ -1183,12 +1219,17 @@ class MySQLUploader:
|
|
|
1183
1219
|
set_typ: Dict[str, str],
|
|
1184
1220
|
allow_null: bool = False,
|
|
1185
1221
|
db_name: str = None,
|
|
1186
|
-
table_name: str = None,
|
|
1222
|
+
table_name: str = None,
|
|
1223
|
+
auto_timestamps: bool = False
|
|
1187
1224
|
) -> Tuple[List[Dict], Dict[str, str]]:
|
|
1188
1225
|
"""
|
|
1189
1226
|
准备要上传的数据,验证并转换数据类型
|
|
1190
1227
|
根据set_typ自动处理所有数据类型的列:补齐缺失的列并丢弃多余的列
|
|
1191
1228
|
"""
|
|
1229
|
+
# 处理自动时间戳功能
|
|
1230
|
+
if auto_timestamps:
|
|
1231
|
+
data, set_typ = self._process_auto_timestamps(data, set_typ, db_name, table_name)
|
|
1232
|
+
|
|
1192
1233
|
# set_typ的键清洗
|
|
1193
1234
|
if not set_typ:
|
|
1194
1235
|
set_typ = {}
|
|
@@ -1300,21 +1341,67 @@ class MySQLUploader:
|
|
|
1300
1341
|
prepared_row[col_name] = self._validate_value(None, filtered_set_typ[col_name], allow_null, db_name, table_name, col_name)
|
|
1301
1342
|
except ValueError as e:
|
|
1302
1343
|
if not allow_null:
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1344
|
+
# 如果不允许空值但验证失败,尝试使用兜底值
|
|
1345
|
+
try:
|
|
1346
|
+
fallback_value = self._get_fallback_value(filtered_set_typ[col_name].lower(), allow_null, db_name, table_name, col_name, None)
|
|
1347
|
+
if fallback_value is not None:
|
|
1348
|
+
prepared_row[col_name] = fallback_value
|
|
1349
|
+
logger.warning(f"行号:{row_idx} -> 缺失列: `{col_name}`, 使用兜底值: {fallback_value}", {'row': self._shorten_for_log(row)})
|
|
1350
|
+
else:
|
|
1351
|
+
error_msg = f"行号:{row_idx} -> 缺失列: `{col_name}`, 且不允许空值"
|
|
1352
|
+
logger.error(error_msg, {'row': self._shorten_for_log(row)})
|
|
1353
|
+
raise ValueError(error_msg)
|
|
1354
|
+
except Exception:
|
|
1355
|
+
error_msg = f"行号:{row_idx} -> 缺失列: `{col_name}`, 且不允许空值"
|
|
1356
|
+
logger.error(error_msg, {'row': self._shorten_for_log(row)})
|
|
1357
|
+
raise ValueError(error_msg)
|
|
1358
|
+
else:
|
|
1359
|
+
prepared_row[col_name] = None
|
|
1307
1360
|
else:
|
|
1308
1361
|
try:
|
|
1309
1362
|
prepared_row[col_name] = self._validate_value(row[col_name], filtered_set_typ[col_name], allow_null, db_name, table_name, col_name)
|
|
1310
1363
|
except ValueError as e:
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1364
|
+
# 如果数据验证失败,检查是否为空值且不允许空值,尝试使用兜底值
|
|
1365
|
+
original_value = row[col_name]
|
|
1366
|
+
is_empty_original = (original_value is None or
|
|
1367
|
+
original_value == '' or
|
|
1368
|
+
(not isinstance(original_value, (list, dict)) and
|
|
1369
|
+
pd.isna(original_value) if hasattr(pd, 'isna') else False))
|
|
1370
|
+
|
|
1371
|
+
if is_empty_original and not allow_null:
|
|
1372
|
+
try:
|
|
1373
|
+
fallback_value = self._get_fallback_value(filtered_set_typ[col_name].lower(), allow_null, db_name, table_name, col_name, original_value)
|
|
1374
|
+
if fallback_value is not None:
|
|
1375
|
+
prepared_row[col_name] = fallback_value
|
|
1376
|
+
logger.warning(f"行:{row_idx}, 列:`{col_name}` -> 原值验证失败,使用兜底值: {fallback_value}", {
|
|
1377
|
+
'原值': original_value,
|
|
1378
|
+
'兜底值': fallback_value,
|
|
1379
|
+
'row': self._shorten_for_log(row)
|
|
1380
|
+
})
|
|
1381
|
+
else:
|
|
1382
|
+
logger.error('数据验证失败', {
|
|
1383
|
+
'列': col_name,
|
|
1384
|
+
'行': row_idx,
|
|
1385
|
+
'报错': str(e),
|
|
1386
|
+
'row': self._shorten_for_log(row),
|
|
1387
|
+
})
|
|
1388
|
+
raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
|
|
1389
|
+
except Exception:
|
|
1390
|
+
logger.error('数据验证失败', {
|
|
1391
|
+
'列': col_name,
|
|
1392
|
+
'行': row_idx,
|
|
1393
|
+
'报错': str(e),
|
|
1394
|
+
'row': self._shorten_for_log(row),
|
|
1395
|
+
})
|
|
1396
|
+
raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
|
|
1397
|
+
else:
|
|
1398
|
+
logger.error('数据验证失败', {
|
|
1399
|
+
'列': col_name,
|
|
1400
|
+
'行': row_idx,
|
|
1401
|
+
'报错': str(e),
|
|
1402
|
+
'row': self._shorten_for_log(row),
|
|
1403
|
+
})
|
|
1404
|
+
raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
|
|
1318
1405
|
prepared_data.append(prepared_row)
|
|
1319
1406
|
return prepared_data, filtered_set_typ
|
|
1320
1407
|
|
|
@@ -1334,7 +1421,8 @@ class MySQLUploader:
|
|
|
1334
1421
|
indexes: Optional[List[str]] = None,
|
|
1335
1422
|
update_on_duplicate: bool = False,
|
|
1336
1423
|
transaction_mode: str = "batch",
|
|
1337
|
-
unique_keys: Optional[List[List[str]]] = None
|
|
1424
|
+
unique_keys: Optional[List[List[str]]] = None,
|
|
1425
|
+
auto_timestamps: bool = False
|
|
1338
1426
|
):
|
|
1339
1427
|
"""
|
|
1340
1428
|
上传数据到数据库的主入口方法
|
|
@@ -1357,6 +1445,7 @@ class MySQLUploader:
|
|
|
1357
1445
|
- 'batch' : 整批提交事务(性能最优)
|
|
1358
1446
|
- 'hybrid' : 混合模式(每N行提交,平衡性能与安全性)
|
|
1359
1447
|
:param unique_keys: 唯一约束列表,每个元素为列名列表,支持多列组合唯一约束。格式:[['col1', 'col2'], ['col3']] 或 None
|
|
1448
|
+
:param auto_timestamps: 是否自动添加创建时间和更新时间列,默认为False。启用后会自动添加'创建时间'和'更新时间'两列
|
|
1360
1449
|
:raises: 可能抛出各种验证和数据库相关异常
|
|
1361
1450
|
|
|
1362
1451
|
---
|
|
@@ -1401,6 +1490,17 @@ class MySQLUploader:
|
|
|
1401
1490
|
- 只要 update_on_duplicate=True 且表存在唯一约束(如 unique_keys),无论 check_duplicate 是否为 True,都会更新旧数据(即 ON DUPLICATE KEY UPDATE 生效)。
|
|
1402
1491
|
- 如需"覆盖"行为,务必设置 update_on_duplicate=True,不管 check_duplicate 是否为 True。
|
|
1403
1492
|
- 如需"跳过"行为,设置 update_on_duplicate=False 即可。
|
|
1493
|
+
|
|
1494
|
+
---
|
|
1495
|
+
auto_timestamps 参数:
|
|
1496
|
+
|
|
1497
|
+
- 当 auto_timestamps=True 时,系统会自动添加'创建时间'和'更新时间'两列
|
|
1498
|
+
- 如果原始数据中已存在这两列,系统会先移除原始数据中的这些列,然后添加新的时间戳
|
|
1499
|
+
- '创建时间':记录数据首次插入的时间,使用当前时间戳
|
|
1500
|
+
- '更新时间':记录数据最后更新的时间,插入时与创建时间相同,更新时会自动更新为当前时间
|
|
1501
|
+
- 时间戳列的数据类型为 DATETIME,格式为 'YYYY-MM-DD HH:MM:SS'
|
|
1502
|
+
- 这两列会自动添加到 set_typ 中,无需手动指定
|
|
1503
|
+
- 建议在需要审计数据变更历史的表中启用此功能
|
|
1404
1504
|
"""
|
|
1405
1505
|
# upload_start = time.time()
|
|
1406
1506
|
# 检查data参数是否为None
|
|
@@ -1410,7 +1510,7 @@ class MySQLUploader:
|
|
|
1410
1510
|
'表': table_name,
|
|
1411
1511
|
})
|
|
1412
1512
|
raise ValueError("data参数不能为None,请传入有效的数据")
|
|
1413
|
-
|
|
1513
|
+
|
|
1414
1514
|
if isinstance(data, list) or (hasattr(data, 'shape') and hasattr(data, '__len__')):
|
|
1415
1515
|
initial_row_count = len(data)
|
|
1416
1516
|
else:
|
|
@@ -1471,7 +1571,7 @@ class MySQLUploader:
|
|
|
1471
1571
|
raise ValueError("分表方式必须是 'year' 或 'month' 或 'None'")
|
|
1472
1572
|
|
|
1473
1573
|
# 准备数据
|
|
1474
|
-
prepared_data, filtered_set_typ = self._prepare_data(data, set_typ, allow_null, db_name, table_name)
|
|
1574
|
+
prepared_data, filtered_set_typ = self._prepare_data(data, set_typ, allow_null, db_name, table_name, auto_timestamps)
|
|
1475
1575
|
|
|
1476
1576
|
# 检查数据库是否存在
|
|
1477
1577
|
if not self._check_database_exists(db_name):
|
|
@@ -2080,6 +2180,70 @@ class MySQLUploader:
|
|
|
2080
2180
|
except Exception as e:
|
|
2081
2181
|
logger.error('创建索引失败', {'库': db_name, '表': table_name, '列': column, '错误': str(e)})
|
|
2082
2182
|
raise
|
|
2183
|
+
|
|
2184
|
+
@_execute_with_retry
|
|
2185
|
+
def _add_column_to_table(self, db_name: str, table_name: str, column: str, column_type: str, allow_null: bool = False):
|
|
2186
|
+
"""
|
|
2187
|
+
添加列到指定表。
|
|
2188
|
+
|
|
2189
|
+
:param db_name: 数据库名
|
|
2190
|
+
:param table_name: 表名
|
|
2191
|
+
:param column: 需要添加的列名
|
|
2192
|
+
:param column_type: 列的数据类型
|
|
2193
|
+
:param allow_null: 是否允许空值,默认为False
|
|
2194
|
+
"""
|
|
2195
|
+
db_name = self._validate_identifier(db_name, is_database=True)
|
|
2196
|
+
table_name = self._validate_identifier(table_name)
|
|
2197
|
+
column = self._validate_identifier(column)
|
|
2198
|
+
|
|
2199
|
+
# 构建ALTER TABLE语句
|
|
2200
|
+
null_constraint = "NULL" if allow_null else "NOT NULL"
|
|
2201
|
+
|
|
2202
|
+
# 为新添加的列设置默认值
|
|
2203
|
+
default_value = ""
|
|
2204
|
+
if not allow_null:
|
|
2205
|
+
column_type_lower = column_type.lower()
|
|
2206
|
+
if any(t in column_type_lower for t in ['int', 'bigint', 'tinyint', 'smallint', 'mediumint']):
|
|
2207
|
+
default_value = " DEFAULT 0"
|
|
2208
|
+
elif any(t in column_type_lower for t in ['decimal', 'float', 'double']):
|
|
2209
|
+
default_value = " DEFAULT 0.0"
|
|
2210
|
+
elif any(t in column_type_lower for t in ['varchar', 'text', 'char', 'mediumtext', 'longtext']):
|
|
2211
|
+
default_value = " DEFAULT 'none'"
|
|
2212
|
+
elif 'date' in column_type_lower:
|
|
2213
|
+
if 'datetime' in column_type_lower or 'timestamp' in column_type_lower:
|
|
2214
|
+
default_value = " DEFAULT '1970-01-01 00:00:00'"
|
|
2215
|
+
else:
|
|
2216
|
+
default_value = " DEFAULT '1970-01-01'"
|
|
2217
|
+
elif 'json' in column_type_lower:
|
|
2218
|
+
default_value = " DEFAULT '{}'"
|
|
2219
|
+
|
|
2220
|
+
sql = f'ALTER TABLE `{db_name}`.`{table_name}` ADD COLUMN `{column}` {column_type} {null_constraint}{default_value}'
|
|
2221
|
+
|
|
2222
|
+
conn = None
|
|
2223
|
+
try:
|
|
2224
|
+
with self._get_connection() as conn:
|
|
2225
|
+
with conn.cursor() as cursor:
|
|
2226
|
+
cursor.execute(sql)
|
|
2227
|
+
conn.commit()
|
|
2228
|
+
logger.debug('已为表添加列', {
|
|
2229
|
+
'库': db_name,
|
|
2230
|
+
'表': table_name,
|
|
2231
|
+
'列': column,
|
|
2232
|
+
'类型': column_type,
|
|
2233
|
+
'允许空值': allow_null
|
|
2234
|
+
})
|
|
2235
|
+
except Exception as e:
|
|
2236
|
+
logger.error('添加列失败', {
|
|
2237
|
+
'库': db_name,
|
|
2238
|
+
'表': table_name,
|
|
2239
|
+
'列': column,
|
|
2240
|
+
'类型': column_type,
|
|
2241
|
+
'错误': str(e),
|
|
2242
|
+
'SQL': sql
|
|
2243
|
+
})
|
|
2244
|
+
if conn is not None:
|
|
2245
|
+
conn.rollback()
|
|
2246
|
+
raise
|
|
2083
2247
|
|
|
2084
2248
|
def __enter__(self):
|
|
2085
2249
|
return self
|
|
@@ -2431,6 +2595,93 @@ class MySQLUploader:
|
|
|
2431
2595
|
|
|
2432
2596
|
return result_df
|
|
2433
2597
|
|
|
2598
|
+
def _process_auto_timestamps(
|
|
2599
|
+
self,
|
|
2600
|
+
data: Union[Dict, List[Dict], pd.DataFrame],
|
|
2601
|
+
set_typ: Dict[str, str],
|
|
2602
|
+
db_name: str,
|
|
2603
|
+
table_name: str
|
|
2604
|
+
) -> Tuple[Union[Dict, List[Dict], pd.DataFrame], Dict[str, str]]:
|
|
2605
|
+
"""
|
|
2606
|
+
处理自动时间戳功能
|
|
2607
|
+
|
|
2608
|
+
:param data: 原始数据
|
|
2609
|
+
:param set_typ: 列类型定义
|
|
2610
|
+
:param db_name: 数据库名
|
|
2611
|
+
:param table_name: 表名
|
|
2612
|
+
:return: 处理后的数据和更新后的set_typ
|
|
2613
|
+
"""
|
|
2614
|
+
|
|
2615
|
+
# 定义时间戳列名
|
|
2616
|
+
created_col = '创建时间'
|
|
2617
|
+
updated_col = '更新时间'
|
|
2618
|
+
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
2619
|
+
|
|
2620
|
+
# 复制set_typ以避免修改原始对象
|
|
2621
|
+
updated_set_typ = set_typ.copy()
|
|
2622
|
+
|
|
2623
|
+
# 添加时间戳列到set_typ
|
|
2624
|
+
updated_set_typ[created_col] = 'DATETIME'
|
|
2625
|
+
updated_set_typ[updated_col] = 'DATETIME'
|
|
2626
|
+
|
|
2627
|
+
# 处理DataFrame格式的数据
|
|
2628
|
+
if hasattr(data, 'shape') and hasattr(data, 'columns'):
|
|
2629
|
+
import pandas as pd
|
|
2630
|
+
df = data.copy()
|
|
2631
|
+
|
|
2632
|
+
# 移除原始数据中可能存在的时间戳列
|
|
2633
|
+
columns_to_remove = []
|
|
2634
|
+
for col in df.columns:
|
|
2635
|
+
if col in [created_col, updated_col]:
|
|
2636
|
+
columns_to_remove.append(col)
|
|
2637
|
+
logger.warning('移除原始数据中的时间戳列', {
|
|
2638
|
+
'库': db_name,
|
|
2639
|
+
'表': table_name,
|
|
2640
|
+
'列': col,
|
|
2641
|
+
'原因': '与自动时间戳功能冲突'
|
|
2642
|
+
})
|
|
2643
|
+
|
|
2644
|
+
if columns_to_remove:
|
|
2645
|
+
df = df.drop(columns=columns_to_remove)
|
|
2646
|
+
|
|
2647
|
+
# 添加时间戳列
|
|
2648
|
+
df[created_col] = current_time
|
|
2649
|
+
df[updated_col] = current_time
|
|
2650
|
+
|
|
2651
|
+
return df, updated_set_typ
|
|
2652
|
+
|
|
2653
|
+
# 处理字典或字典列表格式的数据
|
|
2654
|
+
else:
|
|
2655
|
+
# 确保data是列表格式
|
|
2656
|
+
if isinstance(data, dict):
|
|
2657
|
+
data_list = [data]
|
|
2658
|
+
is_single_dict = True
|
|
2659
|
+
else:
|
|
2660
|
+
data_list = data
|
|
2661
|
+
is_single_dict = False
|
|
2662
|
+
|
|
2663
|
+
# 处理每一行数据
|
|
2664
|
+
processed_data = []
|
|
2665
|
+
for row in data_list:
|
|
2666
|
+
new_row = {}
|
|
2667
|
+
|
|
2668
|
+
# 复制原始数据,但跳过可能存在的时间戳列
|
|
2669
|
+
for key, value in row.items():
|
|
2670
|
+
if key not in [created_col, updated_col]:
|
|
2671
|
+
new_row[key] = value
|
|
2672
|
+
|
|
2673
|
+
# 添加时间戳
|
|
2674
|
+
new_row[created_col] = current_time
|
|
2675
|
+
new_row[updated_col] = current_time
|
|
2676
|
+
|
|
2677
|
+
processed_data.append(new_row)
|
|
2678
|
+
|
|
2679
|
+
# 如果原始数据是单个字典,返回单个字典
|
|
2680
|
+
if is_single_dict:
|
|
2681
|
+
return processed_data[0], updated_set_typ
|
|
2682
|
+
else:
|
|
2683
|
+
return processed_data, updated_set_typ
|
|
2684
|
+
|
|
2434
2685
|
|
|
2435
2686
|
def main():
|
|
2436
2687
|
dir_path = os.path.expanduser("~")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
|
2
|
-
mdbq/__version__.py,sha256=
|
|
2
|
+
mdbq/__version__.py,sha256=IFiIUlZSAqjKo9qfpz69wxp6zBBmSIhmlxQDUZwjhNE,18
|
|
3
3
|
mdbq/auth/__init__.py,sha256=pnPMAt63sh1B6kEvmutUuro46zVf2v2YDAG7q-jV_To,24
|
|
4
4
|
mdbq/auth/auth_backend.py,sha256=iLN7AqiSq7fQgFtNtge_TIlVOR1hrCSZXH6oId6uGX4,116924
|
|
5
5
|
mdbq/auth/crypto.py,sha256=fcZRFCnrKVVdWDUx_zds51ynFYwS9DBvJOrRQVldrfM,15931
|
|
@@ -15,7 +15,7 @@ mdbq/mysql/deduplicator.py,sha256=2fugLyKs_xkvYvoG0C0hRYbJ_w8-4oa1FJ_vavoD7Qo,73
|
|
|
15
15
|
mdbq/mysql/mysql.py,sha256=pDg771xBugCMSTWeskIFTi3pFLgaqgyG3smzf-86Wn8,56772
|
|
16
16
|
mdbq/mysql/s_query.py,sha256=N2xHJf2CiUXjXIVBemdst-wamIP3908EGAJOFG13fCU,50475
|
|
17
17
|
mdbq/mysql/unique_.py,sha256=MaztT-WIyEQUs-OOYY4pFulgHVcXR1BfCy3QUz0XM_U,21127
|
|
18
|
-
mdbq/mysql/uploader.py,sha256=
|
|
18
|
+
mdbq/mysql/uploader.py,sha256=7LIWclzGFWYzrt46NTJn34LJZJaz9eWiV6fIj6-ZK7k,130288
|
|
19
19
|
mdbq/other/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
|
|
20
20
|
mdbq/other/download_sku_picture.py,sha256=X66sVdvVgzoNzmgVJyPtd7bjEvctEKtLPblEPF65EWc,46940
|
|
21
21
|
mdbq/other/error_handler.py,sha256=4p5haAXSY-P78stp4Xwo_MwAngWYqyKj5ogWIuYXMeY,12631
|
|
@@ -35,7 +35,7 @@ mdbq/route/routes.py,sha256=QVGfTvDgu0CpcKCvk1ra74H8uojgqTLUav1fnVAqLEA,29433
|
|
|
35
35
|
mdbq/selenium/__init__.py,sha256=AKzeEceqZyvqn2dEDoJSzDQnbuENkJSHAlbHAD0u0ZI,10
|
|
36
36
|
mdbq/selenium/get_driver.py,sha256=1NTlVUE6QsyjTrVVVqTO2LOnYf578ccFWlWnvIXGtic,20903
|
|
37
37
|
mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
|
|
38
|
-
mdbq-4.1.
|
|
39
|
-
mdbq-4.1.
|
|
40
|
-
mdbq-4.1.
|
|
41
|
-
mdbq-4.1.
|
|
38
|
+
mdbq-4.1.12.dist-info/METADATA,sha256=EI6IFZT65_tpgdTRxL9AdN5JJBKdMX4vIiMztPSKOR8,364
|
|
39
|
+
mdbq-4.1.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
mdbq-4.1.12.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
|
|
41
|
+
mdbq-4.1.12.dist-info/RECORD,,
|
|
File without changes
|