Преглед изворни кода

[enhancement] Feedback Feature (#2683)

version-14
Makarand Bauskar пре 8 година
committed by Rushabh Mehta
родитељ
комит
99d97530b1
16 измењених фајлова са 366 додато и 165 уклоњено
  1. +2
    -0
      frappe/boot.py
  2. +2
    -56
      frappe/core/doctype/communication/communication.js
  3. +2
    -47
      frappe/core/doctype/communication/communication.json
  4. +79
    -41
      frappe/core/doctype/feedback_trigger/feedback_trigger.py
  5. +17
    -6
      frappe/core/doctype/feedback_trigger/test_feedback_trigger.py
  6. +0
    -0
      frappe/core/report/feedback_ratings/__init__.py
  7. +55
    -0
      frappe/core/report/feedback_ratings/feedback_ratings.js
  8. +18
    -0
      frappe/core/report/feedback_ratings/feedback_ratings.json
  9. +37
    -0
      frappe/core/report/feedback_ratings/feedback_ratings.py
  10. +1
    -1
      frappe/hooks.py
  11. +3
    -1
      frappe/public/build.json
  12. +103
    -0
      frappe/public/js/frappe/feedback.js
  13. +6
    -1
      frappe/public/js/frappe/form/toolbar.js
  14. +12
    -10
      frappe/templates/includes/comments/comments.html
  15. +10
    -1
      frappe/www/feedback.html
  16. +19
    -1
      frappe/www/feedback.py

+ 2
- 0
frappe/boot.py Прегледај датотеку

@@ -12,6 +12,7 @@ import frappe.desk.desk_page
from frappe.desk.form.load import get_meta_bundle
from frappe.utils.change_log import get_versions
from frappe.translate import get_lang_dict
from frappe.core.doctype.feedback_trigger.feedback_trigger import get_enabled_feedback_trigger

def get_bootinfo():
"""build and return boot info"""
@@ -68,6 +69,7 @@ def get_bootinfo():
bootinfo.email_accounts = frappe.get_all('User Email', fields=['email_account', 'email_id'],
filters=dict(parent=frappe.session.user))
bootinfo.lang_dict = get_lang_dict()
bootinfo.feedback_triggers = get_enabled_feedback_trigger()

return bootinfo



+ 2
- 56
frappe/core/doctype/communication/communication.js Прегледај датотеку

@@ -33,7 +33,8 @@ frappe.ui.form.on("Communication", {

if(frm.doc.communication_type == "Feedback") {
frm.add_custom_button(__("Resend"), function() {
frm.events.resend_feedback(frm);
feedback = new frappe.utils.Feedback();
feedback.resend_feedback_request(frm.doc);
});
}

@@ -99,60 +100,5 @@ frappe.ui.form.on("Communication", {
}
});
d.show();
},

resend_feedback: function(frm) {
/* resend the feedback request email */
return frappe.call({
method: "frappe.core.doctype.feedback_trigger.feedback_trigger.get_feedback_alert_details",
args: {
request: frm.doc.feedback_request,
reference_name: frm.doc.reference_name,
reference_doctype: frm.doc.reference_doctype
},
callback: function(r) {
if(r.message) {
details = r.message;

dialog = new frappe.ui.Dialog({
title: __("Resend Feedback Request"),
fields: [
{
"reqd": 1,
"label": __("Message"),
"fieldname": "message",
"fieldtype": "Text Editor",
"default": details.message
}
],
});

dialog.set_primary_action(__("Send"), function() {
args = dialog.get_values();
if(!args)
return;
else
details.message = args.message
dialog.hide();

return frappe.call({
method: "frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_alert",
args: {
reference_name: frm.doc.reference_name,
reference_doctype: frm.doc.reference_doctype,
alert_details: details,
},
freeze: true,
callback: function(r) {
frappe.msgprint(__("Feedback Alert for {0} is sent to {1}",
[frm.doc.reference_name, frm.doc.sender]));
}
})
});

dialog.show();
}
}
});
}
});

+ 2
- 47
frappe/core/doctype/communication/communication.json Прегледај датотеку

