diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index afa6f3d0fad..22c47be6926 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -9,7 +9,7 @@ from frappe.core.doctype.data_import.importer import Importer from frappe.model.document import Document from frappe.modules.import_file import import_file_by_path -from frappe.utils.background_jobs import enqueue, is_job_queued +from frappe.utils.background_jobs import enqueue, is_job_enqueued from frappe.utils.csvutils import validate_google_sheets_url @@ -64,13 +64,15 @@ def start_import(self): if is_scheduler_inactive() and not frappe.flags.in_test: frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")) - if not is_job_queued(self.name): + job_id = f"data_import::{self.name}" + + if not is_job_enqueued(job_id): enqueue( start_import, queue="default", timeout=10000, event="data_import", - job_name=self.name, + job_id=job_id, data_import=self.name, now=frappe.conf.developer_mode or frappe.flags.in_test, ) diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index ca1e7724c15..9b2a2ccc187 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -157,10 +157,18 @@ def execute_script(self, filters): return self.get_columns(), loc["result"] def get_data( - self, filters=None, limit=None, user=None, as_dict=False, ignore_prepared_report=False + self, + filters=None, + limit=None, + user=None, + as_dict=False, + ignore_prepared_report=False, + are_default_filters=True, ): if self.report_type in ("Query Report", "Script Report", "Custom Report"): - columns, result = self.run_query_report(filters, user, ignore_prepared_report) + columns, result = self.run_query_report( + filters, user, ignore_prepared_report, are_default_filters + ) else: columns, result = self.run_standard_report(filters, limit, user) @@ -169,10 +177,16 @@ def get_data( return columns, result - def run_query_report(self, filters=None, user=None, ignore_prepared_report=False): + def run_query_report( + self, filters=None, user=None, ignore_prepared_report=False, are_default_filters=True + ): columns, result = [], [] data = frappe.desk.query_report.run( - self.name, filters=filters, user=user, ignore_prepared_report=ignore_prepared_report + self.name, + filters=filters, + user=user, + ignore_prepared_report=ignore_prepared_report, + are_default_filters=are_default_filters, ) for d in data.get("columns"): diff --git a/frappe/core/doctype/rq_job/test_rq_job.py b/frappe/core/doctype/rq_job/test_rq_job.py index 13d2fc37e7e..265583fe83b 100644 --- a/frappe/core/doctype/rq_job/test_rq_job.py +++ b/frappe/core/doctype/rq_job/test_rq_job.py @@ -11,7 +11,7 @@ from frappe.core.doctype.rq_job.rq_job import RQJob, remove_failed_jobs, stop_job from frappe.tests.utils import FrappeTestCase, timeout from frappe.utils import cstr, execute_in_shell -from frappe.utils.background_jobs import is_job_enqueued, is_job_queued +from frappe.utils.background_jobs import is_job_enqueued class TestRQJob(FrappeTestCase): @@ -87,17 +87,6 @@ def test_delete_doc(self): with self.assertRaises(rq_exc.NoSuchJobError): job.refresh() - def test_is_enqueued(self): - - dummy_job = frappe.enqueue(self.BG_JOB, sleep=10, queue="short") - job_name = "uniq_test_job" - actual_job = frappe.enqueue(self.BG_JOB, job_name=job_name, queue="short") - - self.assertTrue(is_job_queued(job_name)) - stop_job(dummy_job.id) - self.check_status(actual_job, "finished") - self.assertFalse(is_job_queued(job_name)) - @timeout(20) def test_multi_queue_burst_consumption(self): for _ in range(3): @@ -110,9 +99,10 @@ def test_multi_queue_burst_consumption(self): @timeout(20) def test_job_id_dedup(self): job_id = "test_dedup" - job = frappe.enqueue(self.BG_JOB, sleep=10, job_id=job_id) + job = frappe.enqueue(self.BG_JOB, sleep=5, job_id=job_id) self.assertTrue(is_job_enqueued(job_id)) - stop_job(job.id) + self.check_status(job, "finished") + self.assertFalse(is_job_enqueued(job_id)) def test_func(fail=False, sleep=0): diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 773ad35dd4e..1dd1f07e9f2 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -33,6 +33,11 @@ def enqueue(self, force=False) -> bool: job_id=self.rq_job_id, ) return True + else: + frappe.logger("scheduler").error( + f"Skipped queueing {self.method} because it was found in queue for {frappe.local.site}" + ) + return False def is_event_due(self, current_time=None): diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 60a69607a2e..24c69dab7cb 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -114,6 +114,7 @@ def get_report_content(self): filters=self.filters, as_dict=True, ignore_prepared_report=True, + are_default_filters=False, ) # add serial numbers diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index f01eef516bc..8186f19ae91 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -64,7 +64,7 @@ frappe.ui.form.save = function (frm, action, callback, btn) { const is_empty_row = function (cells) { for (let i = 0; i < cells.length; i++) { - if (locals[doc.doctype][doc.name][cells[i].fieldname]) { + if (locals[doc.doctype][doc.name] && locals[doc.doctype][doc.name][cells[i].fieldname]) { return false; } } diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 7e5eccf26da..57ab567c1b6 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -791,7 +791,7 @@ $.extend(frappe.model, { get_all_docs: function (doc) { var all = [doc]; for (var key in doc) { - if ($.isArray(doc[key])) { + if ($.isArray(doc[key]) && !key.startsWith("_")) { var children = doc[key]; for (var i = 0, l = children.length; i < l; i++) { all.push(children[i]); diff --git a/frappe/utils/background_jobs.py b/frappe/utils/background_jobs.py index e0fa455f603..0fbc9e15ec6 100755 --- a/frappe/utils/background_jobs.py +++ b/frappe/utils/background_jobs.py @@ -19,6 +19,7 @@ from frappe import _ from frappe.utils import cstr, get_bench_id from frappe.utils.commands import log +from frappe.utils.deprecations import deprecation_warning from frappe.utils.redis_queue import RedisQueue if TYPE_CHECKING: @@ -76,7 +77,7 @@ def enqueue( :param timeout: should be set according to the functions :param event: this is passed to enable clearing of jobs from queues :param is_async: if is_async=False, the method is executed immediately, else via a worker - :param job_name: can be used to name an enqueue call, which can be used to prevent duplicate calls + :param job_name: [DEPRECATED] can be used to name an enqueue call, which can be used to prevent duplicate calls :param now: if now=True, the method is executed via frappe.call :param kwargs: keyword arguments to be passed to the method :param job_id: Assigning unique job id, which can be checked using `is_job_enqueued` @@ -88,11 +89,12 @@ def enqueue( # namespace job ids to sites job_id = create_job_id(job_id) + if job_name: + deprecation_warning("Using enqueue with `job_name` is deprecated, use `job_id` instead.") + if not is_async and not frappe.flags.in_test: - print( - _( - "Using enqueue with is_async=False outside of tests is not recommended, use now=True instead." - ) + deprecation_warning( + "Using enqueue with is_async=False outside of tests is not recommended, use now=True instead." ) call_directly = now or (not is_async and not frappe.flags.in_test) @@ -430,21 +432,6 @@ def test_job(s): time.sleep(s) -def is_job_queued(job_name: str) -> bool: - """Check if job exists with given job_name - - DEPRECATED: Use `job_id` parameter while enqueueing job instead instead. - """ - for queue in get_queues(): - for job_id in queue.get_job_ids(): - if not job_id: - continue - job = queue.fetch_job(job_id) - if job.kwargs.get("job_name") == job_name and job.kwargs.get("site") == frappe.local.site: - return True - return False - - def create_job_id(job_id: str) -> str: """Generate unique job id for deduplication""" return f"{frappe.local.site}::{job_id}"