Wechat File Schedule Box

该项目利用Wechaty库创建了一个微信文件日程匣子,自动将微信中的文件同步到其他网盘,并识别处理日程。Wechaty支持多种微信接入方式,包括网页、iPad等。代码结构清晰,包括文件处理、日程识别、数据库交互等功能,结合Webdav实现文件同步,通过Time-NLP进行日程解析。此工具提高了微信文件管理和日程整理的效率。

使用Wechaty以少量的代码实现一个微信文件日程匣子项目。可快速将微信中的内容同步(转存)到其他网盘中。同时还添加自动识别处理日程等的小功能。

关于Wechaty

官方定义:

Wechaty是一个开源的的个人号微信机器人接口,使用Typescript构建的Node.js应用。支持多种微信接入方案,包括网页,ipad,ios,windows,android 等。同时支持 Linux, Windows, Darwin(OSX/Mac) 和 Docker 多个平台。

一句话概括——是一个能模拟登录微信并收发微信信息、添加自定义处理逻辑的库。值得注意的是其中的模拟登录,可以是模拟Web端登录(Wechaty提供免费支持),也可以是模拟Mac、iPad端登录(Wechaty以收费的云服务形式提供,但是可以申请成为开发者获取使用权限)。

项目背景

在使用微信作为主力办公工具的过程中,经常令人头大的问题之一就是处理文件,不论是同事之间互传文件或者是群聊里发送文件,要统一归档到网盘同步都是很麻烦的事情(尤其在iOS端微信,只能挨个打开在选择用其他应用打开)。因此希望实现一个能自动归档文件的效率小工具。

实现思路

由于Wechaty提供了很方便的File API,通过fileMsg.toFileBox方法就能获取一个消息的文件,因此通过“收到多个文件-转发到文件匣子(一个微信号)-转存到其他位置”的流程,就能实现把文件多选转发到文件匣子,然后自动归档到网盘的效果。

此外,如果msg.type方法还能判断消息类型,从而可以对应日程文本使用正则匹配日期的NLP方法,将转发到匣子的日程文本识别出来存到数据库中供后续处理;如果是URL分享类型的消息,也可以识别出来并统一存储。

代码结构

代码仓库已开源

目录结构如下:

├── LICENSE

├── Node

│ ├── coursHandler.js

│ ├── databasesConfig.js

│ ├── databases.js

│ ├── fileHandler.js

│ ├── functions.js

│ ├── httpServer.js

│ ├── main.js

│ ├── menuHandler.js

│ ├── scheduleHandler.js

│ ├── test.js

│ └── webdav.js

├── package.json

├── package-lock.json

├── public

│ └── cours.json

├── README.md

├── RunDir

├── RunRobot.sh

├── template

│ └── template.html

└── Time-NLP

├── pom.xml
 
├── README.md
 
├── resource
 
├── src
 
└── target

主要代码位于Node/下。其中:

  • main.js初始化Wechaty实例并判断收到消息的类型
  • databases.js和databasesConfig.js配置数据库表结构和连接数据库
  • webdav.js配置node的webdav模块,通过Webdav连接到个人网盘
  • menuHandler.js, scheduler.js, fileHandler.js, coursHandler.js分别处理控制消息、日程消息、文件消息、课程表查询消息
  • httpServer.js, template.html, cours.json则实现了一个简易的webpage用于网页端访问这些信息。

Time-NLP/中的源代码编译后,能够得到class字节码供node的java模块调用,从而实现从含有日程的文本信息解析出日期。

RunDir/的存在主要是因为msg.toFileBox方法会在运行目录保存文件,故在此目录下暂存这些文件。

结语

众所周知微信作为我们每天都在使用的工具,在文件管理等方面较QQ等其他工具功能过于单一,因此可以通过Wechaty搭建一个文件匣子,给微信加上文件自动同步网盘、日程自动收集等小功能,让日常办公更加效率:)