@@ -23,7 +23,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
@@ -51,7 +50,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "To and CC",
@@ -81,7 +79,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Type",
@@ -110,7 +107,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "From",
@@ -138,7 +134,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -166,7 +161,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "To",
@@ -195,7 +189,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "CC",
@@ -225,7 +218,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Phone No.",
@@ -253,7 +245,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Delivery Status",
@@ -282,7 +273,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -309,7 +299,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Message",
@@ -337,7 +326,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
@@ -365,7 +353,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Text Content",
@@ -394,7 +381,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Communication Type",
@@ -423,7 +409,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Comment Type",
@@ -452,7 +437,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -480,7 +464,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
@@ -510,7 +493,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Sent or Received",
@@ -538,7 +520,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "More Information",
@@ -566,7 +547,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Date",
@@ -593,7 +573,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sent Read Receipt",
@@ -621,7 +600,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -648,7 +626,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "From Full Name",
@@ -676,7 +653,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference",
@@ -704,7 +680,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference DocType",
@@ -733,7 +708,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Name",
@@ -762,7 +736,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Owner",
@@ -792,7 +765,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email Account",
@@ -821,7 +793,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "In Reply To",
@@ -851,7 +822,6 @@
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User",
@@ -879,7 +849,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@@ -906,7 +875,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Link DocType",
@@ -935,7 +903,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Link Name",
@@ -964,7 +931,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Timeline DocType",
@@ -993,7 +959,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Timeline Name",
@@ -1022,7 +987,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Timeline field Name",
@@ -1052,7 +1016,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Unread Notification Sent",
@@ -1080,7 +1043,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Seen",
@@ -1108,7 +1070,6 @@
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "User Tags",
@@ -1135,7 +1096,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email Inbox",
@@ -1163,7 +1123,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 1,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Message ID",
@@ -1191,7 +1150,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Signature",
@@ -1214,12 +1172,12 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.communication_type==\"Feedback\"",
"fieldname": "feedback_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Feedback",
@@ -1247,7 +1205,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Feedback",
@@ -1275,7 +1232,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Rating",
@@ -1303,7 +1259,6 @@
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Feedback Request",
@@ -1333,7 +1288,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-27 16:11:37.344686",
"modified": "2017-02-07 18:30:37.003313",
"modified_by": "Administrator",
"module": "Core",
"name": "Communication",


+ 79
- 41
frappe/core/doctype/feedback_trigger/feedback_trigger.py Прегледај датотеку

@@ -22,47 +22,57 @@ class FeedbackTrigger(Document):
try:
eval(self.condition, get_context(temp_doc))
except:
frappe.throw(_("The Condition '{0}' is invalid").format(self.condition))
frappe.throw(_("The condition '{0}' is invalid").format(self.condition))

@frappe.whitelist()
def send_feedback_alert(reference_doctype, reference_name, trigger=None, alert_details=None):
def send_feedback_request(reference_doctype, reference_name, trigger=None, details=None, is_manual=False):
""" send feedback alert """
details = json.loads(alert_details) if alert_details else \
get_feedback_alert_details(reference_doctype, reference_name, trigger=trigger)
details = json.loads(details) if details else \
get_feedback_request_details(reference_doctype, reference_name, trigger=trigger)
if is_manual:
is_feedback_request_already_sent(reference_doctype, reference_name)

feedback_request, url = get_feedback_request_url(reference_doctype,
reference_name, details.get("recipients"), trigger)

feedback_url = "Please click <a href='{url}'>here</a> to submit feedback.".format(url=url)

# appending feedback url to message body
details.update({ "message": "{message}<br>{feedback_url}".format(
message=details.get("message"),
feedback_url=feedback_url)
})

if details:
feedback_request = details.pop("feedback_request")
frappe.sendmail(**details)
frappe.db.set_value("Feedback Request", feedback_request, "is_sent", 1)

def trigger_feedback_alert(doc, method):
def trigger_feedback_request(doc, method):
""" trigger the feedback alert"""

feedback_trigger = frappe.db.get_value("Feedback Trigger", { "enabled": 1, "document_type": doc.doctype })
if feedback_trigger:
frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_alert',
frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_request',
trigger=feedback_trigger, reference_doctype=doc.doctype, reference_name=doc.name, now=frappe.flags.in_test)

