From 623998c787314dfd0eb70b41d2bb26a418de3624 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 20 Aug 2021 11:03:06 +0530 Subject: [PATCH 01/32] fix(db): Cast single dt field only if value is truthy --- frappe/database/database.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index d6ecf0795d..d4fc6c2219 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -557,7 +557,10 @@ class Database(object): if not df: frappe.throw(_('Invalid field name: {0}').format(frappe.bold(fieldname)), self.InvalidColumnName) - val = cast_fieldtype(df.fieldtype, val) + # cast only if value is "set" or is truthy? + # cast_fieldtype returns currnt TS value for Datetime, Date fields + if val: + val = cast_fieldtype(df.fieldtype, val) self.value_cache[doctype][fieldname] = val From cb034e4c52e2213cdbe5f5a053e8a1bec169ebd9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 20 Aug 2021 12:08:39 +0530 Subject: [PATCH 02/32] fix: Consistent return types in cast_fieldtype Note: BREAKING CHANGE --- frappe/database/database.py | 5 +---- frappe/utils/data.py | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index d4fc6c2219..d6ecf0795d 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -557,10 +557,7 @@ class Database(object): if not df: frappe.throw(_('Invalid field name: {0}').format(frappe.bold(fieldname)), self.InvalidColumnName) - # cast only if value is "set" or is truthy? - # cast_fieldtype returns currnt TS value for Datetime, Date fields - if val: - val = cast_fieldtype(df.fieldtype, val) + val = cast_fieldtype(df.fieldtype, val) self.value_cache[doctype][fieldname] = val diff --git a/frappe/utils/data.py b/frappe/utils/data.py index f2c553211d..d89cda1519 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -505,7 +505,17 @@ def has_common(l1, l2): """Returns truthy value if there are common elements in lists l1 and l2""" return set(l1) & set(l2) -def cast_fieldtype(fieldtype, value): +def cast_fieldtype(fieldtype, value=None): + """Cast the value to the Python native object of the Frappe fieldtype provided. + If value is None, the first/lowest value of the `fieldtype` will be returned. + + Mapping of Python types => Frappe types: + * float => ("Currency", "Float", "Percent") + * int => ("Int", "Check") + * datetime.datetime => ("Datetime",) + * datetime.date => ("Date",) + * datetime.time => ("Time",) + """ if fieldtype in ("Currency", "Float", "Percent"): value = flt(value) @@ -517,12 +527,18 @@ def cast_fieldtype(fieldtype, value): value = cstr(value) elif fieldtype == "Date": + if value is None: + value = datetime.datetime(1, 1, 1).date() value = getdate(value) elif fieldtype == "Datetime": + if value is None: + value = datetime.datetime(1, 1, 1) value = get_datetime(value) elif fieldtype == "Time": + if value is None: + value = "0:0:0" value = to_timedelta(value) return value From a2cb9be7a4cd89e563888006e91ba03f353c251c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 20 Aug 2021 12:35:24 +0530 Subject: [PATCH 03/32] feat: frappe.utils.data.cast Cast the value to the Python native object of the Frappe fieldtype provided. If value is None, the first/lowest value of the `fieldtype` will be returned. Mapping of Python types => Frappe types: * float => ("Currency", "Float", "Percent") * int => ("Int", "Check") * datetime.datetime => ("Datetime",) * datetime.date => ("Date",) * datetime.time => ("Time",) Deprecate frappe.utils.data.cast_fieldtype in favour of new util cast which handles types "better" --- frappe/utils/data.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index d89cda1519..4a25ad997a 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -8,6 +8,7 @@ import re, datetime, math, time from code import compile_command from urllib.parse import quote, urljoin from frappe.desk.utils import slug +from click import secho DATE_FORMAT = "%Y-%m-%d" TIME_FORMAT = "%H:%M:%S.%f" @@ -505,7 +506,36 @@ def has_common(l1, l2): """Returns truthy value if there are common elements in lists l1 and l2""" return set(l1) & set(l2) -def cast_fieldtype(fieldtype, value=None): +def cast_fieldtype(fieldtype, value): + # TODO: Add DeprecationWarning for this util + message = ( + "Function `frappe.utils.data.cast` has been deprecated in favour" + " of `frappe.utils.data.cast`. Use the newer util for safer type casting. " + ) + secho(message, fg="yellow") + + if fieldtype in ("Currency", "Float", "Percent"): + value = flt(value) + + elif fieldtype in ("Int", "Check"): + value = cint(value) + + elif fieldtype in ("Data", "Text", "Small Text", "Long Text", + "Text Editor", "Select", "Link", "Dynamic Link"): + value = cstr(value) + + elif fieldtype == "Date": + value = getdate(value) + + elif fieldtype == "Datetime": + value = get_datetime(value) + + elif fieldtype == "Time": + value = to_timedelta(value) + + return value + +def cast(fieldtype, value=None): """Cast the value to the Python native object of the Frappe fieldtype provided. If value is None, the first/lowest value of the `fieldtype` will be returned. @@ -1218,7 +1248,7 @@ def evaluate_filters(doc, filters): def compare(val1, condition, val2, fieldtype=None): ret = False if fieldtype: - val2 = cast_fieldtype(fieldtype, val2) + val2 = cast(fieldtype, val2) if condition in operator_map: ret = operator_map[condition](val1, val2) From ed6533f73705a483853d5b5ae8bf8cc0fca9156a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 20 Aug 2021 12:37:15 +0530 Subject: [PATCH 04/32] fix: Use cast in favour of cast_fieldtype Use newly introduced casting util for Python-Frappe types mapping --- frappe/database/database.py | 21 ++------------------- frappe/model/base_document.py | 4 ++-- frappe/model/meta.py | 12 ++++++------ 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index d6ecf0795d..9fab8e116f 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -14,7 +14,7 @@ import frappe.model.meta from frappe import _ from time import time -from frappe.utils import now, getdate, cast_fieldtype, get_datetime, get_table_name +from frappe.utils import now, getdate, cast, get_datetime, get_table_name from frappe.model.utils.link_count import flush_local_link_count @@ -516,7 +516,6 @@ class Database(object): FROM `tabSingles` WHERE doctype = %s """, doctype) - # result = _cast_result(doctype, result) dict_ = frappe._dict(result) @@ -557,7 +556,7 @@ class Database(object): if not df: frappe.throw(_('Invalid field name: {0}').format(frappe.bold(fieldname)), self.InvalidColumnName) - val = cast_fieldtype(df.fieldtype, val) + val = cast(df.fieldtype, val) self.value_cache[doctype][fieldname] = val @@ -1052,19 +1051,3 @@ def enqueue_jobs_after_commit(): q.enqueue_call(execute_job, timeout=job.get("timeout"), kwargs=job.get("queue_args")) frappe.flags.enqueue_after_commit = [] - -# Helpers -def _cast_result(doctype, result): - batch = [ ] - - try: - for field, value in result: - df = frappe.get_meta(doctype).get_field(field) - if df: - value = cast_fieldtype(df.fieldtype, value) - - batch.append(tuple([field, value])) - except frappe.exceptions.DoesNotExistError: - return result - - return tuple(batch) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 752543f46a..815dd27002 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -9,7 +9,7 @@ from frappe.model.utils.link_count import notify_link_count from frappe.modules import load_doctype_module from frappe.model import display_fieldtypes from frappe.utils import (cint, flt, now, cstr, strip_html, - sanitize_html, sanitize_email, cast_fieldtype) + sanitize_html, sanitize_email, cast) from frappe.utils.html_utils import unescape_html max_positive_value = { @@ -969,7 +969,7 @@ class BaseDocument(object): return self.cast(val, df) def cast(self, value, df): - return cast_fieldtype(df.fieldtype, value) + return cast(df.fieldtype, value) def _extract_images_from_text_editor(self): from frappe.core.doctype.file.file import extract_images_from_doc diff --git a/frappe/model/meta.py b/frappe/model/meta.py index de794ba77f..f89163e092 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -16,7 +16,7 @@ Example: ''' from datetime import datetime import frappe, json, os -from frappe.utils import cstr, cint, cast_fieldtype +from frappe.utils import cstr, cint, cast from frappe.model import default_fields, no_value_fields, optional_fields, data_fieldtypes, table_fields from frappe.model.document import Document from frappe.model.base_document import BaseDocument @@ -322,24 +322,24 @@ class Meta(Document): for ps in property_setters: if ps.doctype_or_field=='DocType': - self.set(ps.property, cast_fieldtype(ps.property_type, ps.value)) + self.set(ps.property, cast(ps.property_type, ps.value)) elif ps.doctype_or_field=='DocField': for d in self.fields: if d.fieldname == ps.field_name: - d.set(ps.property, cast_fieldtype(ps.property_type, ps.value)) + d.set(ps.property, cast(ps.property_type, ps.value)) break elif ps.doctype_or_field=='DocType Link': for d in self.links: if d.name == ps.row_name: - d.set(ps.property, cast_fieldtype(ps.property_type, ps.value)) + d.set(ps.property, cast(ps.property_type, ps.value)) break elif ps.doctype_or_field=='DocType Action': for d in self.actions: if d.name == ps.row_name: - d.set(ps.property, cast_fieldtype(ps.property_type, ps.value)) + d.set(ps.property, cast(ps.property_type, ps.value)) break def add_custom_links_and_actions(self): @@ -532,7 +532,7 @@ class Meta(Document): label = link.group, items = [link.parent_doctype or link.link_doctype] )) - + if not link.is_child_table: if link.link_fieldname != data.fieldname: if data.fieldname: From 863b462bbd4c0d9afb037610d7a5537bf51939b6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 26 Aug 2021 10:58:59 +0530 Subject: [PATCH 05/32] fix: Pass get_query to link selector to get filtered list --- frappe/public/js/frappe/form/grid.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js index 05c70b214d..dd1d622bab 100644 --- a/frappe/public/js/frappe/form/grid.js +++ b/frappe/public/js/frappe/form/grid.js @@ -786,6 +786,7 @@ export default class Grid { doctype: link_field.options, fieldname: link, qty_fieldname: qty, + get_query: link_field.get_query, target: this, txt: "" }); From 8eb7f0f71915a071fbf560dee183ce06a94e0f4a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 26 Aug 2021 10:59:47 +0530 Subject: [PATCH 06/32] fix: Call search after value is set to get latest filtered results --- frappe/public/js/frappe/form/link_selector.js | 97 ++++++++++--------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/frappe/public/js/frappe/form/link_selector.js b/frappe/public/js/frappe/form/link_selector.js index b8f1b3a842..3ae03dbe91 100644 --- a/frappe/public/js/frappe/form/link_selector.js +++ b/frappe/public/js/frappe/form/link_selector.js @@ -96,10 +96,10 @@ frappe.ui.form.LinkSelector = class LinkSelector { .attr('data-value', v[0]) .click(function () { var value = $(this).attr("data-value"); - var $link = this; if (me.target.is_grid) { // set in grid - me.set_in_grid(value); + // call search after value is set to get latest filtered results + me.set_in_grid(value).then(() => me.search()); } else { if (me.target.doctype) me.target.parse_validate_and_set_in_model(value); @@ -110,8 +110,8 @@ frappe.ui.form.LinkSelector = class LinkSelector { me.dialog.hide(); } return false; - }) - }) + }); + }); } else { $('


