From 3ec47b09756d304792f87b6b148714ee93552e66 Mon Sep 17 00:00:00 2001 From: Corentin Flr <10946971+cogk@users.noreply.github.com> Date: Fri, 31 Mar 2023 16:39:38 +0200 Subject: [PATCH 01/14] feat(workspace): Allow user to choose a default workspace --- frappe/core/doctype/user/user.js | 10 ++++++++++ frappe/core/doctype/user/user.json | 16 +++++++++++++++- .../js/frappe/views/workspace/workspace.js | 7 ++++++- frappe/utils/user.py | 5 +++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index dc40fb0336..df79223875 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -1,4 +1,14 @@ frappe.ui.form.on("User", { + setup: function (frm) { + frm.set_query("default_workspace", () => { + return { + filters: { + parent_page: ["is", "not set"], + for_user: ["in", [null, frappe.session.user]], + }, + }; + }); + }, before_load: function (frm) { let update_tz_options = function () { frm.fields_dict.time_zone.set_data(frappe.all_timezones); diff --git a/frappe/core/doctype/user/user.json b/frappe/core/doctype/user/user.json index 894216a3e0..0f93b340a0 100644 --- a/frappe/core/doctype/user/user.json +++ b/frappe/core/doctype/user/user.json @@ -23,6 +23,8 @@ "send_welcome_email", "unsubscribed", "user_image", + "workspaces_section", + "default_workspace", "roles_permissions_tab", "sb1", "role_profile_name", @@ -711,6 +713,18 @@ "label": "Role Profiles", "options": "User Role Profile", "permlevel": 1 + }, + { + "fieldname": "workspaces_section", + "fieldtype": "Section Break", + "label": "Workspaces" + }, + { + "description": "If left empty, the default workspace will be the last visited workspace", + "fieldname": "default_workspace", + "fieldtype": "Link", + "label": "Default Workspace", + "options": "Workspace" } ], "icon": "fa fa-user", @@ -773,7 +787,7 @@ "link_fieldname": "user" } ], - "modified": "2024-02-11 13:16:29.574666", + "modified": "2024-03-06 11:14:38.424148", "modified_by": "Administrator", "module": "Core", "name": "User", diff --git a/frappe/public/js/frappe/views/workspace/workspace.js b/frappe/public/js/frappe/views/workspace/workspace.js index 650ccb01ec..735f8097b5 100644 --- a/frappe/public/js/frappe/views/workspace/workspace.js +++ b/frappe/public/js/frappe/views/workspace/workspace.js @@ -351,7 +351,12 @@ frappe.views.Workspace = class Workspace { get_page_to_show() { let default_page; - if ( + if (frappe.boot.user.default_workspace) { + default_page = { + name: frappe.boot.user.default_workspace.name, + public: frappe.boot.user.default_workspace.public, + }; + } else if ( localStorage.current_page && this.all_pages.filter((page) => page.title == localStorage.current_page).length != 0 ) { diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 027c5dbaf5..8069f22497 100644 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -226,6 +226,7 @@ class UserPermissions: "send_me_a_copy", "user_type", "onboarding_status", + "default_workspace", ], as_dict=True, ) @@ -233,6 +234,10 @@ class UserPermissions: if not self.can_read: self.build_permissions() + if d.get("default_workspace"): + public = frappe.get_cached_value("Workspace", d.default_workspace, "public") + d.default_workspace = {"name": d.default_workspace, "public": public} + d.name = self.name d.onboarding_status = frappe.parse_json(d.onboarding_status) d.roles = self.get_roles() From e3d00e9117a4e39b9e4f170fd5d64010df3cbe9f Mon Sep 17 00:00:00 2001 From: Corentin Flr <10946971+cogk@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:24:13 +0100 Subject: [PATCH 02/14] fix(workspace): Don't allow Welcome as default workspace Co-authored-by: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> --- frappe/core/doctype/user/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/core/doctype/user/user.js b/frappe/core/doctype/user/user.js index df79223875..b37ebda9ee 100644 --- a/frappe/core/doctype/user/user.js +++ b/frappe/core/doctype/user/user.js @@ -3,8 +3,8 @@ frappe.ui.form.on("User", { frm.set_query("default_workspace", () => { return { filters: { - parent_page: ["is", "not set"], for_user: ["in", [null, frappe.session.user]], + title: ["!=", "Welcome Workspace"], }, }; }); From ea193ecd48e8578f9a07825452b16127b70d5b0e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 17 Jan 2024 12:45:24 +0530 Subject: [PATCH 03/14] Revert "Revert "fix: search_link fails when txt contains parentheses (#22892)"" This reverts commit c0cf13b8e863c0538d7dee2a782d254d70e624e4. --- frappe/desk/reportview.py | 40 ++++++++++----------- frappe/model/db_query.py | 45 +++++++++--------------- frappe/tests/test_reportview.py | 62 ++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 4 files changed, 97 insertions(+), 51 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 499f106b5a..a3bf356dd6 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -5,6 +5,8 @@ import json +from sql_metadata import Parser + import frappe import frappe.permissions from frappe import _ @@ -102,7 +104,10 @@ def validate_fields(data): wildcard = update_wildcard_field_param(data) for field in list(data.fields or []): - fieldname = extract_fieldname(field) + fieldname = extract_fieldnames(field)[0] + if not fieldname: + raise_invalid_field(fieldname) + if is_standard(fieldname): continue @@ -182,23 +187,16 @@ def is_standard(fieldname): return fieldname in default_fields or fieldname in optional_fields or fieldname in child_table_fields -def extract_fieldname(field): - for text in (",", "/*", "#"): - if text in field: - raise_invalid_field(field) +def extract_fieldnames(field): + parser = Parser(f"select {field}, _frappe_dummy from _dummy") + columns = [col for col in parser.columns if col != "_frappe_dummy"] - fieldname = field - for sep in (" as ", " AS "): - if sep in fieldname: - fieldname = fieldname.split(sep, 1)[0] + if not columns: + f = field.lower() + if "count(" in f or "sum(" in f or "avg(" in f: + return ["*"] - # certain functions allowed, extract the fieldname from the function - if fieldname.startswith("count(") or fieldname.startswith("sum(") or fieldname.startswith("avg("): - if not fieldname.strip().endswith(")"): - raise_invalid_field(field) - fieldname = fieldname.split("(", 1)[1][:-1] - - return fieldname + return columns def get_meta_and_docfield(fieldname, data): @@ -249,13 +247,13 @@ def get_parenttype_and_fieldname(field, data): parts = field.split(".") parenttype = parts[0] fieldname = parts[1] - if parenttype.startswith("`tab"): - # `tabChild DocType`.`fieldname` - parenttype = parenttype[4:-1] - fieldname = fieldname.strip("`") + df = frappe.get_meta(data.doctype).get_field(parenttype) + if not df: + # tabChild DocType.fieldname + parenttype = parenttype[3:] else: # tablefield.fieldname - parenttype = frappe.get_meta(data.doctype).get_field(parenttype).options + parenttype = df.options else: parenttype = data.doctype fieldname = field.strip("`") diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 18c3d2782c..6888fc9759 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -44,7 +44,6 @@ FIELD_COMMA_PATTERN = re.compile(r"[0-9a-zA-Z]+\s*,") STRICT_FIELD_PATTERN = re.compile(r".*/\*.*") STRICT_UNION_PATTERN = re.compile(r".*\s(union).*\s") ORDER_GROUP_PATTERN = re.compile(r".*[^a-z0-9-_ ,`'\"\.\(\)].*") -FN_PARAMS_PATTERN = re.compile(r".*?\((.*)\).*") SPECIAL_FIELD_CHARS = frozenset(("(", "`", ".", "'", '"', "*")) @@ -619,6 +618,8 @@ class DatabaseQuery: - Query: fields=["*"] - Result: fields=["title", ...] // will also include Frappe's meta field like `name`, `owner`, etc. """ + from frappe.desk.reportview import extract_fieldnames + if self.flags.ignore_permissions: return @@ -631,23 +632,18 @@ class DatabaseQuery: ) for i, field in enumerate(self.fields): - if "distinct" in field.lower(): - # field: 'count(distinct `tabPhoto`.name) as total_count' - # column: 'tabPhoto.name' - if _fn := FN_PARAMS_PATTERN.findall(field): - column = _fn[0].replace("distinct ", "").replace("DISTINCT ", "").replace("`", "") - # field: 'distinct name' - # column: 'name' - else: - column = field.split(" ", 2)[1].replace("`", "") - else: - # field: 'count(`tabPhoto`.name) as total_count' - # column: 'tabPhoto.name' - column = field.split("(")[-1].split(")", 1)[0] - column = strip_alias(column).replace("`", "") + # field: 'count(distinct `tabPhoto`.name) as total_count' + # column: 'tabPhoto.name' + # field: 'count(`tabPhoto`.name) as total_count' + # column: 'tabPhoto.name' + columns = extract_fieldnames(field) + if not columns: + continue - if column == "*" and not in_function("*", field): - asterisk_fields.append(i) + column = columns[0] + if column == "*": + if "*" in field and not in_function("*", field): + asterisk_fields.append(i) continue # handle pseudo columns @@ -686,21 +682,12 @@ class DatabaseQuery: elif "(" in field: if "*" in field: continue - elif _params := FN_PARAMS_PATTERN.findall(field): - params = (x.strip() for x in _params[0].split(",")) - for param in params: - if not ( - not param - or param in permitted_fields - or param.isnumeric() - or "'" in param - or '"' in param - ): + else: + for column in columns: + if not column in permitted_fields: self.remove_field(i) break continue - self.remove_field(i) - # remove if access not allowed else: self.remove_field(i) diff --git a/frappe/tests/test_reportview.py b/frappe/tests/test_reportview.py index 4a24514acf..7581dc42fb 100644 --- a/frappe/tests/test_reportview.py +++ b/frappe/tests/test_reportview.py @@ -2,7 +2,7 @@ # License: MIT. See LICENSE import frappe -from frappe.desk.reportview import export_query +from frappe.desk.reportview import export_query, extract_fieldnames from frappe.tests.utils import FrappeTestCase @@ -32,3 +32,63 @@ class TestReportview(FrappeTestCase): for row in reader: self.assertEqual(int(row["Is Single"]), 1) self.assertEqual(row["Module"], "Core") + + def test_extract_fieldname(self): + self.assertEqual( + extract_fieldnames("count(distinct `tabPhoto`.name) as total_count")[0], "tabPhoto.name" + ) + + self.assertEqual(extract_fieldnames("owner")[0], "owner") + + self.assertEqual(extract_fieldnames("module")[0], "module") + + self.assertEqual(extract_fieldnames("count(`tabPhoto`.name) as total_count")[0], "tabPhoto.name") + + self.assertEqual(extract_fieldnames("count(distinct `tabPhoto`.name)")[0], "tabPhoto.name") + + self.assertEqual(extract_fieldnames("count(`tabPhoto`.name)")[0], "tabPhoto.name") + + self.assertEqual( + extract_fieldnames("count(distinct `tabJob Applicant`.name) as total_count")[0], + "tabJob Applicant.name", + ) + + self.assertEqual( + extract_fieldnames("(1 / nullif(locate('a', `tabAddress`.`name`), 0)) as `_relevance`")[0], + "tabAddress.name", + ) + + self.assertEqual( + extract_fieldnames("(1 / nullif(locate('(a)', `tabAddress`.`name`), 0)) as `_relevance`")[0], + "tabAddress.name", + ) + + self.assertEqual( + extract_fieldnames("EXTRACT(MONTH FROM date_column) AS month")[0], "date_column" + ) + + self.assertEqual(extract_fieldnames("COUNT(*) AS count")[0], "*") + + self.assertEqual(extract_fieldnames("COUNT(1) AS count")[0], "*") + + self.assertEqual(extract_fieldnames("COUNT(1) AS count, SUM(1) AS sum")[0], "*") + + self.assertEqual( + extract_fieldnames("first_name + ' ' + last_name AS full_name"), ["first_name", "last_name"] + ) + + self.assertEqual( + extract_fieldnames("CONCAT(first_name, ' ', last_name) AS full_name"), + ["first_name", "last_name"], + ) + + self.assertEqual( + extract_fieldnames("CONCAT(id, '/', name, '/', age, '/', marks) AS student"), + ["id", "name", "age", "marks"], + ) + + self.assertEqual(extract_fieldnames("tablefield.fiedname")[0], "tablefield.fiedname") + + self.assertEqual( + extract_fieldnames("`tabChild DocType`.`fiedname`")[0], "tabChild DocType.fiedname" + ) diff --git a/pyproject.toml b/pyproject.toml index 180012a687..2e2a7948e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,7 @@ dependencies = [ "semantic-version~=2.10.0", "sentry-sdk~=1.37.1", "sqlparse~=0.4.4", + "sql_metadata~=2.9.0", "tenacity~=8.2.2", "terminaltables~=3.1.10", "traceback-with-variables~=2.0.4", From 414b1beffdd6b45e0d03d20e229ccc386611199c Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 17 Jan 2024 12:45:33 +0530 Subject: [PATCH 04/14] Revert "Revert "perf: cache fieldname extraction"" This reverts commit ffe84c9f9d2b160aa75552badb98caf01104f932. --- frappe/desk/reportview.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index a3bf356dd6..dff741ac4e 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -4,6 +4,7 @@ """build query for doclistview and return results""" import json +from functools import lru_cache from sql_metadata import Parser @@ -187,6 +188,7 @@ def is_standard(fieldname): return fieldname in default_fields or fieldname in optional_fields or fieldname in child_table_fields +@lru_cache def extract_fieldnames(field): parser = Parser(f"select {field}, _frappe_dummy from _dummy") columns = [col for col in parser.columns if col != "_frappe_dummy"] From 3facad0a25811484f109e2df18541c488e0f09bb Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 17 Jan 2024 12:47:06 +0530 Subject: [PATCH 05/14] fix: avoid parsing reserved keywords and simple strings --- frappe/desk/reportview.py | 5 +++++ frappe/tests/test_reportview.py | 9 +++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index dff741ac4e..6a0aa1167d 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -190,6 +190,11 @@ def is_standard(fieldname): @lru_cache def extract_fieldnames(field): + from frappe.database.schema import SPECIAL_CHAR_PATTERN + + if not SPECIAL_CHAR_PATTERN.findall(field): + return [field] + parser = Parser(f"select {field}, _frappe_dummy from _dummy") columns = [col for col in parser.columns if col != "_frappe_dummy"] diff --git a/frappe/tests/test_reportview.py b/frappe/tests/test_reportview.py index 7581dc42fb..98dc3a84e5 100644 --- a/frappe/tests/test_reportview.py +++ b/frappe/tests/test_reportview.py @@ -39,6 +39,7 @@ class TestReportview(FrappeTestCase): ) self.assertEqual(extract_fieldnames("owner")[0], "owner") + self.assertEqual(extract_fieldnames("from")[0], "from") self.assertEqual(extract_fieldnames("module")[0], "module") @@ -63,9 +64,7 @@ class TestReportview(FrappeTestCase): "tabAddress.name", ) - self.assertEqual( - extract_fieldnames("EXTRACT(MONTH FROM date_column) AS month")[0], "date_column" - ) + self.assertEqual(extract_fieldnames("EXTRACT(MONTH FROM date_column) AS month")[0], "date_column") self.assertEqual(extract_fieldnames("COUNT(*) AS count")[0], "*") @@ -89,6 +88,4 @@ class TestReportview(FrappeTestCase): self.assertEqual(extract_fieldnames("tablefield.fiedname")[0], "tablefield.fiedname") - self.assertEqual( - extract_fieldnames("`tabChild DocType`.`fiedname`")[0], "tabChild DocType.fiedname" - ) + self.assertEqual(extract_fieldnames("`tabChild DocType`.`fiedname`")[0], "tabChild DocType.fiedname") From cc4e628edf304d35f8198019d0d206dd2fdadb1e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 22 Feb 2024 17:35:52 +0530 Subject: [PATCH 06/14] fix: remove unnecessary dummy column --- frappe/desk/reportview.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 6a0aa1167d..abc8222114 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -195,8 +195,7 @@ def extract_fieldnames(field): if not SPECIAL_CHAR_PATTERN.findall(field): return [field] - parser = Parser(f"select {field}, _frappe_dummy from _dummy") - columns = [col for col in parser.columns if col != "_frappe_dummy"] + columns = Parser(f"select {field} from _dummy").columns if not columns: f = field.lower() From 5eca52b2c7e3cae3704c7cc9a2315d7c58ff05cb Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 22 Feb 2024 17:40:00 +0530 Subject: [PATCH 07/14] fix: only trim tab if it starts with tab --- frappe/desk/reportview.py | 2 +- frappe/model/db_query.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index abc8222114..a4a86d517c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -254,7 +254,7 @@ def get_parenttype_and_fieldname(field, data): parenttype = parts[0] fieldname = parts[1] df = frappe.get_meta(data.doctype).get_field(parenttype) - if not df: + if not df and parenttype.startswith("tab"): # tabChild DocType.fieldname parenttype = parenttype[3:] else: diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 6888fc9759..4148ff0d8e 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -684,7 +684,7 @@ class DatabaseQuery: continue else: for column in columns: - if not column in permitted_fields: + if column not in permitted_fields: self.remove_field(i) break continue From 065674236cc32fe1bfc889efd373b11cba67c721 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 5 Mar 2024 12:59:47 +0530 Subject: [PATCH 08/14] refactor: duplicate check for * --- frappe/model/db_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 4148ff0d8e..98b697b51f 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -642,7 +642,7 @@ class DatabaseQuery: column = columns[0] if column == "*": - if "*" in field and not in_function("*", field): + if not in_function("*", field): asterisk_fields.append(i) continue From fbaa2549f2f6bf52d58134444e7c1b6b7cdbe420 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 20 Mar 2024 12:30:11 +0530 Subject: [PATCH 09/14] test: add test for searching with paren --- frappe/tests/test_search.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index 7f54d1f9c0..eefe59f9bc 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -176,6 +176,11 @@ class TestSearch(FrappeTestCase): frappe.db.set_value("Language", "es", "idx", 10) self.assertEqual("es", search(txt="es")[0]["value"]) + def test_search_with_paren(self): + search = partial(search_link, doctype="Language", filters=None, page_length=10) + result = search(txt="(txt)") + self.assertEqual(result, []) + @frappe.validate_and_sanitize_search_inputs def get_data(doctype, txt, searchfield, start, page_len, filters): From 33547a635442ae3f4c638aab20eb2f86faf07449 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 20 Mar 2024 13:52:46 +0530 Subject: [PATCH 10/14] ci: group output and limit diff Verbose outut is MASSIVE and unreadable. --- .github/helper/install.sh | 19 +++++++++++-------- .github/helper/install_dependencies.sh | 2 ++ frappe/tests/utils.py | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 3bba9a2600..a69e01616d 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -2,9 +2,11 @@ set -e cd ~ || exit -echo "Setting Up Bench..." - +echo "::group::Install Bench" pip install frappe-bench +echo "::endgroup::" + +echo "::group::Init Bench" bench -v init frappe-bench --skip-assets --python "$(which python)" --frappe-path "${GITHUB_WORKSPACE}" cd ./frappe-bench || exit @@ -13,9 +15,9 @@ if [ "$TYPE" == "ui" ] then bench -v setup requirements --node; fi +echo "::endgroup::" -echo "Setting Up Sites & Database..." - +echo "::group::Create Test Site" mkdir ~/frappe-bench/sites/test_site cp "${GITHUB_WORKSPACE}/.github/helper/db/$DB.json" ~/frappe-bench/sites/test_site/site_config.json @@ -35,9 +37,9 @@ then echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE DATABASE test_frappe" -U postgres; echo "travis" | psql -h 127.0.0.1 -p 5432 -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe'" -U postgres; fi +echo "::endgroup::" -echo "Setting Up Procfile..." - +echo "::group::Modify processes" sed -i 's/^watch:/# watch:/g' Procfile sed -i 's/^schedule:/# schedule:/g' Procfile @@ -51,11 +53,11 @@ if [ "$TYPE" == "ui" ] then sed -i 's/^web: bench serve/web: bench serve --with-coverage/g' Procfile fi - -echo "Starting Bench..." +echo "::endgroup::" bench start &> ~/frappe-bench/bench_start.log & +echo "::group::Install site" if [ "$TYPE" == "server" ] then CI=Yes bench build --app frappe & @@ -69,3 +71,4 @@ then # wait till assets are built successfully wait $build_pid fi +echo "::endgroup::" diff --git a/.github/helper/install_dependencies.sh b/.github/helper/install_dependencies.sh index 574144b823..f382b61dda 100644 --- a/.github/helper/install_dependencies.sh +++ b/.github/helper/install_dependencies.sh @@ -3,6 +3,7 @@ set -e echo "Setting Up System Dependencies..." +echo "::group::apt packages" sudo apt update sudo apt remove mysql-server mysql-client sudo apt install libcups2-dev redis-server mariadb-client-10.6 @@ -12,3 +13,4 @@ install_wkhtmltopdf() { sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb } install_wkhtmltopdf & +echo "::endgroup::" diff --git a/frappe/tests/utils.py b/frappe/tests/utils.py index cf49ec7271..f9d8ee54b5 100644 --- a/frappe/tests/utils.py +++ b/frappe/tests/utils.py @@ -28,7 +28,7 @@ class FrappeTestCase(unittest.TestCase): TEST_SITE = "test_site" SHOW_TRANSACTION_COMMIT_WARNINGS = False - maxDiff = None # prints long diffs but useful in CI + maxDiff = 10_000 # prints long diffs but useful in CI @classmethod def setUpClass(cls) -> None: From f642b1881f33c2b8a82e2c6466681dce4c923eb3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 20 Mar 2024 14:19:29 +0530 Subject: [PATCH 11/14] fix: invalid select star expansions --- frappe/desk/reportview.py | 2 +- frappe/model/db_query.py | 2 +- frappe/tests/test_db_query.py | 5 +++++ frappe/tests/test_reportview.py | 6 ++---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index a4a86d517c..86dfccd47c 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -199,7 +199,7 @@ def extract_fieldnames(field): if not columns: f = field.lower() - if "count(" in f or "sum(" in f or "avg(" in f: + if ("count(" in f or "sum(" in f or "avg(" in f) and "*" in f: return ["*"] return columns diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 98b697b51f..f1a36d79b2 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -641,7 +641,7 @@ class DatabaseQuery: continue column = columns[0] - if column == "*": + if column == "*" and "*" in field: if not in_function("*", field): asterisk_fields.append(i) continue diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 8cd233431a..51df9d8983 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -1173,6 +1173,11 @@ class TestDBQuery(FrappeTestCase): data = get() self.assertEqual(len(data["values"]), 1) + def test_select_star_expansion(self): + count = frappe.get_list("Language", ["SUM(1)", "COUNT(*)"], as_list=1, order_by=None)[0] + self.assertEqual(count[0], frappe.db.count("Language")) + self.assertEqual(count[1], frappe.db.count("Language")) + class TestReportView(FrappeTestCase): @run_only_if(db_type_is.MARIADB) # TODO: postgres name casting is messed up diff --git a/frappe/tests/test_reportview.py b/frappe/tests/test_reportview.py index 98dc3a84e5..5224ca9414 100644 --- a/frappe/tests/test_reportview.py +++ b/frappe/tests/test_reportview.py @@ -68,10 +68,6 @@ class TestReportview(FrappeTestCase): self.assertEqual(extract_fieldnames("COUNT(*) AS count")[0], "*") - self.assertEqual(extract_fieldnames("COUNT(1) AS count")[0], "*") - - self.assertEqual(extract_fieldnames("COUNT(1) AS count, SUM(1) AS sum")[0], "*") - self.assertEqual( extract_fieldnames("first_name + ' ' + last_name AS full_name"), ["first_name", "last_name"] ) @@ -89,3 +85,5 @@ class TestReportview(FrappeTestCase): self.assertEqual(extract_fieldnames("tablefield.fiedname")[0], "tablefield.fiedname") self.assertEqual(extract_fieldnames("`tabChild DocType`.`fiedname`")[0], "tabChild DocType.fiedname") + + self.assertEqual(extract_fieldnames("sum(1)"), []) From 571ca34eca9145b018f7a475a98dc301c4b5b377 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 20 Mar 2024 17:02:24 +0530 Subject: [PATCH 12/14] fix: redirect after login, todo filters (#25521) * Revert "fix: default filter setup on todo list (#25455)" This reverts commit 396bc4102ed159c775a4cde978974d510ceff42f. * Revert "Revert "fix: default filter setup on todo list (#25455)"" This reverts commit 5e6b6997d526446760c42e7e812ceef5fe416492. * test: wait longer for filters to apply * fix: route redirects after login * test: use clear_filters * fix: don't set default filters on ToDo This messes with previous filters logic. --- cypress/integration/awesome_bar.js | 3 +-- cypress/integration/list_paging.js | 2 +- cypress/support/commands.js | 2 +- frappe/desk/doctype/todo/todo_list.js | 6 ------ frappe/www/message.html | 2 +- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/cypress/integration/awesome_bar.js b/cypress/integration/awesome_bar.js index dff04a5693..a8f97a798c 100644 --- a/cypress/integration/awesome_bar.js +++ b/cypress/integration/awesome_bar.js @@ -36,9 +36,8 @@ context("Awesome Bar", () => { cy.get(".title-text").should("contain", "To Do"); cy.wait(200); // Wait a bit longer before checking the filter. cy.get('[data-original-title="ID"] > input').should("have.value", "%test%"); - }); - it("filter preserved, now finds something else", () => { + // filter preserved, now finds something else cy.visit("/app/todo"); cy.get(".title-text").should("contain", "To Do"); cy.wait(200); // Wait a bit longer before checking the filter. diff --git a/cypress/integration/list_paging.js b/cypress/integration/list_paging.js index 494ca6ae74..2ce347828a 100644 --- a/cypress/integration/list_paging.js +++ b/cypress/integration/list_paging.js @@ -12,7 +12,7 @@ context("List Paging", () => { it("test load more with count selection buttons", () => { cy.visit("/app/todo/view/report"); - cy.get(".filter-x-button").click(); + cy.clear_filters(); cy.get(".list-paging-area .list-count").should("contain.text", "20 of"); cy.get(".list-paging-area .btn-more").click(); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 66defa88f7..cf94335a74 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -450,7 +450,7 @@ Cypress.Commands.add("click_menu_button", (name) => { Cypress.Commands.add("clear_filters", () => { cy.get(".filter-x-button").click({ force: true }); - cy.wait(500); + cy.wait(1000); }); Cypress.Commands.add("click_modal_primary_button", (btn_name) => { diff --git a/frappe/desk/doctype/todo/todo_list.js b/frappe/desk/doctype/todo/todo_list.js index c80e3445ca..b21f412801 100644 --- a/frappe/desk/doctype/todo/todo_list.js +++ b/frappe/desk/doctype/todo/todo_list.js @@ -3,12 +3,6 @@ frappe.listview_settings["ToDo"] = { add_fields: ["reference_type", "reference_name"], onload: function (me) { - if (!Object.keys(frappe.route_options).length) { - frappe.route_options = { - allocated_to: frappe.session.user, - status: "Open", - }; - } me.page.set_title(__("To Do")); }, diff --git a/frappe/www/message.html b/frappe/www/message.html index 9039e13186..244675d9f5 100644 --- a/frappe/www/message.html +++ b/frappe/www/message.html @@ -42,7 +42,7 @@ html, body {