Merge branch 'develop' of https://github.com/frappe/erpnext into rebrand-ui

This commit is contained in:
Suraj Shetty
2021-01-19 09:34:06 +05:30
248 changed files with 8764 additions and 2263 deletions

View File

@@ -214,7 +214,7 @@ class Issue(Document):
def before_insert(self):
if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
self.set_response_and_resolution_time()
self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
def set_response_and_resolution_time(self, priority=None, service_level_agreement=None):
service_level_agreement = get_active_service_level_agreement_for(priority=priority,

View File

@@ -135,15 +135,19 @@ class TestIssue(unittest.TestCase):
self.assertEqual(flt(issue.total_hold_time, 2), 2700)
def make_issue(creation=None, customer=None, index=0):
def make_issue(creation=None, customer=None, index=0, priority=None, issue_type=None):
issue = frappe.get_doc({
"doctype": "Issue",
"subject": "Service Level Agreement Issue {0}".format(index),
"customer": customer,
"raised_by": "test@example.com",
"description": "Service Level Agreement Issue",
"issue_type": issue_type,
"priority": priority,
"creation": creation,
"service_level_agreement_creation": creation
"opening_date": creation,
"service_level_agreement_creation": creation,
"company": "_Test Company"
}).insert(ignore_permissions=True)
return issue

View File

@@ -0,0 +1,141 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Issue Analytics"] = {
"filters": [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname: "based_on",
label: __("Based On"),
fieldtype: "Select",
options: ["Customer", "Issue Type", "Issue Priority", "Assigned To"],
default: "Customer",
reqd: 1
},
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_global_default("year_start_date"),
reqd: 1
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.defaults.get_global_default("year_end_date"),
reqd: 1
},
{
fieldname: "range",
label: __("Range"),
fieldtype: "Select",
options: [
{ "value": "Weekly", "label": __("Weekly") },
{ "value": "Monthly", "label": __("Monthly") },
{ "value": "Quarterly", "label": __("Quarterly") },
{ "value": "Yearly", "label": __("Yearly") }
],
default: "Monthly",
reqd: 1
},
{
fieldname: "status",
label: __("Status"),
fieldtype: "Select",
options:[
{label: __('Open'), value: 'Open'},
{label: __('Replied'), value: 'Replied'},
{label: __('Resolved'), value: 'Resolved'},
{label: __('Closed'), value: 'Closed'}
]
},
{
fieldname: "priority",
label: __("Issue Priority"),
fieldtype: "Link",
options: "Issue Priority"
},
{
fieldname: "customer",
label: __("Customer"),
fieldtype: "Link",
options: "Customer"
},
{
fieldname: "project",
label: __("Project"),
fieldtype: "Link",
options: "Project"
},
{
fieldname: "assigned_to",
label: __("Assigned To"),
fieldtype: "Link",
options: "User"
}
],
after_datatable_render: function(datatable_obj) {
$(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click();
},
get_datatable_options(options) {
return Object.assign(options, {
checkboxColumn: true,
events: {
onCheckRow: function(data) {
if (data && data.length) {
row_name = data[2].content;
row_values = data.slice(3).map(function(column) {
return column.content;
})
entry = {
'name': row_name,
'values': row_values
}
let raw_data = frappe.query_report.chart.data;
let new_datasets = raw_data.datasets;
var found = false;
for(var i=0; i < new_datasets.length; i++){
if (new_datasets[i].name == row_name){
found = true;
new_datasets.splice(i,1);
break;
}
}
if (!found){
new_datasets.push(entry);
}
let new_data = {
labels: raw_data.labels,
datasets: new_datasets
}
setTimeout(() => {
frappe.query_report.chart.update(new_data)
},500)
setTimeout(() => {
frappe.query_report.chart.draw(true);
}, 1000)
frappe.query_report.raw_chart_data = new_data;
}
},
}
});
}
};

View File

@@ -0,0 +1,26 @@
{
"add_total_row": 1,
"columns": [],
"creation": "2020-10-09 19:52:10.227317",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"modified": "2020-10-11 19:43:19.358625",
"modified_by": "Administrator",
"module": "Support",
"name": "Issue Analytics",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Issue",
"report_name": "Issue Analytics",
"report_type": "Script Report",
"roles": [
{
"role": "Support Team"
}
]
}

View File