' + __("No Results") + '' + (frappe.model.can_create(me.doctype) ? @@ -130,49 +130,56 @@ frappe.ui.form.LinkSelector = class LinkSelector { }, this.dialog.get_primary_btn()); } - set_in_grid (value) { - var me = this, updated = false; - var d = null; - if (this.qty_fieldname) { - frappe.prompt({ - fieldname: "qty", fieldtype: "Float", label: "Qty", - "default": 1, reqd: 1 - }, function (data) { - $.each(me.target.frm.doc[me.target.df.fieldname] || [], function (i, d) { - if (d[me.fieldname] === value) { - frappe.model.set_value(d.doctype, d.name, me.qty_fieldname, data.qty); - frappe.show_alert(__("Added {0} ({1})", [value, d[me.qty_fieldname]])); - updated = true; - return false; + set_in_grid(value) { + return new Promise((resolve) => { + if (this.qty_fieldname) { + frappe.prompt({ + fieldname: "qty", + fieldtype: "Float", + label: "Qty", + default: 1, + reqd: 1 + }, (data) => { + let updated = (this.target.frm.doc[this.target.df.fieldname] || []).some(d => { + if (d[this.fieldname] === value) { + frappe.model.set_value(d.doctype, d.name, this.qty_fieldname, data.qty).then(() => { + frappe.show_alert(__("Added {0} ({1})", [value, d[this.qty_fieldname]])); + resolve(); + }); + return true; + } + }); + if (!updated) { + let d = null; + frappe.run_serially([ + () => d = this.target.add_new_row(), + () => frappe.timeout(0.1), + () => { + let args = {}; + args[this.fieldname] = value; + args[this.qty_fieldname] = data.qty; + return frappe.model.set_value(d.doctype, d.name, args); + }, + () => frappe.show_alert(__("Added {0} ({1})", [value, data.qty])), + () => resolve() + ]); } + }, __("Set Quantity"), __("Set")); + } else if (this.dynamic_link_field) { + let d = this.target.add_new_row(); + frappe.model.set_value(d.doctype, d.name, this.dynamic_link_field, this.dynamic_link_reference); + frappe.model.set_value(d.doctype, d.name, this.fieldname, value).then(() => { + frappe.show_alert(__("{0} {1} added", [this.dynamic_link_reference, value])); + resolve(); }); - if (!updated) { - frappe.run_serially([ - () => { - d = me.target.add_new_row(); - }, - () => frappe.timeout(0.1), - () => { - let args = {}; - args[me.fieldname] = value; - args[me.qty_fieldname] = data.qty; - - return frappe.model.set_value(d.doctype, d.name, args); - }, - () => frappe.show_alert(__("Added {0} ({1})", [value, data.qty])) - ]); - } - }, __("Set Quantity"), __("Set")); - } else if (me.dynamic_link_field) { - var d = me.target.add_new_row(); - frappe.model.set_value(d.doctype, d.name, me.dynamic_link_field, me.dynamic_link_reference); - frappe.model.set_value(d.doctype, d.name, me.fieldname, value); - frappe.show_alert(__("{0} {1} added", [me.dynamic_link_reference, value])); - } else { - var d = me.target.add_new_row(); - frappe.model.set_value(d.doctype, d.name, me.fieldname, value); - frappe.show_alert(__("{0} added", [value])); - } + } else { + let d = this.target.add_new_row(); + frappe.model.set_value(d.doctype, d.name, this.fieldname, value).then(() => { + frappe.show_alert(__("{0} added", [value])); + resolve(); + }); + } + }); } }; From 8c7e9527087d4f429e7d1c63909e57c8bd4be384 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Thu, 26 Aug 2021 21:43:44 +0530 Subject: [PATCH 07/32] fix: Show Document Status (docstatus) in list view through status indicator pill --- frappe/public/icons/timeless/symbol-defs.svg | 10 ++++++++++ frappe/public/js/frappe/list/list_view.js | 4 +++- frappe/public/js/frappe/utils/utils.js | 7 +++++-- frappe/public/scss/common/indicator.scss | 10 ++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index f216374526..533edc962a 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -709,4 +709,14 @@ + + + + + + + + + + diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 8a0e43c8f3..6c6e59c248 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -950,9 +950,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { get_indicator_html(doc) { const indicator = frappe.get_indicator(doc, this.doctype); + const icon = doc.docstatus == 1 ? 'submitted' : doc.docstatus == 2 ? 'cancelled' : 'dot'; if (indicator) { - return ` + ${frappe.utils.icon(icon, 'xs', '', indicator[1])} ${__(indicator[0])} `; } diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 21841296dc..12b883a552 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1114,14 +1114,17 @@ Object.assign(frappe.utils, { } }, - icon(icon_name, size="sm", icon_class="") { + icon(icon_name, size="sm", icon_class="", stroke_color="") { let size_class = ""; let icon_style = ""; if (typeof size == "object") { - icon_style = `width: ${size.width}; height: ${size.height}`; + icon_style = `width: ${size.width}; height: ${size.height};`; } else { size_class = `icon-${size}`; } + if (stroke_color) { + icon_style += `stroke: ${stroke_color};`; + } return ` `; diff --git a/frappe/public/scss/common/indicator.scss b/frappe/public/scss/common/indicator.scss index 62d7cacc9d..600373baec 100644 --- a/frappe/public/scss/common/indicator.scss +++ b/frappe/public/scss/common/indicator.scss @@ -176,3 +176,13 @@ @keyframes blink { 50% { opacity: 0.5; } } + +.indicator-pill-icon { + margin-right: 3px; +} + +.list-indicator-pill::before { + content: ''; + width: unset; + margin-right: unset; +} From 4f31f9c501f9deb6febe22d23b27cfcc211c846e Mon Sep 17 00:00:00 2001 From: shariquerik Date: Mon, 30 Aug 2021 11:48:39 +0530 Subject: [PATCH 08/32] fix: Show Document Status (docstatus) as a title on hovering the Status Indicator Pill --- frappe/public/icons/timeless/symbol-defs.svg | 10 ---------- frappe/public/js/frappe/list/list_view.js | 8 ++++---- frappe/public/js/frappe/utils/utils.js | 7 ++----- frappe/public/scss/common/indicator.scss | 12 +----------- 4 files changed, 7 insertions(+), 30 deletions(-) diff --git a/frappe/public/icons/timeless/symbol-defs.svg b/frappe/public/icons/timeless/symbol-defs.svg index 1732548bbc..b2f1428967 100644 --- a/frappe/public/icons/timeless/symbol-defs.svg +++ b/frappe/public/icons/timeless/symbol-defs.svg @@ -709,14 +709,4 @@ - - - - - - - - - - diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 914807f57a..f304d313b4 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -952,11 +952,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { get_indicator_html(doc) { const indicator = frappe.get_indicator(doc, this.doctype); - const icon = doc.docstatus == 1 ? 'submitted' : doc.docstatus == 2 ? 'cancelled' : 'dot'; + const title = doc.docstatus == 1 ? 'Document has been submitted' : doc.docstatus == 2 ? + 'Document has been cancelled' : 'Document is in draft state'; if (indicator) { - return ` - ${frappe.utils.icon(icon, 'xs', '', indicator[1])} + return ` ${__(indicator[0])} `; } diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 12b883a552..21841296dc 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -1114,17 +1114,14 @@ Object.assign(frappe.utils, { } }, - icon(icon_name, size="sm", icon_class="", stroke_color="") { + icon(icon_name, size="sm", icon_class="") { let size_class = ""; let icon_style = ""; if (typeof size == "object") { - icon_style = `width: ${size.width}; height: ${size.height};`; + icon_style = `width: ${size.width}; height: ${size.height}`; } else { size_class = `icon-${size}`; } - if (stroke_color) { - icon_style += `stroke: ${stroke_color};`; - } return ` `; diff --git a/frappe/public/scss/common/indicator.scss b/frappe/public/scss/common/indicator.scss index 600373baec..13162ab6b1 100644 --- a/frappe/public/scss/common/indicator.scss +++ b/frappe/public/scss/common/indicator.scss @@ -175,14 +175,4 @@ @keyframes blink { 50% { opacity: 0.5; } -} - -.indicator-pill-icon { - margin-right: 3px; -} - -.list-indicator-pill::before { - content: ''; - width: unset; - margin-right: unset; -} +} \ No newline at end of file From f84344592a29a3eb2a60b961e595d0ea04322dfd Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 30 Aug 2021 13:31:07 +0530 Subject: [PATCH 09/32] Apply suggestions from code review Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- frappe/public/js/frappe/list/list_view.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index f304d313b4..c35b5e2d51 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -952,11 +952,16 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { get_indicator_html(doc) { const indicator = frappe.get_indicator(doc, this.doctype); - const title = doc.docstatus == 1 ? 'Document has been submitted' : doc.docstatus == 2 ? - 'Document has been cancelled' : 'Document is in draft state'; + // sequence is important + const docstatus_description = [ + __('Document is in draft state'), + __('Document has been submitted'), + __('Document has been cancelled') + ] + const title = docstatus_description[doc.docstatus || 0] if (indicator) { return ` + data-filter='${indicator[2]}' title='${title}'> ${__(indicator[0])} `; } From b8c51b13e2f89e231c19bc94b32fb7e8251557ca Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 30 Aug 2021 13:45:30 +0530 Subject: [PATCH 10/32] fix: Revert to using cast_fieldtype in BaseDocument.cast * reference: revert Breaking Change - https://github.com/frappe/frappe/pull/13989#discussion_r695624003 * Show deprecation warning unless `show_warning` is unset --- frappe/model/base_document.py | 4 ++-- frappe/utils/data.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 815dd27002..1e3ef53fbd 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -9,7 +9,7 @@ from frappe.model.utils.link_count import notify_link_count from frappe.modules import load_doctype_module from frappe.model import display_fieldtypes from frappe.utils import (cint, flt, now, cstr, strip_html, - sanitize_html, sanitize_email, cast) + sanitize_html, sanitize_email, cast_fieldtype) from frappe.utils.html_utils import unescape_html max_positive_value = { @@ -969,7 +969,7 @@ class BaseDocument(object): return self.cast(val, df) def cast(self, value, df): - return cast(df.fieldtype, value) + return cast_fieldtype(df.fieldtype, value, show_warning=False) def _extract_images_from_text_editor(self): from frappe.core.doctype.file.file import extract_images_from_doc diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 4a25ad997a..4e972c0189 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -506,13 +506,13 @@ def has_common(l1, l2): """Returns truthy value if there are common elements in lists l1 and l2""" return set(l1) & set(l2) -def cast_fieldtype(fieldtype, value): - # TODO: Add DeprecationWarning for this util - message = ( - "Function `frappe.utils.data.cast` has been deprecated in favour" - " of `frappe.utils.data.cast`. Use the newer util for safer type casting. " - ) - secho(message, fg="yellow") +def cast_fieldtype(fieldtype, value, show_warning=True): + if show_warning: + message = ( + "Function `frappe.utils.data.cast` has been deprecated in favour" + " of `frappe.utils.data.cast`. Use the newer util for safer type casting." + ) + secho(message, fg="yellow") if fieldtype in ("Currency", "Float", "Percent"): value = flt(value) From 7c7eb68e4df5990ec75a8cbdf84cb6f717c69fe2 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Mon, 30 Aug 2021 14:00:40 +0530 Subject: [PATCH 11/32] fix: sider fix --- frappe/public/js/frappe/list/list_view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index c35b5e2d51..503309515c 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -957,8 +957,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { __('Document is in draft state'), __('Document has been submitted'), __('Document has been cancelled') - ] - const title = docstatus_description[doc.docstatus || 0] + ]; + const title = docstatus_description[doc.docstatus || 0]; if (indicator) { return ` From 025be097727c1e778888eff219c824e3d05408c1 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Mon, 30 Aug 2021 18:49:34 +0530 Subject: [PATCH 12/32] fix: Home redirect issue and made default content as [] --- frappe/desk/doctype/workspace/workspace.json | 3 ++- frappe/public/js/frappe/desk.js | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frappe/desk/doctype/workspace/workspace.json b/frappe/desk/doctype/workspace/workspace.json index 020f3153df..1e111b8d12 100644 --- a/frappe/desk/doctype/workspace/workspace.json +++ b/frappe/desk/doctype/workspace/workspace.json @@ -242,6 +242,7 @@ "label": "Parent Page" }, { + "default": "[]", "fieldname": "content", "fieldtype": "Long Text", "hidden": 1, @@ -265,7 +266,7 @@ } ], "links": [], - "modified": "2021-08-19 12:51:00.233017", + "modified": "2021-08-30 18:47:18.227154", "modified_by": "Administrator", "module": "Desk", "name": "Workspace", diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 99fc4da182..a4dc1a6709 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -285,10 +285,6 @@ frappe.Application = class Application { frappe.modules[page.module]=page; frappe.workspaces[frappe.router.slug(page.title)] = page; } - if (!frappe.workspaces['home']) { - // default workspace is settings for Frappe - frappe.workspaces['home'] = frappe.workspaces[Object.keys(frappe.workspaces)[0]]; - } } load_user_permissions() { From be72397bcad856a1acbf1ac0fa2202966504afae Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 30 Aug 2021 18:51:36 +0530 Subject: [PATCH 13/32] test: Add tests for frappe.utils.data.cast --- frappe/tests/test_utils.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py index 95ba763482..0d41f285be 100644 --- a/frappe/tests/test_utils.py +++ b/frappe/tests/test_utils.py @@ -6,12 +6,13 @@ import frappe from frappe.utils import evaluate_filters, money_in_words, scrub_urls, get_url from frappe.utils import validate_url, validate_email_address from frappe.utils import ceil, floor -from frappe.utils.data import validate_python_code +from frappe.utils.data import cast, validate_python_code from PIL import Image from frappe.utils.image import strip_exif_data, optimize_image import io from mimetypes import guess_type +from datetime import datetime, timedelta, date class TestFilters(unittest.TestCase): def test_simple_dict(self): @@ -93,6 +94,34 @@ class TestDataManipulation(unittest.TestCase): self.assertTrue('style="background-image: url(\'{0}/assets/frappe/bg.jpg\') !important"'.format(url) in html) self.assertTrue('email' in html) +class TestFieldCasting(unittest.TestCase): + def test_float_types(self): + FLOAT_TYPES = ("Currency", "Float", "Percent") + for fieldtype in FLOAT_TYPES: + self.assertIsInstance(cast(fieldtype, value=None), float) + self.assertIsInstance(cast(fieldtype, value=1.12), float) + self.assertIsInstance(cast(fieldtype, value=112), float) + + def test_int_types(self): + INT_TYPES = ("Int", "Check") + + for fieldtype in INT_TYPES: + self.assertIsInstance(cast(fieldtype, value=None), int) + self.assertIsInstance(cast(fieldtype, value=1.12), int) + self.assertIsInstance(cast(fieldtype, value=112), int) + + def test_datetime_types(self): + self.assertIsInstance(cast("Datetime", value=None), datetime) + self.assertIsInstance(cast("Datetime", value="12-2-22"), datetime) + + def test_date_types(self): + self.assertIsInstance(cast("Date", value=None), date) + self.assertIsInstance(cast("Date", value="12-12-2021"), date) + + def test_time_types(self): + self.assertIsInstance(cast("Time", value=None), timedelta) + self.assertIsInstance(cast("Time", value="12:03:34"), timedelta) + class TestMathUtils(unittest.TestCase): def test_floor(self): from decimal import Decimal @@ -205,7 +234,6 @@ class TestImage(unittest.TestCase): self.assertLess(len(optimized_content), len(original_content)) class TestPythonExpressions(unittest.TestCase): - def test_validation_for_good_python_expression(self): valid_expressions = [ "foo == bar", @@ -229,4 +257,4 @@ class TestPythonExpressions(unittest.TestCase): "oops = forgot_equals", ] for expr in invalid_expressions: - self.assertRaises(frappe.ValidationError, validate_python_code, expr) \ No newline at end of file + self.assertRaises(frappe.ValidationError, validate_python_code, expr) From 8e96d8d52294d371d9dacd533e844f18356204e2 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 31 Aug 2021 12:55:31 +0530 Subject: [PATCH 14/32] test: Add tests for str fieldstypes cast --- frappe/tests/test_utils.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py index 0d41f285be..3033673224 100644 --- a/frappe/tests/test_utils.py +++ b/frappe/tests/test_utils.py @@ -95,6 +95,17 @@ class TestDataManipulation(unittest.TestCase): self.assertTrue('email' in html) class TestFieldCasting(unittest.TestCase): + def test_str_types(self): + STR_TYPES = ( + "Data", "Text", "Small Text", "Long Text", "Text Editor", "Select", "Link", "Dynamic Link" + ) + for fieldtype in STR_TYPES: + self.assertIsInstance(cast(fieldtype, value=None), str) + self.assertIsInstance(cast(fieldtype, value="12-12-2021"), str) + self.assertIsInstance(cast(fieldtype, value=""), str) + self.assertIsInstance(cast(fieldtype, value=[]), str) + self.assertIsInstance(cast(fieldtype, value=set()), str) + def test_float_types(self): FLOAT_TYPES = ("Currency", "Float", "Percent") for fieldtype in FLOAT_TYPES: From 3858e95e80073fde1e88e2cc21b71bf6ca02b4f6 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 31 Aug 2021 12:57:05 +0530 Subject: [PATCH 15/32] feat(utils): Add util get_timedelta get_timedelta returns None in case of invalid or imparsable inputs. This behaviour is consistent wrt other utils. The util, to_timedelta tries to convert to timedelta objects only if str object is passed. It returns the same object if not string, which is absurd...given its called `to_timedelta`. --- frappe/utils/data.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 4e972c0189..9ff8deb517 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -1,6 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See license.txt +from typing import Optional import frappe import operator import json @@ -68,6 +69,31 @@ def get_datetime(datetime_str=None): except ValueError: return parser.parse(datetime_str) +def get_timedelta(time: str = None) -> Optional[datetime.timedelta]: + """Return `datetime.timedelta` object from string value of a + valid time format. Returns None if `time` is not a valid format + + Args: + time (str): A valid time representation. This string is parsed + using `dateutil.parser.parse`. Examples of valid inputs are: + '0:0:0', '17:21:00', '2012-01-19 17:21:00'. Checkout + https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.parse + + Returns: + datetime.timedelta: Timedelta object equivalent of the passed `time` string + """ + from dateutil import parser + + time = time or "0:0:0" + + try: + t = parser.parse(time) + return datetime.timedelta( + hours=t.hour, minutes=t.minute, seconds=t.second, microseconds=t.microsecond + ) + except Exception: + return None + def to_timedelta(time_str): from dateutil import parser From 8622142d7dd539b36a15b7dfaa98e5e22b6cfe1c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 31 Aug 2021 13:04:32 +0530 Subject: [PATCH 16/32] fix: Use get_timedelta in cast, add to safe_exec list * Return date and datetime objects for Date and Datetime field types respectively if Falsy output is set --- frappe/utils/data.py | 14 +++++++------- frappe/utils/safe_exec.py | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 9ff8deb517..62eb6790e0 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -583,19 +583,19 @@ def cast(fieldtype, value=None): value = cstr(value) elif fieldtype == "Date": - if value is None: + if value: + value = getdate(value) + else: value = datetime.datetime(1, 1, 1).date() - value = getdate(value) elif fieldtype == "Datetime": - if value is None: + if value: + value = get_datetime(value) + else: value = datetime.datetime(1, 1, 1) - value = get_datetime(value) elif fieldtype == "Time": - if value is None: - value = "0:0:0" - value = to_timedelta(value) + value = get_timedelta(value) return value diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 2e27859faa..4de685e53e 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -228,6 +228,7 @@ VALID_UTILS = ( "getdate", "get_datetime", "to_timedelta", +"get_timedelta", "add_to_date", "add_days", "add_months", From 176d6d2d069b918d959a3240c17d153cc4312f54 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 31 Aug 2021 13:07:22 +0530 Subject: [PATCH 17/32] fix: Check if input is str is_invalid_date_string This is sort of a breaking change? Because if an int/dict/list/tuple was passed instead of a str, object doesnt have .startswith (AttributeError) would be raised instead of just returning None. --- frappe/utils/data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 62eb6790e0..5cd41226da 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -18,10 +18,10 @@ DATETIME_FORMAT = DATE_FORMAT + " " + TIME_FORMAT def is_invalid_date_string(date_string): # dateutil parser does not agree with dates like "0001-01-01" or "0000-00-00" - return (not date_string) or (date_string or "").startswith(("0001-01-01", "0000-00-00")) + return not isinstance(date_string, str) or ((not date_string) or (date_string or "").startswith(("0001-01-01", "0000-00-00"))) # datetime functions -def getdate(string_date=None): +def getdate(string_date: str = None): """ Converts string date (yyyy-mm-dd) to datetime.date object. If no input is provided, current date is returned. From 9a32513b472e214aa7c7d2c62f19d5075b8dd3cb Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 30 Aug 2021 02:12:19 +0530 Subject: [PATCH 18/32] chore: Update frappe.utils.data.cast docstring --- frappe/utils/data.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 5cd41226da..2e70658dbd 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -564,8 +564,10 @@ def cast_fieldtype(fieldtype, value, show_warning=True): def cast(fieldtype, value=None): """Cast the value to the Python native object of the Frappe fieldtype provided. If value is None, the first/lowest value of the `fieldtype` will be returned. + If value can't be cast as fieldtype due to an invalid input, None will be returned. Mapping of Python types => Frappe types: + * str => ("Data", "Text", "Small Text", "Long Text", "Text Editor", "Select", "Link", "Dynamic Link") * float => ("Currency", "Float", "Percent") * int => ("Int", "Check") * datetime.datetime => ("Datetime",) From 6be64eb4f9d573b9463972d4e37d28f023beed20 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 31 Aug 2021 17:22:48 +0530 Subject: [PATCH 19/32] fix: Make min width of page action buttons to 35px --- frappe/public/scss/desk/page.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/scss/desk/page.scss b/frappe/public/scss/desk/page.scss index 65d535facc..f9d93fcaef 100644 --- a/frappe/public/scss/desk/page.scss +++ b/frappe/public/scss/desk/page.scss @@ -66,7 +66,7 @@ padding: 4px 10px; } .btn-primary, .btn-secondary { - min-width: 70px; + min-width: 35px; } .custom-btn-group { display: inline-flex; From 8394bbeb4c7578084a21c5cdf841a9b1119340d2 Mon Sep 17 00:00:00 2001 From: gavin Date: Tue, 31 Aug 2021 21:50:07 +0530 Subject: [PATCH 20/32] chore(utils): Add type hints for get_timedelta, getdate Co-authored-by: Ankush Menat --- frappe/utils/data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/utils/data.py b/frappe/utils/data.py index 2e70658dbd..5a7328b07e 100644 --- a/frappe/utils/data.py +++ b/frappe/utils/data.py @@ -21,7 +21,7 @@ def is_invalid_date_string(date_string): return not isinstance(date_string, str) or ((not date_string) or (date_string or "").startswith(("0001-01-01", "0000-00-00"))) # datetime functions -def getdate(string_date: str = None): +def getdate(string_date: Optional[str] = None): """ Converts string date (yyyy-mm-dd) to datetime.date object. If no input is provided, current date is returned. @@ -69,7 +69,7 @@ def get_datetime(datetime_str=None): except ValueError: return parser.parse(datetime_str) -def get_timedelta(time: str = None) -> Optional[datetime.timedelta]: +def get_timedelta(time: Optional[str] = None) -> Optional[datetime.timedelta]: """Return `datetime.timedelta` object from string value of a valid time format. Returns None if `time` is not a valid format From 5c0c9d5417ba482eee32d311fdc87c6ada2e5f85 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 1 Sep 2021 12:06:46 +0530 Subject: [PATCH 21/32] ci: Update mergify.yml --- .mergify.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index 8c7a7dc95d..cd221b134c 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -2,7 +2,9 @@ pull_request_rules: - name: Auto-close PRs on stable branch conditions: - and: - - author!=surajshetty3416 + - or: + - author!=surajshetty3416 + - author!=gavindsouza - or: - base=version-13 - base=version-12 From 3d5e7b64ea6e1b3c71d80526378e7b3362fc8898 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 1 Sep 2021 12:13:11 +0530 Subject: [PATCH 22/32] ci: Update auto-close condition --- .mergify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index cd221b134c..0bd9641d5b 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -2,7 +2,7 @@ pull_request_rules: - name: Auto-close PRs on stable branch conditions: - and: - - or: + - and: - author!=surajshetty3416 - author!=gavindsouza - or: From 95ba4a53c1103a76d0bf523c2d315f57e68e44cc Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Wed, 1 Sep 2021 12:41:01 +0530 Subject: [PATCH 23/32] fix: error while deleting links in cards --- frappe/desk/doctype/workspace/workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/desk/doctype/workspace/workspace.py b/frappe/desk/doctype/workspace/workspace.py index 31bb551330..33c6adbd2f 100644 --- a/frappe/desk/doctype/workspace/workspace.py +++ b/frappe/desk/doctype/workspace/workspace.py @@ -62,7 +62,7 @@ class Workspace(Document): for link in self.links: link = link.as_dict() if link.type == "Card Break": - if card_links and (not current_card['only_for'] or current_card['only_for'] == frappe.get_system_settings('country')): + if card_links and (not current_card.get('only_for') or current_card.get('only_for') == frappe.get_system_settings('country')): current_card['links'] = card_links cards.append(current_card) From f23802d25bfb3591463a7295d1fd0a5fbd4c9789 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 1 Sep 2021 16:30:15 +0530 Subject: [PATCH 24/32] fix: Enable Server Scripts by default --- frappe/utils/safe_exec.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 4de685e53e..7ccd80e346 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -30,8 +30,14 @@ class NamespaceDict(frappe._dict): def safe_exec(script, _globals=None, _locals=None): - # script reports must be enabled via site_config.json - if not frappe.conf.server_script_enabled: + # server scripts can be disabled via site_config.json + # they are enabled by default + if 'server_script_enabled' in frappe.conf: + enabled = frappe.conf.server_script_enabled + else: + enabled = True + + if not enabled: frappe.throw(_('Please Enable Server Scripts'), ServerScriptNotEnabled) # build globals From cf028964f3714a51ad0c758d29f8ba8e47335f74 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 2 Sep 2021 09:36:25 +0530 Subject: [PATCH 25/32] CI: Use codecov coverage reporting Why? - Coveralls had login issues and we were not able to view files to find the actual uncovered lines - Codecov has better reporting (status checks & commit diff coverage, ), UX (better folder/file navigation, faster page loads), and has more features. - Codecov will not require additional step of coverage wrap-up for parallel builds (it manages it internally somehow). --- .github/workflows/server-mariadb-tests.yml | 50 +++------------------- frappe/coverage.py | 3 +- 2 files changed, 7 insertions(+), 46 deletions(-) diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml index fb6e56037c..8fcd9017d0 100644 --- a/.github/workflows/server-mariadb-tests.yml +++ b/.github/workflows/server-mariadb-tests.yml @@ -120,49 +120,9 @@ jobs: CI_BUILD_ID: ${{ github.run_id }} ORCHESTRATOR_URL: http://test-orchestrator.frappe.io - - name: Upload Coverage Data - if: ${{ steps.check-build.outputs.build == 'strawberry' }} - id: upload-coverage-data - run: | - cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE} - cd ${GITHUB_WORKSPACE} - pip3 install coverage==5.5 - pip3 install coveralls==3.0.1 - coveralls - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - COVERALLS_FLAG_NAME: run-${{ matrix.container }} - COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }} - COVERALLS_PARALLEL: true - - - run: echo ${{ steps.check-build.outputs.build }} > guess-the-fruit.txt - - uses: actions/upload-artifact@v1 - with: - name: fruit - path: guess-the-fruit.txt - - coveralls: - name: Coverage Wrap Up - needs: test - container: python:3-slim - runs-on: ubuntu-18.04 - steps: - - uses: actions/download-artifact@v1 + - uses: codecov/codecov-action@v2 with: - name: fruit - - run: echo "WILDCARD=$(cat fruit/guess-the-fruit.txt)" >> $GITHUB_ENV - - - name: Clone - if: ${{ env.WILDCARD == 'strawberry' }} - uses: actions/checkout@v2 - - - name: Coveralls Finished - if: ${{ env.WILDCARD == 'strawberry' }} - run: | - cd ${GITHUB_WORKSPACE} - pip3 install coverage==5.5 - pip3 install coveralls==3.0.1 - coveralls --finish - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + name: MariaDB + fail_ci_if_error: true + files: /home/runner/frappe-bench/sites/coverage.xml + verbose: true diff --git a/frappe/coverage.py b/frappe/coverage.py index 33f945be40..1969cae141 100644 --- a/frappe/coverage.py +++ b/frappe/coverage.py @@ -58,4 +58,5 @@ class CodeCoverage(): def __exit__(self, exc_type, exc_value, traceback): if self.with_coverage: self.coverage.stop() - self.coverage.save() \ No newline at end of file + self.coverage.save() + self.coverage.xml_report() \ No newline at end of file From ba73d99377473dc83391b741c0666eccc02a3179 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 2 Sep 2021 09:36:57 +0530 Subject: [PATCH 26/32] ci: Enable code coverage for postgres build as well --- .github/workflows/server-postgres-tests.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/server-postgres-tests.yml b/.github/workflows/server-postgres-tests.yml index 1539e8c2d5..aef0e50c5a 100644 --- a/.github/workflows/server-postgres-tests.yml +++ b/.github/workflows/server-postgres-tests.yml @@ -116,7 +116,14 @@ jobs: - name: Run Tests if: ${{ steps.check-build.outputs.build == 'strawberry' }} - run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator + run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --use-orchestrator --with-coverage env: CI_BUILD_ID: ${{ github.run_id }} ORCHESTRATOR_URL: http://test-orchestrator.frappe.io + + - uses: codecov/codecov-action@v2 + with: + name: Postgres + fail_ci_if_error: true + files: /home/runner/frappe-bench/sites/coverage.xml + verbose: true From cbd94bd33dbcf72f5fcd525b6bb6edacca133248 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 2 Sep 2021 10:53:53 +0530 Subject: [PATCH 27/32] ci: Set name for coverage data upload --- .github/workflows/server-mariadb-tests.yml | 3 ++- .github/workflows/server-postgres-tests.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml index 8fcd9017d0..57a7fa304d 100644 --- a/.github/workflows/server-mariadb-tests.yml +++ b/.github/workflows/server-mariadb-tests.yml @@ -120,7 +120,8 @@ jobs: CI_BUILD_ID: ${{ github.run_id }} ORCHESTRATOR_URL: http://test-orchestrator.frappe.io - - uses: codecov/codecov-action@v2 + - name: Upload coverage data + uses: codecov/codecov-action@v2 with: name: MariaDB fail_ci_if_error: true diff --git a/.github/workflows/server-postgres-tests.yml b/.github/workflows/server-postgres-tests.yml index aef0e50c5a..fe33a9c917 100644 --- a/.github/workflows/server-postgres-tests.yml +++ b/.github/workflows/server-postgres-tests.yml @@ -121,7 +121,8 @@ jobs: CI_BUILD_ID: ${{ github.run_id }} ORCHESTRATOR_URL: http://test-orchestrator.frappe.io - - uses: codecov/codecov-action@v2 + - name: Upload coverage data + uses: codecov/codecov-action@v2 with: name: Postgres fail_ci_if_error: true From 329919d43d467ed426494d477da07594c100a943 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 2 Sep 2021 11:37:40 +0530 Subject: [PATCH 28/32] chore: Add codecov config file --- codecov.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000..00e85225f4 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,5 @@ +codecov: + require_ci_to_pass: yes +comment: + layout: "diff, flags, files" + require_changes: true \ No newline at end of file From f4e477ef42140a4bde819e7afdb6ad0dc4189aff Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 2 Sep 2021 13:06:48 +0530 Subject: [PATCH 29/32] ci: Run postgres builds as well after a push on develop --- .github/workflows/server-postgres-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/server-postgres-tests.yml b/.github/workflows/server-postgres-tests.yml index fe33a9c917..57ac9c6c60 100644 --- a/.github/workflows/server-postgres-tests.yml +++ b/.github/workflows/server-postgres-tests.yml @@ -3,6 +3,8 @@ name: Server on: pull_request: workflow_dispatch: + push: + branches: [ develop ] concurrency: group: server-postgres-develop-${{ github.event.number }} From a3a2d965790566816dd5f8ad31f8e1782918b449 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 2 Sep 2021 13:28:35 +0530 Subject: [PATCH 30/32] chore: Update code coverage badge coveralls badge -> codecov badge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 11343a632a..6c2804d843 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ - - + + From 6f114b1c198a1553b3f666358188e757bdb5933e Mon Sep 17 00:00:00 2001 From: Lev Date: Thu, 2 Sep 2021 12:21:34 +0300 Subject: [PATCH 31/32] chore: Query Builder autocomplete hack --- frappe/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/__init__.py b/frappe/__init__.py index 6d79cbd760..ea983e7f02 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -140,7 +140,11 @@ lang = local("lang") if typing.TYPE_CHECKING: from frappe.database.mariadb.database import MariaDBDatabase from frappe.database.postgres.database import PostgresDatabase + from pypika import Query + db: typing.Union[MariaDBDatabase, PostgresDatabase] + qb: Query + # end: static analysis hack def init(site, sites_path=None, new_site=False): From 3f69350f099a7df3ee807bd226eec222d7649801 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 2 Sep 2021 18:49:04 +0530 Subject: [PATCH 32/32] ci: Add 0.5% as threshold for coverage failure --- codecov.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index 00e85225f4..eb81252b61 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,5 +1,9 @@ codecov: require_ci_to_pass: yes + status: + project: + default: + threshold: 0.5% comment: layout: "diff, flags, files" - require_changes: true \ No newline at end of file + require_changes: true