Merge pull request #32040 from sokumon/eps-removal
refactor: separate EPS into an app
This commit is contained in:
commit
5d380e6f93
62 changed files with 30 additions and 3522 deletions
|
|
@ -21,10 +21,6 @@ from frappe.permissions import has_permission
|
|||
from frappe.query_builder import DocType
|
||||
from frappe.query_builder.functions import Count
|
||||
from frappe.query_builder.terms import ParameterizedValueWrapper, SubQuery
|
||||
from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_points
|
||||
from frappe.social.doctype.energy_point_settings.energy_point_settings import (
|
||||
is_energy_point_enabled,
|
||||
)
|
||||
from frappe.utils import add_user_info, cstr, get_system_timezone
|
||||
from frappe.utils.change_log import get_versions
|
||||
from frappe.utils.frappecloud import on_frappecloud
|
||||
|
|
@ -99,10 +95,7 @@ def get_bootinfo():
|
|||
bootinfo.lang_dict = get_lang_dict()
|
||||
bootinfo.success_action = get_success_action()
|
||||
bootinfo.update(get_email_accounts(user=frappe.session.user))
|
||||
bootinfo.energy_points_enabled = is_energy_point_enabled()
|
||||
bootinfo.website_tracking_enabled = is_tracking_enabled()
|
||||
bootinfo.sms_gateway_enabled = bool(frappe.db.get_single_value("SMS Settings", "sms_gateway_url"))
|
||||
bootinfo.points = get_energy_points(frappe.session.user)
|
||||
bootinfo.frequently_visited_links = frequently_visited_links()
|
||||
bootinfo.link_preview_doctypes = get_link_preview_doctypes()
|
||||
bootinfo.additional_filters_config = get_additional_filters_from_hooks()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import frappe
|
|||
common_default_keys = ["__default", "__global"]
|
||||
|
||||
doctypes_for_mapping = {
|
||||
"Energy Point Rule",
|
||||
"Assignment Rule",
|
||||
"Milestone Tracker",
|
||||
"Document Naming Rule",
|
||||
|
|
|
|||
|
|
@ -773,11 +773,6 @@
|
|||
"group": "Rules",
|
||||
"link_doctype": "Assignment Rule",
|
||||
"link_fieldname": "document_type"
|
||||
},
|
||||
{
|
||||
"group": "Rules",
|
||||
"link_doctype": "Energy Point Rule",
|
||||
"link_fieldname": "reference_doctype"
|
||||
}
|
||||
],
|
||||
"modified": "2025-03-27 18:16:53.286909",
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class TestScheduledJobType(IntegrationTestCase):
|
|||
def test_weekly_job(self):
|
||||
job = frappe.get_doc(
|
||||
"Scheduled Job Type",
|
||||
dict(method="frappe.social.doctype.energy_point_log.energy_point_log.send_weekly_summary"),
|
||||
dict(method="frappe.desk.form.document_follow.send_weekly_updates"),
|
||||
)
|
||||
job.db_set("last_execution", "2019-01-01 00:00:00")
|
||||
self.assertTrue(job.is_event_due(get_datetime("2019-01-06 00:10:01"))) # +10 min because of jitter
|
||||
|
|
|
|||
|
|
@ -853,11 +853,6 @@
|
|||
"link_doctype": "Activity Log",
|
||||
"link_fieldname": "user"
|
||||
},
|
||||
{
|
||||
"group": "Logs",
|
||||
"link_doctype": "Energy Point Log",
|
||||
"link_fieldname": "user"
|
||||
},
|
||||
{
|
||||
"group": "Logs",
|
||||
"link_doctype": "Route History",
|
||||
|
|
|
|||
|
|
@ -573,9 +573,6 @@ class User(Document):
|
|||
frappe.db.delete("OAuth Authorization Code", {"user": self.name})
|
||||
frappe.db.delete("Token Cache", {"user": self.name})
|
||||
|
||||
# Delete EPS data
|
||||
frappe.db.delete("Energy Point Log", {"user": self.name})
|
||||
|
||||
# Remove user link from Workflow Action
|
||||
frappe.db.set_value("Workflow Action", {"user": self.name}, "user", None)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class NotificationLog(Document):
|
|||
link: DF.Data | None
|
||||
read: DF.Check
|
||||
subject: DF.Text | None
|
||||
type: DF.Literal["", "Mention", "Energy Point", "Assignment", "Share", "Alert"]
|
||||
type: DF.Literal["", "Mention", "Assignment", "Share", "Alert"]
|
||||
# end: auto-generated types
|
||||
|
||||
def after_insert(self):
|
||||
|
|
@ -103,11 +103,7 @@ def make_notification_logs(doc, users):
|
|||
notification = frappe.new_doc("Notification Log")
|
||||
notification.update(doc)
|
||||
notification.for_user = user
|
||||
if (
|
||||
notification.for_user != notification.from_user
|
||||
or doc.type == "Energy Point"
|
||||
or doc.type == "Alert"
|
||||
):
|
||||
if notification.for_user != notification.from_user or doc.type == "Alert":
|
||||
notification.insert(ignore_permissions=True)
|
||||
|
||||
|
||||
|
|
@ -119,9 +115,6 @@ def _get_user_ids(user_emails):
|
|||
|
||||
|
||||
def send_notification_email(doc: NotificationLog):
|
||||
if doc.type == "Energy Point" and doc.email_content is None:
|
||||
return
|
||||
|
||||
from frappe.utils import get_url_to_form, strip_html
|
||||
|
||||
user = frappe.db.get_value("User", doc.for_user, fieldname=["email", "language"], as_dict=True)
|
||||
|
|
@ -158,7 +151,6 @@ def get_email_header(doc, language: str | None = None):
|
|||
"Mention": _("New Mention on {0}", lang=language).format(docname),
|
||||
"Assignment": _("Assignment Update on {0}", lang=language).format(docname),
|
||||
"Share": _("New Document Shared {0}", lang=language).format(docname),
|
||||
"Energy Point": _("Energy Point Update on {0}", lang=language).format(docname),
|
||||
}
|
||||
|
||||
return header_map[doc.type or "Default"]
|
||||
|
|
|
|||
|
|
@ -13,13 +13,10 @@
|
|||
"enable_email_mention",
|
||||
"enable_email_assignment",
|
||||
"enable_email_threads_on_assigned_document",
|
||||
"enable_email_energy_point",
|
||||
"enable_email_share",
|
||||
"enable_email_event_reminders",
|
||||
"user",
|
||||
"seen",
|
||||
"system_notifications_section",
|
||||
"energy_points_system_notifications"
|
||||
"seen"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
|
|
@ -59,13 +56,6 @@
|
|||
"fieldtype": "Check",
|
||||
"label": "Assignments"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "enable_email_notifications",
|
||||
"fieldname": "enable_email_energy_point",
|
||||
"fieldtype": "Check",
|
||||
"label": "Energy Points"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "enable_email_notifications",
|
||||
|
|
@ -89,17 +79,6 @@
|
|||
"hidden": 1,
|
||||
"label": "Seen"
|
||||
},
|
||||
{
|
||||
"fieldname": "system_notifications_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "System Notifications"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "energy_points_system_notifications",
|
||||
"fieldtype": "Check",
|
||||
"label": "Energy Points"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"depends_on": "enable_email_notifications",
|
||||
|
|
@ -119,7 +98,7 @@
|
|||
"in_create": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:31.952985",
|
||||
"modified": "2025-04-16 17:15:25.641232",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Notification Settings",
|
||||
|
|
@ -139,8 +118,9 @@
|
|||
}
|
||||
],
|
||||
"read_only": 1,
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,14 +18,12 @@ class NotificationSettings(Document):
|
|||
from frappe.types import DF
|
||||
|
||||
enable_email_assignment: DF.Check
|
||||
enable_email_energy_point: DF.Check
|
||||
enable_email_event_reminders: DF.Check
|
||||
enable_email_mention: DF.Check
|
||||
enable_email_notifications: DF.Check
|
||||
enable_email_share: DF.Check
|
||||
enable_email_threads_on_assigned_document: DF.Check
|
||||
enabled: DF.Check
|
||||
energy_points_system_notifications: DF.Check
|
||||
seen: DF.Check
|
||||
subscribed_documents: DF.TableMultiSelect[NotificationSubscribedDocument]
|
||||
user: DF.Link | None
|
||||
|
|
|
|||
|
|
@ -123,7 +123,6 @@ def get_docinfo(doc=None, doctype=None, name=None):
|
|||
"permissions": get_doc_permissions(doc),
|
||||
"shared": get_docshares(doc),
|
||||
"views": get_view_logs(doc),
|
||||
"energy_point_logs": get_point_logs(doc.doctype, doc.name),
|
||||
"additional_timeline_content": get_additional_timeline_content(doc.doctype, doc.name),
|
||||
"milestones": get_milestones(doc.doctype, doc.name),
|
||||
"is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user),
|
||||
|
|
@ -246,19 +245,6 @@ def get_comments(doctype: str, name: str, comment_type: str | list[str] = "Comme
|
|||
return comments
|
||||
|
||||
|
||||
def get_point_logs(doctype, docname):
|
||||
from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled
|
||||
|
||||
if not is_energy_point_enabled():
|
||||
return []
|
||||
|
||||
return frappe.get_all(
|
||||
"Energy Point Log",
|
||||
filters={"reference_doctype": doctype, "reference_name": docname, "type": ["!=", "Review"]},
|
||||
fields=["*"],
|
||||
)
|
||||
|
||||
|
||||
def _get_communications(doctype, name, start=0, limit=20):
|
||||
communications = get_communication_data(doctype, name, start, limit)
|
||||
for c in communications:
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
import frappe
|
||||
from frappe.utils import get_fullname
|
||||
|
||||
|
||||
def get_leaderboards():
|
||||
return {
|
||||
"User": {
|
||||
"fields": ["points"],
|
||||
"method": "frappe.desk.leaderboard.get_energy_point_leaderboard",
|
||||
"company_disabled": 1,
|
||||
"icon": "users",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_energy_point_leaderboard(date_range, company=None, field=None, limit=None):
|
||||
users = frappe.get_list(
|
||||
"User",
|
||||
filters={
|
||||
"name": ["not in", ["Administrator", "Guest"]],
|
||||
"enabled": 1,
|
||||
"user_type": ["!=", "Website User"],
|
||||
},
|
||||
pluck="name",
|
||||
)
|
||||
|
||||
filters = [["type", "!=", "Review"], ["user", "in", users]]
|
||||
if date_range:
|
||||
date_range = frappe.parse_json(date_range)
|
||||
filters.append(["creation", "between", [date_range[0], date_range[1]]])
|
||||
energy_point_users = frappe.get_all(
|
||||
"Energy Point Log",
|
||||
fields=["user as name", "sum(points) as value"],
|
||||
filters=filters,
|
||||
group_by="user",
|
||||
order_by="value desc",
|
||||
)
|
||||
|
||||
energy_point_users_list = list(map(lambda x: x["name"], energy_point_users))
|
||||
for user in users:
|
||||
if user not in energy_point_users_list:
|
||||
energy_point_users.append({"name": user, "value": 0})
|
||||
|
||||
for user in energy_point_users:
|
||||
user_id = user["name"]
|
||||
user["name"] = get_fullname(user["name"])
|
||||
user["formatted_name"] = f'<a href="/app/user-profile/{user_id}">{get_fullname(user_id)}</a>'
|
||||
|
||||
return energy_point_users
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
.list-filters {
|
||||
overflow-y: hidden;
|
||||
padding: 5px
|
||||
}
|
||||
|
||||
.list-filter-item {
|
||||
min-width: 150px;
|
||||
float: left;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.list-item_content {
|
||||
flex: 1;
|
||||
padding-right: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select-time, .select-doctype, .select-filter, .select-sort {
|
||||
background: #f0f4f7;
|
||||
}
|
||||
|
||||
.from-date-field .clearfix{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.from-date-field {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.select-time:focus, .select-doctype:focus, .select-filter:focus, .select-sort:focus {
|
||||
background: #f0f4f7;
|
||||
}
|
||||
|
||||
.header-btn-base {
|
||||
border: none;
|
||||
outline: 0;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.header-btn-round {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.item-title-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.rank {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.leaderboard .result {
|
||||
border-top: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.leaderboard .list-item {
|
||||
padding-left: 45px;
|
||||
}
|
||||
|
||||
.leaderboard .list-item_content {
|
||||
padding-right: 60px;
|
||||
}
|
||||
|
||||
.leaderboard-sidebar {
|
||||
padding-left: 0;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.leaderboard-list {
|
||||
padding: var(-padding-sm) 0;
|
||||
min-height: 70vh;
|
||||
}
|
||||
|
||||
.leaderboard-empty-state {
|
||||
align-items: center;
|
||||
height: 70vh;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
|
|
@ -1,409 +0,0 @@
|
|||
frappe.pages["leaderboard"].on_page_load = (wrapper) => {
|
||||
frappe.leaderboard = new Leaderboard(wrapper);
|
||||
|
||||
$(wrapper).bind("show", () => {
|
||||
// Get which leaderboard to show
|
||||
let doctype = frappe.get_route()[1];
|
||||
frappe.leaderboard.show_leaderboard(doctype);
|
||||
});
|
||||
};
|
||||
|
||||
class Leaderboard {
|
||||
constructor(parent) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: parent,
|
||||
title: __("Leaderboard"),
|
||||
single_column: false,
|
||||
card_layout: true,
|
||||
});
|
||||
|
||||
this.parent = parent;
|
||||
this.page = this.parent.page;
|
||||
this.page.sidebar.html(
|
||||
`<ul class="standard-sidebar leaderboard-sidebar overlay-sidebar"></ul>`
|
||||
);
|
||||
this.$sidebar_list = this.page.sidebar.find("ul");
|
||||
|
||||
this.get_leaderboard_config();
|
||||
}
|
||||
|
||||
get_leaderboard_config() {
|
||||
this.doctypes = [];
|
||||
this.filters = {};
|
||||
this.leaderboard_limit = 20;
|
||||
|
||||
frappe
|
||||
.xcall("frappe.desk.page.leaderboard.leaderboard.get_leaderboard_config")
|
||||
.then((config) => {
|
||||
this.leaderboard_config = config;
|
||||
for (let doctype in this.leaderboard_config) {
|
||||
this.doctypes.push(doctype);
|
||||
this.filters[doctype] = this.leaderboard_config[doctype].fields.map(
|
||||
(field) => {
|
||||
if (typeof field === "object") {
|
||||
return field.label || field.fieldname;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// For translation. Do not remove this
|
||||
// __("This Week"), __("This Month"), __("This Quarter"), __("This Year"),
|
||||
// __("Last Week"), __("Last Month"), __("Last Quarter"), __("Last Year"),
|
||||
// __("All Time"), __("Select From Date")
|
||||
this.timespans = [
|
||||
"This Week",
|
||||
"This Month",
|
||||
"This Quarter",
|
||||
"This Year",
|
||||
"Last Week",
|
||||
"Last Month",
|
||||
"Last Quarter",
|
||||
"Last Year",
|
||||
"All Time",
|
||||
"Select Date Range",
|
||||
];
|
||||
|
||||
// for saving current selected filters
|
||||
const _initial_doctype = frappe.get_route()[1] || this.doctypes[0];
|
||||
const _initial_timespan = this.timespans[0];
|
||||
const _initial_filter = this.filters[_initial_doctype];
|
||||
|
||||
this.options = {
|
||||
selected_doctype: _initial_doctype,
|
||||
selected_filter: _initial_filter,
|
||||
selected_filter_item: _initial_filter[0],
|
||||
selected_timespan: _initial_timespan,
|
||||
};
|
||||
|
||||
this.message = null;
|
||||
this.make();
|
||||
});
|
||||
}
|
||||
|
||||
make() {
|
||||
this.$container = $(`<div class="leaderboard page-main-content">
|
||||
<div class="leaderboard-graph"></div>
|
||||
<div class="leaderboard-list"></div>
|
||||
</div>`).appendTo(this.page.main);
|
||||
|
||||
this.$graph_area = this.$container.find(".leaderboard-graph");
|
||||
|
||||
this.doctypes.map((doctype) => {
|
||||
const icon = this.leaderboard_config[doctype].icon;
|
||||
this.get_sidebar_item(doctype, icon).appendTo(this.$sidebar_list);
|
||||
});
|
||||
|
||||
this.setup_leaderboard_fields();
|
||||
|
||||
this.render_selected_doctype();
|
||||
|
||||
this.render_search_box();
|
||||
|
||||
// Get which leaderboard to show
|
||||
let doctype = frappe.get_route()[1];
|
||||
this.show_leaderboard(doctype);
|
||||
}
|
||||
|
||||
setup_leaderboard_fields() {
|
||||
this.company_select = this.page.add_field({
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_default("company"),
|
||||
reqd: 1,
|
||||
change: (e) => {
|
||||
this.make_request();
|
||||
},
|
||||
});
|
||||
|
||||
this.timespan_select = this.page.add_select(
|
||||
__("Timespan"),
|
||||
this.timespans.map((d) => {
|
||||
return { label: __(d), value: d };
|
||||
})
|
||||
);
|
||||
this.create_date_range_field();
|
||||
|
||||
this.type_select = this.page.add_select(
|
||||
__("Field"),
|
||||
this.options.selected_filter.map((d) => {
|
||||
return { label: __(frappe.model.unscrub(d)), value: d };
|
||||
})
|
||||
);
|
||||
|
||||
this.timespan_select.on("change", (e) => {
|
||||
this.options.selected_timespan = e.currentTarget.value;
|
||||
if (this.options.selected_timespan === "Select Date Range") {
|
||||
this.date_range_field.show();
|
||||
} else {
|
||||
this.date_range_field.hide();
|
||||
}
|
||||
this.make_request();
|
||||
});
|
||||
|
||||
this.type_select.on("change", (e) => {
|
||||
this.options.selected_filter_item = e.currentTarget.value;
|
||||
this.make_request();
|
||||
});
|
||||
}
|
||||
|
||||
create_date_range_field() {
|
||||
let timespan_field = $(this.parent).find(
|
||||
`.frappe-control[data-original-title="${__("Timespan")}"]`
|
||||
);
|
||||
this.date_range_field = $(`<div class="from-date-field"></div>`)
|
||||
.insertAfter(timespan_field)
|
||||
.hide();
|
||||
|
||||
let date_field = frappe.ui.form.make_control({
|
||||
df: {
|
||||
fieldtype: "DateRange",
|
||||
fieldname: "selected_date_range",
|
||||
placeholder: __("Date Range"),
|
||||
default: [frappe.datetime.month_start(), frappe.datetime.now_date()],
|
||||
input_class: "input-xs",
|
||||
reqd: 1,
|
||||
change: () => {
|
||||
this.selected_date_range = date_field.get_value();
|
||||
if (this.selected_date_range) this.make_request();
|
||||
},
|
||||
},
|
||||
parent: $(this.parent).find(".from-date-field"),
|
||||
render_input: 1,
|
||||
});
|
||||
}
|
||||
|
||||
render_selected_doctype() {
|
||||
this.$sidebar_list.on("click", "li", (e) => {
|
||||
let $li = $(e.currentTarget);
|
||||
let doctype = $li.find(".doctype-text").attr("doctype-value");
|
||||
|
||||
this.company_select.set_value(
|
||||
frappe.defaults.get_default("company") || this.company_select.get_value()
|
||||
);
|
||||
this.options.selected_doctype = doctype;
|
||||
this.options.selected_filter = this.filters[doctype];
|
||||
this.options.selected_filter_item = this.filters[doctype][0];
|
||||
|
||||
this.type_select.empty().add_options(
|
||||
this.options.selected_filter.map((d) => {
|
||||
return { label: __(frappe.model.unscrub(d)), value: d };
|
||||
})
|
||||
);
|
||||
if (this.leaderboard_config[this.options.selected_doctype].company_disabled) {
|
||||
$(this.parent).find("[data-original-title=Company]").hide();
|
||||
} else {
|
||||
$(this.parent).find("[data-original-title=Company]").show();
|
||||
}
|
||||
|
||||
this.$sidebar_list.find("li").removeClass("active selected");
|
||||
$li.addClass("active selected");
|
||||
|
||||
frappe.set_route("leaderboard", this.options.selected_doctype);
|
||||
this.make_request();
|
||||
});
|
||||
}
|
||||
|
||||
render_search_box() {
|
||||
this.$search_box = $(`<div class="leaderboard-search form-group col-md-3">
|
||||
<input type="text" placeholder=${__(
|
||||
"Search"
|
||||
)} data-element="search" class="form-control leaderboard-search-input input-xs">
|
||||
</div>`);
|
||||
|
||||
$(this.parent).find(".page-form").append(this.$search_box);
|
||||
}
|
||||
|
||||
show_leaderboard(doctype) {
|
||||
if (this.doctypes.length) {
|
||||
if (this.doctypes.includes(doctype)) {
|
||||
this.options.selected_doctype = doctype;
|
||||
this.$sidebar_list
|
||||
.find(`[doctype-value = "${this.options.selected_doctype}"]`)
|
||||
.trigger("click");
|
||||
}
|
||||
|
||||
this.$search_box.find(".leaderboard-search-input").val("");
|
||||
frappe.set_route("leaderboard", this.options.selected_doctype);
|
||||
}
|
||||
}
|
||||
|
||||
make_request() {
|
||||
frappe.model.with_doctype(this.options.selected_doctype, () => {
|
||||
this.get_leaderboard(this.get_leaderboard_data);
|
||||
});
|
||||
}
|
||||
|
||||
get_leaderboard(notify) {
|
||||
let company = this.company_select.get_value();
|
||||
if (!company && !this.leaderboard_config[this.options.selected_doctype].company_disabled) {
|
||||
notify(this, null);
|
||||
frappe.show_alert(__("Please select Company"));
|
||||
return;
|
||||
}
|
||||
frappe
|
||||
.call(this.leaderboard_config[this.options.selected_doctype].method, {
|
||||
date_range: this.get_date_range(),
|
||||
company: company,
|
||||
field: this.options.selected_filter_item,
|
||||
limit: this.leaderboard_limit,
|
||||
})
|
||||
.then((r) => {
|
||||
let results = r.message || [];
|
||||
|
||||
let graph_items = results.slice(0, 10);
|
||||
|
||||
this.$graph_area.show().empty();
|
||||
|
||||
const custom_options = {
|
||||
data: {
|
||||
datasets: [{ values: graph_items.map((d) => d.value) }],
|
||||
labels: graph_items.map((d) => d.name),
|
||||
},
|
||||
format_tooltip_x: (d) => d[this.options.selected_filter_item],
|
||||
height: 140,
|
||||
};
|
||||
frappe.utils.make_chart(".leaderboard-graph", custom_options);
|
||||
|
||||
notify(this, r);
|
||||
});
|
||||
}
|
||||
|
||||
get_leaderboard_data(me, res) {
|
||||
if (res && res.message.length) {
|
||||
me.message = null;
|
||||
me.$container.find(".leaderboard-list").html(me.render_list_view(res.message));
|
||||
frappe.utils.setup_search($(me.parent), ".list-item-container", ".list-id");
|
||||
} else {
|
||||
me.$graph_area.hide();
|
||||
me.message = __("No Items Found");
|
||||
me.$container.find(".leaderboard-list").html(me.render_list_view());
|
||||
}
|
||||
}
|
||||
|
||||
render_list_view(items = []) {
|
||||
var html = `${this.render_message()}
|
||||
<div class="result" style="${this.message ? "display: none;" : ""}">
|
||||
${this.render_result(items)}
|
||||
</div>`;
|
||||
|
||||
return $(html);
|
||||
}
|
||||
|
||||
render_result(items) {
|
||||
var html = `${this.render_list_header()}
|
||||
${this.render_list_result(items)}`;
|
||||
return html;
|
||||
}
|
||||
|
||||
render_list_header() {
|
||||
const _selected_filter = this.options.selected_filter.map((i) => frappe.model.unscrub(i));
|
||||
const fields = ["rank", "name", this.options.selected_filter_item];
|
||||
const filters = fields
|
||||
.map((filter) => {
|
||||
const col = __(frappe.model.unscrub(filter));
|
||||
return `<div class="leaderboard-item list-item_content ellipsis text-muted list-item__content--flex-2
|
||||
header-btn-base ${filter}
|
||||
${col && _selected_filter.indexOf(col) !== -1 ? "text-right" : ""}">
|
||||
<span class="list-col-title ellipsis">
|
||||
${col}
|
||||
</span>
|
||||
</div>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return `<div class="list-headers">
|
||||
<div class="list-item" data-list-renderer="List">${filters}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render_list_result(items) {
|
||||
let _html = items
|
||||
.map((item, index) => {
|
||||
const $value = $(this.get_item_html(item, index + 1));
|
||||
const $item_container = $(`<div class="list-item-container">`).append($value);
|
||||
return $item_container[0].outerHTML;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return `<div class="result-list">
|
||||
<div class="list-items">
|
||||
${_html}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render_message() {
|
||||
const display_class = this.message ? "" : "hide";
|
||||
return `<div class="leaderboard-empty-state ${display_class}">
|
||||
<div class="no-result text-center">
|
||||
<img src="/assets/frappe/images/ui-states/search-empty-state.svg"
|
||||
alt="Empty State"
|
||||
class="null-state"
|
||||
>
|
||||
<div class="empty-state-text">${this.message}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_item_html(item, index) {
|
||||
const fields = this.leaderboard_config[this.options.selected_doctype].fields;
|
||||
const value = frappe.format(
|
||||
item.value,
|
||||
fields.find((field) => {
|
||||
let fieldname = field.fieldname || field;
|
||||
return fieldname === this.options.selected_filter_item;
|
||||
})
|
||||
);
|
||||
|
||||
const link = `/app/${frappe.router.slug(this.options.selected_doctype)}/${item.name}`;
|
||||
const name_html = item.formatted_name
|
||||
? `<span class="text-muted ellipsis list-id">${item.formatted_name}</span>`
|
||||
: `<a class="grey list-id ellipsis" href="${link}"> ${item.name} </a>`;
|
||||
return `<div class="list-item">
|
||||
<div class="list-item_content ellipsis list-item__content--flex-2 rank text-center">
|
||||
<span class="text-muted ellipsis">${index}</span>
|
||||
</div>
|
||||
<div class="list-item_content ellipsis list-item__content--flex-2 name">
|
||||
${name_html}
|
||||
</div>
|
||||
<div class="list-item_content ellipsis list-item__content--flex-2 value text-right">
|
||||
<span class="text-muted ellipsis">${value}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
get_sidebar_item(item, icon) {
|
||||
let icon_html = icon ? frappe.utils.icon(icon, "md") : "";
|
||||
return $(`<li class="standard-sidebar-item">
|
||||
<span>${icon_html}</span>
|
||||
<a class="sidebar-link">
|
||||
<span class="doctype-text" doctype-value="${item}">${__(item)}</span>
|
||||
</a>
|
||||
</li>`);
|
||||
}
|
||||
|
||||
get_date_range() {
|
||||
let timespan = this.options.selected_timespan.toLowerCase();
|
||||
let current_date = frappe.datetime.now_date();
|
||||
let date_range_map = {
|
||||
"this week": [frappe.datetime.week_start(), frappe.datetime.week_end()],
|
||||
"this month": [frappe.datetime.month_start(), frappe.datetime.month_end()],
|
||||
"this quarter": [frappe.datetime.quarter_start(), frappe.datetime.quarter_end()],
|
||||
"this year": [frappe.datetime.year_start(), frappe.datetime.year_end()],
|
||||
"last week": [frappe.datetime.add_days(current_date, -7), current_date],
|
||||
"last month": [frappe.datetime.add_months(current_date, -1), current_date],
|
||||
"last quarter": [frappe.datetime.add_months(current_date, -3), current_date],
|
||||
"last year": [frappe.datetime.add_months(current_date, -12), current_date],
|
||||
"all time": null,
|
||||
"select date range": this.selected_date_range || [
|
||||
frappe.datetime.month_start(),
|
||||
current_date,
|
||||
],
|
||||
};
|
||||
return date_range_map[timespan];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"content": null,
|
||||
"creation": "2017-06-06 02:54:24.785360",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"idx": 0,
|
||||
"modified": "2019-09-27 17:44:51.909947",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "leaderboard",
|
||||
"owner": "Administrator",
|
||||
"page_name": "leaderboard",
|
||||
"roles": [],
|
||||
"script": null,
|
||||
"standard": "Yes",
|
||||
"style": null,
|
||||
"system_page": 0,
|
||||
"title": "Leaderboard"
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
import frappe
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_leaderboard_config():
|
||||
leaderboard_config = frappe._dict()
|
||||
leaderboard_hooks = frappe.get_hooks("leaderboards")
|
||||
for hook in leaderboard_hooks:
|
||||
leaderboard_config.update(frappe.get_attr(hook)())
|
||||
|
||||
return leaderboard_config
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
.recent-activity .new-timeline {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.recent-activity .new-timeline:before {
|
||||
top: 25px;
|
||||
}
|
||||
|
||||
.recent-activity-title {
|
||||
font-weight: 700;
|
||||
font-size: var(--text-xl);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.recent-activity .recent-activity-footer {
|
||||
margin-left: calc(var(--timeline-left-padding) + var(--timeline-item-left-margin));
|
||||
max-width: var(--timeline-content-max-width);
|
||||
}
|
||||
|
||||
.recent-activity .show-more-activity-btn {
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: max-content;
|
||||
margin-top: 35px;
|
||||
font-size: var(--text-md);
|
||||
}
|
||||
|
||||
.recent-activity {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<div class="row user-profile">
|
||||
<div class="col-md-12">
|
||||
<div class="performance-graphs">
|
||||
<div class="frappe-card chart-column-container heatmap-container hidden-xs hidden-sm">
|
||||
<div class="title-area">
|
||||
<h4 class="card-title">{%=__("Overview") %}</h4>
|
||||
<div class="card-options heatmap-options"></div>
|
||||
</div>
|
||||
<div class="chart-wrapper performance-heatmap">
|
||||
<div class="null-state">
|
||||
<span>{%=__("No Data to Show") %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="frappe-card chart-column-container percentage-chart-container">
|
||||
<div class="title-area">
|
||||
<h4 class="card-title">{%=__("Type Distribution") %}</h4>
|
||||
<div class="card-options percentage-chart-options"></div>
|
||||
</div>
|
||||
<div class="chart-wrapper performance-percentage-chart">
|
||||
<div class="null-state">
|
||||
<span>{%=__("No Data to Show") %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="frappe-card chart-column-container line-chart-container">
|
||||
<div class="title-area">
|
||||
<h4 class="card-title">{%=__("Energy Points") %}</h4>
|
||||
<div class="card-options line-chart-options"></div>
|
||||
</div>
|
||||
<div class="chart-wrapper performance-line-chart">
|
||||
<div class="null-state">
|
||||
<span>{%=__("No Data to Show") %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="recent-activity">
|
||||
<div class="recent-activity-title">{%=__("Recent Activity") %}</div>
|
||||
<div class="recent-activity-list"></div>
|
||||
<div class="recent-activity-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
frappe.pages["user-profile"].on_page_load = function (wrapper) {
|
||||
frappe.require("user_profile_controller.bundle.js", () => {
|
||||
let user_profile = new frappe.ui.UserProfile(wrapper);
|
||||
user_profile.show();
|
||||
});
|
||||
};
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"content": null,
|
||||
"creation": "2019-07-22 12:23:38.425877",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"idx": 0,
|
||||
"modified": "2020-03-02 15:17:13.041650",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "user-profile",
|
||||
"owner": "Administrator",
|
||||
"page_name": "User Profile",
|
||||
"roles": [
|
||||
{
|
||||
"role": "All"
|
||||
}
|
||||
],
|
||||
"script": null,
|
||||
"standard": "Yes",
|
||||
"style": null,
|
||||
"system_page": 0,
|
||||
"title": "User Profile"
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
from datetime import datetime
|
||||
|
||||
import frappe
|
||||
from frappe.query_builder import Interval, Order
|
||||
from frappe.query_builder.functions import Date, Sum, UnixTimestamp
|
||||
from frappe.utils import getdate
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_energy_points_heatmap_data(user, date):
|
||||
try:
|
||||
date = getdate(date)
|
||||
except Exception:
|
||||
date = getdate()
|
||||
|
||||
eps_log = frappe.qb.DocType("Energy Point Log")
|
||||
|
||||
return dict(
|
||||
frappe.qb.from_(eps_log)
|
||||
.select(UnixTimestamp(Date(eps_log.creation)), Sum(eps_log.points))
|
||||
.where(eps_log.user == user)
|
||||
.where(eps_log["type"] != "Review")
|
||||
.where(Date(eps_log.creation) > Date(date) - Interval(years=1))
|
||||
.where(Date(eps_log.creation) < Date(date) + Interval(years=1))
|
||||
.groupby(Date(eps_log.creation))
|
||||
.orderby(Date(eps_log.creation), order=Order.asc)
|
||||
.run()
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_energy_points_percentage_chart_data(user, field):
|
||||
result = frappe.get_all(
|
||||
"Energy Point Log",
|
||||
filters={"user": user, "type": ["!=", "Review"]},
|
||||
group_by=field,
|
||||
order_by=field,
|
||||
fields=[field, "ABS(sum(points)) as points"],
|
||||
as_list=True,
|
||||
)
|
||||
|
||||
return {
|
||||
"labels": [r[0] for r in result if r[0] is not None],
|
||||
"datasets": [{"values": [r[1] for r in result]}],
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_user_rank(user):
|
||||
month_start = datetime.today().replace(day=1)
|
||||
monthly_rank = frappe.get_all(
|
||||
"Energy Point Log",
|
||||
group_by="`tabEnergy Point Log`.`user`",
|
||||
filters={"creation": [">", month_start], "type": ["!=", "Review"]},
|
||||
fields=["user", "sum(points)"],
|
||||
order_by="sum(points) desc",
|
||||
as_list=True,
|
||||
)
|
||||
|
||||
all_time_rank = frappe.get_all(
|
||||
"Energy Point Log",
|
||||
group_by="`tabEnergy Point Log`.`user`",
|
||||
filters={"type": ["!=", "Review"]},
|
||||
fields=["user", "sum(points)"],
|
||||
order_by="sum(points) desc",
|
||||
as_list=True,
|
||||
)
|
||||
|
||||
return {
|
||||
"monthly_rank": [i + 1 for i, r in enumerate(monthly_rank) if r[0] == user],
|
||||
"all_time_rank": [i + 1 for i, r in enumerate(all_time_rank) if r[0] == user],
|
||||
}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_profile_info(profile_info):
|
||||
profile_info = frappe.parse_json(profile_info)
|
||||
keys = ["location", "interest", "user_image", "bio"]
|
||||
|
||||
for key in keys:
|
||||
if key not in profile_info:
|
||||
profile_info[key] = None
|
||||
|
||||
user = frappe.get_doc("User", frappe.session.user)
|
||||
user.update(profile_info)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_energy_points_list(start, limit, user):
|
||||
return frappe.db.get_list(
|
||||
"Energy Point Log",
|
||||
filters={"user": user, "type": ["!=", "Review"]},
|
||||
fields=[
|
||||
"name",
|
||||
"user",
|
||||
"points",
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
"reason",
|
||||
"type",
|
||||
"seen",
|
||||
"rule",
|
||||
"owner",
|
||||
"creation",
|
||||
"revert_of",
|
||||
],
|
||||
start=start,
|
||||
limit=limit,
|
||||
order_by="creation desc",
|
||||
)
|
||||
|
|
@ -1,494 +0,0 @@
|
|||
import BaseTimeline from "../../../public/js/frappe/form/footer/base_timeline";
|
||||
frappe.provide("frappe.energy_points");
|
||||
|
||||
class UserProfile {
|
||||
constructor(wrapper) {
|
||||
this.wrapper = $(wrapper);
|
||||
this.page = frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
});
|
||||
this.sidebar = this.wrapper.find(".layout-side-section");
|
||||
this.main_section = this.wrapper.find(".layout-main-section");
|
||||
this.wrapper.bind("show", () => {
|
||||
this.show();
|
||||
});
|
||||
}
|
||||
|
||||
show() {
|
||||
let route = frappe.get_route();
|
||||
this.user_id = route[1] || frappe.session.user;
|
||||
frappe.dom.freeze(__("Loading user profile") + "...");
|
||||
frappe.db.exists("User", this.user_id).then((exists) => {
|
||||
frappe.dom.unfreeze();
|
||||
if (exists) {
|
||||
this.make_user_profile();
|
||||
} else {
|
||||
frappe.msgprint(__("User does not exist"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
make_user_profile() {
|
||||
this.user = frappe.user_info(this.user_id);
|
||||
this.page.set_title(this.user.fullname);
|
||||
this.setup_user_search();
|
||||
this.main_section.empty().append(frappe.render_template("user_profile"));
|
||||
this.energy_points = 0;
|
||||
this.review_points = 0;
|
||||
this.rank = 0;
|
||||
this.month_rank = 0;
|
||||
this.render_user_details();
|
||||
this.render_points_and_rank();
|
||||
this.render_heatmap();
|
||||
this.render_line_chart();
|
||||
this.render_percentage_chart("type", "Type Distribution");
|
||||
this.create_percentage_chart_filters();
|
||||
this.setup_user_activity_timeline();
|
||||
}
|
||||
|
||||
setup_user_search() {
|
||||
this.$user_search_button = this.page.set_secondary_action(
|
||||
__("Change User"),
|
||||
() => this.show_user_search_dialog(),
|
||||
{ icon: "change", size: "sm" }
|
||||
);
|
||||
}
|
||||
|
||||
show_user_search_dialog() {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __("Change User"),
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Link",
|
||||
fieldname: "user",
|
||||
options: "User",
|
||||
label: __("User"),
|
||||
},
|
||||
],
|
||||
primary_action_label: __("Go"),
|
||||
primary_action: ({ user }) => {
|
||||
dialog.hide();
|
||||
frappe.set_route("user-profile", user);
|
||||
},
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
render_heatmap() {
|
||||
this.heatmap = new frappe.Chart(".performance-heatmap", {
|
||||
type: "heatmap",
|
||||
countLabel: __("Energy Points"),
|
||||
data: {},
|
||||
discreteDomains: 1,
|
||||
radius: 3,
|
||||
height: 150,
|
||||
});
|
||||
this.update_heatmap_data();
|
||||
this.create_heatmap_chart_filters();
|
||||
}
|
||||
|
||||
update_heatmap_data(date_from) {
|
||||
frappe
|
||||
.xcall("frappe.desk.page.user_profile.user_profile.get_energy_points_heatmap_data", {
|
||||
user: this.user_id,
|
||||
date: date_from || frappe.datetime.year_start(),
|
||||
})
|
||||
.then((r) => {
|
||||
this.heatmap.update({ dataPoints: r });
|
||||
});
|
||||
}
|
||||
|
||||
render_line_chart() {
|
||||
this.line_chart_filters = [
|
||||
["Energy Point Log", "user", "=", this.user_id, false],
|
||||
["Energy Point Log", "type", "!=", "Review", false],
|
||||
];
|
||||
|
||||
this.line_chart_config = {
|
||||
timespan: "Last Month",
|
||||
time_interval: "Daily",
|
||||
type: "Line",
|
||||
value_based_on: "points",
|
||||
chart_type: "Sum",
|
||||
document_type: "Energy Point Log",
|
||||
name: __("Energy Points"),
|
||||
width: "half",
|
||||
based_on: "creation",
|
||||
};
|
||||
|
||||
this.line_chart = new frappe.Chart(".performance-line-chart", {
|
||||
type: "line",
|
||||
height: 200,
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{}],
|
||||
},
|
||||
colors: ["purple"],
|
||||
axisOptions: {
|
||||
xIsSeries: 1,
|
||||
},
|
||||
});
|
||||
this.update_line_chart_data();
|
||||
this.create_line_chart_filters();
|
||||
}
|
||||
|
||||
update_line_chart_data() {
|
||||
this.line_chart_config.filters_json = JSON.stringify(this.line_chart_filters);
|
||||
|
||||
frappe
|
||||
.xcall("frappe.desk.doctype.dashboard_chart.dashboard_chart.get", {
|
||||
chart: this.line_chart_config,
|
||||
no_cache: 1,
|
||||
})
|
||||
.then((chart) => {
|
||||
this.line_chart.update(chart);
|
||||
});
|
||||
}
|
||||
|
||||
render_percentage_chart(field, title) {
|
||||
frappe
|
||||
.xcall(
|
||||
"frappe.desk.page.user_profile.user_profile.get_energy_points_percentage_chart_data",
|
||||
{
|
||||
user: this.user_id,
|
||||
field: field,
|
||||
}
|
||||
)
|
||||
.then((chart) => {
|
||||
if (chart.labels.length) {
|
||||
this.percentage_chart = new frappe.Chart(".performance-percentage-chart", {
|
||||
type: "percentage",
|
||||
data: {
|
||||
labels: chart.labels,
|
||||
datasets: chart.datasets,
|
||||
},
|
||||
truncateLegends: 1,
|
||||
barOptions: {
|
||||
height: 11,
|
||||
depth: 1,
|
||||
},
|
||||
height: 200,
|
||||
maxSlices: 8,
|
||||
colors: [
|
||||
"purple",
|
||||
"blue",
|
||||
"cyan",
|
||||
"teal",
|
||||
"pink",
|
||||
"red",
|
||||
"orange",
|
||||
"yellow",
|
||||
],
|
||||
});
|
||||
} else {
|
||||
this.wrapper.find(".percentage-chart-container").hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
create_line_chart_filters() {
|
||||
let filters = [
|
||||
{
|
||||
label: "All",
|
||||
options: ["All", "Auto", "Criticism", "Appreciation", "Revert"],
|
||||
action: (selected_item) => {
|
||||
if (selected_item === "All") {
|
||||
this.line_chart_filters = [
|
||||
["Energy Point Log", "user", "=", this.user_id, false],
|
||||
["Energy Point Log", "type", "!=", "Review", false],
|
||||
];
|
||||
} else {
|
||||
this.line_chart_filters[1] = [
|
||||
"Energy Point Log",
|
||||
"type",
|
||||
"=",
|
||||
selected_item,
|
||||
false,
|
||||
];
|
||||
}
|
||||
this.update_line_chart_data();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Last Month",
|
||||
options: ["Last Week", "Last Month", "Last Quarter", "Last Year"],
|
||||
action: (selected_item) => {
|
||||
this.line_chart_config.timespan = selected_item;
|
||||
this.update_line_chart_data();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Daily",
|
||||
options: ["Daily", "Weekly", "Monthly"],
|
||||
action: (selected_item) => {
|
||||
this.line_chart_config.time_interval = selected_item;
|
||||
this.update_line_chart_data();
|
||||
},
|
||||
},
|
||||
];
|
||||
frappe.dashboard_utils.render_chart_filters(
|
||||
filters,
|
||||
"chart-filter",
|
||||
".line-chart-options",
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
create_percentage_chart_filters() {
|
||||
let filters = [
|
||||
{
|
||||
label: "Type",
|
||||
options: ["Type", "Reference Doctype", "Rule"],
|
||||
fieldnames: ["type", "reference_doctype", "rule"],
|
||||
action: (selected_item, fieldname) => {
|
||||
let title = selected_item + " Distribution";
|
||||
this.render_percentage_chart(fieldname, title);
|
||||
},
|
||||
},
|
||||
];
|
||||
frappe.dashboard_utils.render_chart_filters(
|
||||
filters,
|
||||
"chart-filter",
|
||||
".percentage-chart-options"
|
||||
);
|
||||
}
|
||||
|
||||
create_heatmap_chart_filters() {
|
||||
let filters = [
|
||||
{
|
||||
label: frappe.dashboard_utils.get_year(frappe.datetime.now_date()),
|
||||
options: frappe.dashboard_utils.get_years_since_creation(
|
||||
frappe.boot.user.creation
|
||||
),
|
||||
action: (selected_item) => {
|
||||
this.update_heatmap_data(frappe.datetime.obj_to_str(selected_item));
|
||||
},
|
||||
},
|
||||
];
|
||||
frappe.dashboard_utils.render_chart_filters(filters, "chart-filter", ".heatmap-options");
|
||||
}
|
||||
|
||||
edit_profile() {
|
||||
let edit_profile_dialog = new frappe.ui.Dialog({
|
||||
title: __("Edit Profile"),
|
||||
fields: [
|
||||
{
|
||||
fieldtype: "Attach Image",
|
||||
fieldname: "user_image",
|
||||
label: "Profile Image",
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
fieldname: "interest",
|
||||
label: "Interests",
|
||||
},
|
||||
{
|
||||
fieldtype: "Column Break",
|
||||
},
|
||||
{
|
||||
fieldtype: "Data",
|
||||
fieldname: "location",
|
||||
label: "Location",
|
||||
},
|
||||
{
|
||||
fieldtype: "Section Break",
|
||||
fieldname: "Interest",
|
||||
},
|
||||
{
|
||||
fieldtype: "Small Text",
|
||||
fieldname: "bio",
|
||||
label: "Bio",
|
||||
},
|
||||
],
|
||||
primary_action: (values) => {
|
||||
edit_profile_dialog.disable_primary_action();
|
||||
frappe
|
||||
.xcall("frappe.desk.page.user_profile.user_profile.update_profile_info", {
|
||||
profile_info: values,
|
||||
})
|
||||
.then((user) => {
|
||||
user.image = user.user_image;
|
||||
this.user = Object.assign(values, user);
|
||||
edit_profile_dialog.hide();
|
||||
this.render_user_details();
|
||||
})
|
||||
.finally(() => {
|
||||
edit_profile_dialog.enable_primary_action();
|
||||
});
|
||||
},
|
||||
primary_action_label: __("Save"),
|
||||
});
|
||||
|
||||
edit_profile_dialog.set_values({
|
||||
user_image: this.user.image,
|
||||
location: this.user.location,
|
||||
interest: this.user.interest,
|
||||
bio: this.user.bio,
|
||||
});
|
||||
edit_profile_dialog.show();
|
||||
}
|
||||
|
||||
render_user_details() {
|
||||
this.sidebar.empty().append(
|
||||
frappe.render_template("user_profile_sidebar", {
|
||||
user_image: this.user.image,
|
||||
user_abbr: this.user.abbr,
|
||||
user_location: this.user.location,
|
||||
user_interest: this.user.interest,
|
||||
user_bio: this.user.bio,
|
||||
})
|
||||
);
|
||||
|
||||
this.setup_user_profile_links();
|
||||
}
|
||||
|
||||
setup_user_profile_links() {
|
||||
if (this.user_id !== frappe.session.user) {
|
||||
this.wrapper.find(".profile-links").hide();
|
||||
} else {
|
||||
this.wrapper.find(".edit-profile-link").on("click", () => {
|
||||
this.edit_profile();
|
||||
});
|
||||
|
||||
this.wrapper.find(".user-settings-link").on("click", () => {
|
||||
this.go_to_user_settings();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get_user_rank() {
|
||||
return frappe
|
||||
.xcall("frappe.desk.page.user_profile.user_profile.get_user_rank", {
|
||||
user: this.user_id,
|
||||
})
|
||||
.then((r) => {
|
||||
if (r.monthly_rank.length) this.month_rank = r.monthly_rank[0];
|
||||
if (r.all_time_rank.length) this.rank = r.all_time_rank[0];
|
||||
});
|
||||
}
|
||||
|
||||
get_user_points() {
|
||||
return frappe
|
||||
.xcall(
|
||||
"frappe.social.doctype.energy_point_log.energy_point_log.get_user_energy_and_review_points",
|
||||
{
|
||||
user: this.user_id,
|
||||
}
|
||||
)
|
||||
.then((r) => {
|
||||
if (r[this.user_id]) {
|
||||
this.energy_points = r[this.user_id].energy_points;
|
||||
this.review_points = r[this.user_id].review_points;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render_points_and_rank() {
|
||||
let $profile_details = this.wrapper.find(".user-stats");
|
||||
let $profile_details_wrapper = this.wrapper.find(".user-stats-detail");
|
||||
|
||||
const _get_stat_dom = (value, label, icon) => {
|
||||
return `<div class="user-stats-item mt-4">
|
||||
${frappe.utils.icon(icon, "lg", "no-stroke")}
|
||||
<div>
|
||||
<div class="stat-value">${value}</div>
|
||||
<div class="stat-label">${label}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
this.get_user_rank().then(() => {
|
||||
this.get_user_points().then(() => {
|
||||
let html = $(`
|
||||
${_get_stat_dom(this.energy_points, __("Energy Points"), "color-energy-points")}
|
||||
${_get_stat_dom(this.review_points, __("Review Points"), "color-review-points")}
|
||||
${_get_stat_dom(this.rank, __("Rank"), "color-rank")}
|
||||
${_get_stat_dom(this.month_rank, __("Monthly Rank"), "color-monthly-rank")}
|
||||
`);
|
||||
|
||||
$profile_details.append(html);
|
||||
$profile_details_wrapper.removeClass("hide");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
go_to_user_settings() {
|
||||
frappe.set_route("Form", "User", this.user_id);
|
||||
}
|
||||
|
||||
setup_user_activity_timeline() {
|
||||
this.user_activity_timeline = new UserProfileTimeline({
|
||||
parent: this.wrapper.find(".recent-activity-list"),
|
||||
footer: this.wrapper.find(".recent-activity-footer"),
|
||||
user: this.user_id,
|
||||
});
|
||||
|
||||
this.user_activity_timeline.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
class UserProfileTimeline extends BaseTimeline {
|
||||
make() {
|
||||
super.make();
|
||||
this.activity_start = 0;
|
||||
this.activity_limit = 20;
|
||||
this.setup_show_more_activity();
|
||||
}
|
||||
prepare_timeline_contents() {
|
||||
return this.get_user_activity_data().then((activities) => {
|
||||
if (!activities.length) {
|
||||
this.show_more_button.hide();
|
||||
this.timeline_wrapper.html(`<div>${__("No activities to show")}</div>`);
|
||||
return;
|
||||
}
|
||||
this.show_more_button.toggle(activities.length === this.activity_limit);
|
||||
this.timeline_items = activities.map((activity) =>
|
||||
this.get_activity_timeline_item(activity)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
get_user_activity_data() {
|
||||
return frappe.xcall("frappe.desk.page.user_profile.user_profile.get_energy_points_list", {
|
||||
start: this.activity_start,
|
||||
limit: this.activity_limit,
|
||||
user: this.user,
|
||||
});
|
||||
}
|
||||
|
||||
get_activity_timeline_item(data) {
|
||||
let icon =
|
||||
data.type == "Appreciation" ? "clap" : data.type == "Criticism" ? "criticize" : null;
|
||||
return {
|
||||
icon: icon,
|
||||
creation: data.creation,
|
||||
is_card: true,
|
||||
content: frappe.energy_points.format_history_log(data),
|
||||
};
|
||||
}
|
||||
|
||||
setup_show_more_activity() {
|
||||
this.show_more_button = $(
|
||||
`<a class="show-more-activity-btn">${__("Show More Activity")}</a>`
|
||||
);
|
||||
this.show_more_button.hide();
|
||||
this.footer.append(this.show_more_button);
|
||||
this.show_more_button.on("click", () => this.show_more_activity());
|
||||
}
|
||||
|
||||
show_more_activity() {
|
||||
this.activity_start += this.activity_limit;
|
||||
this.get_user_activity_data().then((activities) => {
|
||||
if (!activities.length || activities.length < this.activity_limit) {
|
||||
this.show_more_button.hide();
|
||||
}
|
||||
let timeline_items = activities.map((activity) =>
|
||||
this.get_activity_timeline_item(activity)
|
||||
);
|
||||
timeline_items.map((item) => this.add_timeline_item(item, true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
frappe.provide("frappe.ui");
|
||||
frappe.ui.UserProfile = UserProfile;
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<div class="user-profile-sidebar">
|
||||
<div class="user-image-container hidden-xs hidden-sm">
|
||||
<div class="sidebar-image-wrapper">
|
||||
{% if user_image %}
|
||||
<img class="sidebar-image" src="{{ user_image }}" />
|
||||
{% else %}
|
||||
<div class="sidebar-standard-image hide">
|
||||
<div class="standard-image"></div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-details">
|
||||
{% if user_bio %}
|
||||
<div class="detail-item">
|
||||
<p class="user-bio">{{user_bio}}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user_location %}
|
||||
<div class="detail-item">
|
||||
<h5>{%=__("Intro") %}</h5>
|
||||
<p class="user-location text-muted mt-3">
|
||||
<span class="interest-icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.4827 6.57143C12.4827 9.39265 7.91127 14 7.91127 14C7.91127 14 3.33984 9.39265 3.33984 6.57143C3.33984 3.67837 5.70197 2 7.91127 2C10.1206 2 12.4827 3.67837 12.4827 6.57143Z" stroke="#192734" stroke-miterlimit="10" stroke-linecap="square" stroke-linejoin="round"/>
|
||||
<path d="M7.91354 7.87763C8.6349 7.87763 9.21967 7.29285 9.21967 6.5715C9.21967 5.85015 8.6349 5.26538 7.91354 5.26538C7.19219 5.26538 6.60742 5.85015 6.60742 6.5715C6.60742 7.29285 7.19219 7.87763 7.91354 7.87763Z" stroke="#192734" stroke-miterlimit="10" stroke-linecap="square"/>
|
||||
</svg>
|
||||
</span>
|
||||
{{user_location}}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if user_interest %}
|
||||
<div class="detail-item">
|
||||
<h5>{%=__("Interests") %}</h5>
|
||||
<p class="user-interests text-muted mt-3">
|
||||
{{user_interest}}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="detail-item user-stats-detail hide">
|
||||
<h5>{%=__("Details") %}</h5>
|
||||
<div class="user-stats mt-3">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-links detail-item">
|
||||
<p><a class="edit-profile-link">{%=__("Edit Profile") %}</a></p>
|
||||
<p><a class="user-settings-link">{%=__("User Settings") %}</a></p>
|
||||
<p>
|
||||
<a class="leaderboard-link" href="/app/leaderboard/User"
|
||||
>{%=__("Leaderboard") %}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -81,8 +81,6 @@ email_append_to = ["Event", "ToDo", "Communication"]
|
|||
|
||||
calendars = ["Event"]
|
||||
|
||||
leaderboards = "frappe.desk.leaderboard.get_leaderboards"
|
||||
|
||||
# login
|
||||
|
||||
on_session_creation = [
|
||||
|
|
@ -177,7 +175,6 @@ doc_events = {
|
|||
"frappe.core.doctype.file.utils.attach_files_to_document",
|
||||
],
|
||||
"on_change": [
|
||||
"frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points",
|
||||
"frappe.automation.doctype.milestone_tracker.milestone_tracker.evaluate_milestone",
|
||||
],
|
||||
"after_delete": ["frappe.core.doctype.permission_log.permission_log.make_perm_log"],
|
||||
|
|
@ -248,7 +245,6 @@ scheduler_events = {
|
|||
"frappe.email.doctype.notification.notification.trigger_daily_alerts",
|
||||
"frappe.website.doctype.personal_data_deletion_request.personal_data_deletion_request.remove_unverified_record",
|
||||
"frappe.desk.form.document_follow.send_daily_updates",
|
||||
"frappe.social.doctype.energy_point_settings.energy_point_settings.allocate_review_points",
|
||||
"frappe.integrations.doctype.google_contacts.google_contacts.sync",
|
||||
"frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat_entry",
|
||||
],
|
||||
|
|
@ -263,13 +259,11 @@ scheduler_events = {
|
|||
"frappe.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_weekly",
|
||||
"frappe.desk.form.document_follow.send_weekly_updates",
|
||||
"frappe.utils.change_log.check_for_update",
|
||||
"frappe.social.doctype.energy_point_log.energy_point_log.send_weekly_summary",
|
||||
"frappe.integrations.doctype.google_drive.google_drive.weekly_backup",
|
||||
"frappe.desk.doctype.changelog_feed.changelog_feed.fetch_changelog_feed",
|
||||
],
|
||||
"monthly": [
|
||||
"frappe.email.doctype.auto_email_report.auto_email_report.send_monthly",
|
||||
"frappe.social.doctype.energy_point_log.energy_point_log.send_monthly_summary",
|
||||
],
|
||||
"monthly_long": [
|
||||
"frappe.integrations.doctype.s3_backup_settings.s3_backup_settings.take_backups_monthly"
|
||||
|
|
|
|||
|
|
@ -199,8 +199,6 @@ def rename_doc(
|
|||
|
||||
rename_versions(doctype, old, new)
|
||||
|
||||
rename_eps_records(doctype, old, new)
|
||||
|
||||
# call after_rename
|
||||
new_doc = frappe.get_doc(doctype, new)
|
||||
|
||||
|
|
@ -329,14 +327,6 @@ def rename_versions(doctype: str, old: str, new: str) -> None:
|
|||
).run()
|
||||
|
||||
|
||||
def rename_eps_records(doctype: str, old: str, new: str) -> None:
|
||||
EPL = frappe.qb.DocType("Energy Point Log")
|
||||
|
||||
frappe.qb.update(EPL).set(EPL.reference_name, new).where(
|
||||
(EPL.reference_doctype == doctype) & (EPL.reference_name == old)
|
||||
).run()
|
||||
|
||||
|
||||
def rename_parent_and_child(doctype: str, old: str, new: str, meta: "Meta") -> None:
|
||||
frappe.qb.update(doctype).set("name", new).where(Field("name") == old).run()
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ def remove_orphan_doctypes():
|
|||
continue
|
||||
try:
|
||||
get_controller(doctype=doctype)
|
||||
except ImportError:
|
||||
except (ImportError, frappe.DoesNotExistError):
|
||||
orphan_doctypes.append(doctype)
|
||||
except Exception:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ ignore_doctypes = {
|
|||
"Access Log",
|
||||
"View Log",
|
||||
"Activity Log",
|
||||
"Energy Point Log",
|
||||
"Notification Log",
|
||||
"Email Queue",
|
||||
"DocShare",
|
||||
|
|
|
|||
|
|
@ -8,5 +8,4 @@ Desk
|
|||
Integrations
|
||||
Printing
|
||||
Contacts
|
||||
Social
|
||||
Automation
|
||||
|
|
@ -245,4 +245,4 @@ frappe.desk.doctype.workspace.patches.update_app
|
|||
frappe.patches.v16_0.move_role_desk_settings_to_user
|
||||
frappe.printing.doctype.print_format.patches.sets_wkhtmltopdf_as_default_for_pdf_generator_field
|
||||
frappe.patches.v14_0.fix_user_settings_collation
|
||||
execute:frappe.core.doctype.system_settings.system_settings.sync_system_settings
|
||||
execute:frappe.core.doctype.system_settings.system_settings.sync_system_settings
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ import "./frappe/ui/workspace_sidebar_loading_skeleton.html";
|
|||
import "./frappe/desk.js";
|
||||
import "./frappe/query_string.js";
|
||||
|
||||
import "./frappe/utils/energy_point_utils.js";
|
||||
import "./frappe/utils/dashboard_utils.js";
|
||||
import "./frappe/ui/chart.js";
|
||||
import "./frappe/ui/datatable.js";
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ frappe.Application = class Application {
|
|||
this.set_favicon();
|
||||
this.set_fullwidth_if_enabled();
|
||||
this.add_browser_class();
|
||||
this.setup_energy_point_listeners();
|
||||
this.setup_copy_doc_listener();
|
||||
this.setup_broadcast_listeners();
|
||||
|
||||
|
|
@ -499,12 +498,6 @@ frappe.Application = class Application {
|
|||
}
|
||||
}
|
||||
|
||||
setup_energy_point_listeners() {
|
||||
frappe.realtime.on("energy_point_alert", (message) => {
|
||||
frappe.show_alert(message);
|
||||
});
|
||||
}
|
||||
|
||||
setup_copy_doc_listener() {
|
||||
$("body").on("paste", (e) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -148,4 +148,5 @@ class BaseTimeline {
|
|||
}
|
||||
}
|
||||
|
||||
frappe.ui.BaseTimeline = BaseTimeline;
|
||||
export default BaseTimeline;
|
||||
|
|
|
|||
|
|
@ -161,7 +161,6 @@ class FormTimeline extends BaseTimeline {
|
|||
this.timeline_items.push(...this.get_comment_timeline_contents());
|
||||
if (!this.only_communication) {
|
||||
this.timeline_items.push(...this.get_view_timeline_contents());
|
||||
this.timeline_items.push(...this.get_energy_point_timeline_contents());
|
||||
this.timeline_items.push(...this.get_version_timeline_contents());
|
||||
this.timeline_items.push(...this.get_share_timeline_contents());
|
||||
this.timeline_items.push(...this.get_workflow_timeline_contents());
|
||||
|
|
@ -494,36 +493,30 @@ class FormTimeline extends BaseTimeline {
|
|||
get_custom_timeline_contents() {
|
||||
let custom_timeline_contents = [];
|
||||
(this.doc_info.additional_timeline_content || []).forEach((custom_item) => {
|
||||
custom_timeline_contents.push({
|
||||
icon: custom_item.icon,
|
||||
icon_size: "sm",
|
||||
is_card: custom_item.is_card,
|
||||
creation: custom_item.creation,
|
||||
content:
|
||||
custom_item.content ||
|
||||
frappe.render_template(custom_item.template, custom_item.template_data),
|
||||
});
|
||||
if (custom_item.timeline_badge) {
|
||||
custom_timeline_contents.push({
|
||||
timeline_badge: custom_item.timeline_badge,
|
||||
creation: custom_item.creation,
|
||||
content: frappe.utils.eval(custom_item.method, {
|
||||
custom_item: custom_item,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
custom_timeline_contents.push({
|
||||
icon: custom_item.icon,
|
||||
timeline_badge: custom_item.timeline_badge,
|
||||
icon_size: "sm",
|
||||
is_card: custom_item.is_card,
|
||||
creation: custom_item.creation,
|
||||
content:
|
||||
custom_item.content ||
|
||||
frappe.render_template(custom_item.template, custom_item.template_data),
|
||||
});
|
||||
}
|
||||
});
|
||||
return custom_timeline_contents;
|
||||
}
|
||||
|
||||
get_energy_point_timeline_contents() {
|
||||
let energy_point_timeline_contents = [];
|
||||
(this.doc_info.energy_point_logs || []).forEach((log) => {
|
||||
let timeline_badge = `
|
||||
<div class="timeline-badge ${log.points > 0 ? "appreciation" : "criticism"} bold">
|
||||
${log.points}
|
||||
</div>`;
|
||||
|
||||
energy_point_timeline_contents.push({
|
||||
timeline_badge: timeline_badge,
|
||||
creation: log.creation,
|
||||
content: frappe.energy_points.format_form_log(log),
|
||||
});
|
||||
});
|
||||
return energy_point_timeline_contents;
|
||||
}
|
||||
|
||||
setup_reply(communication_box, communication_doc) {
|
||||
let actions = communication_box.find(".custom-actions");
|
||||
let reply = $(`<a class="action-btn reply">${frappe.utils.icon("reply", "md")}</a>`).click(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import "./assign_to";
|
||||
import "./attachments";
|
||||
import "./share";
|
||||
import "./review";
|
||||
import "./document_follow";
|
||||
import "./user_image";
|
||||
import "./form_sidebar_users";
|
||||
|
|
@ -28,7 +27,6 @@ frappe.ui.form.Sidebar = class {
|
|||
this.image_wrapper = this.image_section.find(".sidebar-image-wrapper");
|
||||
this.make_assignments();
|
||||
this.make_attachments();
|
||||
this.make_review();
|
||||
this.make_shared();
|
||||
|
||||
this.make_tags();
|
||||
|
|
@ -214,18 +212,6 @@ frappe.ui.form.Sidebar = class {
|
|||
|
||||
refresh_image() {}
|
||||
|
||||
make_review() {
|
||||
const review_wrapper = this.sidebar.find(".form-reviews");
|
||||
if (frappe.boot.energy_points_enabled && !this.frm.is_new()) {
|
||||
this.frm.reviews = new frappe.ui.form.Review({
|
||||
parent: review_wrapper,
|
||||
frm: this.frm,
|
||||
});
|
||||
} else {
|
||||
review_wrapper.remove();
|
||||
}
|
||||
}
|
||||
|
||||
reload_docinfo(callback) {
|
||||
frappe.call({
|
||||
method: "frappe.desk.form.load.get_docinfo",
|
||||
|
|
|
|||
|
|
@ -1,198 +0,0 @@
|
|||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.ui.form.Review = class Review {
|
||||
constructor({ parent, frm }) {
|
||||
this.parent = parent;
|
||||
this.frm = frm;
|
||||
this.points = frappe.boot.points;
|
||||
this.reviews = this.parent.find(".reviews");
|
||||
this.setup_add_review_button();
|
||||
this.update_reviewers();
|
||||
}
|
||||
update_points() {
|
||||
return frappe
|
||||
.xcall("frappe.social.doctype.energy_point_log.energy_point_log.get_energy_points", {
|
||||
user: frappe.session.user,
|
||||
})
|
||||
.then((data) => {
|
||||
frappe.boot.points = data;
|
||||
this.points = data;
|
||||
});
|
||||
}
|
||||
setup_add_review_button() {
|
||||
const review_button = this.reviews.find(".add-review-btn");
|
||||
|
||||
if (!this.points.review_points) {
|
||||
review_button.click(false);
|
||||
review_button.popover({
|
||||
trigger: "hover",
|
||||
content: () => {
|
||||
return `<div class="text-medium">
|
||||
${__("You do not have enough review points")}
|
||||
</div>`;
|
||||
},
|
||||
html: true,
|
||||
});
|
||||
} else {
|
||||
review_button.click(() => this.show_review_dialog());
|
||||
}
|
||||
}
|
||||
|
||||
show_review_dialog() {
|
||||
const user_options = this.frm.get_involved_users();
|
||||
const review_dialog = new frappe.ui.Dialog({
|
||||
title: __("Add Review"),
|
||||
fields: [
|
||||
{
|
||||
fieldname: "to_user",
|
||||
fieldtype: "Autocomplete",
|
||||
label: __("To User"),
|
||||
reqd: 1,
|
||||
options: user_options,
|
||||
ignore_validation: 1,
|
||||
description: __("Only users involved in the document are listed"),
|
||||
},
|
||||
{
|
||||
fieldname: "review_type",
|
||||
fieldtype: "Select",
|
||||
label: __("Action"),
|
||||
options: [
|
||||
{
|
||||
label: __("Appreciate"),
|
||||
value: "Appreciation",
|
||||
},
|
||||
{
|
||||
label: __("Criticize"),
|
||||
value: "Criticism",
|
||||
},
|
||||
],
|
||||
default: "Appreciation",
|
||||
},
|
||||
{
|
||||
fieldname: "points",
|
||||
fieldtype: "Int",
|
||||
label: __("Points"),
|
||||
reqd: 1,
|
||||
description: __("Currently you have {0} review points", [
|
||||
this.points.review_points,
|
||||
]),
|
||||
},
|
||||
{
|
||||
fieldtype: "Small Text",
|
||||
fieldname: "reason",
|
||||
reqd: 1,
|
||||
label: __("Reason"),
|
||||
},
|
||||
],
|
||||
primary_action: (values) => {
|
||||
review_dialog.disable_primary_action();
|
||||
if (values.points > this.points.review_points) {
|
||||
return frappe.msgprint(__("You do not have enough points"));
|
||||
}
|
||||
frappe
|
||||
.xcall("frappe.social.doctype.energy_point_log.energy_point_log.review", {
|
||||
doc: {
|
||||
doctype: this.frm.doc.doctype,
|
||||
name: this.frm.doc.name,
|
||||
},
|
||||
to_user: values.to_user,
|
||||
points: values.points,
|
||||
review_type: values.review_type,
|
||||
reason: values.reason,
|
||||
})
|
||||
.then((review) => {
|
||||
review_dialog.hide();
|
||||
review_dialog.clear();
|
||||
this.frm.get_docinfo().energy_point_logs.unshift(review);
|
||||
this.frm.timeline.refresh();
|
||||
this.update_reviewers();
|
||||
this.update_points();
|
||||
})
|
||||
.finally(() => {
|
||||
review_dialog.enable_primary_action();
|
||||
});
|
||||
},
|
||||
primary_action_label: __("Submit"),
|
||||
});
|
||||
review_dialog.show();
|
||||
}
|
||||
update_reviewers() {
|
||||
const review_logs = this.frm
|
||||
.get_docinfo()
|
||||
.energy_point_logs.filter((log) => ["Appreciation", "Criticism"].includes(log.type));
|
||||
|
||||
this.reviews.find(".review").remove();
|
||||
review_logs.forEach((log) => {
|
||||
let review_pill = $(`
|
||||
<div class="review ${log.points < 0 ? "criticism" : "appreciation"} cursor-pointer">
|
||||
${frappe.avatar(log.owner)}
|
||||
<span class="review-points">
|
||||
${log.points > 0 ? "+" : ""}${log.points}
|
||||
</span>
|
||||
</div>
|
||||
`);
|
||||
this.reviews.append(review_pill);
|
||||
this.setup_detail_popover(review_pill, log);
|
||||
});
|
||||
}
|
||||
setup_detail_popover(el, data) {
|
||||
let subject = "";
|
||||
let fullname = frappe.user.full_name(data.user);
|
||||
let timestamp = `<span class="text-muted">${frappe.datetime.comment_when(
|
||||
data.creation
|
||||
)}</span>`;
|
||||
let message_parts = [Math.abs(data.points), fullname, timestamp];
|
||||
if (data.type === "Appreciation") {
|
||||
if (data.points == 1) {
|
||||
subject = __("{0} appreciation point for {1}", message_parts);
|
||||
} else {
|
||||
subject = __("{0} appreciation points for {1}", message_parts);
|
||||
}
|
||||
} else {
|
||||
if (data.points == -1) {
|
||||
subject = __("{0} criticism point for {1}", message_parts);
|
||||
} else {
|
||||
subject = __("{0} criticism points for {1}", message_parts);
|
||||
}
|
||||
}
|
||||
|
||||
el.popover({
|
||||
animation: true,
|
||||
trigger: "hover",
|
||||
delay: 500,
|
||||
placement: "top",
|
||||
template: `
|
||||
<div class="review-popover popover" role="tooltip">
|
||||
<div class="arrow"></div>
|
||||
<h3 class="popover-header"></h3>
|
||||
<div class="popover-body">
|
||||
</div>
|
||||
</div>'
|
||||
`,
|
||||
content: () => {
|
||||
return `
|
||||
<div class="text-medium">
|
||||
<div class="body">
|
||||
<div>${data.reason}</div>
|
||||
</div>
|
||||
|
||||
<div class="subject">
|
||||
${frappe.utils.icon("review")}
|
||||
${subject}
|
||||
|
||||
<p class="mt-1">
|
||||
<!-- ${frappe.avatar("shivam@example.com", "avatar-xs")} -->
|
||||
<span>${frappe.user.full_name(data.owner)}</span> - ${timestamp}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
html: true,
|
||||
container: "body",
|
||||
});
|
||||
|
||||
return el;
|
||||
}
|
||||
};
|
||||
|
|
@ -74,18 +74,6 @@
|
|||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="sidebar-section form-reviews">
|
||||
<div class="reviews">
|
||||
<span class="form-sidebar-items">
|
||||
<span>
|
||||
<span class="ellipsis">{%= __("Reviews") %}</span>
|
||||
</span>
|
||||
<button class="add-review-btn btn btn-link icon-btn">
|
||||
<svg class="es-icon icon-sm"><use href="#es-line-add"></use></svg>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-section form-tags">
|
||||
<div>
|
||||
<span class="form-sidebar-items">
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// MIT License. See license.txt
|
||||
|
||||
frappe.provide("frappe.energy_points");
|
||||
|
||||
Object.assign(frappe.energy_points, {
|
||||
get_points(points) {
|
||||
return `<span class="bold" style="color: ${points >= 0 ? "#45A163" : "#e42121"}">
|
||||
${points > 0 ? "+" : ""}${points}
|
||||
</span>`;
|
||||
},
|
||||
format_form_log(log) {
|
||||
const separator = `<span> - </span>`;
|
||||
return `<span>
|
||||
<!--${this.get_points(log.points)} -->
|
||||
<a href="/app/energy-point-log/${log.name}">${this.get_form_log_message(log)}</a>
|
||||
${log.reason ? separator + log.reason : ""}
|
||||
</span>`;
|
||||
},
|
||||
format_history_log(log) {
|
||||
// redundant code to honor readability and to avoid confusion
|
||||
const separator = `<span> - </span>`;
|
||||
const route = frappe.utils.get_form_link(log.reference_doctype, log.reference_name);
|
||||
return `<div class="flex">
|
||||
<span class="${log.points >= 0 ? "green" : "red"} mr-2">
|
||||
${this.get_points(log.points)}
|
||||
</span>
|
||||
<a href="${route}" class="text-muted">${this.get_history_log_message(log)}</a>
|
||||
${log.reason ? separator + log.reason : ""}
|
||||
${separator + frappe.datetime.comment_when(log.creation)}
|
||||
</div>`;
|
||||
},
|
||||
get_history_log_message(log) {
|
||||
const owner_name = frappe.user.full_name(log.owner).bold();
|
||||
const ref_doc = log.reference_name;
|
||||
|
||||
if (log.type === "Appreciation") {
|
||||
return __("{0} appreciated on {1}", [owner_name, ref_doc]);
|
||||
}
|
||||
if (log.type === "Criticism") {
|
||||
return __("{0} criticized on {1}", [owner_name, ref_doc]);
|
||||
}
|
||||
if (log.type === "Revert") {
|
||||
return __("{0} reverted {1}", [owner_name, log.revert_of]);
|
||||
}
|
||||
return __("via automatic rule {0} on {1}", [log.rule.bold(), ref_doc]);
|
||||
},
|
||||
get_form_log_message(log) {
|
||||
// redundant code to honor readability and to avoid confusion
|
||||
const owner_name = frappe.user.full_name(log.owner).bold();
|
||||
const user = frappe.user.full_name(log.user).bold();
|
||||
if (log.type === "Appreciation") {
|
||||
return __("{0} appreciated {1}", [owner_name, user]);
|
||||
}
|
||||
if (log.type === "Criticism") {
|
||||
return __("{0} criticized {1}", [owner_name, user]);
|
||||
}
|
||||
if (log.type === "Revert") {
|
||||
return __("{0} reverted {1}", [owner_name, log.revert_of]);
|
||||
}
|
||||
return __("gained by {0} via automatic rule {1}", [user, log.rule.bold()]);
|
||||
},
|
||||
});
|
||||
|
|
@ -1 +0,0 @@
|
|||
import "../../desk/page/user_profile/user_profile_controller";
|
||||
|
|
@ -866,60 +866,6 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.form-reviews {
|
||||
.reviews {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.review {
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
height: 28px;
|
||||
border-radius: 14px;
|
||||
font-size: var(--text-xs);
|
||||
margin-bottom: var(--margin-sm);
|
||||
margin-right: var(--margin-xs);
|
||||
border: 1px solid var(--dark-border-color);
|
||||
padding: 2px 3px;
|
||||
align-items: center;
|
||||
min-width: 60px;
|
||||
background: var(--fg-color);
|
||||
.avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.review-points {
|
||||
margin-left: 3px;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.review-popover {
|
||||
padding: 0px;
|
||||
min-width: 200px;
|
||||
max-width: 250px;
|
||||
|
||||
.popover-body,
|
||||
.popover-content {
|
||||
padding: 0;
|
||||
}
|
||||
.body {
|
||||
border-bottom: 1px solid $border-color;
|
||||
}
|
||||
|
||||
.subject,
|
||||
.body {
|
||||
padding: var(--padding-sm);
|
||||
overflow-wrap: break-word;
|
||||
p {
|
||||
margin-top: var(--margin-xs);
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.liked-by-popover {
|
||||
.popover-body {
|
||||
min-height: 30px;
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Energy Point Log", {
|
||||
refresh: function (frm) {
|
||||
frm.events.make_reference_name_link(frm);
|
||||
if (frm.doc.reverted) {
|
||||
frm.set_intro(__("This document has been reverted"));
|
||||
} else if (frm.doc.type === "Auto" && frappe.user_roles.includes("System Manager")) {
|
||||
frm.add_custom_button(__("Revert"), () => frm.events.show_revert_dialog(frm));
|
||||
}
|
||||
},
|
||||
show_revert_dialog(frm) {
|
||||
const revert_dialog = new frappe.ui.Dialog({
|
||||
title: __("Revert"),
|
||||
fields: [
|
||||
{
|
||||
fieldname: "reason",
|
||||
fieldtype: "Small Text",
|
||||
label: __("Reason"),
|
||||
reqd: 1,
|
||||
},
|
||||
],
|
||||
primary_action: (values) => {
|
||||
return frm
|
||||
.call("revert", {
|
||||
reason: values.reason,
|
||||
})
|
||||
.then((res) => {
|
||||
let revert_log = res.message;
|
||||
revert_dialog.hide();
|
||||
revert_dialog.clear();
|
||||
frappe.model.docinfo[frm.doc.reference_doctype][
|
||||
frm.doc.reference_name
|
||||
].energy_point_logs.unshift(revert_log);
|
||||
frm.refresh();
|
||||
});
|
||||
},
|
||||
primary_action_label: __("Submit"),
|
||||
});
|
||||
revert_dialog.show();
|
||||
},
|
||||
make_reference_name_link(frm) {
|
||||
let dt = frm.doc.reference_doctype;
|
||||
let dn = frm.doc.reference_name;
|
||||
frm.fields_dict.reference_name.$input_wrapper
|
||||
.find(".control-value")
|
||||
.wrapInner(`<a href='/app/${frappe.router.slug(dt)}/${dn}'></a>`);
|
||||
},
|
||||
});
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2018-06-21 14:58:55.913619",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"user",
|
||||
"type",
|
||||
"points",
|
||||
"rule",
|
||||
"column_break_5",
|
||||
"reference_doctype",
|
||||
"reference_name",
|
||||
"reverted",
|
||||
"revert_of",
|
||||
"section_break_10",
|
||||
"reason",
|
||||
"seen"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "User",
|
||||
"options": "User",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Select",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Type",
|
||||
"options": "Auto\nAppreciation\nCriticism\nReview\nRevert",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rule",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Rule",
|
||||
"options": "Energy Point Rule",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Reference Document Type",
|
||||
"options": "DocType",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Reference Name",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "points",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "Points",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reason",
|
||||
"fieldtype": "Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Reason",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "reverted",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Reverted"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.type === 'Revert'",
|
||||
"fieldname": "revert_of",
|
||||
"fieldtype": "Link",
|
||||
"label": "Revert Of",
|
||||
"options": "Energy Point Log",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "seen",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Seen"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:25.006938",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Energy Point Log",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Desk User"
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"title_field": "user"
|
||||
}
|
||||
|
|
@ -1,391 +0,0 @@
|
|||
# Copyright (c) 2018, Frappe Technologies and contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.doctype.notification_log.notification_log import (
|
||||
enqueue_create_notification,
|
||||
get_title,
|
||||
get_title_html,
|
||||
)
|
||||
from frappe.desk.doctype.notification_settings.notification_settings import (
|
||||
is_email_notifications_enabled,
|
||||
is_email_notifications_enabled_for_type,
|
||||
)
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, get_fullname, get_link_to_form, getdate
|
||||
|
||||
|
||||
class EnergyPointLog(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
points: DF.Int
|
||||
reason: DF.Text | None
|
||||
reference_doctype: DF.Link | None
|
||||
reference_name: DF.Data | None
|
||||
revert_of: DF.Link | None
|
||||
reverted: DF.Check
|
||||
rule: DF.Link | None
|
||||
seen: DF.Check
|
||||
type: DF.Literal["Auto", "Appreciation", "Criticism", "Review", "Revert"]
|
||||
user: DF.Link
|
||||
# end: auto-generated types
|
||||
|
||||
def validate(self):
|
||||
self.map_milestone_reference()
|
||||
if self.type in ["Appreciation", "Criticism"] and self.user == self.owner:
|
||||
frappe.throw(_("You cannot give review points to yourself"))
|
||||
|
||||
def map_milestone_reference(self):
|
||||
# link energy point to the original reference, if set by milestone
|
||||
if self.reference_doctype == "Milestone":
|
||||
self.reference_doctype, self.reference_name = frappe.db.get_value(
|
||||
"Milestone", self.reference_name, ["reference_type", "reference_name"]
|
||||
)
|
||||
|
||||
def after_insert(self):
|
||||
alert_dict = get_alert_dict(self)
|
||||
if alert_dict:
|
||||
frappe.publish_realtime(
|
||||
"energy_point_alert", message=alert_dict, user=self.user, after_commit=True
|
||||
)
|
||||
|
||||
frappe.cache.hdel("energy_points", self.user)
|
||||
|
||||
if self.type != "Review" and frappe.get_cached_value(
|
||||
"Notification Settings", self.user, "energy_points_system_notifications"
|
||||
):
|
||||
reference_user = self.user if self.type == "Auto" else self.owner
|
||||
notification_doc = {
|
||||
"type": "Energy Point",
|
||||
"document_type": self.reference_doctype,
|
||||
"document_name": self.reference_name,
|
||||
"subject": get_notification_message(self),
|
||||
"from_user": reference_user,
|
||||
"email_content": f"<div>{self.reason}</div>" if self.reason else None,
|
||||
}
|
||||
|
||||
enqueue_create_notification(self.user, notification_doc)
|
||||
|
||||
def on_trash(self):
|
||||
if self.type == "Revert":
|
||||
reference_log = frappe.get_doc("Energy Point Log", self.revert_of)
|
||||
reference_log.reverted = 0
|
||||
reference_log.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def revert(self, reason, ignore_permissions=False):
|
||||
if not ignore_permissions:
|
||||
frappe.only_for("System Manager")
|
||||
|
||||
if self.type != "Auto":
|
||||
frappe.throw(_("This document cannot be reverted"))
|
||||
|
||||
if self.get("reverted"):
|
||||
return
|
||||
|
||||
self.reverted = 1
|
||||
self.save(ignore_permissions=True)
|
||||
|
||||
return frappe.get_doc(
|
||||
{
|
||||
"doctype": "Energy Point Log",
|
||||
"points": -(self.points),
|
||||
"type": "Revert",
|
||||
"user": self.user,
|
||||
"reason": reason,
|
||||
"reference_doctype": self.reference_doctype,
|
||||
"reference_name": self.reference_name,
|
||||
"revert_of": self.name,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def get_notification_message(doc):
|
||||
owner_name = get_fullname(doc.owner)
|
||||
points = doc.points
|
||||
title = get_title(doc.reference_doctype, doc.reference_name)
|
||||
|
||||
if doc.type == "Auto":
|
||||
owner_name = frappe.bold("You")
|
||||
if points == 1:
|
||||
message = _("{0} gained {1} point for {2} {3}")
|
||||
else:
|
||||
message = _("{0} gained {1} points for {2} {3}")
|
||||
message = message.format(owner_name, frappe.bold(points), doc.rule, get_title_html(title))
|
||||
elif doc.type == "Appreciation":
|
||||
if points == 1:
|
||||
message = _("{0} appreciated your work on {1} with {2} point")
|
||||
else:
|
||||
message = _("{0} appreciated your work on {1} with {2} points")
|
||||
message = message.format(frappe.bold(owner_name), get_title_html(title), frappe.bold(points))
|
||||
elif doc.type == "Criticism":
|
||||
if points == 1:
|
||||
message = _("{0} criticized your work on {1} with {2} point")
|
||||
else:
|
||||
message = _("{0} criticized your work on {1} with {2} points")
|
||||
|
||||
message = message.format(frappe.bold(owner_name), get_title_html(title), frappe.bold(points))
|
||||
elif doc.type == "Revert":
|
||||
if points == 1:
|
||||
message = _("{0} reverted your point on {1}")
|
||||
else:
|
||||
message = _("{0} reverted your points on {1}")
|
||||
message = message.format(frappe.bold(owner_name), get_title_html(title))
|
||||
|
||||
return message
|
||||
|
||||
|
||||
def get_alert_dict(doc):
|
||||
alert_dict = frappe._dict()
|
||||
owner_name = get_fullname(doc.owner)
|
||||
if doc.reference_doctype:
|
||||
doc_link = get_link_to_form(doc.reference_doctype, doc.reference_name)
|
||||
points = doc.points
|
||||
bold_points = frappe.bold(doc.points)
|
||||
if doc.type == "Auto":
|
||||
if points == 1:
|
||||
message = _("You gained {0} point")
|
||||
else:
|
||||
message = _("You gained {0} points")
|
||||
alert_dict.message = message.format(bold_points)
|
||||
alert_dict.indicator = "green"
|
||||
elif doc.type == "Appreciation":
|
||||
if points == 1:
|
||||
message = _("{0} appreciated your work on {1} with {2} point")
|
||||
else:
|
||||
message = _("{0} appreciated your work on {1} with {2} points")
|
||||
alert_dict.message = message.format(owner_name, doc_link, bold_points)
|
||||
alert_dict.indicator = "green"
|
||||
elif doc.type == "Criticism":
|
||||
if points == 1:
|
||||
message = _("{0} criticized your work on {1} with {2} point")
|
||||
else:
|
||||
message = _("{0} criticized your work on {1} with {2} points")
|
||||
|
||||
alert_dict.message = message.format(owner_name, doc_link, bold_points)
|
||||
alert_dict.indicator = "red"
|
||||
elif doc.type == "Revert":
|
||||
if points == 1:
|
||||
message = _("{0} reverted your point on {1}")
|
||||
else:
|
||||
message = _("{0} reverted your points on {1}")
|
||||
alert_dict.message = message.format(
|
||||
owner_name,
|
||||
doc_link,
|
||||
)
|
||||
alert_dict.indicator = "red"
|
||||
|
||||
return alert_dict
|
||||
|
||||
|
||||
def create_energy_points_log(ref_doctype, ref_name, doc, apply_only_once=False):
|
||||
doc = frappe._dict(doc)
|
||||
if doc.rule:
|
||||
log_exists = check_if_log_exists(
|
||||
ref_doctype, ref_name, doc.rule, None if apply_only_once else doc.user
|
||||
)
|
||||
if log_exists:
|
||||
return frappe.get_doc("Energy Point Log", log_exists)
|
||||
|
||||
new_log = frappe.new_doc("Energy Point Log")
|
||||
new_log.reference_doctype = ref_doctype
|
||||
new_log.reference_name = ref_name
|
||||
new_log.update(doc)
|
||||
new_log.insert(ignore_permissions=True)
|
||||
return new_log
|
||||
|
||||
|
||||
def check_if_log_exists(ref_doctype, ref_name, rule, user=None):
|
||||
"""'Checks if Energy Point Log already exists"""
|
||||
filters = frappe._dict(
|
||||
{"rule": rule, "reference_doctype": ref_doctype, "reference_name": ref_name, "reverted": 0}
|
||||
)
|
||||
|
||||
if user:
|
||||
filters.user = user
|
||||
|
||||
return frappe.db.exists("Energy Point Log", filters)
|
||||
|
||||
|
||||
def create_review_points_log(user, points, reason=None, doctype=None, docname=None):
|
||||
return frappe.get_doc(
|
||||
{
|
||||
"doctype": "Energy Point Log",
|
||||
"points": points,
|
||||
"type": "Review",
|
||||
"user": user,
|
||||
"reason": reason,
|
||||
"reference_doctype": doctype,
|
||||
"reference_name": docname,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_review_points(user, points):
|
||||
frappe.only_for("System Manager")
|
||||
create_review_points_log(user, points)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_energy_points(user):
|
||||
points = get_user_energy_and_review_points(user)
|
||||
return frappe._dict(points.get(user, {}))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_user_energy_and_review_points(user=None, from_date=None, as_dict=True):
|
||||
conditions = ""
|
||||
given_points_condition = ""
|
||||
values = frappe._dict()
|
||||
if user:
|
||||
conditions = "WHERE `user` = %(user)s"
|
||||
values.user = user
|
||||
if from_date:
|
||||
conditions += "WHERE" if not conditions else "AND"
|
||||
given_points_condition += "AND `creation` >= %(from_date)s"
|
||||
conditions += " `creation` >= %(from_date)s OR `type`='Review'"
|
||||
values.from_date = from_date
|
||||
|
||||
points_list = frappe.db.sql(
|
||||
f"""
|
||||
SELECT
|
||||
SUM(CASE WHEN `type` != 'Review' THEN `points` ELSE 0 END) AS energy_points,
|
||||
SUM(CASE WHEN `type` = 'Review' THEN `points` ELSE 0 END) AS review_points,
|
||||
SUM(CASE
|
||||
WHEN `type`='Review' AND `points` < 0 {given_points_condition}
|
||||
THEN ABS(`points`)
|
||||
ELSE 0
|
||||
END) as given_points,
|
||||
`user`
|
||||
FROM `tabEnergy Point Log`
|
||||
{conditions}
|
||||
GROUP BY `user`
|
||||
ORDER BY `energy_points` DESC
|
||||
""",
|
||||
values=values,
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
if not as_dict:
|
||||
return points_list
|
||||
|
||||
dict_to_return = frappe._dict()
|
||||
for d in points_list:
|
||||
dict_to_return[d.pop("user")] = d
|
||||
return dict_to_return
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def review(doc, points, to_user, reason, review_type="Appreciation"):
|
||||
current_review_points = get_energy_points(frappe.session.user).review_points
|
||||
doc = doc.as_dict() if hasattr(doc, "as_dict") else frappe._dict(json.loads(doc))
|
||||
points = abs(cint(points))
|
||||
if current_review_points < points:
|
||||
frappe.msgprint(_("You do not have enough review points"))
|
||||
return
|
||||
|
||||
review_doc = create_energy_points_log(
|
||||
doc.doctype,
|
||||
doc.name,
|
||||
{
|
||||
"type": review_type,
|
||||
"reason": reason,
|
||||
"points": points if review_type == "Appreciation" else -points,
|
||||
"user": to_user,
|
||||
},
|
||||
)
|
||||
|
||||
# deduct review points from reviewer
|
||||
create_review_points_log(
|
||||
user=frappe.session.user,
|
||||
points=-points,
|
||||
reason=reason,
|
||||
doctype=review_doc.doctype,
|
||||
docname=review_doc.name,
|
||||
)
|
||||
|
||||
return review_doc
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_reviews(doctype, docname):
|
||||
return frappe.get_all(
|
||||
"Energy Point Log",
|
||||
filters={
|
||||
"reference_doctype": doctype,
|
||||
"reference_name": docname,
|
||||
"type": ["in", ("Appreciation", "Criticism")],
|
||||
},
|
||||
fields=["points", "owner", "type", "user", "reason", "creation"],
|
||||
)
|
||||
|
||||
|
||||
def send_weekly_summary():
|
||||
send_summary("Weekly")
|
||||
|
||||
|
||||
def send_monthly_summary():
|
||||
send_summary("Monthly")
|
||||
|
||||
|
||||
def send_summary(timespan):
|
||||
from frappe.social.doctype.energy_point_settings.energy_point_settings import (
|
||||
is_energy_point_enabled,
|
||||
)
|
||||
from frappe.utils.user import get_enabled_system_users
|
||||
|
||||
if not is_energy_point_enabled():
|
||||
return
|
||||
|
||||
if not is_email_notifications_enabled_for_type(frappe.session.user, "Energy Point"):
|
||||
return
|
||||
|
||||
from_date = frappe.utils.add_to_date(None, weeks=-1)
|
||||
if timespan == "Monthly":
|
||||
from_date = frappe.utils.add_to_date(None, months=-1)
|
||||
|
||||
user_points = get_user_energy_and_review_points(from_date=from_date, as_dict=False)
|
||||
|
||||
# do not send report if no activity found
|
||||
if not user_points or not user_points[0].energy_points:
|
||||
return
|
||||
|
||||
from_date = getdate(from_date)
|
||||
to_date = getdate()
|
||||
|
||||
# select only those users that have energy point email notifications enabled
|
||||
all_users = [
|
||||
user.email
|
||||
for user in get_enabled_system_users()
|
||||
if is_email_notifications_enabled_for_type(user.name, "Energy Point")
|
||||
]
|
||||
|
||||
frappe.sendmail(
|
||||
subject=f"{timespan} energy points summary",
|
||||
recipients=all_users,
|
||||
template="energy_points_summary",
|
||||
args={
|
||||
"top_performer": user_points[0],
|
||||
"top_reviewer": max(user_points, key=lambda x: x["given_points"]),
|
||||
"standings": user_points[:10], # top 10
|
||||
"footer_message": get_footer_message(timespan).format(from_date, to_date),
|
||||
},
|
||||
with_container=1,
|
||||
)
|
||||
|
||||
|
||||
def get_footer_message(timespan):
|
||||
if timespan == "Monthly":
|
||||
return _("Stats based on last month's performance (from {0} to {1})")
|
||||
else:
|
||||
return _("Stats based on last week's performance (from {0} to {1})")
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
frappe.listview_settings["Energy Point Log"] = {
|
||||
hide_name_column: true,
|
||||
add_fields: ["type", "reference_doctype", "reference_name"],
|
||||
get_indicator: function (doc) {
|
||||
let colors = {
|
||||
Appreciation: "green",
|
||||
Criticism: "red",
|
||||
Auto: "blue",
|
||||
Revert: "orange",
|
||||
Review: "grey",
|
||||
};
|
||||
return [__(doc.type), colors[doc.type], "type,=," + doc.type];
|
||||
},
|
||||
|
||||
button: {
|
||||
show: function (doc) {
|
||||
return doc.reference_name;
|
||||
},
|
||||
get_label: function () {
|
||||
return __("View Ref");
|
||||
},
|
||||
get_description: function (doc) {
|
||||
return __("Open {0}", [`${doc.reference_doctype} ${doc.reference_name}`]);
|
||||
},
|
||||
action: function (doc) {
|
||||
frappe.set_route("Form", doc.reference_doctype, doc.reference_name);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -1,389 +0,0 @@
|
|||
# Copyright (c) 2019, Frappe Technologies and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
from unittest.case import skipIf
|
||||
|
||||
import frappe
|
||||
from frappe.cache_manager import clear_doctype_map
|
||||
from frappe.desk.form.assign_to import add as assign_to
|
||||
from frappe.desk.page.user_profile.user_profile import get_energy_points_heatmap_data
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
from frappe.utils.testutils import add_custom_field, clear_custom_fields
|
||||
|
||||
from .energy_point_log import create_review_points_log, review
|
||||
from .energy_point_log import get_energy_points as _get_energy_points
|
||||
|
||||
|
||||
class UnitTestEnergyPointLog(UnitTestCase):
|
||||
"""
|
||||
Unit tests for EnergyPointLog.
|
||||
Use this class for testing individual functions and methods.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TestEnergyPointLog(IntegrationTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
settings = frappe.get_single("Energy Point Settings")
|
||||
settings.enabled = 1
|
||||
settings.save()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
settings = frappe.get_single("Energy Point Settings")
|
||||
settings.enabled = 0
|
||||
settings.save()
|
||||
|
||||
def setUp(self):
|
||||
clear_doctype_map("Energy Point Rule")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.delete("Energy Point Log")
|
||||
frappe.db.delete("Energy Point Rule")
|
||||
clear_doctype_map("Energy Point Rule")
|
||||
|
||||
def test_user_energy_point(self):
|
||||
frappe.set_user("test@example.com")
|
||||
todo_point_rule = create_energy_point_rule_for_todo()
|
||||
energy_point_of_user = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
self.assertEqual(points_after_closing_todo, energy_point_of_user + todo_point_rule.points)
|
||||
|
||||
created_todo.save()
|
||||
points_after_double_save = get_points("test@example.com")
|
||||
|
||||
# point should not be awarded more than once for same doc
|
||||
self.assertEqual(points_after_double_save, energy_point_of_user + todo_point_rule.points)
|
||||
|
||||
def test_points_based_on_multiplier_field(self):
|
||||
frappe.set_user("test@example.com")
|
||||
add_custom_field("ToDo", "multiplier", "Float")
|
||||
multiplier_value = 0.51
|
||||
|
||||
todo_point_rule = create_energy_point_rule_for_todo("multiplier")
|
||||
energy_point_of_user = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
created_todo.status = "Closed"
|
||||
created_todo.multiplier = multiplier_value
|
||||
created_todo.save()
|
||||
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
self.assertEqual(
|
||||
points_after_closing_todo,
|
||||
energy_point_of_user + round(todo_point_rule.points * multiplier_value),
|
||||
)
|
||||
|
||||
clear_custom_fields("ToDo")
|
||||
|
||||
def test_points_based_on_max_points(self):
|
||||
frappe.set_user("test@example.com")
|
||||
# here multiplier is high
|
||||
# let see if points get capped to max_point limit
|
||||
multiplier_value = 15
|
||||
max_points = 50
|
||||
|
||||
add_custom_field("ToDo", "multiplier", "Float")
|
||||
todo_point_rule = create_energy_point_rule_for_todo("multiplier", max_points=max_points)
|
||||
energy_point_of_user = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
created_todo.status = "Closed"
|
||||
created_todo.multiplier = multiplier_value
|
||||
created_todo.save()
|
||||
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
# test max_points cap
|
||||
self.assertNotEqual(
|
||||
points_after_closing_todo,
|
||||
energy_point_of_user + round(todo_point_rule.points * multiplier_value),
|
||||
)
|
||||
|
||||
self.assertEqual(points_after_closing_todo, energy_point_of_user + max_points)
|
||||
|
||||
clear_custom_fields("ToDo")
|
||||
|
||||
def test_disabled_energy_points(self):
|
||||
settings = frappe.get_single("Energy Point Settings")
|
||||
settings.enabled = 0
|
||||
settings.save()
|
||||
|
||||
frappe.set_user("test@example.com")
|
||||
create_energy_point_rule_for_todo()
|
||||
energy_point_of_user = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
# no change in points
|
||||
self.assertEqual(points_after_closing_todo, energy_point_of_user)
|
||||
|
||||
settings.enabled = 1
|
||||
settings.save()
|
||||
|
||||
def test_review(self):
|
||||
created_todo = create_a_todo()
|
||||
review_points = 20
|
||||
create_review_points_log("test2@example.com", review_points)
|
||||
|
||||
# reviewer
|
||||
frappe.set_user("test2@example.com")
|
||||
|
||||
review_points_before_review = get_points("test2@example.com", "review_points")
|
||||
self.assertEqual(review_points_before_review, review_points)
|
||||
|
||||
# for appreciation
|
||||
appreciation_points = 5
|
||||
energy_points_before_review = get_points("test@example.com")
|
||||
review(created_todo, appreciation_points, "test@example.com", "good job")
|
||||
energy_points_after_review = get_points("test@example.com")
|
||||
review_points_after_review = get_points("test2@example.com", "review_points")
|
||||
self.assertEqual(energy_points_after_review, energy_points_before_review + appreciation_points)
|
||||
self.assertEqual(review_points_after_review, review_points_before_review - appreciation_points)
|
||||
|
||||
# for criticism
|
||||
criticism_points = 2
|
||||
todo = create_a_todo(description="Bad patch")
|
||||
energy_points_before_review = energy_points_after_review
|
||||
review_points_before_review = review_points_after_review
|
||||
review(todo, criticism_points, "test@example.com", "You could have done better.", "Criticism")
|
||||
energy_points_after_review = get_points("test@example.com")
|
||||
review_points_after_review = get_points("test2@example.com", "review_points")
|
||||
self.assertEqual(energy_points_after_review, energy_points_before_review - criticism_points)
|
||||
self.assertEqual(review_points_after_review, review_points_before_review - criticism_points)
|
||||
|
||||
def test_user_energy_point_as_admin(self):
|
||||
frappe.set_user("Administrator")
|
||||
create_energy_point_rule_for_todo()
|
||||
created_todo = create_a_todo()
|
||||
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
|
||||
points_after_closing_todo = get_points("Administrator")
|
||||
|
||||
# no points for admin
|
||||
self.assertEqual(points_after_closing_todo, 0)
|
||||
|
||||
def test_revert_points_on_cancelled_doc(self):
|
||||
frappe.set_user("test@example.com")
|
||||
create_energy_point_rule_for_todo()
|
||||
created_todo = create_a_todo()
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
|
||||
energy_point_logs = frappe.get_all("Energy Point Log")
|
||||
|
||||
self.assertEqual(len(energy_point_logs), 1)
|
||||
|
||||
# for submit and cancel permission
|
||||
frappe.set_user("Administrator")
|
||||
# submit
|
||||
created_todo.docstatus = 1
|
||||
created_todo.save()
|
||||
|
||||
# cancel
|
||||
created_todo.docstatus = 2
|
||||
created_todo.save()
|
||||
|
||||
energy_point_logs = frappe.get_all("Energy Point Log", fields=["reference_name", "type", "reverted"])
|
||||
|
||||
self.assertListEqual(
|
||||
energy_point_logs,
|
||||
[
|
||||
{"reference_name": created_todo.name, "type": "Revert", "reverted": 0},
|
||||
{"reference_name": created_todo.name, "type": "Auto", "reverted": 1},
|
||||
],
|
||||
)
|
||||
|
||||
def test_energy_point_for_new_document_creation(self):
|
||||
frappe.set_user("test@example.com")
|
||||
todo_point_rule = create_energy_point_rule_for_todo(for_doc_event="New")
|
||||
|
||||
points_before_todo_creation = get_points("test@example.com")
|
||||
create_a_todo()
|
||||
points_after_todo_creation = get_points("test@example.com")
|
||||
|
||||
self.assertEqual(points_after_todo_creation, points_before_todo_creation + todo_point_rule.points)
|
||||
|
||||
def test_point_allocation_for_assigned_users(self):
|
||||
todo = create_a_todo()
|
||||
|
||||
assign_users_to_todo(todo.name, ["test@example.com", "test2@example.com"])
|
||||
|
||||
test_user_before_points = get_points("test@example.com")
|
||||
test2_user_before_points = get_points("test2@example.com")
|
||||
|
||||
rule = create_energy_point_rule_for_todo(for_assigned_users=1)
|
||||
|
||||
todo.status = "Closed"
|
||||
todo.save()
|
||||
|
||||
test_user_after_points = get_points("test@example.com")
|
||||
test2_user_after_points = get_points("test2@example.com")
|
||||
|
||||
self.assertEqual(test_user_after_points, test_user_before_points + rule.points)
|
||||
|
||||
self.assertEqual(test2_user_after_points, test2_user_before_points + rule.points)
|
||||
|
||||
@skipIf(
|
||||
frappe.conf.db_type == "sqlite",
|
||||
"Not for SQLite for now",
|
||||
)
|
||||
def test_eps_heatmap_query(self):
|
||||
# Just asserts that query works, not correctness.
|
||||
self.assertIsInstance(get_energy_points_heatmap_data(user="test@example.com", date=None), dict)
|
||||
|
||||
def test_points_on_field_value_change(self):
|
||||
rule = create_energy_point_rule_for_todo(for_doc_event="Value Change", field_to_check="description")
|
||||
|
||||
frappe.set_user("test@example.com")
|
||||
points_before_todo_creation = get_points("test@example.com")
|
||||
todo = create_a_todo()
|
||||
todo.status = "Closed"
|
||||
todo.save()
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
self.assertEqual(points_after_closing_todo, points_before_todo_creation)
|
||||
|
||||
todo.description = "This is new todo"
|
||||
todo.save()
|
||||
points_after_changing_todo_description = get_points("test@example.com")
|
||||
self.assertEqual(points_after_changing_todo_description, points_before_todo_creation + rule.points)
|
||||
|
||||
def test_apply_only_once(self):
|
||||
frappe.set_user("test@example.com")
|
||||
todo_point_rule = create_energy_point_rule_for_todo(apply_once=True, user_field="modified_by")
|
||||
first_user_points = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
|
||||
first_user_points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
self.assertEqual(first_user_points_after_closing_todo, first_user_points + todo_point_rule.points)
|
||||
|
||||
frappe.set_user("test2@example.com")
|
||||
second_user_points = get_points("test2@example.com")
|
||||
created_todo.save(ignore_permissions=True)
|
||||
second_user_points_after_closing_todo = get_points("test2@example.com")
|
||||
|
||||
# point should not be awarded more than once for same doc (irrespective of user)
|
||||
self.assertEqual(second_user_points_after_closing_todo, second_user_points)
|
||||
|
||||
def test_allow_creation_of_new_log_if_the_previous_log_was_reverted(self):
|
||||
frappe.set_user("test@example.com")
|
||||
todo_point_rule = create_energy_point_rule_for_todo()
|
||||
energy_point_of_user = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
log_name = frappe.db.exists("Energy Point Log", {"reference_name": created_todo.name})
|
||||
frappe.get_doc("Energy Point Log", log_name).revert("Just for test")
|
||||
points_after_reverting_todo = get_points("test@example.com")
|
||||
created_todo.save()
|
||||
points_after_saving_todo_again = get_points("test@example.com")
|
||||
|
||||
rule_points = todo_point_rule.points
|
||||
self.assertEqual(points_after_closing_todo, energy_point_of_user + rule_points)
|
||||
self.assertEqual(points_after_reverting_todo, points_after_closing_todo - rule_points)
|
||||
self.assertEqual(points_after_saving_todo_again, points_after_reverting_todo + rule_points)
|
||||
|
||||
def test_energy_points_disabled_user(self):
|
||||
frappe.set_user("test@example.com")
|
||||
user = frappe.get_doc("User", "test@example.com")
|
||||
user.enabled = 0
|
||||
user.save()
|
||||
todo_point_rule = create_energy_point_rule_for_todo()
|
||||
energy_point_of_user = get_points("test@example.com")
|
||||
|
||||
created_todo = create_a_todo()
|
||||
|
||||
created_todo.status = "Closed"
|
||||
created_todo.save()
|
||||
points_after_closing_todo = get_points("test@example.com")
|
||||
|
||||
# do not update energy points for disabled user
|
||||
self.assertEqual(points_after_closing_todo, energy_point_of_user)
|
||||
|
||||
with self.set_user("Administrator"):
|
||||
user.enabled = 1
|
||||
user.save()
|
||||
|
||||
created_todo.save()
|
||||
points_after_re_saving_todo = get_points("test@example.com")
|
||||
self.assertEqual(points_after_re_saving_todo, energy_point_of_user + todo_point_rule.points)
|
||||
|
||||
|
||||
def create_energy_point_rule_for_todo(
|
||||
multiplier_field=None,
|
||||
for_doc_event="Custom",
|
||||
max_points=None,
|
||||
for_assigned_users=0,
|
||||
field_to_check=None,
|
||||
apply_once=False,
|
||||
user_field="owner",
|
||||
):
|
||||
name = "ToDo Closed"
|
||||
point_rule_exists = frappe.db.exists("Energy Point Rule", name)
|
||||
|
||||
if point_rule_exists:
|
||||
return frappe.get_doc("Energy Point Rule", name)
|
||||
|
||||
return frappe.get_doc(
|
||||
{
|
||||
"doctype": "Energy Point Rule",
|
||||
"rule_name": name,
|
||||
"points": 5,
|
||||
"reference_doctype": "ToDo",
|
||||
"condition": 'doc.status == "Closed"',
|
||||
"for_doc_event": for_doc_event,
|
||||
"user_field": user_field,
|
||||
"for_assigned_users": for_assigned_users,
|
||||
"multiplier_field": multiplier_field,
|
||||
"max_points": max_points,
|
||||
"field_to_check": field_to_check,
|
||||
"apply_only_once": apply_once,
|
||||
}
|
||||
).insert(ignore_permissions=1)
|
||||
|
||||
|
||||
def create_a_todo(description=None):
|
||||
if not description:
|
||||
description = "Fix a bug"
|
||||
return frappe.get_doc(
|
||||
{
|
||||
"doctype": "ToDo",
|
||||
"description": description,
|
||||
}
|
||||
).insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def get_points(user, point_type="energy_points"):
|
||||
return _get_energy_points(user).get(point_type, 0)
|
||||
|
||||
|
||||
def assign_users_to_todo(todo_name, users):
|
||||
for user in users:
|
||||
assign_to({"assign_to": [user], "doctype": "ToDo", "name": todo_name})
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Energy Point Rule", {
|
||||
validate(frm) {
|
||||
frm.set_df_property("user_field", "reqd", !frm.doc.for_assigned_users);
|
||||
frm.set_df_property("condition", "reqd", frm.doc.for_doc_event === "Custom");
|
||||
},
|
||||
refresh(frm) {
|
||||
frm.events.set_field_options(frm);
|
||||
},
|
||||
for_doc_event(frm) {
|
||||
if (frm.doc.for_assigned_users) {
|
||||
frm.set_value("for_assigned_users", !frm.doc.for_doc_event === "New");
|
||||
}
|
||||
},
|
||||
reference_doctype(frm) {
|
||||
frm.events.set_field_options(frm);
|
||||
},
|
||||
set_field_options(frm) {
|
||||
// sets options for field_to_check, user_field and multiplier fields
|
||||
// based on reference doctype
|
||||
const reference_doctype = frm.doc.reference_doctype;
|
||||
if (!reference_doctype) return;
|
||||
|
||||
frappe.model.with_doctype(reference_doctype, () => {
|
||||
const map_for_options = (df) => ({ label: df.label, value: df.fieldname });
|
||||
const fields = frappe.meta
|
||||
.get_docfields(frm.doc.reference_doctype)
|
||||
.filter(frappe.model.is_value_type);
|
||||
|
||||
const fields_to_check = fields.map(map_for_options);
|
||||
|
||||
const user_fields = fields
|
||||
.filter(
|
||||
(df) =>
|
||||
(df.fieldtype === "Link" && df.options === "User") ||
|
||||
df.fieldtype === "Data"
|
||||
)
|
||||
.map(map_for_options)
|
||||
.concat([
|
||||
{ label: __("Owner"), value: "owner" },
|
||||
{ label: __("Modified By"), value: "modified_by" },
|
||||
]);
|
||||
|
||||
const multiplier_fields = fields
|
||||
.filter((df) => ["Int", "Float"].includes(df.fieldtype))
|
||||
.map(map_for_options);
|
||||
|
||||
// blank option for the ability to unset the multiplier field
|
||||
multiplier_fields.unshift(null);
|
||||
|
||||
frm.set_df_property("field_to_check", "options", fields_to_check);
|
||||
frm.set_df_property("user_field", "options", user_fields);
|
||||
frm.set_df_property("multiplier_field", "options", multiplier_fields);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"autoname": "field:rule_name",
|
||||
"creation": "2018-06-20 10:08:36.275253",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enabled",
|
||||
"section_break_2",
|
||||
"rule_name",
|
||||
"reference_doctype",
|
||||
"for_doc_event",
|
||||
"field_to_check",
|
||||
"points",
|
||||
"for_assigned_users",
|
||||
"user_field",
|
||||
"multiplier_field",
|
||||
"max_points",
|
||||
"column_break_12",
|
||||
"apply_only_once",
|
||||
"condition"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Enabled",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rule_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Rule Name",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Reference Document Type",
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:['Custom', 'Value Change'].includes(doc.for_doc_event)",
|
||||
"description": "If the condition is satisfied user will be rewarded with the points. eg. doc.status == 'Closed'\n",
|
||||
"fieldname": "condition",
|
||||
"fieldtype": "Code",
|
||||
"in_list_view": 1,
|
||||
"label": "Condition"
|
||||
},
|
||||
{
|
||||
"fieldname": "points",
|
||||
"fieldtype": "Int",
|
||||
"label": "Points",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.for_assigned_users || doc.for_doc_event==='New'",
|
||||
"description": "The user from this field will be rewarded points",
|
||||
"fieldname": "user_field",
|
||||
"fieldtype": "Select",
|
||||
"label": "User Field"
|
||||
},
|
||||
{
|
||||
"fieldname": "multiplier_field",
|
||||
"fieldtype": "Select",
|
||||
"label": "Multiplier Field"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.multiplier_field",
|
||||
"description": "Maximum points allowed after multiplying points with the multiplier value\n(Note: For no limit leave this field empty or set 0)",
|
||||
"fieldname": "max_points",
|
||||
"fieldtype": "Int",
|
||||
"label": "Maximum Points"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_2",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "Custom",
|
||||
"fieldname": "for_doc_event",
|
||||
"fieldtype": "Select",
|
||||
"label": "For Document Event",
|
||||
"options": "New\nSubmit\nCancel\nValue Change\nCustom"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.for_doc_event !=='New'",
|
||||
"description": "Users assigned to the reference document will get points.",
|
||||
"fieldname": "for_assigned_users",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allot Points To Assigned Users"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.for_doc_event=='Value Change'",
|
||||
"fieldname": "field_to_check",
|
||||
"fieldtype": "Select",
|
||||
"label": "Field To Check"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Apply this rule only once per document",
|
||||
"fieldname": "apply_only_once",
|
||||
"fieldtype": "Check",
|
||||
"label": "Apply Only Once"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:25.142267",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Energy Point Rule",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
# Copyright (c) 2018, Frappe Technologies and contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
import frappe.cache_manager
|
||||
from frappe import _
|
||||
from frappe.core.doctype.user.user import get_enabled_users
|
||||
from frappe.model import log_types
|
||||
from frappe.model.document import Document
|
||||
from frappe.social.doctype.energy_point_log.energy_point_log import create_energy_points_log
|
||||
from frappe.social.doctype.energy_point_settings.energy_point_settings import (
|
||||
is_energy_point_enabled,
|
||||
)
|
||||
|
||||
|
||||
class EnergyPointRule(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
apply_only_once: DF.Check
|
||||
condition: DF.Code | None
|
||||
enabled: DF.Check
|
||||
field_to_check: DF.Literal[None]
|
||||
for_assigned_users: DF.Check
|
||||
for_doc_event: DF.Literal["New", "Submit", "Cancel", "Value Change", "Custom"]
|
||||
max_points: DF.Int
|
||||
multiplier_field: DF.Literal[None]
|
||||
points: DF.Int
|
||||
reference_doctype: DF.Link
|
||||
rule_name: DF.Data
|
||||
user_field: DF.Literal[None]
|
||||
# end: auto-generated types
|
||||
|
||||
def on_update(self):
|
||||
frappe.cache_manager.clear_doctype_map("Energy Point Rule", self.reference_doctype)
|
||||
|
||||
def on_trash(self):
|
||||
frappe.cache_manager.clear_doctype_map("Energy Point Rule", self.reference_doctype)
|
||||
|
||||
def apply(self, doc):
|
||||
if self.rule_condition_satisfied(doc):
|
||||
multiplier = 1
|
||||
|
||||
points = self.points
|
||||
if self.multiplier_field:
|
||||
multiplier = doc.get(self.multiplier_field) or 1
|
||||
points = round(points * multiplier)
|
||||
max_points = self.max_points
|
||||
if max_points and points > max_points:
|
||||
points = max_points
|
||||
|
||||
reference_doctype = doc.doctype
|
||||
reference_name = doc.name
|
||||
users = []
|
||||
if self.for_assigned_users:
|
||||
users = doc.get_assigned_users()
|
||||
else:
|
||||
users = [doc.get(self.user_field)]
|
||||
rule = self.name
|
||||
|
||||
# incase of zero as result after roundoff
|
||||
if not points:
|
||||
return
|
||||
|
||||
try:
|
||||
for user in users:
|
||||
if not is_eligible_user(user):
|
||||
continue
|
||||
create_energy_points_log(
|
||||
reference_doctype,
|
||||
reference_name,
|
||||
{"points": points, "user": user, "rule": rule},
|
||||
self.apply_only_once,
|
||||
)
|
||||
except Exception:
|
||||
self.log_error("Energy points failed")
|
||||
|
||||
def rule_condition_satisfied(self, doc):
|
||||
if self.for_doc_event == "New":
|
||||
# indicates that this was a new doc
|
||||
return doc.get_doc_before_save() is None
|
||||
if self.for_doc_event == "Submit":
|
||||
return doc.docstatus.is_submitted()
|
||||
if self.for_doc_event == "Cancel":
|
||||
return doc.docstatus.is_cancelled()
|
||||
if self.for_doc_event == "Value Change":
|
||||
field_to_check = self.field_to_check
|
||||
if not field_to_check:
|
||||
return False
|
||||
doc_before_save = doc.get_doc_before_save()
|
||||
# check if the field has been changed
|
||||
# if condition is set check if it is satisfied
|
||||
return (
|
||||
doc_before_save
|
||||
and doc_before_save.get(field_to_check) != doc.get(field_to_check)
|
||||
and (not self.condition or self.eval_condition(doc))
|
||||
)
|
||||
|
||||
if self.for_doc_event == "Custom" and self.condition:
|
||||
return self.eval_condition(doc)
|
||||
return False
|
||||
|
||||
def eval_condition(self, doc):
|
||||
return self.condition and frappe.safe_eval(self.condition, None, {"doc": doc.as_dict()})
|
||||
|
||||
|
||||
def process_energy_points(doc, state):
|
||||
if (
|
||||
frappe.flags.in_patch
|
||||
or frappe.flags.in_install
|
||||
or frappe.flags.in_migrate
|
||||
or frappe.flags.in_import
|
||||
or frappe.flags.in_setup_wizard
|
||||
or doc.doctype in log_types
|
||||
):
|
||||
return
|
||||
|
||||
if not is_energy_point_enabled():
|
||||
return
|
||||
|
||||
old_doc = doc.get_doc_before_save()
|
||||
|
||||
# check if doc has been cancelled
|
||||
if old_doc and old_doc.docstatus.is_submitted() and doc.docstatus.is_cancelled():
|
||||
return revert_points_for_cancelled_doc(doc)
|
||||
|
||||
for d in frappe.cache_manager.get_doctype_map(
|
||||
"Energy Point Rule", doc.doctype, dict(reference_doctype=doc.doctype, enabled=1)
|
||||
):
|
||||
frappe.get_doc("Energy Point Rule", d.get("name")).apply(doc)
|
||||
|
||||
|
||||
def revert_points_for_cancelled_doc(doc):
|
||||
energy_point_logs = frappe.get_all(
|
||||
"Energy Point Log",
|
||||
{"reference_doctype": doc.doctype, "reference_name": doc.name, "type": "Auto"},
|
||||
)
|
||||
for log in energy_point_logs:
|
||||
reference_log = frappe.get_doc("Energy Point Log", log.name)
|
||||
reference_log.revert(_("Reference document has been cancelled"), ignore_permissions=True)
|
||||
|
||||
|
||||
def get_energy_point_doctypes():
|
||||
return [
|
||||
d.reference_doctype
|
||||
for d in frappe.get_all("Energy Point Rule", ["reference_doctype"], {"enabled": 1})
|
||||
]
|
||||
|
||||
|
||||
def is_eligible_user(user):
|
||||
"""Checks if user is eligible to get energy points"""
|
||||
enabled_users = get_enabled_users()
|
||||
return user and user in enabled_users and user != "Administrator"
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Energy Point Settings", {
|
||||
refresh: function (frm) {
|
||||
if (frm.doc.enabled) {
|
||||
frm.add_custom_button(__("Give Review Points"), show_review_points_dialog);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function show_review_points_dialog() {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Give Review Points"),
|
||||
fields: [
|
||||
{
|
||||
label: "User",
|
||||
fieldname: "user",
|
||||
fieldtype: "Link",
|
||||
options: "User",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
label: "Points",
|
||||
fieldname: "points",
|
||||
fieldtype: "Int",
|
||||
reqd: 1,
|
||||
},
|
||||
],
|
||||
primary_action: function (values) {
|
||||
frappe
|
||||
.xcall(
|
||||
"frappe.social.doctype.energy_point_log.energy_point_log.add_review_points",
|
||||
{
|
||||
user: values.user,
|
||||
points: values.points,
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
frappe.show_alert({
|
||||
message: __("Successfully Done"),
|
||||
indicator: "green",
|
||||
});
|
||||
})
|
||||
.finally(() => dialog.hide());
|
||||
},
|
||||
primary_action_label: __("Submit"),
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2019-03-19 13:17:51.710241",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enabled",
|
||||
"section_break_2",
|
||||
"review_levels",
|
||||
"point_allocation_periodicity",
|
||||
"last_point_allocation_date"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"depends_on": "enabled",
|
||||
"fieldname": "section_break_2",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "review_levels",
|
||||
"fieldtype": "Table",
|
||||
"label": "Review Levels",
|
||||
"options": "Review Level"
|
||||
},
|
||||
{
|
||||
"default": "Weekly",
|
||||
"fieldname": "point_allocation_periodicity",
|
||||
"fieldtype": "Select",
|
||||
"label": "Point Allocation Periodicity",
|
||||
"options": "Daily\nWeekly\nMonthly"
|
||||
},
|
||||
{
|
||||
"fieldname": "last_point_allocation_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Last Point Allocation Date",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:25.273422",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Energy Point Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "ASC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.social.doctype.energy_point_log.energy_point_log import create_review_points_log
|
||||
from frappe.utils import add_to_date, getdate, today
|
||||
|
||||
|
||||
class EnergyPointSettings(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.social.doctype.review_level.review_level import ReviewLevel
|
||||
from frappe.types import DF
|
||||
|
||||
enabled: DF.Check
|
||||
last_point_allocation_date: DF.Date | None
|
||||
point_allocation_periodicity: DF.Literal["Daily", "Weekly", "Monthly"]
|
||||
review_levels: DF.Table[ReviewLevel]
|
||||
# end: auto-generated types
|
||||
|
||||
def on_update(self):
|
||||
if self.has_value_changed("enabled"):
|
||||
frappe.cache.delete_key("bootinfo")
|
||||
|
||||
|
||||
def is_energy_point_enabled():
|
||||
return frappe.client_cache.get_doc("Energy Point Settings").enabled
|
||||
|
||||
|
||||
def allocate_review_points():
|
||||
settings = frappe.get_single("Energy Point Settings")
|
||||
|
||||
if not can_allocate_today(settings.last_point_allocation_date, settings.point_allocation_periodicity):
|
||||
return
|
||||
|
||||
user_point_map = {}
|
||||
|
||||
for level in settings.review_levels:
|
||||
users = get_users_with_role(level.role)
|
||||
for user in users:
|
||||
user_point_map.setdefault(user, 0)
|
||||
# to avoid duplicate point allocation
|
||||
user_point_map[user] = max([user_point_map[user], level.review_points])
|
||||
|
||||
for user, points in user_point_map.items():
|
||||
create_review_points_log(user, points)
|
||||
|
||||
settings.last_point_allocation_date = today()
|
||||
settings.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def can_allocate_today(last_date, periodicity):
|
||||
if not last_date:
|
||||
return True
|
||||
|
||||
days_to_add = {"Daily": 1, "Weekly": 7, "Monthly": 30}.get(periodicity, 1)
|
||||
|
||||
next_allocation_date = add_to_date(last_date, days=days_to_add)
|
||||
|
||||
return getdate(next_allocation_date) <= getdate()
|
||||
|
||||
|
||||
def get_users_with_role(role):
|
||||
return [
|
||||
p[0]
|
||||
for p in frappe.db.sql(
|
||||
"""SELECT DISTINCT `tabUser`.`name`
|
||||
FROM `tabHas Role`, `tabUser`
|
||||
WHERE `tabHas Role`.`role`=%s
|
||||
AND `tabUser`.`name`!='Administrator'
|
||||
AND `tabHas Role`.`parent`=`tabUser`.`name`
|
||||
AND `tabUser`.`enabled`=1""",
|
||||
role,
|
||||
)
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright (c) 2021, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase, UnitTestCase
|
||||
|
||||
|
||||
class UnitTestEnergyPointSettings(UnitTestCase):
|
||||
"""
|
||||
Unit tests for EnergyPointSettings.
|
||||
Use this class for testing individual functions and methods.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class TestEnergyPointSettings(IntegrationTestCase):
|
||||
pass
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Review Level", {
|
||||
// refresh: function(frm) {
|
||||
// }
|
||||
});
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"creation": "2019-03-19 13:16:12.762352",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"level_name",
|
||||
"role",
|
||||
"review_points"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "level_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Level Name",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "role",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Role",
|
||||
"options": "Role",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "review_points",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "Review Points",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-23 16:03:36.743533",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Review Level",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# License: MIT. See LICENSE
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class ReviewLevel(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
level_name: DF.Data
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
review_points: DF.Int
|
||||
role: DF.Link
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
{% from "frappe/templates/includes/avatar_macro.html" import avatar %}
|
||||
{% if top_performer.energy_points %}
|
||||
<h1 class="text-2xl">{{ _('Top Performer') }} 🏆 </h1>
|
||||
<p>
|
||||
<span class="text-muted">
|
||||
{{ avatar(top_performer.user) }}
|
||||
{{
|
||||
_('{0} gained {1} points').format(
|
||||
frappe.get_fullname(top_performer.user),
|
||||
frappe.utils.cint(top_performer.energy_points)
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if top_reviewer.given_points %}
|
||||
<h1 class="text-xl">{{ _('Top Reviewer') }} ❤️ </h1>
|
||||
<p>
|
||||
<span class="text-muted">
|
||||
{{ avatar(top_reviewer.user) }}
|
||||
{{
|
||||
_('{0} gave {1} points').format(
|
||||
frappe.get_fullname(top_reviewer.user),
|
||||
frappe.utils.cint(top_reviewer.given_points)
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<h3 class="text-regular" style="margin-top: 35px">{{ _('Standings') }} ({{ _('Top {0}').format(standings|length) }}) </h3>
|
||||
|
||||
<table class='table table-bordered'>
|
||||
<tr class="text-muted text-medium">
|
||||
<td> # </td>
|
||||
<td style='width: 70%'>{{ _('User') }}</td>
|
||||
<td style='width: 15%'>{{ _('Energy Points') }}</td>
|
||||
<td style='width: 15%'>{{ _('Points Given') }}</td>
|
||||
</tr>
|
||||
{% for user in standings %}
|
||||
<tr class="text-small">
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ frappe.get_fullname(user.user) }}</td>
|
||||
<td class="text-muted">{{ frappe.utils.cint(user.energy_points) }}</td>
|
||||
<td class="text-muted">{{ frappe.utils.cint(user.given_points) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p class="text-muted text-medium">
|
||||
{{ footer_message }}
|
||||
</p>
|
||||
Loading…
Add table
Reference in a new issue