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.
 
 
 
 
 
 

164 lines
5.1 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe
  5. from frappe.utils import time_diff_in_seconds, now, now_datetime, DATETIME_FORMAT
  6. from dateutil.relativedelta import relativedelta
  7. @frappe.whitelist()
  8. def get_notifications():
  9. if frappe.flags.in_install:
  10. return
  11. config = get_notification_config()
  12. groups = config.get("for_doctype").keys() + config.get("for_module").keys()
  13. cache = frappe.cache()
  14. notification_count = {}
  15. for name in groups:
  16. count = cache.hget("notification_count:" + name, frappe.session.user)
  17. if count is not None:
  18. notification_count[name] = count
  19. return {
  20. "open_count_doctype": get_notifications_for_doctypes(config, notification_count),
  21. "open_count_module": get_notifications_for_modules(config, notification_count),
  22. "open_count_other": get_notifications_for_other(config, notification_count),
  23. "new_messages": get_new_messages()
  24. }
  25. def get_new_messages():
  26. last_update = frappe.cache().hget("notifications_last_update", frappe.session.user)
  27. now_timestamp = now()
  28. frappe.cache().hset("notifications_last_update", frappe.session.user, now_timestamp)
  29. if not last_update:
  30. return []
  31. if last_update and time_diff_in_seconds(now_timestamp, last_update) > 1800:
  32. # no update for 30 mins, consider only the last 30 mins
  33. last_update = (now_datetime() - relativedelta(seconds=1800)).strftime(DATETIME_FORMAT)
  34. return frappe.db.sql("""select sender_full_name, content
  35. from `tabCommunication`
  36. where communication_type in ('Chat', 'Notification')
  37. and reference_doctype='user'
  38. and reference_name = %s
  39. and creation > %s
  40. order by creation desc""", (frappe.session.user, last_update), as_dict=1)
  41. def get_notifications_for_modules(config, notification_count):
  42. """Notifications for modules"""
  43. return get_notifications_for("for_module", config, notification_count)
  44. def get_notifications_for_other(config, notification_count):
  45. """Notifications for other items"""
  46. return get_notifications_for("for_other", config, notification_count)
  47. def get_notifications_for(notification_type, config, notification_count):
  48. open_count = {}
  49. notification_map = config.get(notification_type) or {}
  50. for m in notification_map:
  51. try:
  52. if m in notification_count:
  53. open_count[m] = notification_count[m]
  54. else:
  55. open_count[m] = frappe.get_attr(notification_map[m])()
  56. frappe.cache().hset("notification_count:" + m, frappe.session.user, open_count[m])
  57. except frappe.PermissionError:
  58. pass
  59. # frappe.msgprint("Permission Error in notifications for {0}".format(m))
  60. return open_count
  61. def get_notifications_for_doctypes(config, notification_count):
  62. """Notifications for DocTypes"""
  63. can_read = frappe.get_user().get_can_read()
  64. open_count_doctype = {}
  65. for d in config.for_doctype:
  66. if d in can_read:
  67. condition = config.for_doctype[d]
  68. if d in notification_count:
  69. open_count_doctype[d] = notification_count[d]
  70. else:
  71. try:
  72. if isinstance(condition, dict):
  73. result = frappe.get_list(d, fields=["count(*)"],
  74. filters=condition, as_list=True)[0][0]
  75. else:
  76. result = frappe.get_attr(condition)()
  77. except frappe.PermissionError:
  78. pass
  79. # frappe.msgprint("Permission Error in notifications for {0}".format(d))
  80. except Exception, e:
  81. # OperationalError: (1412, 'Table definition has changed, please retry transaction')
  82. if e.args[0]!=1412:
  83. raise
  84. else:
  85. open_count_doctype[d] = result
  86. frappe.cache().hset("notification_count:" + d, frappe.session.user, result)
  87. return open_count_doctype
  88. def clear_notifications(user="*"):
  89. if user=="*":
  90. frappe.cache().delete_keys("notification_count:")
  91. else:
  92. # delete count for user
  93. frappe.cache().hdel_keys("notification_count:", user)
  94. def delete_notification_count_for(doctype):
  95. frappe.cache().delete_key("notification_count:" + doctype)
  96. def clear_doctype_notifications(doc, method=None, *args, **kwargs):
  97. config = get_notification_config()
  98. doctype = doc.doctype
  99. if doctype in config.for_doctype:
  100. delete_notification_count_for(doctype)
  101. return
  102. if doctype in config.for_module_doctypes:
  103. delete_notification_count_for(config.for_module_doctypes[doctype])
  104. def get_notification_info_for_boot():
  105. out = get_notifications()
  106. config = get_notification_config()
  107. can_read = frappe.get_user().get_can_read()
  108. conditions = {}
  109. module_doctypes = {}
  110. doctype_info = dict(frappe.db.sql("""select name, module from tabDocType"""))
  111. for d in list(set(can_read + config.for_doctype.keys())):
  112. if d in config.for_doctype:
  113. conditions[d] = config.for_doctype[d]
  114. if d in doctype_info:
  115. module_doctypes.setdefault(doctype_info[d], []).append(d)
  116. out.update({
  117. "conditions": conditions,
  118. "module_doctypes": module_doctypes,
  119. })
  120. return out
  121. def get_notification_config():
  122. def _get():
  123. config = frappe._dict()
  124. for notification_config in frappe.get_hooks().notification_config:
  125. nc = frappe.get_attr(notification_config)()
  126. for key in ("for_doctype", "for_module", "for_module_doctypes", "for_other"):
  127. config.setdefault(key, {})
  128. config[key].update(nc.get(key, {}))
  129. return config
  130. return frappe.cache().get_value("notification_config", _get)