@frappe.whitelist()
def get_feedback_alert_details(reference_doctype, reference_name, trigger=None, request=None):
def get_feedback_request_details(reference_doctype, reference_name, trigger=None, request=None):
feedback_url = ""

if not trigger and not request:
frappe.throw("Can not find Feedback Alert for {0}".format(reference_name))
if not trigger and not request and not frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype }):
frappe.throw("Can not find Feedback Trigger for {0}".format(reference_name))
elif not trigger and request:
trigger = frappe.db.get_value("Feedback Request", request, "feedback_trigger")
else:
trigger = frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype })

# check if feedback mail alert is already sent but feedback is not submitted
# to avoid sending multiple feedback mail alerts
if not trigger:
frappe.throw(_("Feedback Trigger not found"))

feedback_requests = frappe.get_all("Feedback Request", {
"is_sent": 1,
"is_feedback_submitted": 0,
"reference_name": reference_name,
"reference_doctype": reference_doctype
}, ["name"])
if feedback_requests:
frappe.throw(_("Feedback Alert Mail has been already sent to the recipient"))
# check if feedback request mail is already sent but feedback is not submitted
# to avoid sending multiple feedback request mail
is_feedback_request_already_sent(reference_doctype, reference_name)

feedback_trigger = frappe.get_doc("Feedback Trigger", trigger)
doc = frappe.get_doc(reference_doctype, reference_name)
@@ -70,40 +80,68 @@ def get_feedback_alert_details(reference_doctype, reference_name, trigger=None,
context = get_context(doc)

recipients = doc.get(feedback_trigger.email_fieldname, None)
if recipients and eval(feedback_trigger.condition, context):
communications = frappe.get_all("Communication", filters={
"reference_doctype": reference_doctype,
"reference_name": reference_name,
"communication_type": "Communication"
}, fields=["name"])

if recipients and eval(feedback_trigger.condition, context) and len(communications) >= 1:
subject = feedback_trigger.subject
feedback_request = frappe.get_doc({
"doctype": "Feedback Request",
"reference_name": doc.name,
"reference_doctype": doc.doctype,
"feedback_trigger": feedback_trigger.name
}).insert(ignore_permissions=True)

feedback_url = "{base_url}/feedback?reference_doctype={doctype}&reference_name={docname}&email={email_id}&key={nonce}".format(
base_url=get_url(),
doctype=doc.doctype,
docname=doc.name,
email_id=recipients,
nonce=feedback_request.name
)
context.update({ "alert": feedback_trigger, "feedback_url": feedback_url })
context.update({ "feedback_trigger": feedback_trigger })

if "{" in subject:
subject = frappe.render_template(feedback_trigger.subject, context)

feedback_alert_message = frappe.render_template(feedback_trigger.message, context)
feedback_request_message = frappe.render_template(feedback_trigger.message, context)

return {
"subject": subject,
"recipients": recipients,
"reference_name":doc.name,
"reference_doctype":doc.doctype,
"message": feedback_alert_message,
"feedback_request": feedback_request.name
"message": feedback_request_message,
}
else:
return None
frappe.throw("Feedback conditions does not match !!")

def get_feedback_request_url(reference_doctype, reference_name, recipients, trigger="Manual"):
feedback_request = frappe.get_doc({
"doctype": "Feedback Request",
"reference_name": reference_name,
"reference_doctype": reference_doctype,
"feedback_trigger": trigger
}).insert(ignore_permissions=True)

feedback_url = "{base_url}/feedback?reference_doctype={doctype}&reference_name={docname}&email={email_id}&key={nonce}".format(
base_url=get_url(),
doctype=reference_doctype,
docname=reference_name,
email_id=recipients,
nonce=feedback_request.name
)

return [ feedback_request.name, feedback_url ]

def is_feedback_request_already_sent(reference_doctype, reference_name):
feedback_request = frappe.get_all("Feedback Request", {
"is_sent": 1,
"is_feedback_submitted": 0,
"reference_name": reference_name,
"reference_doctype": reference_doctype
}, ["name"])

if feedback_request:
frappe.throw(_("Feedback request mail has been already sent to the recipient"))

def get_enabled_feedback_trigger():
""" get mapper of all the enable feedback trigger """

triggers = frappe.get_all("Feedback Trigger", filters={"enabled": 1},
fields=["document_type", "name"], as_list=True)

triggers = { dt[0]: dt[1] for dt in triggers }
return triggers

def get_context(doc):
return { "doc": doc }

+ 17
- 6
frappe/core/doctype/feedback_trigger/test_feedback_trigger.py Прегледај датотеку

@@ -41,10 +41,10 @@ class TestFeedbackTrigger(unittest.TestCase):
"doctype": "Feedback Trigger",
"document_type": "ToDo",
"email_field": "assigned_by",
"email_fieldname": "assigned_by",
"subject": "{{ doc.name }} Task Completed",
"condition": "doc.status == 'Closed'",
"message": """Task {{ doc.name }} is Completed by {{ doc.owner }}.
<br>Please visit the {{ feedback_url }} and give your feedback
regarding the Task {{ doc.name }}"""
}).insert(ignore_permissions=True)

