中国本土应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
19KB

  1. # -*- coding: utf-8 -*-
  2. import logging
  3. import datetime
  4. import re
  5. from odoo import _, api, fields, models
  6. from odoo.exceptions import UserError, ValidationError
  7. from io import StringIO
  8. import pandas as pd
  9. pd.set_option("max_colwidth", 4096) # 设置最大列宽
  10. pd.set_option("display.max_columns", 30) # 设置最大列数
  11. pd.set_option("expand_frame_repr", False) # 当列太多时不换行
  12. import time
  13. from odoo.addons.oec_im_wecom_api.api.wecom_abstract_api import ApiException
  14. _logger = logging.getLogger(__name__)
  15. class WeComApps(models.Model):
  16. _inherit = "wecom.apps"
  17. # ————————————————————————————————————
  18. # 应用回调服务
  19. # ————————————————————————————————————
  20. def generate_service_by_code(self, code):
  21. """
  22. 根据code生成回调服务
  23. :param code:
  24. :return:
  25. """
  26. if code == "contacts":
  27. service = self.app_callback_service_ids.sudo().search(
  28. [
  29. ("app_id", "=", self.id),
  30. ("code", "=", code),
  31. "|",
  32. ("active", "=", True),
  33. ("active", "=", False),
  34. ]
  35. )
  36. if not service:
  37. service.create(
  38. {
  39. "app_id": self.id,
  40. "name": _("Contacts synchronization"),
  41. "code": code,
  42. "callback_url_token": "",
  43. "callback_aeskey": "",
  44. "description": _(
  45. "When members modify their personal information, the modified information will be pushed to the following URL in the form of events to ensure the synchronization of the address book."
  46. ),
  47. }
  48. )
  49. else:
  50. service.write(
  51. {
  52. "name": _("Contacts synchronization"),
  53. "code": code,
  54. "callback_url": service.generate_contact_service(),
  55. "description": _(
  56. "When members modify their personal information, the modified information will be pushed to the following URL in the form of events to ensure the synchronization of the address book."
  57. ),
  58. }
  59. )
  60. # ————————————————————————————————————
  61. # 应用参数配置
  62. # ————————————————————————————————————
  63. def generate_parameters_by_code(self, code):
  64. """
  65. 根据code生成参数
  66. :param code:
  67. :return:
  68. 注意:14使用 get_object_reference 方法,15 没有此方法,
  69. 故在 \wecom_base\models\ir_model.py 添加了 get_object_reference方法
  70. """
  71. if code == "contacts":
  72. # 从xml 获取数据
  73. ir_model_data = self.env["ir.model.data"]
  74. contacts_allow_sync_hr = ir_model_data.get_object_reference(
  75. "oec_im_wecom_contacts_sync", "wecom_app_config_contacts_allow_sync_hr"
  76. )[
  77. 1
  78. ] # 1
  79. contacts_sync_hr_department_id = ir_model_data.get_object_reference(
  80. "oec_im_wecom_contacts_sync",
  81. "wecom_app_config_contacts_sync_hr_department_id",
  82. )[
  83. 1
  84. ] # 2
  85. contacts_edit_enabled = ir_model_data.get_object_reference(
  86. "oec_im_wecom_contacts_sync", "wecom_app_config_contacts_edit_enabled"
  87. )[
  88. 1
  89. ] # 3
  90. contacts_allow_add_system_users = ir_model_data.get_object_reference(
  91. "oec_im_wecom_contacts_sync",
  92. "wecom_app_config_contacts_allow_add_system_users",
  93. )[
  94. 1
  95. ] # 4
  96. contacts_use_default_avatar_when_adding_employees = ir_model_data.get_object_reference(
  97. "oec_im_wecom_contacts_sync",
  98. "wecom_app_config_contacts_use_default_avatar_when_adding_employees",
  99. )[
  100. 1
  101. ] # 5
  102. contacts_update_avatar_every_time_sync_employees = (
  103. ir_model_data.get_object_reference(
  104. "oec_im_wecom_contacts_sync",
  105. "wecom_app_config_contacts_update_avatar_every_time_sync_employees",
  106. )[1]
  107. ) # 6
  108. # enabled_join_qrcode = ir_model_data.get_object_reference(
  109. # "oec_im_wecom_contacts_sync", "wecom_app_config_contacts_enabled_join_qrcode"
  110. # )[
  111. # 1
  112. # ] # 7
  113. # join_qrcode = ir_model_data.get_object_reference(
  114. # "oec_im_wecom_contacts_sync", "wecom_app_config_contacts_join_qrcode"
  115. # )[
  116. # 1
  117. # ] # 8
  118. # join_qrcode_size_type = ir_model_data.get_object_reference(
  119. # "oec_im_wecom_contacts_sync", "wecom_app_config_contacts_join_qrcode_size_type"
  120. # )[
  121. # 1
  122. # ] # 9
  123. # join_qrcode_last_time = ir_model_data.get_object_reference(
  124. # "oec_im_wecom_contacts_sync",
  125. # "wecom_app_config_acontacts_join_qrcode_last_time",
  126. # )[
  127. # 1
  128. # ] # 10
  129. vals_list = [
  130. contacts_allow_sync_hr, # 1
  131. contacts_sync_hr_department_id, # 2
  132. contacts_edit_enabled, # 3
  133. contacts_allow_add_system_users, # 4
  134. contacts_use_default_avatar_when_adding_employees, # 5
  135. contacts_update_avatar_every_time_sync_employees, # 6
  136. # enabled_join_qrcode, # 7
  137. # join_qrcode, # 8
  138. # join_qrcode_size_type, # 9
  139. # join_qrcode_last_time, # 10
  140. ]
  141. for id in vals_list:
  142. app_config_id = self.env["wecom.app_config"].search([("id", "=", id)])
  143. app_config = (
  144. self.env["wecom.app_config"]
  145. .sudo()
  146. .search([("app_id", "=", self.id), ("key", "=", app_config_id.key)])
  147. )
  148. if not app_config:
  149. app_config.sudo().create(
  150. {
  151. "name": app_config_id.name,
  152. "app_id": self.id,
  153. "key": app_config_id.key,
  154. "ttype": app_config_id.ttype,
  155. "value": ""
  156. if app_config_id.key == "join_qrcode"
  157. or app_config_id.key == "join_qrcode_last_time"
  158. else app_config_id.value,
  159. "description": app_config_id.description,
  160. }
  161. )
  162. else:
  163. app_config.sudo().write(
  164. {
  165. "name": app_config_id.name,
  166. "description": app_config_id.description,
  167. }
  168. )
  169. super(WeComApps, self).generate_parameters_by_code(code)
  170. # ————————————————————————————————————
  171. # 通讯录
  172. # ————————————————————————————————————
  173. def cron_sync_contacts(self):
  174. """
  175. 自动任务同步组织架构
  176. 同步内容: 1. wecom.department
  177. 2. wecom.user
  178. 3. wecom.tag
  179. """
  180. results = []
  181. total_time = 0
  182. sync_start_time = time.time()
  183. for app in self.search(
  184. [("company_id", "!=", False), ("type_code", "=", "['contacts']")]
  185. ):
  186. _logger.info(
  187. _(
  188. "Automatic task: start to synchronize the enterprise wechat organizational structure of the company [%s]"
  189. )
  190. % (app.company_id.name)
  191. )
  192. result = app.sync_contacts()
  193. results.append(result)
  194. _logger.info(
  195. _(
  196. "Automatic task: end synchronizing the enterprise wechat organizational structure of the company [%s]"
  197. )
  198. % (app.company_id.name)
  199. )
  200. df = pd.DataFrame(results)
  201. (
  202. sync_task_state,
  203. wecom_department_sync_state,
  204. wecom_user_sync_state,
  205. wecom_tag_sync_state,
  206. ) = self.handle_sync_all_state(
  207. df
  208. ) # 处理同步状态
  209. # 处理同步结果和时间
  210. sync_task_result = ""
  211. wecom_department_sync_result = ""
  212. wecom_user_sync_result = ""
  213. wecom_tag_sync_result = ""
  214. wecom_department_sync_times = 0
  215. wecom_user_sync_times = 0
  216. wecom_tag_sync_times = 0
  217. rows = len(df) # 获取所有行数
  218. for index, row in df.iterrows():
  219. if row["sync_state"] == "fail":
  220. sync_task_result += self.handle_sync_result(
  221. index, rows, row["sync_result"]
  222. )
  223. wecom_department_sync_result += self.handle_sync_result(
  224. index, rows, row["wecom_department_sync_result"]
  225. )
  226. wecom_user_sync_result += self.handle_sync_result(
  227. index, rows, row["wecom_user_sync_result"]
  228. )
  229. wecom_tag_sync_result += self.handle_sync_result(
  230. index, rows, row["wecom_tag_sync_result"]
  231. )
  232. wecom_department_sync_times += row["wecom_department_sync_times"]
  233. wecom_user_sync_times += row["wecom_user_sync_times"]
  234. wecom_tag_sync_times += row["wecom_tag_sync_times"]
  235. sync_end_time = time.time()
  236. total_time = sync_end_time - sync_start_time
  237. _logger.info(
  238. _(
  239. """
  240. Automatic task: end the synchronization of the enterprise wechat organizational structure of all companies.
  241. =======================================================================================================================
  242. Task Synchronization status:%s, Synchronization task time:%s seconds,
  243. Synchronization results:
  244. %s
  245. -----------------------------------------------------------------------------------------------------------------------
  246. Wecom department sync status:%s, Synchronize Wecom department time: %s seconds,
  247. Synchronize Wecom department results:
  248. %s
  249. -----------------------------------------------------------------------------------------------------------------------
  250. Wecom User sync status:%s, Synchronize Wecom User time: %s seconds,
  251. Synchronize Wecom User results:
  252. %s
  253. -----------------------------------------------------------------------------------------------------------------------
  254. Wecom tag sync status:%s, Synchronize Wecom tag time: %s seconds,
  255. Synchronize Wecom tag results:
  256. %s
  257. ======================================================================================================================="""
  258. )
  259. % (
  260. # 任务
  261. self.get_state_name(sync_task_state),
  262. total_time,
  263. sync_task_result,
  264. # 企微部门
  265. self.get_state_name(wecom_department_sync_state),
  266. wecom_department_sync_times,
  267. wecom_department_sync_result,
  268. # 企微员工
  269. self.get_state_name(wecom_user_sync_state),
  270. wecom_user_sync_times,
  271. wecom_user_sync_result,
  272. # 企微标签
  273. self.get_state_name(wecom_tag_sync_state),
  274. wecom_tag_sync_times,
  275. wecom_tag_sync_result,
  276. )
  277. )
  278. def sync_contacts(self):
  279. """
  280. 同步通讯录
  281. """
  282. # result = {
  283. # }
  284. result = {}
  285. result.update(
  286. {
  287. "company_name": self.company_id.name,
  288. "sync_state": "completed",
  289. "wecom_department_sync_state": "fail",
  290. "wecom_department_sync_times": 0,
  291. "wecom_department_sync_result": "",
  292. "wecom_user_sync_state": "fail",
  293. "wecom_user_sync_times": 0,
  294. "wecom_user_sync_result": "",
  295. "wecom_tag_sync_state": "fail",
  296. "wecom_tag_sync_times": 0,
  297. "wecom_tag_sync_result": "",
  298. }
  299. )
  300. # 同步企微部门
  301. sync_department_result = (
  302. self.env["wecom.department"]
  303. .with_context(company_id=self.company_id)
  304. .download_wecom_deps()
  305. )
  306. # [{'name': 'download_department_data', 'state': True, 'time': 0.3037421703338623, 'msg': 'Department list sync completed.'}]
  307. (
  308. wecom_department_sync_state,
  309. wecom_department_sync_times,
  310. wecom_department_sync_result,
  311. ) = self.handle_sync_task_state(sync_department_result, self.company_id)
  312. result.update(
  313. {
  314. "wecom_department_sync_state": wecom_department_sync_state,
  315. "wecom_department_sync_times": wecom_department_sync_times,
  316. "wecom_department_sync_result": wecom_department_sync_result,
  317. }
  318. )
  319. if result["wecom_department_sync_state"] == "fail":
  320. return result
  321. # 同步企微用户
  322. sync_user_result = (
  323. self.env["wecom.user"]
  324. .with_context(company_id=self.company_id)
  325. .download_wecom_users()
  326. )
  327. (
  328. wecom_user_sync_state,
  329. wecom_user_sync_times,
  330. wecom_user_sync_result,
  331. ) = self.handle_sync_task_state(sync_user_result, self.company_id)
  332. result.update(
  333. {
  334. "wecom_user_sync_state": wecom_user_sync_state,
  335. "wecom_user_sync_times": wecom_user_sync_times,
  336. "wecom_user_sync_result": wecom_user_sync_result,
  337. }
  338. )
  339. if result["wecom_user_sync_state"] == "fail":
  340. return result
  341. # 同步企微标签
  342. sync_wecom_tag_result = (
  343. self.env["wecom.tag"]
  344. .with_context(company_id=self.company_id)
  345. .download_wecom_tags()
  346. )
  347. (
  348. wecom_tag_sync_state,
  349. wecom_tag_sync_times,
  350. wecom_tag_sync_result,
  351. ) = self.handle_sync_task_state(sync_wecom_tag_result, self.company_id)
  352. result.update(
  353. {
  354. "wecom_tag_sync_state": wecom_tag_sync_state,
  355. "wecom_tag_sync_times": wecom_tag_sync_times,
  356. "wecom_tag_sync_result": wecom_tag_sync_result,
  357. }
  358. )
  359. if result["wecom_tag_sync_state"] == "fail":
  360. return result
  361. return result
  362. def get_state_name(self, key):
  363. """
  364. 获取状态名称
  365. """
  366. STATE = {
  367. "completed": _("All completed"),
  368. "partially": _("Partially complete"),
  369. "fail": _("All failed"),
  370. }
  371. return dict(STATE).get(key, _("Unknown")) # 如果没有找到,返回Unknown
  372. def handle_sync_result(self, index, rows, result):
  373. """
  374. 处理同步结果
  375. """
  376. if result is None:
  377. return ""
  378. if index < rows - 1:
  379. result = "%s:%s \n" % (str(index + 1), result)
  380. else:
  381. result = "%s:%s" % (str(index + 1), result)
  382. return result
  383. def handle_sync_all_state(self, df):
  384. """
  385. 处理同步状态
  386. """
  387. all_state_rows = len(df) # 获取所有行数
  388. fail_state_rows = len(df[df["sync_state"] == "fail"]) # 获取失败行数
  389. # 获取部门失败行数
  390. fail_department_state_rows = len(
  391. df[df["wecom_department_sync_state"] == "fail"]
  392. )
  393. # 获取用户失败行数
  394. fail_user_state_rows = len(df[df["wecom_user_sync_state"] == "fail"])
  395. # 获取标签失败行数
  396. fail_tag_state_rows = len(df[df["wecom_tag_sync_state"] == "fail"])
  397. sync_state = None
  398. wecom_department_sync_state = None
  399. wecom_employee_sync_state = None
  400. wecom_tag_sync_state = None
  401. if fail_state_rows == all_state_rows:
  402. sync_state = "fail"
  403. elif fail_state_rows > 0 and fail_state_rows < all_state_rows:
  404. sync_state = "partially"
  405. elif fail_state_rows == 0:
  406. sync_state = "completed"
  407. # 部门
  408. if fail_department_state_rows == all_state_rows:
  409. wecom_department_sync_state = "fail"
  410. elif (
  411. fail_department_state_rows > 0
  412. and fail_department_state_rows < all_state_rows
  413. ):
  414. wecom_department_sync_state = "partially"
  415. elif fail_department_state_rows == 0:
  416. wecom_department_sync_state = "completed"
  417. # 员工
  418. if fail_user_state_rows == all_state_rows:
  419. wecom_user_sync_state = "fail"
  420. elif fail_user_state_rows > 0 and fail_user_state_rows < all_state_rows:
  421. wecom_user_sync_state = "partially"
  422. elif fail_user_state_rows == 0:
  423. wecom_user_sync_state = "completed"
  424. if fail_tag_state_rows == all_state_rows:
  425. wecom_tag_sync_state = "fail"
  426. elif fail_tag_state_rows > 0 and fail_tag_state_rows < all_state_rows:
  427. wecom_tag_sync_state = "partially"
  428. elif fail_tag_state_rows == 0:
  429. wecom_tag_sync_state = "completed"
  430. return (
  431. sync_state,
  432. wecom_department_sync_state,
  433. wecom_user_sync_state,
  434. wecom_tag_sync_state,
  435. )
  436. def handle_sync_task_state(self, result, company):
  437. """
  438. 处理部门、用户、标签的 同步状态
  439. """
  440. df = pd.DataFrame(result)
  441. all_rows = len(df) # 获取所有行数
  442. fail_rows = len(df[df["state"] == False]) # 获取失败行数
  443. sync_state = None
  444. if fail_rows == all_rows:
  445. sync_state = "fail"
  446. elif fail_rows > 0 and fail_rows < all_rows:
  447. sync_state = "partially"
  448. elif fail_rows == 0:
  449. sync_state = "completed"
  450. sync_result = ""
  451. sync_times = 0
  452. for index, row in df.iterrows():
  453. sync_times += row["time"]
  454. sync_result += "[%s] %s" % (company.name, row["msg"])
  455. return sync_state, sync_times, sync_result
上海开阖软件有限公司 沪ICP备12045867号-1