seitime-frappe/frappe/desk/form/document_follow.py
Gavin D'souza 3446026555 chore: Update header: license.txt => LICENSE
The license.txt file has been replaced with LICENSE for quite a while
now. INAL but it didn't seem accurate to say "hey, checkout license.txt
although there's no such file". Apart from this, there were
inconsistencies in the headers altogether...this change brings
consistency.
2021-09-03 12:02:59 +05:30

298 lines
7.4 KiB
Python

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
import frappe
import frappe.utils
from frappe.utils import get_url_to_form
from frappe.model import log_types
from frappe import _
from itertools import groupby
@frappe.whitelist()
def update_follow(doctype, doc_name, following):
if following:
return follow_document(doctype, doc_name, frappe.session.user)
else:
return unfollow_document(doctype, doc_name, frappe.session.user)
@frappe.whitelist()
def follow_document(doctype, doc_name, user):
'''
param:
Doctype name
doc name
user email
condition:
avoided for some doctype
follow only if track changes are set to 1
'''
if (doctype in ("Communication", "ToDo", "Email Unsubscribe", "File", "Comment", "Email Account", "Email Domain")
or doctype in log_types):
return
if ((not frappe.get_meta(doctype).track_changes)
or user == "Administrator"):
return
if not frappe.db.get_value("User", user, "document_follow_notify", ignore=True, cache=True):
return
if not is_document_followed(doctype, doc_name, user):
doc = frappe.new_doc("Document Follow")
doc.update({
"ref_doctype": doctype,
"ref_docname": doc_name,
"user": user
})
doc.save()
return doc
@frappe.whitelist()
def unfollow_document(doctype, doc_name, user):
doc = frappe.get_all(
"Document Follow",
filters={
"ref_doctype": doctype,
"ref_docname": doc_name,
"user": user
},
fields=["name"],
limit=1
)
if doc:
frappe.delete_doc("Document Follow", doc[0].name)
return 1
return 0
def get_message(doc_name, doctype, frequency, user):
activity_list = get_version(doctype, doc_name, frequency, user) + get_comments(doctype, doc_name, frequency, user)
return sorted(activity_list, key=lambda k: k["time"], reverse=True)
def send_email_alert(receiver, docinfo, timeline):
if receiver:
frappe.sendmail(
subject=_("Document Follow Notification"),
recipients=[receiver],
template="document_follow",
args={
"docinfo": docinfo,
"timeline": timeline,
}
)
def send_document_follow_mails(frequency):
'''
param:
frequency for sanding mails
task:
set receiver according to frequency
group document list according to user
get changes, activity, comments on doctype
call method to send mail
'''
users = frappe.get_list("Document Follow",
fields=["*"])
sorted_users = sorted(users, key=lambda k: k['user'])
grouped_by_user = {}
for k, v in groupby(sorted_users, key=lambda k: k['user']):
grouped_by_user[k] = list(v)
for user in grouped_by_user:
user_frequency = frappe.db.get_value("User", user, "document_follow_frequency")
message = []
valid_document_follows = []
if user_frequency == frequency:
for d in grouped_by_user[user]:
content = get_message(d.ref_docname, d.ref_doctype, frequency, user)
if content:
message = message + content
valid_document_follows.append({
"reference_docname": d.ref_docname,
"reference_doctype": d.ref_doctype,
"reference_url": get_url_to_form(d.ref_doctype, d.ref_docname)
})
if message and frappe.db.get_value("User", user, "document_follow_notify", ignore=True):
send_email_alert(user, valid_document_follows, message)
def get_version(doctype, doc_name, frequency, user):
timeline = []
filters = get_filters("docname", doc_name, frequency, user)
version = frappe.get_all("Version",
filters=filters,
fields=["ref_doctype", "data", "modified", "modified", "modified_by"]
)
if version:
for v in version:
change = frappe.parse_json(v.data)
time = frappe.utils.format_datetime(v.modified, "hh:mm a")
timeline_items = []
if change.changed:
timeline_items = get_field_changed(change.changed, time, doctype, doc_name, v)
if change.row_changed:
timeline_items = get_row_changed(change.row_changed, time, doctype, doc_name, v)
if change.added:
timeline_items = get_added_row(change.added, time, doctype, doc_name, v)
timeline = timeline + timeline_items
return timeline
def get_comments(doctype, doc_name, frequency, user):
from html2text import html2text
timeline = []
filters = get_filters("reference_name", doc_name, frequency, user)
comments = frappe.get_all("Comment",
filters=filters,
fields=["content", "modified", "modified_by", "comment_type"]
)
for comment in comments:
if comment.comment_type == "Like":
by = ''' By : <b>{0}<b>'''.format(comment.modified_by)
elif comment.comment_type == "Comment":
by = '''Commented by : <b>{0}<b>'''.format(comment.modified_by)
else:
by = ''
time = frappe.utils.format_datetime(comment.modified, "hh:mm a")
timeline.append({
"time": comment.modified,
"data": {
"time": time,
"comment": html2text(str(comment.content)),
"by": by
},
"doctype": doctype,
"doc_name": doc_name,
"type": "comment"
})
return timeline
def is_document_followed(doctype, doc_name, user):
return frappe.db.exists(
"Document Follow",
{
"ref_doctype": doctype,
"ref_docname": doc_name,
"user": user
}
)
@frappe.whitelist()
def get_follow_users(doctype, doc_name):
return frappe.get_all(
"Document Follow",
filters={
"ref_doctype": doctype,
"ref_docname":doc_name
},
fields=["user"]
)
def get_row_changed(row_changed, time, doctype, doc_name, v):
from html2text import html2text
items = []
for d in row_changed:
d[2] = d[2] if d[2] else ' '
d[0] = d[0] if d[0] else ' '
d[3][0][1] = d[3][0][1] if d[3][0][1] else ' '
items.append({
"time": v.modified,
"data": {
"time": time,
"table_field": d[0],
"row": str(d[1]),
"field": d[3][0][0],
"from": html2text(str(d[3][0][1])),
"to": html2text(str(d[3][0][2]))
},
"doctype": doctype,
"doc_name": doc_name,
"type": "row changed",
"by": v.modified_by
})
return items
def get_added_row(added, time, doctype, doc_name, v):
items = []
for d in added:
items.append({
"time": v.modified,
"data": {
"to": d[0],
"time": time
},
"doctype": doctype,
"doc_name": doc_name,
"type": "row added",
"by": v.modified_by
})
return items
def get_field_changed(changed, time, doctype, doc_name, v):
from html2text import html2text
items = []
for d in changed:
d[1] = d[1] if d[1] else ' '
d[2] = d[2] if d[2] else ' '
d[0] = d[0] if d[0] else ' '
items.append({
"time": v.modified,
"data": {
"time": time,
"field": d[0],
"from": html2text(str(d[1])),
"to": html2text(str(d[2]))
},
"doctype": doctype,
"doc_name": doc_name,
"type": "field changed",
"by": v.modified_by
})
return items
def send_hourly_updates():
send_document_follow_mails("Hourly")
def send_daily_updates():
send_document_follow_mails("Daily")
def send_weekly_updates():
send_document_follow_mails("Weekly")
def get_filters(search_by, name, frequency, user):
filters = []
if frequency == "Weekly":
filters = [
[search_by, "=", name],
["modified", ">", frappe.utils.add_days(frappe.utils.nowdate(),-7)],
["modified", "<", frappe.utils.nowdate()],
["modified_by", "!=", user]
]
elif frequency == "Daily":
filters = [
[search_by, "=", name],
["modified", ">", frappe.utils.add_days(frappe.utils.nowdate(),-1)],
["modified", "<", frappe.utils.nowdate()],
["modified_by", "!=", user]
]
elif frequency == "Hourly":
filters = [
[search_by, "=", name],
["modified", ">", frappe.utils.add_to_date(frappe.utils.now_datetime(), hours=-1)],
["modified", "<", frappe.utils.now_datetime()],
["modified_by", "!=", user]
]
return filters