seitime-frappe/frappe/utils/goal.py
Akhil Narang 6ca4d4d167
refactor(treewide): ruff format
Signed-off-by: Akhil Narang <me@akhilnarang.dev>
2025-09-26 13:16:43 +05:30

142 lines
4 KiB
Python

# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE
from contextlib import suppress
import frappe
from frappe import _
from frappe.query_builder.functions import DateFormat, Function
from frappe.query_builder.utils import DocType
from frappe.utils.data import add_to_date, cstr, flt, now_datetime
from frappe.utils.formatters import format_value
def get_monthly_results(
goal_doctype: str,
goal_field: str,
date_col: str,
filters: dict,
aggregation: str = "sum",
) -> dict:
"""Get monthly aggregation values for given field of doctype"""
Table = DocType(goal_doctype)
date_format = "%m-%Y" if frappe.db.db_type != "postgres" else "MM-YYYY"
return dict(
frappe.qb.get_query(
table=goal_doctype,
fields=[
DateFormat(Table[date_col], date_format).as_("month_year"),
Function(aggregation, goal_field),
],
filters=filters,
validate_filters=True,
)
.groupby("month_year")
.run()
)
@frappe.whitelist()
def get_monthly_goal_graph_data(
title: str,
doctype: str,
docname: str,
goal_value_field: str,
goal_total_field: str,
goal_history_field: str,
goal_doctype: str,
goal_doctype_link: str,
goal_field: str,
date_field: str,
aggregation: str = "sum",
filters: str | dict | None = None,
) -> dict:
"""
Get month-wise graph data for a doctype based on aggregation values of a field in the goal doctype
:param title: Graph title
:param doctype: doctype of graph doc
:param docname: of the doc to set the graph in
:param goal_value_field: goal field of doctype
:param goal_total_field: current month value field of doctype
:param goal_history_field: cached history field
:param goal_doctype: doctype the goal is based on
:param goal_doctype_link: doctype link field in goal_doctype
:param goal_field: field from which the goal is calculated
:param aggregation: a value like 'count', 'sum', 'avg'
:param filters: optional filters
:return: dict of graph data
"""
doc = frappe.get_doc(doctype, docname)
doc.check_permission()
meta = doc.meta
goal = doc.get(goal_value_field)
today_date = now_datetime().date()
current_month_value = doc.get(goal_total_field)
current_month_year = today_date.strftime("%m-%Y") # eg: "02-2022"
formatted_value = format_value(current_month_value, meta.get_field(goal_total_field), doc)
history = doc.get(goal_history_field)
month_to_value_dict = None
if history and "{" in cstr(history):
with suppress(ValueError):
month_to_value_dict = frappe.parse_json(history)
if month_to_value_dict is None: # nosemgrep
doc_filter = {}
with suppress(ValueError):
doc_filter = frappe.parse_json(filters or "{}")
if doctype != goal_doctype:
doc_filter[goal_doctype_link] = docname
month_to_value_dict = get_monthly_results(
goal_doctype, goal_field, date_field, doc_filter, aggregation
)
month_to_value_dict[current_month_year] = current_month_value
month_labels = []
dataset_values = []
values_formatted = []
y_markers = {}
summary_values = [
{"title": _("This month"), "color": "#ffa00a", "value": formatted_value},
]
if flt(goal) > 0:
formatted_goal = format_value(goal, meta.get_field(goal_value_field), doc)
summary_values += [
{"title": _("Goal"), "color": "#5e64ff", "value": formatted_goal},
{
"title": _("Completed"),
"color": "#28a745",
"value": f"{round(flt(current_month_value) / flt(goal) * 100)}%",
},
]
y_markers = {"yMarkers": [{"label": _("Goal"), "lineType": "dashed", "value": flt(goal)}]}
for i in range(12):
date_value = add_to_date(today_date, months=-i, as_datetime=True)
month_word = date_value.strftime("%b %y") # eg: "Feb 22"
month_labels.insert(0, month_word)
month_value = date_value.strftime("%m-%Y") # eg: "02-2022"
val = month_to_value_dict.get(month_value, 0)
dataset_values.insert(0, val)
values_formatted.insert(0, format_value(val, meta.get_field(goal_total_field), doc))
return {
"title": title,
"data": {
"datasets": [{"values": dataset_values, "formatted": values_formatted}],
"labels": month_labels,
**y_markers,
},
"summary": summary_values,
}