@@ -0,0 +1,222 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
from six import iteritems
from frappe import _, scrub
from frappe.utils import getdate, flt, add_to_date, add_days
from erpnext.accounts.utils import get_fiscal_year
def execute(filters=None):
return IssueAnalytics(filters).run()
class IssueAnalytics(object):
def __init__(self, filters=None):
"""Issue Analytics Report"""
self.filters = frappe._dict(filters or {})
self.get_period_date_ranges()
def run(self):
self.get_columns()
self.get_data()
self.get_chart_data()
return self.columns, self.data, None, self.chart
def get_columns(self):
self.columns = []
if self.filters.based_on == 'Customer':
self.columns.append({
'label': _('Customer'),
'options': 'Customer',
'fieldname': 'customer',
'fieldtype': 'Link',
'width': 200
})
elif self.filters.based_on == 'Assigned To':
self.columns.append({
'label': _('User'),
'fieldname': 'user',
'fieldtype': 'Link',
'options': 'User',
'width': 200
})
elif self.filters.based_on == 'Issue Type':
self.columns.append({
'label': _('Issue Type'),
'fieldname': 'issue_type',
'fieldtype': 'Link',
'options': 'Issue Type',
'width': 200
})
elif self.filters.based_on == 'Issue Priority':
self.columns.append({
'label': _('Issue Priority'),
'fieldname': 'priority',
'fieldtype': 'Link',
'options': 'Issue Priority',
'width': 200
})
for end_date in self.periodic_daterange:
period = self.get_period(end_date)
self.columns.append({
'label': _(period),
'fieldname': scrub(period),
'fieldtype': 'Int',
'width': 120
})
self.columns.append({
'label': _('Total'),
'fieldname': 'total',
'fieldtype': 'Int',
'width': 120
})
def get_data(self):
self.get_issues()
self.get_rows()
def get_period(self, date):
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
if self.filters.range == 'Weekly':
period = 'Week ' + str(date.isocalendar()[1])
elif self.filters.range == 'Monthly':
period = str(months[date.month - 1])
elif self.filters.range == 'Quarterly':
period = 'Quarter ' + str(((date.month - 1) // 3) + 1)
else:
year = get_fiscal_year(date, self.filters.company)
period = str(year[0])
if getdate(self.filters.from_date).year != getdate(self.filters.to_date).year and self.filters.range != 'Yearly':
period += ' ' + str(date.year)
return period
def get_period_date_ranges(self):
from dateutil.relativedelta import relativedelta, MO
from_date, to_date = getdate(self.filters.from_date), getdate(self.filters.to_date)
increment = {
'Monthly': 1,
'Quarterly': 3,
'Half-Yearly': 6,
'Yearly': 12
}.get(self.filters.range, 1)
if self.filters.range in ['Monthly', 'Quarterly']:
from_date = from_date.replace(day=1)
elif self.filters.range == 'Yearly':
from_date = get_fiscal_year(from_date)[1]
else:
from_date = from_date + relativedelta(from_date, weekday=MO(-1))
self.periodic_daterange = []
for dummy in range(1, 53):
if self.filters.range == 'Weekly':
period_end_date = add_days(from_date, 6)
else:
period_end_date = add_to_date(from_date, months=increment, days=-1)
if period_end_date > to_date:
period_end_date = to_date
self.periodic_daterange.append(period_end_date)
from_date = add_days(period_end_date, 1)
if period_end_date == to_date:
break
def get_issues(self):
filters = self.get_common_filters()
self.field_map = {
'Customer': 'customer',
'Issue Type': 'issue_type',
'Issue Priority': 'priority',
'Assigned To': '_assign'
}
self.entries = frappe.db.get_all('Issue',
fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date'],
filters=filters,
debug=1
)
def get_common_filters(self):
filters = {}
filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])
if self.filters.get('assigned_to'):
filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')
for entry in ['company', 'status', 'priority', 'customer', 'project']:
if self.filters.get(entry):
filters[entry] = self.filters.get(entry)
return filters
def get_rows(self):
self.data = []
self.get_periodic_data()
for entity, period_data in iteritems(self.issue_periodic_data):
if self.filters.based_on == 'Customer':
row = {'customer': entity}
elif self.filters.based_on == 'Assigned To':
row = {'user': entity}
elif self.filters.based_on == 'Issue Type':
row = {'issue_type': entity}
elif self.filters.based_on == 'Issue Priority':
row = {'priority': entity}
total = 0
for end_date in self.periodic_daterange:
period = self.get_period(end_date)
amount = flt(period_data.get(period, 0.0))
row[scrub(period)] = amount
total += amount
row['total'] = total
self.data.append(row)
def get_periodic_data(self):
self.issue_periodic_data = frappe._dict()
for d in self.entries:
period = self.get_period(d.get('opening_date'))
if self.filters.based_on == 'Assigned To':
if d._assign:
for entry in json.loads(d._assign):
self.issue_periodic_data.setdefault(entry, frappe._dict()).setdefault(period, 0.0)
self.issue_periodic_data[entry][period] += 1
else:
field = self.field_map.get(self.filters.based_on)
value = d.get(field)
if not value:
value = _('Not Specified')
self.issue_periodic_data.setdefault(value, frappe._dict()).setdefault(period, 0.0)
self.issue_periodic_data[value][period] += 1
def get_chart_data(self):
length = len(self.columns)
labels = [d.get('label') for d in self.columns[1:length-1]]
self.chart = {
'data': {
'labels': labels,
'datasets': []
},
'type': 'line'
}