# -*- coding: utf-8 -*- import os import cx_Oracle as cx import pandas as pd import datetime as dt import schedule import time import requests from threading import Thread import base64 import hashlib import matplotlib.pyplot as plt import numpy as np from matplotlib.patches import Rectangle class Wechat: def __init__(self, secret): self.secret = secret def access_token(self): """获取企业微信access_token""" url = f'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=wwed5e7f3fd1a3a553&corpsecret={self.secret}' response = requests.get(url).json() return response['access_token'] def oracle_connect(sql): """Oracle数据库连接查询""" con = cx.connect("admin/xxb20140415@192.168.3.16:1521/zzjdata") cursor = con.cursor() cursor.execute(sql) column_s = [col[0] for col in cursor.description] data = cursor.fetchall() cursor.close() con.close() return data, column_s def generate_store_performance_sql(): """生成门店绩效环比数据SQL""" sql = """ select a.c_mdfq 大区,a.c_dq 地市,a.c_mdfq1 地区,a.tjbh 门店编码,a.mc 门店名称,b.lb 类别,nvl(b.jshj,0) 本周销售,nvl(b.ml,0) 本周毛利,nvl(c.jshj,0) 上周销售,nvl(c.ml,0) 上周毛利 from (select a.subbh,nvl(e.sx,'未分类') lb,sum(a.jshj) jshj,sum(a.zhml) ml from c_zkmtmdspxshzb a left join (select * from spjxlbz where lbz='1') e on e.hh=a.hh where a.kdrq BETWEEN TRUNC(SYSDATE, 'IW') AND TRUNC(SYSDATE-1) group by a.subbh,nvl(e.sx,'未分类')) b left join (select a.subbh,nvl(e.sx,'未分类') lb,sum(a.jshj) jshj,sum(a.zhml) ml from c_zkmtmdspxshzb a left join (select * from spjxlbz where lbz='1') e on e.hh=a.hh where a.kdrq BETWEEN TRUNC(SYSDATE, 'IW') - 7 AND TRUNC(SYSDATE- 1) - 7 group by a.subbh,nvl(e.sx,'未分类')) c on b.subbh=c.subbh and b.lb=c.lb left join gl_custom a on a.tjbh=b.subbh and a.tjbh=c.subbh where a.c_mdfq <>' 'and a.c_dq<>' ' and a.c_mdfq1<>' ' and a.c_mdfq <>'电商' and a.c_mdfq <>'大客户部' and a.c_mdfq <>'海南地区' and a.c_mdfq <>'黑龙江地区' """ return sql def calculate_growth_rate(current_value, last_value): """计算环比增长率""" if last_value == 0: return 0 return round((current_value - last_value) / last_value * 100, 2) def calculate_profit_margin(sales, profit): """计算毛利率""" if sales == 0: return 0 return round(profit / sales * 100, 2) def format_growth_rate(growth_rate): """格式化环比增长率显示:负数加负号,正数不加正号""" if growth_rate > 0: return f"{growth_rate:.2f}%" elif growth_rate < 0: return f"{growth_rate:.2f}%" else: return "0.00%" def transform_data_for_region_comparison(df): """转换数据为大区环比对比表格式(转置后)- 合并类别项,并添加合计列""" try: print(f"原始数据行数: {len(df)}") print(f"大区列表: {sorted(df['大区'].unique())}") print(f"类别列表: {sorted(df['类别'].unique())}") categories = sorted(df['类别'].unique()) regions = sorted(df['大区'].unique()) result_data = [] # 全国总计 total_current_sales = df['本周销售'].sum() total_last_sales = df['上周销售'].sum() total_current_profit = df['本周毛利'].sum() total_last_profit = df['上周毛利'].sum() total_sales_growth = calculate_growth_rate(total_current_sales, total_last_sales) total_profit_growth = calculate_growth_rate(total_current_profit, total_last_profit) total_current_margin = calculate_profit_margin(total_current_sales, total_current_profit) total_last_margin = calculate_profit_margin(total_last_sales, total_last_profit) total_margin_growth = calculate_growth_rate(total_current_margin, total_last_margin) # 为每个类别处理 for category in categories: cat_df = df[df['类别'] == category] # 类别内全国数据 cat_current_sales = cat_df['本周销售'].sum() cat_last_sales = cat_df['上周销售'].sum() cat_current_profit = cat_df['本周毛利'].sum() cat_last_profit = cat_df['上周毛利'].sum() cat_sales_growth = calculate_growth_rate(cat_current_sales, cat_last_sales) cat_profit_growth = calculate_growth_rate(cat_current_profit, cat_last_profit) cat_current_margin = calculate_profit_margin(cat_current_sales, cat_current_profit) cat_last_margin = calculate_profit_margin(cat_last_sales, cat_last_profit) cat_margin_growth = calculate_growth_rate(cat_current_margin, cat_last_margin) # 销售、毛利、毛利率三行(用于表格) sales_row = {'类别': category, '指标': '销售环比', 'row_type': 'category_start'} profit_row = {'类别': '', '指标': '毛利环比', 'row_type': 'category_middle'} margin_row = {'类别': '', '指标': '毛利率环比', 'row_type': 'category_end'} # 计算每个大区 + 合计 for region in regions: region_cat_df = cat_df[cat_df['大区'] == region] rcs = region_cat_df['本周销售'].sum() rls = region_cat_df['上周销售'].sum() rcp = region_cat_df['本周毛利'].sum() rlp = region_cat_df['上周毛利'].sum() rcm = calculate_profit_margin(rcs, rcp) rlm = calculate_profit_margin(rls, rlp) sg = calculate_growth_rate(rcs, rls) pg = calculate_growth_rate(rcp, rlp) mg = calculate_growth_rate(rcm, rlm) sales_row[region] = format_growth_rate(sg) profit_row[region] = format_growth_rate(pg) margin_row[region] = format_growth_rate(mg) # 🔽 新增:该类别所有大区平均/加权?这里采用销售额加权平均 # 简单做法:取所有门店汇总后的整体增长率(已在上面计算过 cat_sales_growth) sales_row['合计'] = format_growth_rate(cat_sales_growth) profit_row['合计'] = format_growth_rate(cat_profit_growth) margin_row['合计'] = format_growth_rate(cat_margin_growth) result_data.extend([sales_row, profit_row, margin_row]) # 🔽 总计行(所有类别汇总) total_sales_row = {'类别': '总计', '指标': '销售环比', 'row_type': 'total_start'} total_profit_row = {'类别': '', '指标': '毛利环比', 'row_type': 'total_middle'} total_margin_row = {'类别': '', '指标': '毛利率环比', 'row_type': 'total_end'} for region in regions: region_df = df[df['大区'] == region] rcs = region_df['本周销售'].sum() rls = region_df['上周销售'].sum() rcp = region_df['本周毛利'].sum() rlp = region_df['上周毛利'].sum() rcm = calculate_profit_margin(rcs, rcp) rlm = calculate_profit_margin(rls, rlp) sg = calculate_growth_rate(rcs, rls) pg = calculate_growth_rate(rcp, rlp) mg = calculate_growth_rate(rcm, rlm) total_sales_row[region] = format_growth_rate(sg) total_profit_row[region] = format_growth_rate(pg) total_margin_row[region] = format_growth_rate(mg) # 总计行的合计列 total_sales_row['合计'] = format_growth_rate(total_sales_growth) total_profit_row['合计'] = format_growth_rate(total_profit_growth) total_margin_row['合计'] = format_growth_rate(total_margin_growth) result_data.extend([total_sales_row, total_profit_row, total_margin_row]) columns = ['类别', '指标'] + regions + ['合计'] result_df = pd.DataFrame(result_data)[columns] print(f"大区对比表形状: {result_df.shape}") return result_df, categories, regions, result_data except Exception as e: print(f"大区数据转换失败: {str(e)}") import traceback traceback.print_exc() return None, None, None, None def transform_data_for_city_comparison(df, target_region): """转换数据为指定大区的地市环比对比表格式(转置后)- 合并类别项""" try: region_df = df[df['大区'] == target_region] print(f"处理大区 {target_region}, 数据行数: {len(region_df)}") categories = sorted(region_df['类别'].unique()) cities = sorted(region_df['地市'].unique()) print(f"地市列表: {cities}") print(f"类别列表: {categories}") # 准备转置后的数据结构 result_data = [] # 计算大区合计数据 region_current_sales = region_df['本周销售'].sum() region_last_sales = region_df['上周销售'].sum() region_current_profit = region_df['本周毛利'].sum() region_last_profit = region_df['上周毛利'].sum() region_current_margin = calculate_profit_margin(region_current_sales, region_current_profit) region_last_margin = calculate_profit_margin(region_last_sales, region_last_profit) region_sales_growth = calculate_growth_rate(region_current_sales, region_last_sales) region_profit_growth = calculate_growth_rate(region_current_profit, region_last_profit) region_margin_growth = calculate_growth_rate(region_current_margin, region_last_margin) # 为每个类别添加三行数据 for category in categories: # 获取该类别数据 cat_df = region_df[region_df['类别'] == category] # 计算大区该类别数据 cat_current_sales = cat_df['本周销售'].sum() cat_last_sales = cat_df['上周销售'].sum() cat_current_profit = cat_df['本周毛利'].sum() cat_last_profit = cat_df['本周毛利'].sum() # 销售环比行 - 合并类别,同一类别只显示一次 sales_row = {'类别': category, '指标': '销售环比', 'row_type': 'category_start'} # 毛利环比行 - 类别留空 profit_row = {'类别': '', '指标': '毛利环比', 'row_type': 'category_middle'} # 毛利率环比行 - 类别留空 margin_row = {'类别': '', '指标': '毛利率环比', 'row_type': 'category_end'} # 为每个地市计算数据 for city in cities: city_cat_df = cat_df[cat_df['地市'] == city] # 当前地市该类别数据 city_current_sales = city_cat_df['本周销售'].sum() city_last_sales = city_cat_df['上周销售'].sum() city_current_profit = city_cat_df['本周毛利'].sum() city_last_profit = city_cat_df['上周毛利'].sum() city_current_margin = calculate_profit_margin(city_current_sales, city_current_profit) city_last_margin = calculate_profit_margin(city_last_sales, city_last_profit) # 计算环比 sales_growth = calculate_growth_rate(city_current_sales, city_last_sales) profit_growth = calculate_growth_rate(city_current_profit, city_last_profit) margin_growth = calculate_growth_rate(city_current_margin, city_last_margin) # 填充数据 sales_row[city] = f"{sales_growth:+.2f}%" profit_row[city] = f"{profit_growth:+.2f}%" margin_row[city] = f"{margin_growth:+.2f}%" # 添加大区合计数据 cat_sales_growth = calculate_growth_rate(cat_current_sales, cat_last_sales) cat_profit_growth = calculate_growth_rate(cat_current_profit, cat_last_profit) cat_current_margin = calculate_profit_margin(cat_current_sales, cat_current_profit) cat_last_margin = calculate_profit_margin(cat_last_sales, cat_last_profit) cat_margin_growth = calculate_growth_rate(cat_current_margin, cat_last_margin) sales_row[f'{target_region}合计'] = f"{cat_sales_growth:+.2f}%" profit_row[f'{target_region}合计'] = f"{cat_profit_growth:+.2f}%" margin_row[f'{target_region}合计'] = f"{cat_margin_growth:+.2f}%" result_data.extend([sales_row, profit_row, margin_row]) # 添加总计行 - 总计行也合并显示 total_sales_row = {'类别': '总计', '指标': '销售环比', 'row_type': 'total_start'} total_profit_row = {'类别': '', '指标': '毛利环比', 'row_type': 'total_middle'} total_margin_row = {'类别': '', '指标': '毛利率环比', 'row_type': 'total_end'} for city in cities: city_df = region_df[region_df['地市'] == city] city_current_sales = city_df['本周销售'].sum() city_last_sales = city_df['上周销售'].sum() city_current_profit = city_df['本周毛利'].sum() city_last_profit = city_df['上周毛利'].sum() city_current_margin = calculate_profit_margin(city_current_sales, city_current_profit) city_last_margin = calculate_profit_margin(city_last_sales, city_last_profit) sales_growth = calculate_growth_rate(city_current_sales, city_last_sales) profit_growth = calculate_growth_rate(city_current_profit, city_last_profit) margin_growth = calculate_growth_rate(city_current_margin, city_last_margin) total_sales_row[city] = f"{sales_growth:+.2f}%" total_profit_row[city] = f"{profit_growth:+.2f}%" total_margin_row[city] = f"{margin_growth:+.2f}%" total_sales_row[f'{target_region}合计'] = f"{region_sales_growth:+.2f}%" total_profit_row[f'{target_region}合计'] = f"{region_profit_growth:+.2f}%" total_margin_row[f'{target_region}合计'] = f"{region_margin_growth:+.2f}%" result_data.extend([total_sales_row, total_profit_row, total_margin_row]) # 构建DataFrame columns = ['类别', '指标'] + cities + [f'{target_region}合计'] result_df = pd.DataFrame(result_data)[columns] print(f"地市对比表形状: {result_df.shape}") return result_df, categories, cities, result_data except Exception as e: print(f"地市数据转换失败: {str(e)}") import traceback traceback.print_exc() return None, None, None, None def create_table_image(df, title, output_path, categories, regions, raw_data=None): """使用matplotlib创建转置表格图片,正确实现类别合并与居中""" try: plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei'] plt.rcParams['axes.unicode_minus'] = False # 构建表头 header = ['类别', '指标'] + list(regions) + ['合计'] # 包含合计列 table_data = [] for _, row in df.iterrows(): row_data = [row['类别'], row['指标']] for col in regions + ['合计']: row_data.append(row.get(col, '-')) table_data.append(row_data) n_rows = len(table_data) + 1 n_cols = len(header) fig_height = max(8, n_rows * 0.6) fig_width = max(14, n_cols * 1.6) fig, ax = plt.subplots(figsize=(fig_width, fig_height)) ax.axis('tight') ax.axis('off') # 创建表格 table = ax.table(cellText=table_data, colLabels=header, cellLoc='center', loc='center', bbox=[0, 0, 1, 1]) table.auto_set_font_size(False) table.set_fontsize(9) table.scale(1, 2.2) # 增加行高,便于居中 # 设置表头样式 for j in range(n_cols): table[(0, j)].set_facecolor('#F4B084') table[(0, j)].set_text_props(weight='bold', color='white') # ----------------------------- # 🔧 实现“类别”列跨三行合并 & 垂直居中 # ----------------------------- if raw_data is not None: i = 1 # 数据行索引(从表格第1行开始) while i < n_rows: row_type = raw_data[i - 1].get('row_type', '') if row_type == 'category_start': # 检查是否有后续两行属于同一类别 if i + 2 < n_rows: # 获取这三行对应的原始数据类型 next1_type = raw_data[i].get('row_type', '') next2_type = raw_data[i + 1].get('row_type', '') if next1_type == 'category_middle' and next2_type == 'category_end': # ✅ 可以合并这三行 # 清除第二、第三行的类别文本 table[(i + 1, 0)].set_text_props(text='') table[(i + 2, 0)].set_text_props(text='') # 设置统一背景色(浅灰) for r in [i, i + 1, i + 2]: table[(r, 0)].set_facecolor('#E6F3FF') # 计算垂直居中位置:第 i+1 行(中间) middle_row_idx = i + 1 category_name = raw_data[i - 1]['类别'] # 在中间行重新设置类别文本(覆盖默认值) cell = table[(middle_row_idx, 0)] cell._text.set_text(category_name) cell._text.set_weight('bold') cell._text.set_color('black') # 可选:添加边框强调 for r in [i, i + 1, i + 2]: table[(r, 0)].set_edgecolor('black') table[(r, 0)].visible_edges = ['left', 'right', 'top', 'bottom'] # 跳过已处理的两行 i += 3 continue elif row_type == 'total_start': # 处理“总计”三行合并 if i + 2 < n_rows: if (raw_data[i].get('row_type') == 'total_middle' and raw_data[i + 1].get('row_type') == 'total_end'): for r in [i, i + 1, i + 2]: table[(r, 0)].set_facecolor('#FFFFCC') table[(r, 0)]._text.set_text('') middle_row_idx = i + 1 cell = table[(middle_row_idx, 0)] cell._text.set_text("总计") cell._text.set_weight('bold') cell._text.set_color('red') cell.set_facecolor('#FFFFCC') i += 3 continue i += 1 # ----------------------------- # 🔴 标红每列中“销售环比”的最低三项 # ----------------------------- sales_rows = [] for idx, row in enumerate(table_data): if row[1] == '销售环比' and row[0] not in ['', '总计']: # 排除空和总计 sales_rows.append(idx) for col_name in header: if col_name in ['类别', '指标']: # 跳过非数值列 continue col_idx = header.index(col_name) values = [] for row_idx in sales_rows: text = table_data[row_idx][col_idx] try: num = float(text.replace('%', '').replace('-', '').replace('+', '')) if text.startswith('-'): num = -num values.append((row_idx, num)) except: continue # 排序取最小3个 sorted_vals = sorted(values, key=lambda x: x[1]) bottom3_row_indices = [x[0] for x in sorted_vals[:3]] for row_idx in bottom3_row_indices: table_cell = table[(row_idx + 1, col_idx)] # +1 因为有表头 table_cell._text.set_color('red') table_cell._text.set_weight('bold') # 设置标题 plt.title(title, fontsize=14, fontweight='bold', pad=40) plt.tight_layout() plt.savefig(output_path, dpi=300, bbox_inches='tight', facecolor='white') plt.close() print(f"✅ 图片生成成功: {output_path}") return True except Exception as e: print(f"❌ 生成图片失败: {str(e)}") import traceback traceback.print_exc() return False def create_performance_report(path, df, categories, regions, report_type, region_name=None, raw_data=None): """创建绩效环比报表图片""" try: # 获取当前日期 now = dt.datetime.now() date_str = now.strftime("%Y%m%d") # 计算上周时间范围 yesterday = now - dt.timedelta(days=1) last_week_start = (yesterday - dt.timedelta(days=yesterday.weekday() + 7)).strftime("%m月%d日") last_week_end = (yesterday - dt.timedelta(days=yesterday.weekday() + 1)).strftime("%m月%d日") this_week_start = (yesterday - dt.timedelta(days=yesterday.weekday())).strftime("%m月%d日") this_week_end = yesterday.strftime("%m月%d日") if report_type == 'region': title = f"各大区各绩效类别环比对比表\n({last_week_start}-{last_week_end} vs {this_week_start}-{this_week_end})" file_prefix = '大区绩效环比表' else: title = f"{region_name}各地市环比对比表\n({last_week_start}-{last_week_end} vs {this_week_start}-{this_week_end})" file_prefix = f'{region_name}地市绩效环比表' # 生成图片 file_mc = f'{file_prefix}_{date_str}' output_path = os.path.join(path, f'{file_mc}.png') success = create_table_image(df, title, output_path, categories, regions, raw_data) if success: print(f"图片生成成功: {output_path}") return output_path else: return None except Exception as e: print(f"生成绩效环比报表失败: {str(e)}") return None def application_push_image(access_token, image_path, user_id="037565", agent_id="1000077"): """推送图片到企业微信(个人)""" try: # 上传图片获取media_id with open(image_path, 'rb') as f: files = {'media': f} upload_url = f'https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type=image' upload_response = requests.post(upload_url, files=files).json() if 'media_id' not in upload_response: print(f"图片上传失败: {upload_response}") return None media_id = upload_response['media_id'] # 发送图片消息 send_url = f'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={access_token}' payload = { "touser": user_id, "msgtype": "image", "agentid": agent_id, "image": {"media_id": media_id}, "safe": 0 } response = requests.post(send_url, json=payload).json() return response except Exception as e: print(f"图片发送失败: {str(e)}") return None def send_store_performance_report(path): """发送门店绩效环比报表""" try: # 初始化微信 wechat = Wechat('tXNMrgcTgeV3IAqJhWB7mOe_bcKe9EtdCDze_75mGeY') access_token = wechat.access_token() # 获取原始数据 sql = generate_store_performance_sql() data, columns = oracle_connect(sql) # 转换为DataFrame df = pd.DataFrame(data, columns=columns) print(f"获取到 {len(df)} 条原始数据") # 数据验证 print("\n数据验证:") print(f"大区列表: {sorted(df['大区'].unique())}") print(f"地市列表: {sorted(df['地市'].unique())}") print(f"类别列表: {sorted(df['类别'].unique())}") image_paths = [] # 1. 生成各大区环比对比表(第一张图片) print("\n" + "=" * 50) print("生成各大区环比对比表") print("=" * 50) region_df, region_categories, region_list, region_raw_data = transform_data_for_region_comparison(df) if region_df is not None and len(region_df) > 0: region_image_path = create_performance_report(path, region_df, region_categories, region_list, 'region', raw_data=region_raw_data) if region_image_path: image_paths.append(('各大区环比对比表', region_image_path)) # 2. 生成各大地市环比对比表(第2-5张图片) regions = sorted(df['大区'].unique()) print(f"\n需要处理的大区: {regions}") for region in regions: print("\n" + "=" * 50) print(f"生成 {region} 地市环比对比表") print("=" * 50) city_df, city_categories, city_list, city_raw_data = transform_data_for_city_comparison(df, region) if city_df is not None and len(city_df) > 0: # 将城市列表转换为显示用的区域列表 display_regions = city_list + [f'{region}合计'] city_image_path = create_performance_report(path, city_df, city_categories, display_regions, 'city', region, raw_data=city_raw_data) if city_image_path: image_paths.append((f'{region}地市环比对比表', city_image_path)) # 推送所有报表图片给个人用户037565 for image_name, image_path in image_paths: if os.path.exists(image_path): # 只发送给个人用户037565 result = application_push_image(access_token, image_path, "037565") if result and result.get('errcode') == 0: print(f"{dt.datetime.now()} - {image_name} 个人推送成功") else: print(f"{dt.datetime.now()} - {image_name} 个人推送失败") # 删除临时文件 os.remove(image_path) return True except Exception as e: print(f"{dt.datetime.now()} - 门店绩效环比报表推送失败: {str(e)}") return False def run_store_performance_scheduler(path): """运行门店绩效环比报表定时任务""" # 每天早上7:30执行 schedule.every().day.at("07:30").do(send_store_performance_report, path=path) # 立即测试一次 print(f"{dt.datetime.now()} - 正在执行门店绩效环比报表首次测试...") send_store_performance_report(path) while True: schedule.run_pending() time.sleep(60) if __name__ == '__main__': print(f'门店绩效环比报表程序启动时间: {dt.datetime.now()}') output_path = r'D:\门店绩效环比报表' if not os.path.exists(output_path): os.makedirs(output_path) # 启动定时任务线程 scheduler_thread = Thread(target=run_store_performance_scheduler, args=(output_path,)) scheduler_thread.daemon = True scheduler_thread.start() try: while True: time.sleep(60) except KeyboardInterrupt: print("门店绩效环比报表程序已停止") E:\Anaconda3-2024.02.1\python.exe E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py 门店绩效环比报表程序启动时间: 2025-10-12 09:43:51.970665 2025-10-12 09:43:51.971662 - 正在执行门店绩效环比报表首次测试... 获取到 24499 条原始数据 数据验证: 大区列表: ['豫北大区', '豫南大区', '郑州东区', '郑州西区'] 地市列表: ['三门峡地区', '信阳地区', '南阳地区', '周口地区', '商丘地区', '安阳地区', '平顶山地区', '开封地区', '新乡地区', '洛阳地区', '济源地区', '漯河地区', '濮阳地区', '焦作地区', '许昌地区', '郑东1区', '郑东2区', '郑东3区', '郑西1区', '郑西2区', '郑西3区', '驻马店地区', '鹤壁地区'] 类别列表: ['A', 'A+', 'B', 'C', 'C+', 'D', 'D+', 'E', 'F', 'b', 'c', '未分类'] ================================================== 生成各大区环比对比表 ================================================== 原始数据行数: 24499 大区列表: ['豫北大区', '豫南大区', '郑州东区', '郑州西区'] 类别列表: ['A', 'A+', 'B', 'C', 'C+', 'D', 'D+', 'E', 'F', 'b', 'c', '未分类'] 大区对比表形状: (39, 7) E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py:338: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail. fig, ax = plt.subplots(figsize=(fig_width, fig_height)) ❌ 生成图片失败: unhashable type: 'list' Traceback (most recent call last): File "E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py", line 397, in create_table_image table[(r, 0)].visible_edges = ['left', 'right', 'top', 'bottom'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\Anaconda3-2024.02.1\Lib\site-packages\matplotlib\table.py", line 209, in visible_edges elif value in self._edge_aliases: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list' 需要处理的大区: ['豫北大区', '豫南大区', '郑州东区', '郑州西区'] ================================================== 生成 豫北大区 地市环比对比表 ================================================== 处理大区 豫北大区, 数据行数: 7583 地市列表: ['三门峡地区', '商丘地区', '安阳地区', '开封地区', '新乡地区', '洛阳地区', '济源地区', '濮阳地区', '焦作地区', '鹤壁地区'] 类别列表: ['A', 'A+', 'B', 'C', 'C+', 'D', 'D+', 'E', 'F', 'b', 'c', '未分类'] 地市对比表形状: (39, 13) E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py:338: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail. fig, ax = plt.subplots(figsize=(fig_width, fig_height)) Traceback (most recent call last): File "E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py", line 397, in create_table_image table[(r, 0)].visible_edges = ['left', 'right', 'top', 'bottom'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\Anaconda3-2024.02.1\Lib\site-packages\matplotlib\table.py", line 209, in visible_edges elif value in self._edge_aliases: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list' ❌ 生成图片失败: unhashable type: 'list' ================================================== 生成 豫南大区 地市环比对比表 ================================================== 处理大区 豫南大区, 数据行数: 8132 地市列表: ['信阳地区', '南阳地区', '周口地区', '平顶山地区', '漯河地区', '许昌地区', '驻马店地区'] 类别列表: ['A', 'A+', 'B', 'C', 'C+', 'D', 'D+', 'E', 'F', 'b', 'c', '未分类'] 地市对比表形状: (39, 10) E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py:338: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail. fig, ax = plt.subplots(figsize=(fig_width, fig_height)) ❌ 生成图片失败: unhashable type: 'list' ================================================== 生成 郑州东区 地市环比对比表 ================================================== 处理大区 郑州东区, 数据行数: 4559 地市列表: ['郑东1区', '郑东2区', '郑东3区'] 类别列表: ['A', 'A+', 'B', 'C', 'C+', 'D', 'D+', 'E', 'F', 'b', 'c', '未分类'] Traceback (most recent call last): File "E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py", line 397, in create_table_image table[(r, 0)].visible_edges = ['left', 'right', 'top', 'bottom'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\Anaconda3-2024.02.1\Lib\site-packages\matplotlib\table.py", line 209, in visible_edges elif value in self._edge_aliases: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list' 地市对比表形状: (39, 6) E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py:338: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail. fig, ax = plt.subplots(figsize=(fig_width, fig_height)) Traceback (most recent call last): File "E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py", line 397, in create_table_image table[(r, 0)].visible_edges = ['left', 'right', 'top', 'bottom'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\Anaconda3-2024.02.1\Lib\site-packages\matplotlib\table.py", line 209, in visible_edges elif value in self._edge_aliases: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list' ❌ 生成图片失败: unhashable type: 'list' ================================================== 生成 郑州西区 地市环比对比表 ================================================== 处理大区 郑州西区, 数据行数: 4225 地市列表: ['郑西1区', '郑西2区', '郑西3区'] 类别列表: ['A', 'A+', 'B', 'C', 'C+', 'D', 'D+', 'E', 'F', 'b', 'c', '未分类'] 地市对比表形状: (39, 6) E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py:338: UserWarning: Starting a Matplotlib GUI outside of the main thread will likely fail. fig, ax = plt.subplots(figsize=(fig_width, fig_height)) ❌ 生成图片失败: unhashable type: 'list' Traceback (most recent call last): File "E:\Pycharm-2023.3.4\project\pythonProject\venv\推送\1-部长绩效类别周一早推送.py", line 397, in create_table_image table[(r, 0)].visible_edges = ['left', 'right', 'top', 'bottom'] ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\Anaconda3-2024.02.1\Lib\site-packages\matplotlib\table.py", line 209, in visible_edges elif value in self._edge_aliases: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: unhashable type: 'list'
10-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值