@@ -52,16 +52,27 @@ class TestFeedbackTrigger(unittest.TestCase):
todo = frappe.get_doc({
"doctype": "ToDo",
"owner": "test-feedback@example.com",
"allocated_by": "test-feedback@example.com",
"assigned_by": "test-feedback@example.com",
"description": "Unable To Submit Sales Order #SO-00001"
}).insert(ignore_permissions=True)
})

# feedback alert mail should be sent only on 'Closed' status
self.assertRaises(frappe.ValidationError, todo.insert, ignore_permissions=True)

email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))

# feedback alert mail should be sent only on 'Closed' status
self.assertFalse(email_queue)

# add a communication
frappe.get_doc({
"reference_doctype": "ToDo",
"reference_name": todo.name,
"communication_type": "Communication",
"content": "Test Communication",
"subject": "Test Communication",
"doctype": "Communication"
}).insert(ignore_permissions=True)

# check if feedback mail alert is triggered
todo.status = "Closed"
todo.save(ignore_permissions=True)
@@ -99,4 +110,4 @@ class TestFeedbackTrigger(unittest.TestCase):
self.assertRaises(Exception, accept, key=feedback_request, sender="test-feedback@example.com",
reference_doctype="ToDo", reference_name=todo.name, feedback="Thank You !!", rating=4)

frappe.delete_doc("ToDo", todo.name)
frappe.delete_doc("ToDo", todo.name)

+ 0
- 0
frappe/core/report/feedback_ratings/__init__.py Прегледај датотеку


+ 55
- 0
frappe/core/report/feedback_ratings/feedback_ratings.js Прегледај датотеку

@@ -0,0 +1,55 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.query_reports["Feedback Ratings"] = {
"filters": [
{
"fieldname": "party_type",
"label": __("Party Type"),
"fieldtype": "Link",
"options": "DocType",
"reqd": 1,
"default": "Issue"
},
{
"fieldname": "party_name",
"label": __("Party"),
"fieldtype": "Dynamic Link",
"get_options": function() {
var party_type = frappe.query_report_filters_by_name.party_type.get_value();
if(!party_type) {
frappe.throw(__("Please select Party Type first"));
}
return party_type;
}
},
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
'reqd': 1,
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
'reqd': 1,
"default":frappe.datetime.nowdate()
}
],

get_chart_data: function(columns, result) {
return {
data: {
x: 'Date',
columns: [
['Date'].concat($.map(result, function(d) { return d[0]; })),
['Average Feedback'].concat($.map(result, function(d) { return d[1]; }))
]
},
chart_type: 'line',

}
}
}

+ 18
- 0
frappe/core/report/feedback_ratings/feedback_ratings.json Прегледај датотеку

@@ -0,0 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-02-05 20:38:21.890174",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-02-07 12:36:50.992811",
"modified_by": "Administrator",
"module": "Core",
"name": "Feedback Ratings",
"owner": "Administrator",
"ref_doctype": "Feedback Trigger",
"report_name": "Feedback Ratings",
"report_type": "Script Report"
}

