pyxllib 0.3.96__py3-none-any.whl → 0.3.197__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.
Files changed (306) hide show
  1. pyxllib/algo/geo.py +12 -0
  2. pyxllib/algo/intervals.py +1 -1
  3. pyxllib/algo/matcher.py +78 -0
  4. pyxllib/algo/pupil.py +187 -19
  5. pyxllib/algo/specialist.py +2 -1
  6. pyxllib/algo/stat.py +38 -2
  7. {pyxlpr → pyxllib/autogui}/__init__.py +1 -1
  8. pyxllib/autogui/activewin.py +246 -0
  9. pyxllib/autogui/all.py +9 -0
  10. pyxllib/{ext/autogui → autogui}/autogui.py +40 -11
  11. pyxllib/autogui/uiautolib.py +362 -0
  12. pyxllib/autogui/wechat.py +827 -0
  13. pyxllib/autogui/wechat_msg.py +421 -0
  14. pyxllib/autogui/wxautolib.py +84 -0
  15. pyxllib/cv/slidercaptcha.py +137 -0
  16. pyxllib/data/echarts.py +123 -12
  17. pyxllib/data/jsonlib.py +89 -0
  18. pyxllib/data/pglib.py +514 -30
  19. pyxllib/data/sqlite.py +231 -4
  20. pyxllib/ext/JLineViewer.py +14 -1
  21. pyxllib/ext/drissionlib.py +277 -0
  22. pyxllib/ext/kq5034lib.py +0 -1594
  23. pyxllib/ext/robustprocfile.py +497 -0
  24. pyxllib/ext/unixlib.py +6 -5
  25. pyxllib/ext/utools.py +108 -95
  26. pyxllib/ext/webhook.py +32 -14
  27. pyxllib/ext/wjxlib.py +88 -0
  28. pyxllib/ext/wpsapi.py +124 -0
  29. pyxllib/ext/xlwork.py +9 -0
  30. pyxllib/ext/yuquelib.py +1003 -71
  31. pyxllib/file/docxlib.py +1 -1
  32. pyxllib/file/libreoffice.py +165 -0
  33. pyxllib/file/movielib.py +9 -0
  34. pyxllib/file/packlib/__init__.py +112 -75
  35. pyxllib/file/pdflib.py +1 -1
  36. pyxllib/file/pupil.py +1 -1
  37. pyxllib/file/specialist/dirlib.py +1 -1
  38. pyxllib/file/specialist/download.py +10 -3
  39. pyxllib/file/specialist/filelib.py +266 -55
  40. pyxllib/file/xlsxlib.py +205 -50
  41. pyxllib/file/xlsyncfile.py +341 -0
  42. pyxllib/prog/cachetools.py +64 -0
  43. pyxllib/prog/filelock.py +42 -0
  44. pyxllib/prog/multiprogs.py +940 -0
  45. pyxllib/prog/newbie.py +9 -2
  46. pyxllib/prog/pupil.py +129 -60
  47. pyxllib/prog/specialist/__init__.py +176 -2
  48. pyxllib/prog/specialist/bc.py +5 -2
  49. pyxllib/prog/specialist/browser.py +11 -2
  50. pyxllib/prog/specialist/datetime.py +68 -0
  51. pyxllib/prog/specialist/tictoc.py +12 -13
  52. pyxllib/prog/specialist/xllog.py +5 -5
  53. pyxllib/prog/xlosenv.py +7 -0
  54. pyxllib/text/airscript.js +744 -0
  55. pyxllib/text/charclasslib.py +17 -5
  56. pyxllib/text/jiebalib.py +6 -3
  57. pyxllib/text/jinjalib.py +32 -0
  58. pyxllib/text/jsa_ai_prompt.md +271 -0
  59. pyxllib/text/jscode.py +159 -4
  60. pyxllib/text/nestenv.py +1 -1
  61. pyxllib/text/newbie.py +12 -0
  62. pyxllib/text/pupil/common.py +26 -0
  63. pyxllib/text/specialist/ptag.py +2 -2
  64. pyxllib/text/templates/echart_base.html +11 -0
  65. pyxllib/text/templates/highlight_code.html +17 -0
  66. pyxllib/text/templates/latex_editor.html +103 -0
  67. pyxllib/text/xmllib.py +76 -14
  68. pyxllib/xl.py +2 -1
  69. pyxllib-0.3.197.dist-info/METADATA +48 -0
  70. pyxllib-0.3.197.dist-info/RECORD +126 -0
  71. {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +1 -2
  72. pyxllib/ext/autogui/__init__.py +0 -8
  73. pyxllib-0.3.96.dist-info/METADATA +0 -51
  74. pyxllib-0.3.96.dist-info/RECORD +0 -333
  75. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  76. pyxlpr/ai/__init__.py +0 -5
  77. pyxlpr/ai/clientlib.py +0 -1281
  78. pyxlpr/ai/specialist.py +0 -286
  79. pyxlpr/ai/torch_app.py +0 -172
  80. pyxlpr/ai/xlpaddle.py +0 -655
  81. pyxlpr/ai/xltorch.py +0 -705
  82. pyxlpr/data/__init__.py +0 -11
  83. pyxlpr/data/coco.py +0 -1325
  84. pyxlpr/data/datacls.py +0 -365
  85. pyxlpr/data/datasets.py +0 -200
  86. pyxlpr/data/gptlib.py +0 -1291
  87. pyxlpr/data/icdar/__init__.py +0 -96
  88. pyxlpr/data/icdar/deteval.py +0 -377
  89. pyxlpr/data/icdar/icdar2013.py +0 -341
  90. pyxlpr/data/icdar/iou.py +0 -340
  91. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  92. pyxlpr/data/imtextline.py +0 -473
  93. pyxlpr/data/labelme.py +0 -866
  94. pyxlpr/data/removeline.py +0 -179
  95. pyxlpr/data/specialist.py +0 -57
  96. pyxlpr/eval/__init__.py +0 -85
  97. pyxlpr/paddleocr.py +0 -776
  98. pyxlpr/ppocr/__init__.py +0 -15
  99. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  100. pyxlpr/ppocr/data/__init__.py +0 -135
  101. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  102. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  103. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  104. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  105. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  106. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  107. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  108. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  109. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  110. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  111. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  112. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  113. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  114. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  115. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  116. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  117. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  118. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  119. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  120. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  121. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  122. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  123. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  124. pyxlpr/ppocr/losses/__init__.py +0 -61
  125. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  126. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  127. pyxlpr/ppocr/losses/center_loss.py +0 -88
  128. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  129. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  130. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  131. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  132. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  133. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  134. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  135. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  136. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  137. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  138. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  139. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  140. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  141. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  142. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  143. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  144. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  145. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  146. pyxlpr/ppocr/metrics/__init__.py +0 -44
  147. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  148. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  149. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  150. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  151. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  152. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  153. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  154. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  155. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  156. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  157. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  158. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  159. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  160. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  161. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  162. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  163. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  164. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  165. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  166. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  167. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  168. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  169. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  170. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  171. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  172. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  173. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  174. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  175. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  176. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  177. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  178. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  179. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  180. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  181. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  182. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  183. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  184. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  185. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  186. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  187. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  188. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  189. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  190. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  191. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  192. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  193. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  194. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  195. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  196. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  197. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  198. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  199. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  200. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  201. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  202. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  203. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  204. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  205. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  206. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  207. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  208. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  209. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  210. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  211. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  212. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  213. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  214. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  215. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  216. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  217. pyxlpr/ppocr/tools/__init__.py +0 -14
  218. pyxlpr/ppocr/tools/eval.py +0 -83
  219. pyxlpr/ppocr/tools/export_center.py +0 -77
  220. pyxlpr/ppocr/tools/export_model.py +0 -129
  221. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  222. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  223. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  224. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  225. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  226. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  227. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  228. pyxlpr/ppocr/tools/infer_det.py +0 -134
  229. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  230. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  231. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  232. pyxlpr/ppocr/tools/infer_table.py +0 -107
  233. pyxlpr/ppocr/tools/program.py +0 -596
  234. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  235. pyxlpr/ppocr/tools/train.py +0 -163
  236. pyxlpr/ppocr/tools/xlprog.py +0 -748
  237. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  238. pyxlpr/ppocr/utils/__init__.py +0 -24
  239. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  240. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  241. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  242. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  243. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  244. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  245. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  246. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  247. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  248. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  249. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  250. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  251. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  252. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  253. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  254. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  255. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  256. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  257. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  258. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  259. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  260. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  261. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  262. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  263. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  264. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  265. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  266. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  267. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  268. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  269. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  270. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  271. pyxlpr/ppocr/utils/dict90.txt +0 -90
  272. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  273. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  274. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  275. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  276. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  277. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  278. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  279. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  280. pyxlpr/ppocr/utils/gen_label.py +0 -81
  281. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  282. pyxlpr/ppocr/utils/iou.py +0 -54
  283. pyxlpr/ppocr/utils/logging.py +0 -69
  284. pyxlpr/ppocr/utils/network.py +0 -84
  285. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  286. pyxlpr/ppocr/utils/profiler.py +0 -110
  287. pyxlpr/ppocr/utils/save_load.py +0 -150
  288. pyxlpr/ppocr/utils/stats.py +0 -72
  289. pyxlpr/ppocr/utils/utility.py +0 -80
  290. pyxlpr/ppstructure/__init__.py +0 -13
  291. pyxlpr/ppstructure/predict_system.py +0 -187
  292. pyxlpr/ppstructure/table/__init__.py +0 -13
  293. pyxlpr/ppstructure/table/eval_table.py +0 -72
  294. pyxlpr/ppstructure/table/matcher.py +0 -192
  295. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  296. pyxlpr/ppstructure/table/predict_table.py +0 -221
  297. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  298. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  299. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  300. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  301. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  302. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  303. pyxlpr/ppstructure/utility.py +0 -71
  304. pyxlpr/xlai.py +0 -10
  305. /pyxllib/{ext/autogui → autogui}/virtualkey.py +0 -0
  306. {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/prog/newbie.py CHANGED
@@ -28,7 +28,7 @@ class SingletonForEveryClass(type):
28
28
 
29
29
 
30
30
  class SingletonForEveryInitArgs(type):
31
- """Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
31
+ """ Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
32
32
 
33
33
  注意!注意!注意!重要的事说三遍!
34
34
  这里的单例类不是传统意义上的单例类。
@@ -259,6 +259,9 @@ def human_readable_number(value, base_type='K', precision=4):
259
259
  :param str base_type: 进制类型,'K'为1000进制, 'KB'为1024进制(KiB同理), '万'为中文万进制
260
260
  :return: 美化后的字符串
261
261
  """
262
+ if value is None:
263
+ return ''
264
+
262
265
  if abs(value) < 1:
263
266
  return f'{value:.{precision}g}'
264
267
 
@@ -267,13 +270,17 @@ def human_readable_number(value, base_type='K', precision=4):
267
270
  'K': (['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 1000),
268
271
  'KB': (['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024),
269
272
  'KiB': (['', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'], 1024),
270
- '万': (['', '万', '亿', '万亿', '亿亿'], 10000)
273
+ '万': (['', '万', '亿', '万亿', '亿亿'], 10000),
274
+ '秒': (['秒', [60, '分'], [60, '时'], [24, '天'], [7, '周'], [4.345, '月'], [12, '年']], 60),
271
275
  }.get(base_type, ([''], 1)) # 默认为空单位和基数1
272
276
 
273
277
  x, i = abs(value), 0
274
278
  while x >= base and i < len(units) - 1:
275
279
  x /= base
276
280
  i += 1
281
+ if isinstance(units[i], list):
282
+ base = units[i][0]
283
+ units[i] = units[i][1]
277
284
 
278
285
  x = f'{x:.{precision}g}' # 四舍五入到指定精度
279
286
  prefix = '-' if value < 0 else '' # 负数处理
pyxllib/prog/pupil.py CHANGED
@@ -6,10 +6,11 @@
6
6
 
7
7
 
8
8
  """ 封装一些代码开发中常用的功能,工程组件 """
9
- import builtins
10
- from collections import Counter, UserDict
9
+
10
+ from collections import Counter
11
11
  from concurrent.futures import ThreadPoolExecutor
12
- import ctypes
12
+ from urllib.parse import urlparse
13
+ import builtins
13
14
  import datetime
14
15
  import functools
15
16
  import hashlib
@@ -23,20 +24,22 @@ import os
23
24
  import pprint
24
25
  import queue
25
26
  import random
27
+ import re
26
28
  import signal
27
29
  import socket
28
- import socketserver
29
30
  import subprocess
30
31
  import sys
31
32
  import tempfile
32
33
  import threading
33
34
  import time
34
35
  import traceback
35
- from urllib.parse import urlparse
36
36
 
37
37
  from pyxllib.prog.newbie import classproperty, typename
38
38
 
39
39
 
40
+ # from loguru import logger
41
+
42
+
40
43
  def system_information():
41
44
  """主要是测试一些系统变量值,顺便再演示一次Timer用法"""
42
45
 
@@ -554,6 +557,9 @@ class Timeout:
554
557
  return wrapper
555
558
 
556
559
  def __enter__(self):
560
+ if self.seconds == 0: # 可以设置0来关闭超时功能
561
+ return
562
+
557
563
  def overtime(signum, frame):
558
564
  raise TimeoutError(f'with 上下文代码块运行超时 > [{self.seconds} 秒]')
559
565
 
@@ -567,6 +573,9 @@ class Timeout:
567
573
  self.alarm.start()
568
574
 
569
575
  def __exit__(self, exc_type, exc_val, exc_tb):
576
+ if self.seconds == 0:
577
+ return
578
+
570
579
  # with已经运行完了,马上关闭警告器
571
580
  self.alarm.cancel()
572
581
 
@@ -595,6 +604,7 @@ def inject_members(from_obj, to_obj, member_list=None, *,
595
604
  # 把XlDocxTable的成员方法绑定到docx.table.Table里
596
605
  >> inject_members(XlDocxTable, docx.table.Table)
597
606
 
607
+ 240826周一,其他可参考学习的三方现成工具库:from fastcore.foundation import patch
598
608
  """
599
609
  # 1 整理需要注入的方法清单
600
610
  dst = set(dir(to_obj))
@@ -622,20 +632,6 @@ def inject_members(from_obj, to_obj, member_list=None, *,
622
632
  logging.warning(f'Conflict of the same name! {to_obj}.{x}')
623
633
 
624
634
 
625
- def find_free_ports(count=1):
626
- """ 随机获得可用端口
627
-
628
- :param count: 需要的端口数量(会保证给出的端口号不重复)
629
- :return: list
630
- """
631
- ports = set()
632
- while len(ports) < count:
633
- with socketserver.TCPServer(("localhost", 0), None) as s:
634
- ports.add(s.server_address[1])
635
-
636
- return list(ports)
637
-
638
-
639
635
  def __debug系列():
640
636
  pass
641
637
 
@@ -972,47 +968,6 @@ class OutputLogger(logging.Logger):
972
968
  self.print(msg)
973
969
 
974
970
 
975
- class MultiProcessLauncher:
976
- """ 注意这个类暂时只有linux能使用 """
977
-
978
- def __init__(self):
979
- self.workers = []
980
-
981
- def add_process(self, cmd, name=None, **kwargs):
982
- if name is None:
983
- name = cmd.split()[0]
984
-
985
- p = subprocess.Popen(cmd.split(), preexec_fn=self._set_pdeathsig(signal.SIGTERM))
986
- worker = {'name': name, 'process': p, 'command': cmd}
987
- worker.update(kwargs)
988
- self.workers.append(worker)
989
-
990
- def add_python_module(self, module, args='', name=None):
991
- """ 添加并启动一个Python模块作为后台进程。
992
-
993
- :param module: 要执行的Python模块名(python -m 后面的部分)
994
- :param args: 模块的参数
995
- :param name: 进程的名称,默认为模块名
996
- """
997
- cmd = f'{sys.executable} -m {module} {args}'
998
- self.add_process(cmd, name=name)
999
-
1000
- def stop_all(self):
1001
- """ 停止所有后台进程 """
1002
- for worker in self.workers:
1003
- worker.process.terminate()
1004
- self.workers = []
1005
-
1006
- def _set_pdeathsig(self, sig=signal.SIGTERM):
1007
- """ 在主服务退出时,这些进程也会全部自动关闭 """
1008
-
1009
- def callable():
1010
- libc = ctypes.CDLL("libc.so.6")
1011
- return libc.prctl(1, sig)
1012
-
1013
- return callable
1014
-
1015
-
1016
971
  def xlmd5(content):
1017
972
  if isinstance(content, str):
1018
973
  content = content.encode('utf-8')
@@ -1095,6 +1050,8 @@ def safe_div(a, b):
1095
1050
 
1096
1051
  def inplace_decorate(parent, func_name, wrapper):
1097
1052
  """ 将指定的函数替换为装饰器版本
1053
+ 允许在运行时动态地将一个函数或方法替换为其装饰版本。通常用于添加日志、性能测试、事务处理等。
1054
+ (既然可以写成装饰版本,相当于其实要完全替换成另外的函数也是可以的)
1098
1055
 
1099
1056
  当然,因为py一切皆对象,这里处理的不是函数,而是其他变量等对象也是可以的
1100
1057
 
@@ -1126,3 +1083,115 @@ def inplace_decorate(parent, func_name, wrapper):
1126
1083
  return wrapper(original_func, *args, **kwargs)
1127
1084
 
1128
1085
  parent[func_name] = decorated_func
1086
+
1087
+
1088
+ def check_counter(data, top_n=10):
1089
+ """ 将一个数据data转为Counter进行频数分析 """
1090
+ # 1 如果是list、tuple类型,需要转counter
1091
+ if isinstance(data, (list, tuple)):
1092
+ data = Counter(data)
1093
+ if not isinstance(data, Counter):
1094
+ raise ValueError(f'输入的数据类型不对,应该是Counter类型,而不是{typename(data)}')
1095
+
1096
+ # 2 列出出现次数最多的top_n条目
1097
+ # 打印基本统计信息
1098
+ total_items = sum(data.values())
1099
+ print(f"总条目数: {total_items}")
1100
+
1101
+ if top_n > 0:
1102
+ top_items = data.most_common(top_n)
1103
+ max_n = len(data)
1104
+ print(f"出现次数最多的{min(top_n, max_n)}/{max_n}条数据(频率):")
1105
+ for item, count in top_items:
1106
+ print(f"\t{item}\t{count}")
1107
+
1108
+ # 3 打印基本统计信息
1109
+ # 对原始Counter的计数值进行再计数
1110
+ count_frequencies = Counter(data.values())
1111
+
1112
+ # 打印各计数值出现的次数
1113
+ print("各计数值出现的次数,频率的频率(频率分布):")
1114
+ for count, frequency in count_frequencies.most_common():
1115
+ print(f"\t{count}\t{frequency}")
1116
+
1117
+
1118
+ def tprint(*args, **kwargs):
1119
+ """ 带时间戳的print """
1120
+ print(utc_now2(), *args, **kwargs)
1121
+
1122
+
1123
+ def is_valid_identifier(name):
1124
+ """ 判断是否是合法的标识符 """
1125
+ return re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name)
1126
+
1127
+
1128
+ def get_number_width(n):
1129
+ """ 判断数值n的长度
1130
+
1131
+ 参考资料:https://jstrieb.github.io/posts/digit-length/
1132
+
1133
+ >>> get_number_width(0)
1134
+ 1
1135
+ >>> get_number_width(9)
1136
+ 1
1137
+ >>> get_number_width(10)
1138
+ 2
1139
+ >>> get_number_width(97)
1140
+ 2
1141
+ """
1142
+ # assert n > 0
1143
+ # return math.ceil(math.log10(n + 1))
1144
+
1145
+ return 1 if n == 0 else (math.floor(math.log10(n)) + 1)
1146
+
1147
+
1148
+ def aligned_range(start, stop=None, step=1):
1149
+ """ 返回按照域宽对齐的数字序列 """
1150
+ if stop is None:
1151
+ start, stop = 0, start
1152
+
1153
+ max_width = get_number_width(stop - step)
1154
+ format_str = '{:0' + str(max_width) + 'd}'
1155
+
1156
+ return [format_str.format(i) for i in range(start, stop, step)]
1157
+
1158
+
1159
+ def percentage_and_value(numbers, precision=2, *, total=None, sep='.'):
1160
+ """ 对输入的一串数值,转换成一种特殊的表达格式 "百分比.次数"
1161
+
1162
+ :param list numbers: 数值列表
1163
+ :param int precision: 百分比的精度(小数点后的位数),默认为 2
1164
+ :param int total: 总数,如果不输入,则默认为输入数值的和
1165
+ :param str sep: 分隔符
1166
+ :return: 整数部分是比例,小数部分是原始数值的整数部分
1167
+ """
1168
+ if total is None:
1169
+ total = sum(numbers)
1170
+ width = get_number_width(total)
1171
+ result = []
1172
+ for num in numbers:
1173
+ percent = safe_div(num, total) * 10 ** precision
1174
+ result.append(f"{percent:.0f}{sep}{num:0{width}d}")
1175
+ return result
1176
+
1177
+
1178
+ def get_local_ip():
1179
+ """ 获得本地ip,代码由deepseek提供 """
1180
+ try:
1181
+ # 使用UDP协议连接到外部服务器
1182
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
1183
+ s.connect(("8.8.8.8", 80)) # Google的DNS服务器和常用端口
1184
+ local_ip = s.getsockname()[0] # 获取套接字的本地地址
1185
+ s.close()
1186
+ return local_ip
1187
+ except Exception as e:
1188
+ # 如果失败,尝试通过主机名获取IP列表
1189
+ try:
1190
+ hostname = socket.gethostname()
1191
+ ips = socket.gethostbyname_ex(hostname)[2] # 获取所有IPv4地址
1192
+ for ip in ips:
1193
+ if ip != "127.0.0.1": # 排除回环地址
1194
+ return ip
1195
+ return "127.0.0.1" # 默认返回回环地址
1196
+ except:
1197
+ raise ValueError("无法获取IP地址")
@@ -25,7 +25,7 @@ import requests
25
25
  from humanfriendly import parse_size
26
26
 
27
27
  from pyxllib.prog.newbie import human_readable_size
28
- from pyxllib.prog.pupil import get_installed_packages
28
+ from pyxllib.prog.pupil import get_installed_packages, aligned_range, percentage_and_value
29
29
  from pyxllib.prog.xlosenv import XlOsEnv
30
30
  from pyxllib.file.specialist import cache_file
31
31
 
@@ -102,6 +102,7 @@ def distribute_package(root, version=None, repository=None, *,
102
102
  :param version_file: 保存版本号的文件,注意看正则规则,需要满足特定的范式,才会自动更新版本号
103
103
  :param delete_dist: 上传完是否自动删除dist目录,要检查上传包是否有遗漏时,要关闭
104
104
  """
105
+ import sys
105
106
  from pyxllib.file.specialist import XlPath
106
107
 
107
108
  # 1 切换工作目录
@@ -114,7 +115,7 @@ def distribute_package(root, version=None, repository=None, *,
114
115
  f.write_text(s)
115
116
 
116
117
  # 3 打包
117
- subprocess.run('python setup.py sdist bdist_wheel')
118
+ subprocess.run(f'{sys.executable} setup.py sdist')
118
119
 
119
120
  # 4 上传
120
121
  if upload:
@@ -215,3 +216,176 @@ class ProgressBar:
215
216
  time.sleep(1)
216
217
  pbar.n = self.progress
217
218
  pbar.refresh()
219
+
220
+
221
+ class BitMaskTool:
222
+ """ 二进制位掩码工具
223
+
224
+ 概念术语
225
+ bitval,每一个位上的具体取值,0或1
226
+ bitsum,所有位上的取值的和,即二进制数的十进制表示
227
+ """
228
+
229
+ def __init__(self, bit_names=None, bitsum_counter=None):
230
+ """ 初始化 BitMaskTool 对象
231
+
232
+ :param list bit_names: 每一位功能开启时,显示的标签名。未输入时,填充0,1,2,3...注意总数,要按域宽对齐
233
+ :param dict/list bitsum_counter: 各种值出现的次数,可以是一个字典或者一个包含出现次数的列表
234
+ """
235
+ # 1 每一位功能开启时,显示的标签名。未输入时,填充0,1,2,3...注意总数,要按域宽对齐
236
+ if bit_names is None:
237
+ bit_names = list(aligned_range(len(bit_names)))
238
+ self.bit_names = bit_names
239
+ # 2 各种值出现的次数
240
+ if isinstance(bitsum_counter, (list, tuple)):
241
+ bitsum_counter = Counter(bitsum_counter)
242
+ self.bitsum_counter = bitsum_counter or {}
243
+
244
+ def get_bitsum_names(self, bitsum):
245
+ """ 从bitsum得到names的拼接
246
+
247
+ >> get_bitsum_names(3)
248
+ '语义定位,图表'
249
+ """
250
+ if not isinstance(bitsum, int):
251
+ try:
252
+ bitsum = int(bitsum)
253
+ except ValueError:
254
+ bitsum = 0
255
+
256
+ tags = []
257
+ for i, k in enumerate(self.bit_names):
258
+ if (1 << i) & bitsum:
259
+ tags.append(k)
260
+ return ','.join(tags)
261
+
262
+ def count_bitsum_relations(self, target_bitsum, relation='='):
263
+ """ 计算特定关系的 bitsum 数量
264
+
265
+ :param int target_bitsum: 目标 bitsum
266
+ :param str relation: 关系类型,可以是 '=', '⊂', '⊃'
267
+ 假设bitval对应的bit_names为n1,n2,n3,n4。
268
+ 那么bitsum相当于是bitval的一个集合
269
+ 比如a={n1,n3,n4},b={n1,n3},因为a完全包含b,所以认为a⊃b,或者a⊋b、b⊂a、b⊊a
270
+ :return int: 符合条件的 bitsum 数量
271
+ """
272
+ count = 0
273
+ if relation == '=':
274
+ # 直接计算等于 target_bitsum 的数量
275
+ count = self.bitsum_counter.get(target_bitsum, 0)
276
+ elif relation == '⊂':
277
+ # 计算所有被 target_bitsum 包含的 bitsum 的数量
278
+ for bitsum, num in self.bitsum_counter.items():
279
+ if bitsum and bitsum & target_bitsum == bitsum:
280
+ count += num
281
+ elif relation == '⊃':
282
+ # 计算所有包含 target_bitsum 的 bitsum 的数量
283
+ for bitsum, num in self.bitsum_counter.items():
284
+ if bitsum & target_bitsum == target_bitsum:
285
+ count += num
286
+ return count
287
+
288
+ def check_bitflag(self, max_bitsum_len=None, reletion='=',
289
+ filter_zero=False, sort_by=None, *,
290
+ min_bitsum_len=0):
291
+ """ 检查并返回 bitsum 关系的 DataFrame
292
+
293
+ :param int max_bitsum_len: 最大 bitsum 长度
294
+ :param str reletion: 关系类型,可以是 '=', '⊂', '⊃'
295
+ 支持输入多个字符,表示要同时计算多种关系
296
+ :param bool filter_zero: 是否过滤掉零值
297
+ :param None|str sort_by: 排序字段
298
+ None, 默认排序
299
+ count, 按照数量从大到小排序
300
+ bitsum, 按照 bitsum 从小到大排序
301
+ :param int min_bitsum_len: 最小 bitsum 长度
302
+ :return: 包含 bitsum 关系的 DataFrame
303
+ """
304
+ from itertools import combinations
305
+
306
+ total = sum(self.bitsum_counter.values())
307
+ rows, columns = [], ['类型', '名称', '百分比.次数']
308
+ rows.append([-1, '总计', total])
309
+
310
+ if max_bitsum_len is None:
311
+ max_bitsum_len = len(self.bit_names)
312
+
313
+ bitvals = [(1 << i) for i in range(len(self.bit_names))]
314
+ for m in range(min_bitsum_len, max_bitsum_len + 1):
315
+ for comb in combinations(bitvals, m):
316
+ bitsum = sum(comb)
317
+ count = self.count_bitsum_relations(bitsum, relation=reletion)
318
+ if filter_zero and count == 0:
319
+ continue
320
+ rows.append([f'{reletion}{bitsum}',
321
+ self.get_bitsum_names(bitsum),
322
+ count])
323
+
324
+ if sort_by == 'count':
325
+ rows.sort(key=lambda x: x[2], reverse=True)
326
+ elif sort_by == 'bitsum':
327
+ rows.sort(key=lambda x: int(x[0][1:]) if isinstance(x[0], str) else x[0])
328
+
329
+ df = pd.DataFrame.from_records(rows, columns=columns)
330
+ df['百分比.次数'] = percentage_and_value(df['百分比.次数'], 2, total=total)
331
+ return df
332
+
333
+ def report(self):
334
+ """ 生成统计报告 """
335
+ html_content = []
336
+
337
+ html_content.append('<h1>1 包含每一位bitval特征的数量</h1>')
338
+ df1 = self.check_bitflag(1, '⊃')
339
+ html_content.append(df1.to_html())
340
+
341
+ html_content.append('<h1>2 每一种具体bitsum组合的数量</h1>')
342
+ df2 = self.check_bitflag(reletion='=', filter_zero=True, sort_by='bitsum')
343
+ html_content.append(df2.to_html())
344
+
345
+ return '\n'.join(html_content)
346
+
347
+
348
+ def loguru_setup_jsonl_logfile(logger, log_dir, rotation_size="10 MB"):
349
+ """
350
+ 给loguru的日志器添加导出文件的功能,使用jsonl格式
351
+
352
+ :param logger: 日志记录器,一般是from loguru import logger的logger
353
+ :param log_dir: 存储日志的目录,因为有多个文件,这里要输入的是所在的目录
354
+ :param rotation_size: 文件多大后分割
355
+ :return:
356
+ """
357
+ from datetime import datetime
358
+
359
+ os.makedirs(log_dir, exist_ok=True) # 自动创建日志目录
360
+
361
+ # 日志文件名匹配的正则表达式,格式为 年月日_时分秒.log
362
+ log_filename_pattern = re.compile(r"(\d{8}_\d{6})\.jsonl")
363
+
364
+ # 找到最新的日志文件
365
+ def find_latest_log_file(log_dir):
366
+ log_files = []
367
+ for file in os.listdir(log_dir):
368
+ if log_filename_pattern.match(file):
369
+ log_files.append(file)
370
+
371
+ if log_files:
372
+ # 根据时间排序,选择最新的日志文件
373
+ log_files.sort(reverse=True)
374
+ return os.path.join(log_dir, log_files[0])
375
+ return None
376
+
377
+ # 检查是否有未写满的日志文件
378
+ latest_log_file = find_latest_log_file(log_dir)
379
+
380
+ if latest_log_file:
381
+ log_path = latest_log_file
382
+ else:
383
+ # 生成新的日志文件名
384
+ log_filename = datetime.now().strftime("%Y%m%d_%H%M%S") + ".jsonl"
385
+ log_path = os.path.join(log_dir, log_filename)
386
+
387
+ # 配置 logger,写入日志文件,设置旋转条件,使用 JSON 序列化
388
+ logger.add(log_path, rotation=rotation_size, serialize=True)
389
+
390
+ # 输出初始化成功信息
391
+ logger.info(f"日志系统已初始化,日志文件路径:{log_path}")
@@ -16,7 +16,10 @@ from pyxllib.file.specialist import File, Dir, filesmatch, get_encoding, XlPath
16
16
  # BCompare.exe, bcompare函数要用
17
17
 
18
18
  class BCompare(Explorer):
19
- def __init__(self, app='BCompare', shell=False):
19
+ def __init__(self, app='bcomp', shell=False):
20
+ """
21
+ 240512周日20:06,本来写的是BCompare,但是友鑫mac电脑上发现似乎有问题,所以改成bcomp,这种在windows上也能用
22
+ """
20
23
  super().__init__(app, shell)
21
24
 
22
25
  @classmethod
@@ -48,7 +51,7 @@ class BCompare(Explorer):
48
51
  default_suffix = None
49
52
  for i, arg in enumerate(args):
50
53
  f = XlPath.safe_init(arg)
51
- if f.is_file(): # 是文件对象,且存在
54
+ if f is not None and f.is_file(): # 是文件对象,且存在
52
55
  new_args.append(f)
53
56
  if not default_suffix:
54
57
  default_suffix = f.suffix
@@ -13,6 +13,7 @@ import subprocess
13
13
  import sys
14
14
  import datetime
15
15
  import platform
16
+ import re
16
17
 
17
18
  import pandas as pd
18
19
  from bs4 import BeautifulSoup
@@ -118,9 +119,12 @@ class Explorer:
118
119
  TODO 获得返回值分析
119
120
  """
120
121
  args = [self.app] + list(args)
122
+
121
123
  if 'shell' not in kwargs:
122
124
  kwargs.update({'shell': self.shell})
123
-
125
+ if re.match(r'open\s', self.app):
126
+ args = args[0] + ' ' + args[1]
127
+ kwargs.update({'shell': True})
124
128
  try:
125
129
  if wait:
126
130
  subprocess.run(args, **kwargs)
@@ -161,6 +165,7 @@ class Browser(Explorer):
161
165
  # 这里默认设置为 'google-chrome',如果你想使用其他的浏览器,例如Firefox,可以修改为 'firefox'
162
166
  app = 'google-chrome'
163
167
  else:
168
+ app = 'open -a "Google Chrome"'
164
169
  # 其他系统的处理
165
170
  pass
166
171
  super().__init__(app, shell)
@@ -225,6 +230,9 @@ class Browser(Explorer):
225
230
  file = file.rename(get_etag(str(file)) + file.suffix, if_exists='replace')
226
231
  self.__call__(arg, file, **kwargs)
227
232
 
233
+ def url(self, *args, wait=True, **kwargs):
234
+ super().__call__(*args, wait=wait, **kwargs)
235
+
228
236
  def __call__(self, arg, file=None, *, wait=True, clsmsg=True, to_html_args=None,
229
237
  **kwargs): # NOQA Browser的操作跟标准接口略有差异
230
238
  """ 该版本会把arg转存文件重设为文件名
@@ -232,7 +240,8 @@ class Browser(Explorer):
232
240
  :param file: 默认可以不输入,会按七牛的etag哈希值生成临时文件
233
241
  如果输入,则按照指定的名称生成文件
234
242
  """
235
- if XlPath.safe_init(arg).is_file():
243
+ f = XlPath.safe_init(arg)
244
+ if f is not None and f.is_file():
236
245
  file = arg
237
246
  else:
238
247
  file = str(self.to_brower_file(arg, file, clsmsg=clsmsg, to_html_args=to_html_args))
@@ -16,6 +16,8 @@ a = a - a.isoweekday() + kwargs['weekday'] # 先减去当前星期几,再加
16
16
  import datetime
17
17
  import re
18
18
 
19
+ from fastcore.utils import GetAttr
20
+
19
21
 
20
22
  def parse_datetime(*argv):
21
23
  """ 解析字符串日期时间
@@ -29,6 +31,9 @@ def parse_datetime(*argv):
29
31
  >>> parse_datetime('w200301周日', 'w%y%m%d周日') # 周日必须写全,有缺失会报ValueError
30
32
  datetime.datetime(2020, 3, 1, 0, 0)
31
33
 
34
+ >>> parse_datetime('2019.3.6 22:30:40')
35
+ datetime.datetime(2019, 3, 6, 22, 30, 40)
36
+
32
37
  >>> parse_datetime(180213)
33
38
  datetime.datetime(2018, 2, 13, 0, 0)
34
39
  >>> parse_datetime('180213')
@@ -99,6 +104,11 @@ def parse_datetime(*argv):
99
104
  # 1 没有参数则默认当前运行时间
100
105
  if not argv:
101
106
  dt = datetime.datetime.now()
107
+ if not dt and isinstance(argv[0], datetime.datetime):
108
+ dt = argv[0]
109
+ if not dt and isinstance(argv[0], datetime.date):
110
+ # 要转成datetime类型,time部分默认00:00:00
111
+ dt = datetime.datetime.combine(argv[0], datetime.time())
102
112
  if not dt and isinstance(argv[0], float):
103
113
  dt = datetime.datetime.fromtimestamp(argv[0])
104
114
  # 2 如果上述解析不了,且argv恰好为两个参数,则判断为使用strptime初始化
@@ -129,3 +139,61 @@ def parse_timedelta(s):
129
139
  d = {k: int(v) for k, v in zip(['seconds', 'minutes', 'hours'], parts)}
130
140
  td = datetime.timedelta(**d)
131
141
  return td
142
+
143
+
144
+ class XlWeekTag(GetAttr):
145
+ """ 个人周标签转换工具 """
146
+ _default = 'dt'
147
+
148
+ def __init__(self, dt=None):
149
+ """
150
+ :param datetime|其他类日期表达 dt: 可以输入一个日期,默认为今天
151
+ """
152
+ if dt is None:
153
+ dt = datetime.date.today()
154
+ self.dt = parse_datetime(dt)
155
+
156
+ def __1_日期移动(self):
157
+ pass
158
+
159
+ def add_days(self, days):
160
+ """ 增加指定天数,支持负数 """
161
+ return self.__class__(self.dt + datetime.timedelta(days=days))
162
+
163
+ def monday(self):
164
+ """ 移动到本周周一 """
165
+ return self.add_days(-self.dt.weekday())
166
+
167
+ def next_week(self):
168
+ """ 移动到下一周 """
169
+ return self.add_days(7)
170
+
171
+ def prev_week(self):
172
+ """ 移动到上一周 """
173
+ return self.add_days(-7)
174
+
175
+ def __2_生成标签(self):
176
+ pass
177
+
178
+ def weektag(self):
179
+ """
180
+ :return: 周标签名,例如 'w250414',表示所属周的周一是2025年4月14日
181
+ """
182
+ monday = self.monday() # 获取本周周一
183
+ tag = 'w' + monday.strftime('%y%m%d')
184
+ return tag
185
+
186
+ def daytag(self):
187
+ """
188
+ :return: 日标签名,例如 '250415周二',表示当天的日期标记,一般是语雀周报中使用
189
+ """
190
+ ch = '一二三四五六日'[self.dt.weekday()]
191
+ tag = self.dt.strftime('%y%m%d') + '周' + ch
192
+ return tag
193
+
194
+ def week_daytags(self):
195
+ # 循环获得本周每天的daytag
196
+ monday = self.monday()
197
+ tags = [monday.add_days(i).daytag() for i in range(7)]
198
+ return tags
199
+