View File

@@ -0,0 +1,211 @@
from __future__ import unicode_literals
import unittest
import frappe
from frappe.utils import getdate, add_months
from erpnext.support.report.issue_analytics.issue_analytics import execute
from erpnext.support.doctype.issue.test_issue import make_issue, create_customer
from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
from frappe.desk.form.assign_to import add as add_assignment
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
class TestIssueAnalytics(unittest.TestCase):
@classmethod
def setUpClass(self):
frappe.db.sql("delete from `tabIssue` where company='_Test Company'")
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
current_month_date = getdate()
last_month_date = add_months(current_month_date, -1)
self.current_month = str(months[current_month_date.month - 1]).lower() + '_' + str(current_month_date.year)
self.last_month = str(months[last_month_date.month - 1]).lower() + '_' + str(last_month_date.year)
def test_issue_analytics(self):
create_service_level_agreements_for_issues()
create_issue_types()
create_records()
self.compare_result_for_customer()
self.compare_result_for_issue_type()
self.compare_result_for_issue_priority()
self.compare_result_for_assignment()
def compare_result_for_customer(self):
filters = {
'company': '_Test Company',
'based_on': 'Customer',
'from_date': add_months(getdate(), -1),
'to_date': getdate(),
'range': 'Monthly'
}
report = execute(filters)
expected_data = [
{
'customer': '__Test Customer 2',
self.last_month: 1.0,
self.current_month: 0.0,
'total': 1.0
},
{
'customer': '__Test Customer 1',
self.last_month: 0.0,
self.current_month: 1.0,
'total': 1.0
},
{
'customer': '__Test Customer',
self.last_month: 1.0,
self.current_month: 1.0,
'total': 2.0
}
]
self.assertEqual(expected_data, report[1]) # rows
self.assertEqual(len(report[0]), 4) # cols
def compare_result_for_issue_type(self):
filters = {
'company': '_Test Company',
'based_on': 'Issue Type',
'from_date': add_months(getdate(), -1),
'to_date': getdate(),
'range': 'Monthly'
}
report = execute(filters)
expected_data = [
{
'issue_type': 'Discomfort',
self.last_month: 1.0,
self.current_month: 0.0,
'total': 1.0
},
{
'issue_type': 'Service Request',
self.last_month: 0.0,
self.current_month: 1.0,
'total': 1.0
},
{
'issue_type': 'Bug',
self.last_month: 1.0,
self.current_month: 1.0,
'total': 2.0
}
]
self.assertEqual(expected_data, report[1]) # rows
self.assertEqual(len(report[0]), 4) # cols
def compare_result_for_issue_priority(self):
filters = {
'company': '_Test Company',
'based_on': 'Issue Priority',
'from_date': add_months(getdate(), -1),
'to_date': getdate(),
'range': 'Monthly'
}
report = execute(filters)
expected_data = [
{
'priority': 'Medium',
self.last_month: 1.0,
self.current_month: 1.0,
'total': 2.0
},
{
'priority': 'Low',
self.last_month: 1.0,
self.current_month: 0.0,
'total': 1.0
},
{
'priority': 'High',
self.last_month: 0.0,
self.current_month: 1.0,
'total': 1.0
}
]
self.assertEqual(expected_data, report[1]) # rows
self.assertEqual(len(report[0]), 4) # cols
def compare_result_for_assignment(self):
filters = {
'company': '_Test Company',
'based_on': 'Assigned To',
'from_date': add_months(getdate(), -1),
'to_date': getdate(),
'range': 'Monthly'
}
report = execute(filters)
expected_data = [
{
'user': 'test@example.com',
self.last_month: 1.0,
self.current_month: 1.0,
'total': 2.0
},
{
'user': 'test1@example.com',
self.last_month: 2.0,
self.current_month: 1.0,
'total': 3.0
}
]
self.assertEqual(expected_data, report[1]) # rows
self.assertEqual(len(report[0]), 4) # cols
def create_issue_types():
for entry in ['Bug', 'Service Request', 'Discomfort']:
if not frappe.db.exists('Issue Type', entry):
frappe.get_doc({
'doctype': 'Issue Type',
'__newname': entry
}).insert()
def create_records():
create_customer("__Test Customer", "_Test SLA Customer Group", "__Test SLA Territory")
create_customer("__Test Customer 1", "_Test SLA Customer Group", "__Test SLA Territory")
create_customer("__Test Customer 2", "_Test SLA Customer Group", "__Test SLA Territory")
current_month_date = getdate()
last_month_date = add_months(current_month_date, -1)
issue = make_issue(current_month_date, "__Test Customer", 2, "High", "Bug")
add_assignment({
"assign_to": ["test@example.com"],
"doctype": "Issue",
"name": issue.name
})
issue = make_issue(last_month_date, "__Test Customer", 2, "Low", "Bug")
add_assignment({
"assign_to": ["test1@example.com"],
"doctype": "Issue",
"name": issue.name
})
issue = make_issue(current_month_date, "__Test Customer 1", 2, "Medium", "Service Request")
add_assignment({
"assign_to": ["test1@example.com"],
"doctype": "Issue",
"name": issue.name
})
issue = make_issue(last_month_date, "__Test Customer 2", 2, "Medium", "Discomfort")
add_assignment({
"assign_to": ["test@example.com", "test1@example.com"],
"doctype": "Issue",
"name": issue.name
})

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Issue Summary"] = {
"filters": [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname: "based_on",
label: __("Based On"),
fieldtype: "Select",
options: ["Customer", "Issue Type", "Issue Priority", "Assigned To"],
default: "Customer",
reqd: 1
},
{
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_global_default("year_start_date"),
reqd: 1
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.defaults.get_global_default("year_end_date"),
reqd: 1
},
{
fieldname: "status",
label: __("Status"),
fieldtype: "Select",
options:[
{label: __('Open'), value: 'Open'},
{label: __('Replied'), value: 'Replied'},
{label: __('Resolved'), value: 'Resolved'},
{label: __('Closed'), value: 'Closed'}
]
},
{
fieldname: "priority",
label: __("Issue Priority"),
fieldtype: "Link",
options: "Issue Priority"
},
{
fieldname: "customer",
label: __("Customer"),
fieldtype: "Link",
options: "Customer"
},
{
fieldname: "project",
label: __("Project"),
fieldtype: "Link",
options: "Project"
},
{
fieldname: "assigned_to",
label: __("Assigned To"),
fieldtype: "Link",
options: "User"
}
]
};