+ 37
- 0
frappe/core/report/feedback_ratings/feedback_ratings.py Прегледај датотеку

@@ -0,0 +1,37 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe

def execute(filters=None):
columns, data = get_columns(filters), get_data(filters)
return columns, data

def get_columns(filters):
return [
"Date:Date",
"Average Rating",
]

def get_data(filters):
data = []
party_type = filters.get("party_type")
party = filters.get("party_name")
filters = {
"reference_doctype": party_type,
"communication_type": "Feedback",
"creation": ["Between", [filters.get("from_date"), filters.get("to_date")]]
}
fields = ["DATE_FORMAT(DATE(creation),'%m-%d-%Y')", "avg(rating) as rating"]

if not party_type:
return []

if party:
filters.update({ "reference_name": party })
party_details = frappe.get_list("Communication", filters=filters, fields=fields,
order_by="creation", group_by="DATE_FORMAT(DATE(creation),'%m-%d-%Y')", as_list=True)

return party_details or []

+ 1
- 1
frappe/hooks.py Прегледај датотеку

@@ -113,7 +113,7 @@ doc_events = {
"frappe.desk.notifications.clear_doctype_notifications",
],
"on_trash": "frappe.desk.notifications.clear_doctype_notifications",
"on_change": "frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_alert"
"on_change": "frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request"
},
"Email Group Member": {
"validate": "frappe.email.doctype.email_group.email_group.restrict_email_group"


+ 3
- 1
frappe/public/build.json Прегледај датотеку

@@ -149,7 +149,9 @@
"public/js/frappe/query_string.js",

"public/js/frappe/ui/charts.js",
"public/js/frappe/misc/rating_icons.html"
"public/js/frappe/misc/rating_icons.html",

"public/js/frappe/feedback.js"
],
"js/d3.min.js": [
"public/js/lib/d3.min.js",


+ 103
- 0
frappe/public/js/frappe/feedback.js Прегледај датотеку

@@ -0,0 +1,103 @@
frappe.provide("frappe.utils")

frappe.utils.Feedback = Class.extend({
resend_feedback_request: function(doc) {
/* resend the feedback request email */
args = {
reference_name: doc.reference_name,
reference_doctype: doc.reference_doctype,
request: doc.feedback_request,
}
this.get_feedback_request_details(args, true)
},

manual_feedback_request: function(doc) {
var me = this;

args = {
reference_doctype: doc.doctype,
reference_name: doc.name
}
if(frappe.boot.feedback_triggers[doc.doctype]) {
feedback_trigger = frappe.boot.feedback_triggers[doc.doctype]
$.extend(args, { trigger: feedback_trigger })
me.get_feedback_request_details(args, false)
} else{
me.make_feedback_request_dialog(args, false)
}
},

get_feedback_request_details: function(args, is_resend) {
var me = this;

return frappe.call({
method: "frappe.core.doctype.feedback_trigger.feedback_trigger.get_feedback_request_details",
'args': args,
callback: function(r) {
console.log(r)
if(r.message) {
me.make_feedback_request_dialog(r.message, is_resend)
}
}
});
},

make_feedback_request_dialog: function(args, is_resend) {
var me = this;
dialog = new frappe.ui.Dialog({
title: __("{0} Feedback Request", [ is_resend? "Resend": "Send" ]),
fields: [
{
"reqd": 1,
"label": __("Recipient"),
"fieldname": "recipients",
"fieldtype": "Data",
"options": "Email"
},
{
"reqd": 1,
"label": __("Subject"),
"fieldname": "subject",
"fieldtype": "Data"
},
{
"reqd": 1,
"label": __("Message"),
"fieldname": "message",
"fieldtype": "Text Editor"
}
],
});

$.each(args, function(field, value){
dialog.set_value(field, value);
})

dialog.set_primary_action(__("Send"), function() {
$.extend(args,{ details: dialog.get_values() });
if(!args)
return;

dialog.hide();
me.send_feedback_request(args)
});

dialog.show();
},

send_feedback_request: function(args) {
$.extend(args, { is_manual: true })
return frappe.call({
method: "frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_request",
'args': args,
freeze: true,
callback: function(r) {
docname = args.reference_name;
recipients = args.details.recipients || ""
frappe.msgprint(__("Feedback Request for {0} is sent to {1}",
[docname, recipients]));
}
});
}
})


+ 6
- 1
frappe/public/js/frappe/form/toolbar.js Прегледај датотеку

@@ -170,13 +170,18 @@ frappe.ui.form.Toolbar = Class.extend({
}, true);
}
}
// feedback
this.page.add_menu_item(__("Ask a Feedback"), function() {
feedback = new frappe.utils.Feedback();
feedback.manual_feedback_request(me.frm.doc);
}, true)

// New
if(p[CREATE] && !this.frm.meta.issingle) {
this.page.add_menu_item(__("New {0} (Ctrl+B)", [__(me.frm.doctype)]), function() {
frappe.new_doc(me.frm.doctype, true);}, true);
}

},
can_save: function() {
return this.get_docstatus()===0;


+ 12
- 10
frappe/templates/includes/comments/comments.html Прегледај датотеку

@@ -2,19 +2,20 @@
{% if comment_text %}
<div class="comment-header">{{ comment_text }}</div>
{% endif %}
{% if not comment_list %}
<div class="no-comment">
<p class="text-muted small">{{ _("No comments yet. Start a new discussion.") }}</p>
</div>
{% endif %}
{% if not comment_list %}
<div class="no-comment">
<p class="text-muted small">{{ _("No comments yet. Start a new discussion.") }}</p>
</div>
{% endif %}

<div itemscope itemtype="http://schema.org/UserComments" id="comment-list">
{% for comment in comment_list %}
{% include "templates/includes/comments/comment.html" %}
{% endfor %}
</div>
<div itemscope itemtype="http://schema.org/UserComments" id="comment-list">
{% for comment in comment_list %}
{% include "templates/includes/comments/comment.html" %}
{% endfor %}
</div>
</div>

{% if not is_communication %}
<div class="add-comment-section">
<div class="text-muted hidden login-required">
<a href="/login?redirect-to={{ pathname }}">{{ _("Login to comment") }}</a>
@@ -47,6 +48,7 @@
</div>
</div>
</div>
{% endif %}
<script>
frappe.ready(function() {
var login_required = {{ login_required and 1 or 0 }};


+ 10
- 1
frappe/www/feedback.html Прегледај датотеку

@@ -20,7 +20,16 @@
<textarea class='form-control feedback-text' style='min-height: 300px;'></textarea>
</div>

<p><button class='btn btn-primary btn-sm btn-submit'>{{ _("Submit") }}</button></p>
<p><button class='btn btn-primary btn-sm btn-submit'>{{ _("Submit") }}</button></p>

{% if comment_list -%}
<div class="comments">
<br><br>
<h3>{{ _("Communication") }}</h3>
{% include 'templates/includes/comments/comments.html' %}
</div>
{% endif %}

</div>
<div class="feedback-result" style="display: none">
<div class='page-card'>


+ 19
- 1
frappe/www/feedback.py Прегледај датотеку

@@ -1,6 +1,24 @@
import frappe
from frappe.core.doctype.feedback_request.feedback_request import is_valid_feedback_request

no_cache = True

def get_context(context):
reference_doctype = frappe.form_dict.get("reference_doctype")
reference_name = frappe.form_dict.get("reference_name")
communications = frappe.get_all("Communication", filters={
"reference_doctype": reference_doctype,
"reference_name": reference_name,
"communication_type": "Communication"
}, fields=["*"], limit_page_length=10, order_by="creation desc")

return {
"reference_doctype": reference_doctype,
"reference_name": reference_name,
"comment_list": communications,
"is_communication": True
}

@frappe.whitelist(allow_guest=True)
def accept(key, sender, reference_doctype, reference_name, feedback, rating):
""" save the feedback in communication """
@@ -8,7 +26,7 @@ def accept(key, sender, reference_doctype, reference_name, feedback, rating):
frappe.throw("Invalid Reference Doctype, Reference Name")

if not is_valid_feedback_request(key):
frappe.throw("Link is Expired")
frappe.throw("Link is expired")

frappe.get_doc({
"rating": rating,


Loading…
Откажи
Сачувај