View File

@@ -0,0 +1,26 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2020-10-12 01:01:55.181777",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"modified": "2020-10-12 14:54:55.655920",
"modified_by": "Administrator",
"module": "Support",
"name": "Issue Summary",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Issue",
"report_name": "Issue Summary",
"report_type": "Script Report",
"roles": [
{
"role": "Support Team"
}
]
}

View File

@@ -0,0 +1,353 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
from six import iteritems
from frappe import _, scrub
from frappe.utils import flt
def execute(filters=None):
return IssueSummary(filters).run()
class IssueSummary(object):
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
def run(self):
self.get_columns()
self.get_data()
self.get_chart_data()
self.get_report_summary()
return self.columns, self.data, None, self.chart, self.report_summary
def get_columns(self):
self.columns = []
if self.filters.based_on == 'Customer':
self.columns.append({
'label': _('Customer'),
'options': 'Customer',
'fieldname': 'customer',
'fieldtype': 'Link',
'width': 200
})
elif self.filters.based_on == 'Assigned To':
self.columns.append({
'label': _('User'),
'fieldname': 'user',
'fieldtype': 'Link',
'options': 'User',
'width': 200
})
elif self.filters.based_on == 'Issue Type':
self.columns.append({
'label': _('Issue Type'),
'fieldname': 'issue_type',
'fieldtype': 'Link',
'options': 'Issue Type',
'width': 200
})
elif self.filters.based_on == 'Issue Priority':
self.columns.append({
'label': _('Issue Priority'),
'fieldname': 'priority',
'fieldtype': 'Link',
'options': 'Issue Priority',
'width': 200
})
self.statuses = ['Open', 'Replied', 'Resolved', 'Closed']
for status in self.statuses:
self.columns.append({
'label': _(status),
'fieldname': scrub(status),
'fieldtype': 'Int',
'width': 80
})
self.columns.append({
'label': _('Total Issues'),
'fieldname': 'total_issues',
'fieldtype': 'Int',
'width': 100
})
self.sla_status_map = {
'SLA Failed': 'failed',
'SLA Fulfilled': 'fulfilled',
'SLA Ongoing': 'ongoing'
}
for label, fieldname in self.sla_status_map.items():
self.columns.append({
'label': _(label),
'fieldname': fieldname,
'fieldtype': 'Int',
'width': 100
})
self.metrics = ['Avg First Response Time', 'Avg Response Time', 'Avg Hold Time',
'Avg Resolution Time', 'Avg User Resolution Time']
for metric in self.metrics:
self.columns.append({
'label': _(metric),
'fieldname': scrub(metric),
'fieldtype': 'Duration',
'width': 170
})
def get_data(self):
self.get_issues()
self.get_rows()
def get_issues(self):
filters = self.get_common_filters()
self.field_map = {
'Customer': 'customer',
'Issue Type': 'issue_type',
'Issue Priority': 'priority',
'Assigned To': '_assign'
}
self.entries = frappe.db.get_all('Issue',
fields=[self.field_map.get(self.filters.based_on), 'name', 'opening_date', 'status', 'avg_response_time',
'first_response_time', 'total_hold_time', 'user_resolution_time', 'resolution_time', 'agreement_status'],
filters=filters
)
def get_common_filters(self):
filters = {}
filters['opening_date'] = ('between', [self.filters.from_date, self.filters.to_date])
if self.filters.get('assigned_to'):
filters['_assign'] = ('like', '%' + self.filters.get('assigned_to') + '%')
for entry in ['company', 'status', 'priority', 'customer', 'project']:
if self.filters.get(entry):
filters[entry] = self.filters.get(entry)
return filters
def get_rows(self):
self.data = []
self.get_summary_data()
for entity, data in iteritems(self.issue_summary_data):
if self.filters.based_on == 'Customer':
row = {'customer': entity}
elif self.filters.based_on == 'Assigned To':
row = {'user': entity}
elif self.filters.based_on == 'Issue Type':
row = {'issue_type': entity}
elif self.filters.based_on == 'Issue Priority':
row = {'priority': entity}
for status in self.statuses:
count = flt(data.get(status, 0.0))
row[scrub(status)] = count
row['total_issues'] = data.get('total_issues', 0.0)
for sla_status in self.sla_status_map.values():
value = flt(data.get(sla_status), 0.0)
row[sla_status] = value
for metric in self.metrics:
value = flt(data.get(scrub(metric)), 0.0)
row[scrub(metric)] = value
self.data.append(row)
def get_summary_data(self):
self.issue_summary_data = frappe._dict()
for d in self.entries:
status = d.status
agreement_status = scrub(d.agreement_status)
if self.filters.based_on == 'Assigned To':
if d._assign:
for entry in json.loads(d._assign):
self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(status, 0.0)
self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(agreement_status, 0.0)
self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault('total_issues', 0.0)
self.issue_summary_data[entry][status] += 1
self.issue_summary_data[entry][agreement_status] += 1
self.issue_summary_data[entry]['total_issues'] += 1
else:
field = self.field_map.get(self.filters.based_on)
value = d.get(field)
if not value:
value = _('Not Specified')
self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(status, 0.0)
self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(agreement_status, 0.0)
self.issue_summary_data.setdefault(value, frappe._dict()).setdefault('total_issues', 0.0)
self.issue_summary_data[value][status] += 1
self.issue_summary_data[value][agreement_status] += 1
self.issue_summary_data[value]['total_issues'] += 1
self.get_metrics_data()
def get_metrics_data(self):
issues = []
metrics_list = ['avg_response_time', 'avg_first_response_time', 'avg_hold_time',
'avg_resolution_time', 'avg_user_resolution_time']
for entry in self.entries:
issues.append(entry.name)
field = self.field_map.get(self.filters.based_on)
if issues:
if self.filters.based_on == 'Assigned To':
assignment_map = frappe._dict()
for d in self.entries:
if d._assign:
for entry in json.loads(d._assign):
for metric in metrics_list:
self.issue_summary_data.setdefault(entry, frappe._dict()).setdefault(metric, 0.0)
self.issue_summary_data[entry]['avg_response_time'] += d.get('avg_response_time') or 0.0
self.issue_summary_data[entry]['avg_first_response_time'] += d.get('first_response_time') or 0.0
self.issue_summary_data[entry]['avg_hold_time'] += d.get('total_hold_time') or 0.0
self.issue_summary_data[entry]['avg_resolution_time'] += d.get('resolution_time') or 0.0
self.issue_summary_data[entry]['avg_user_resolution_time'] += d.get('user_resolution_time') or 0.0
if not assignment_map.get(entry):
assignment_map[entry] = 0
assignment_map[entry] += 1
for entry in assignment_map:
for metric in metrics_list:
self.issue_summary_data[entry][metric] /= flt(assignment_map.get(entry))
else:
data = frappe.db.sql("""
SELECT
{0}, AVG(first_response_time) as avg_frt,
AVG(avg_response_time) as avg_resp_time,
AVG(total_hold_time) as avg_hold_time,
AVG(resolution_time) as avg_resolution_time,
AVG(user_resolution_time) as avg_user_resolution_time
FROM `tabIssue`
WHERE
name IN %(issues)s
GROUP BY {0}
""".format(field), {'issues': issues}, as_dict=1)
for entry in data:
value = entry.get(field)
if not value:
value = _('Not Specified')
for metric in metrics_list:
self.issue_summary_data.setdefault(value, frappe._dict()).setdefault(metric, 0.0)
self.issue_summary_data[value]['avg_response_time'] = entry.get('avg_resp_time') or 0.0
self.issue_summary_data[value]['avg_first_response_time'] = entry.get('avg_frt') or 0.0
self.issue_summary_data[value]['avg_hold_time'] = entry.get('avg_hold_time') or 0.0
self.issue_summary_data[value]['avg_resolution_time'] = entry.get('avg_resolution_time') or 0.0
self.issue_summary_data[value]['avg_user_resolution_time'] = entry.get('avg_user_resolution_time') or 0.0
def get_chart_data(self):
if not self.data:
return None
labels = []
open_issues = []
replied_issues = []
resolved_issues = []
closed_issues = []
entity = self.filters.based_on
entity_field = self.field_map.get(entity)
if entity == 'Assigned To':
entity_field = 'user'
for entry in self.data:
labels.append(entry.get(entity_field))
open_issues.append(entry.get('open'))
replied_issues.append(entry.get('replied'))
resolved_issues.append(entry.get('resolved'))
closed_issues.append(entry.get('closed'))
self.chart = {
'data': {
'labels': labels[:30],
'datasets': [
{
'name': 'Open',
'values': open_issues[:30]
},
{
'name': 'Replied',
'values': replied_issues[:30]
},
{
'name': 'Resolved',
'values': resolved_issues[:30]
},
{
'name': 'Closed',
'values': closed_issues[:30]
}
]
},
'type': 'bar',
'barOptions': {
'stacked': True
}
}
def get_report_summary(self):
if not self.data:
return None
open_issues = 0
replied = 0
resolved = 0
closed = 0
for entry in self.data:
open_issues += entry.get('open')
replied += entry.get('replied')
resolved += entry.get('resolved')
closed += entry.get('closed')
self.report_summary = [
{
'value': open_issues,
'indicator': 'Red',
'label': _('Open'),
'datatype': 'Int',
},
{
'value': replied,
'indicator': 'Grey',
'label': _('Replied'),
'datatype': 'Int',
},
{
'value': resolved,
'indicator': 'Green',
'label': _('Resolved'),
'datatype': 'Int',
},
{
'value': closed,
'indicator': 'Green',
'label': _('Closed'),
'datatype': 'Int',
}
]