internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: frappe/frappe

Issue:
Enable Scheduler from desk
Feature to enable scheduler from desk.


Discussion:

Resolving patch:
diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py
--- a/frappe/utils/scheduler.py
+++ b/frappe/utils/scheduler.py
@@ -176,6 +176,11 @@ def _get_last_modified_timestamp(doctype):
 
 @frappe.whitelist()
 def activate_scheduler():
+	frappe.only_for("Administrator")
+
+	if frappe.local.conf.maintenance_mode:
+		frappe.throw(frappe._("Scheduler can not be re-enabled when maintenance mode is active."))
+
 	if is_scheduler_disabled():
 		enable_scheduler()
 	if frappe.conf.pause_scheduler:


Test patch:
Repository: frappe/frappe

Issue:
Virtual fields display broken in child tables
## Description of the issue

Virtual fields content is not displayed anymore in child grids.
The cell remains blank instead of showing the virtual field result.

## Context information (for bug reports)

**Output of `bench version`**
```
frappe 14.30.0
```
(custom app)

## Steps to reproduce the issue

1. Create a field as virtual in a child doctype
2. Implement the backend code thru a @property
3. Add a grid to your doctype to display the child doctype

### Observed result

The backend code is well executed, but the result is not displayed client-side in the corresponding column of the child grid (even though the user has full permissions to view it)

### Expected result

The result of the virtual field computation should be displayed.

## Solution

in file: 

```
/apps/frappe/frappe/model/base_document.py
```

in method: 

```
get_valid_dict()
```

this line:

```
permitted_fields = get_permitted_fields(doctype=self.doctype)
```

should be replaced by:

```
if parent_doc := getattr(self, "parent_doc", None):
    permitted_fields = get_permitted_fields(doctype=self.doctype, parenttype=parent_doc.doctype)
else:
    permitted_fields = get_permitted_fields(doctype=self.doctype)
```




Discussion:
Bug introduced with PR #20244

Resolving patch:
diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -304,7 +304,9 @@ def get_valid_dict(
 		self, sanitize=True, convert_dates_to_str=False, ignore_nulls=False, ignore_virtual=False
 	) -> dict:
 		d = _dict()
-		permitted_fields = get_permitted_fields(doctype=self.doctype)
+		permitted_fields = get_permitted_fields(
+			doctype=self.doctype, parenttype=getattr(self, "parenttype", None)
+		)
 
 		for fieldname in self.meta.get_valid_columns():
 			field_value = getattr(self, fieldname, None)


Test patch:
Repository: frappe/frappe

Issue:
Stale form-meta cache
Even after this PR, this is still happening. We are likely missing some edge case: https://github.com/frappe/frappe/pull/21216


Meta gets invalidated but `FormMeta` is not invalidated correctly after customize form. It's random, hard to reproduce rn. 


Discussion:

Resolving patch:
diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py
--- a/frappe/desk/form/load.py
+++ b/frappe/desk/form/load.py
@@ -80,7 +80,7 @@ def get_meta_bundle(doctype):
 	bundle = [frappe.desk.form.meta.get_meta(doctype)]
 	for df in bundle[0].fields:
 		if df.fieldtype in frappe.model.table_fields:
-			bundle.append(frappe.desk.form.meta.get_meta(df.options, not frappe.conf.developer_mode))
+			bundle.append(frappe.desk.form.meta.get_meta(df.options))
 	return bundle
 
 
diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py
--- a/frappe/desk/form/meta.py
+++ b/frappe/desk/form/meta.py
@@ -34,12 +34,14 @@
 )
 
 
-def get_meta(doctype, cached=True):
+def get_meta(doctype, cached=True) -> "FormMeta":
 	# don't cache for developer mode as js files, templates may be edited
-	if cached and not frappe.conf.developer_mode:
+	cached = cached and not frappe._dev_server
+	if cached:
 		meta = frappe.cache.hget("doctype_form_meta", doctype)
 		if not meta:
-			meta = FormMeta(doctype)
+			# Cache miss - explicitly get meta from DB to avoid
+			meta = FormMeta(doctype, cached=False)
 			frappe.cache.hset("doctype_form_meta", doctype, meta)
 	else:
 		meta = FormMeta(doctype)
@@ -51,8 +53,8 @@ def get_meta(doctype, cached=True):
 
 
 class FormMeta(Meta):
-	def __init__(self, doctype):
-		self.__dict__.update(frappe.get_meta(doctype).__dict__)
+	def __init__(self, doctype, *, cached=True):
+		self.__dict__.update(frappe.get_meta(doctype, cached=cached).__dict__)
 		self.load_assets()
 
 	def load_assets(self):
diff --git a/frappe/model/meta.py b/frappe/model/meta.py
--- a/frappe/model/meta.py
+++ b/frappe/model/meta.py
@@ -56,14 +56,12 @@
 
 
 def get_meta(doctype, cached=True) -> "Meta":
-	if not cached:
-		return Meta(doctype)
-
-	if meta := frappe.cache.hget("doctype_meta", doctype):
+	cached = cached and isinstance(doctype, str)
+	if cached and (meta := frappe.cache.hget("doctype_meta", doctype)):
 		return meta
 
 	meta = Meta(doctype)
-	frappe.cache.hset("doctype_meta", doctype, meta)
+	frappe.cache.hset("doctype_meta", meta.name, meta)
 	return meta
 
 


Test patch:
Repository: frappe/frappe

Issue:
Portal Sidebar not displaying in v14-beta.3
The portal sidebar does not display consistently on different kinds of routes. On some pages, the sidebar is not rendered, while on others it is. This has been tested on both existing sites and fresh installs.

/me (no sidebar)
![Screenshot 2022-05-02 211924](https://user-images.githubusercontent.com/9303694/166262480-9ba381fa-2a63-4596-b1a5-f72e1650fb0a.png)


/orders (single link on sidebar)
![Screenshot 2022-05-02 212052](https://user-images.githubusercontent.com/9303694/166262875-bbb2e7d3-95ae-4122-8ab9-bfa246384317.png)


/orders/SPECIFIC-DOC (full sidebar)
![Screenshot 2022-05-02 212148](https://user-images.githubusercontent.com/9303694/166262823-cb14af17-516b-4ddf-a54a-1c1b623aae54.png)




Discussion:
On further investigation, some factors are a bit more clear:

- No list is printing on `/me` because `context.show_sidebar` is never set to true in `me.py`. This may be intentional, but I wonder if it presents a problem.
- On this list page, `show_sidebar` is indeed set to true, but the sidebar isn't getting built properly because template_page.py sets no `basepath`. This seems to happen for any page using a "fallback" template like list.html, etc.

https://github.com/frappe/frappe/blob/e5b36b214a6fd428946945e05742e0e3d97642a9/frappe/website/page_renderers/template_page.py#L105-L107
I was able to get the performance I expected by setting the `basepath` variable in the `render()` method of `list_page.py`. I think this is preferable to doing it in `template_page.py`, as it appears intentional for there to be no sidebar there. 

The updated method looks like this:
```python
def render(self):
	frappe.local.form_dict.doctype = self.path
	#vvvvvvvvvvvvvvvvvvvvvvvvv# NEW LINE
	self.basepath="{base_path}/www".format(base_path=frappe.get_app_path("frappe"))
	#^^^^^^^^^^^^^^^^^^^^^^^^^#
	self.set_standard_path("list")
	return super().render()
```

I can send a pull request if it would be helpful, but I'm a bit unclear about the full implications of the basepath variable.
@this-gavagai This Worked for me as well. But How only see the sidebar if i go routes present in sidebar menu example
/orders,
/invoices.
How to show it without opening these routes.

Resolving patch:
diff --git a/frappe/website/page_renderers/template_page.py b/frappe/website/page_renderers/template_page.py
--- a/frappe/website/page_renderers/template_page.py
+++ b/frappe/website/page_renderers/template_page.py
@@ -109,8 +109,7 @@ def post_process_context(self):
 		super().post_process_context()
 
 	def add_sidebar_and_breadcrumbs(self):
-		if self.basepath:
-			self.context.sidebar_items = get_sidebar_items(self.context.website_sidebar, self.basepath)
+		self.context.sidebar_items = get_sidebar_items(self.context.website_sidebar, self.basepath)
 
 		if self.context.add_breadcrumbs and not self.context.parents:
 			parent_path = os.path.dirname(self.path)


Test patch:
Repository: frappe/frappe

Issue:
better highlight print format errors
if error occurs in html print format it's exactly clear from where it's coming from or how to debug it


Discussion:

Resolving patch:
diff --git a/frappe/exceptions.py b/frappe/exceptions.py
--- a/frappe/exceptions.py
+++ b/frappe/exceptions.py
@@ -252,6 +252,10 @@ class SessionBootFailed(ValidationError):
 	http_status_code = 500
 
 
+class PrintFormatError(ValidationError):
+	pass
+
+
 class TooManyWritesError(Exception):
 	pass
 
diff --git a/frappe/utils/error.py b/frappe/utils/error.py
--- a/frappe/utils/error.py
+++ b/frappe/utils/error.py
@@ -157,5 +157,5 @@ def guess_exception_source(exception: str) -> str | None:
 				app_name = matches.group("app_name")
 				apps[app_name] += app_priority.get(app_name, 0)
 
-		if probably_source := apps.most_common(1):
+		if (probably_source := apps.most_common(1)) and probably_source[0][0] != "frappe":
 			return f"{probably_source[0][0]} (app)"
diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py
--- a/frappe/utils/pdf.py
+++ b/frappe/utils/pdf.py
@@ -1,5 +1,6 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: MIT. See LICENSE
+import contextlib
 import io
 import os
 import re
@@ -43,7 +44,30 @@ def pdf_header_html(soup, head, content, styles, html_id, css):
 
 
 def pdf_body_html(template, args, **kwargs):
-	return template.render(args, filters={"len": len})
+	try:
+		return template.render(args, filters={"len": len})
+	except Exception as e:
+		# Guess line number ?
+		frappe.throw(
+			_("Error in print format on line {0}: {1}").format(
+				_guess_template_error_line_number(template), e
+			),
+			exc=frappe.PrintFormatError,
+			title=_("Print Format Error"),
+		)
+
+
+def _guess_template_error_line_number(template) -> int | None:
+	"""Guess line on which exception occured from current traceback."""
+	with contextlib.suppress(Exception):
+		import sys
+		import traceback
+
+		_, _, tb = sys.exc_info()
+
+		for frame in reversed(traceback.extract_tb(tb)):
+			if template.filename in frame.filename:
+				return frame.lineno
 
 
 def pdf_footer_html(soup, head, content, styles, html_id, css):


Test patch:
Repository: frappe/frappe

Issue:
TypeError: 'NoneType' object is not iterable
Getting this everywhere inside desk.
```
Traceback (most recent call last):
  File "apps/frappe/frappe/app.py", line 108, in application
    response = frappe.api.handle()
  File "apps/frappe/frappe/api.py", line 53, in handle
    return _RESTAPIHandler(call, doctype, name).get_response()
  File "apps/frappe/frappe/api.py", line 69, in get_response
    return self.handle_method()
  File "apps/frappe/frappe/api.py", line 79, in handle_method
    return frappe.handler.handle()
  File "apps/frappe/frappe/handler.py", line 48, in handle
    data = execute_cmd(cmd)
  File "apps/frappe/frappe/handler.py", line 86, in execute_cmd
    return frappe.call(method, **frappe.form_dict)
  File "apps/frappe/frappe/__init__.py", line 1671, in call
    return fn(*args, **newargs)
  File "apps/frappe/frappe/utils/typing_validations.py", line 31, in wrapper
    return func(*args, **kwargs)
  File "apps/frappe/frappe/__init__.py", line 836, in wrapper_fn
    retval = fn(*args, **get_newargs(fn, kwargs))
  File "apps/frappe/frappe/desk/notifications.py", line 277, in get_open_count
    internal_links_data_for_d = get_internal_links(doc, internal_link_for_doctype, d)
  File "apps/frappe/frappe/desk/notifications.py", line 314, in get_internal_links
    for row in doc.get(table_fieldname):
TypeError: 'NoneType' object is not iterable
```


Discussion:
@anandbaburajan any idea why this could be happening? IG if the child table specified in link doesn't actually exist, i.e. bad configuration?

related https://github.com/frappe/frappe/pull/21722
@ankush yep.

Resolving patch:
diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py
--- a/frappe/desk/notifications.py
+++ b/frappe/desk/notifications.py
@@ -311,7 +311,7 @@ def get_internal_links(doc, link, link_doctype):
 	elif isinstance(link, list):
 		# get internal links in child documents
 		table_fieldname, link_fieldname = link
-		for row in doc.get(table_fieldname):
+		for row in doc.get(table_fieldname) or []:
 			value = row.get(link_fieldname)
 			if value and value not in names:
 				names.append(value)


Test patch:
Repository: frappe/frappe

Issue:
limit rating field type values to max 1
Rating field stores a floating point value between 0-1, this is required to allow converting number of stars without patching data. 

However, you can end up writing some buggy code that stores value more than 1.0, this should be prevented by server-side validation. 

Currently no validation on:
1. API
2. Frontend (client script can modify it)




Implementation:
- `base_document.py` contains many fieldtype specific validations, add a new one there for rating fieldtype.



Discussion:
@ankush  you mean minimum 1?
@bhavesh95863 no, maximum. 

Rating field values are stored as floating-point between 0 and 1 (version-14). This is for allowing variable number of stars. 

Resolving patch:
diff --git a/frappe/model/document.py b/frappe/model/document.py
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -553,6 +553,7 @@ def _validate(self):
 		self._validate_selects()
 		self._validate_non_negative()
 		self._validate_length()
+		self._fix_rating_value()
 		self._validate_code_fields()
 		self._sync_autoname_field()
 		self._extract_images_from_text_editor()
@@ -600,6 +601,15 @@ def get_msg(df):
 				msg = get_msg(df)
 				frappe.throw(msg, frappe.NonNegativeError, title=_("Negative Value"))
 
+	def _fix_rating_value(self):
+		for field in self.meta.get("fields", {"fieldtype": "Rating"}):
+			value = self.get(field.fieldname)
+			if not isinstance(value, float):
+				value = flt(value)
+
+			# Make sure rating is between 0 and 1
+			self.set(field.fieldname, max(0, min(value, 1)))
+
 	def validate_workflow(self):
 		"""Validate if the workflow transition is valid"""
 		if frappe.flags.in_install == "frappe":


Test patch:
diff --git a/frappe/tests/test_rating.py b/frappe/tests/test_rating.py
new file mode 100644
--- /dev/null
+++ b/frappe/tests/test_rating.py
@@ -0,0 +1,29 @@
+import frappe
+from frappe.core.doctype.doctype.test_doctype import new_doctype
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestRating(FrappeTestCase):
+	def setUp(self):
+		doc = new_doctype(
+			fields=[
+				{
+					"fieldname": "rating",
+					"fieldtype": "Rating",
+					"label": "rating",
+					"reqd": 1,  # mandatory
+				},
+			],
+		)
+		doc.insert()
+		self.doctype_name = doc.name
+
+	def test_negative_rating(self):
+		doc = frappe.new_doc(doctype=self.doctype_name, rating=-1)
+		doc.insert()
+		self.assertEqual(doc.rating, 0)
+
+	def test_positive_rating(self):
+		doc = frappe.new_doc(doctype=self.doctype_name, rating=5)
+		doc.insert()
+		self.assertEqual(doc.rating, 1)
Repository: frappe/frappe

Issue:
API v2: `frappe.get_message_log()` performs extra `json.loads`
### Description

Since the nice API refactor PR​#22300 by @ankush, and if I understand it correctly, the message_log now contains `dict`s (before it contained JSON strings). Now, this means that the `frappe.get_message_log` function performs an extra/buggy `json.loads`.

https://github.com/frappe/frappe/blob/716ff2add21ba346dd238b4d6c5fef14a480fc54/frappe/__init__.py#L549-L550

### Reproduction

```md
$ bench console
In [1]: frappe.msgprint("Lorem")
In [2]: frappe.get_message_log()

> TypeError: the JSON object must be str, bytes or bytearray, not _dict
```

### Suggested patch
```py
# frappe/__init__.py
def get_message_log() -> list[dict]:
       return [msg_out for msg_out in local.message_log]
```

_Have a nice day!_


Discussion:

Resolving patch:
diff --git a/frappe/__init__.py b/frappe/__init__.py
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -546,8 +546,8 @@ def clear_messages():
 	local.message_log = []
 
 
-def get_message_log():
-	return [json.loads(msg_out) for msg_out in local.message_log]
+def get_message_log() -> list[dict]:
+	return [msg_out for msg_out in local.message_log]
 
 
 def clear_last_message():


Test patch:
Repository: frappe/frappe

Issue:
Add jitter on scheduled jobs
hourly, daily long processes if all started at once can cause sudden increase in workload if you have many sites/benches. 


Adding simple jitter to scheduled time can lessen the impact of such issues. Jitter is common pattern used for solving problems with "frequency" becomes a problem. E.g. gunicorn adds jitter to avoid restarting all workers at same time, profilers add jitter to avoid amplifying some pattern of repeated work. retry/backoff implementations also use to avoid creating patterns.


Possible implementation: When importing scheduled job types add some random delays in cron. E.g. daily jobs will start in the range of  12:00-12:15 AM instead of all starting at 12:00 AM.


Cons: Some jobs are required to be executed at specific times e.g. birthday reminders. So adding negative offset can introduce bugs for them, positive offset however should be fine AFAIK. 


Discussion:

Resolving patch:
diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py
--- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py
+++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py
@@ -2,7 +2,8 @@
 # License: MIT. See LICENSE
 
 import json
-from datetime import datetime
+from datetime import datetime, timedelta
+from random import randint
 
 import click
 from croniter import croniter
@@ -110,7 +111,12 @@ def get_next_execution(self):
 		# immediately, even when it's meant to be daily.
 		# A dynamic fallback like current time might miss the scheduler interval and job will never start.
 		last_execution = get_datetime(self.last_execution or self.creation)
-		return croniter(self.cron_format, last_execution).get_next(datetime)
+		next_execution = croniter(self.cron_format, last_execution).get_next(datetime)
+
+		jitter = 0
+		if self.frequency in ("Hourly Long", "Daily Long"):
+			jitter = randint(1, 600)
+		return next_execution + timedelta(seconds=jitter)
 
 	def execute(self):
 		self.scheduler_log = None


Test patch:
Repository: frappe/frappe

Issue:
show traceback from setup wizard failure in UI somewhere
internal issue: 6197

When users can't see the traceback it's hard for anyone to debug. Right now traceback gets stored in log file. It should also be sent as response. 


Discussion:

Resolving patch:
diff --git a/frappe/desk/page/setup_wizard/setup_wizard.py b/frappe/desk/page/setup_wizard/setup_wizard.py
--- a/frappe/desk/page/setup_wizard/setup_wizard.py
+++ b/frappe/desk/page/setup_wizard/setup_wizard.py
@@ -83,11 +83,14 @@ def process_setup_stages(stages, user_input, is_background_task=False):
 				task.get("fn")(task.get("args"))
 	except Exception:
 		handle_setup_exception(user_input)
+		message = current_task.get("fail_msg") if current_task else "Failed to complete setup"
+		frappe.log_error(title=f"Setup failed: {message}")
 		if not is_background_task:
-			return {"status": "fail", "fail": current_task.get("fail_msg")}
+			frappe.response["setup_wizard_failure_message"] = message
+			raise
 		frappe.publish_realtime(
 			"setup_task",
-			{"status": "fail", "fail_msg": current_task.get("fail_msg")},
+			{"status": "fail", "fail_msg": message},
 			user=frappe.session.user,
 		)
 	else:


Test patch:
Repository: frappe/frappe

Issue:
hook for adding doctypes to log settings 



Discussion:

Resolving patch:
diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py
--- a/frappe/core/doctype/log_settings/log_settings.py
+++ b/frappe/core/doctype/log_settings/log_settings.py
@@ -10,20 +10,6 @@
 from frappe.utils import cint
 from frappe.utils.caching import site_cache
 
-DEFAULT_LOGTYPES_RETENTION = {
-	"Error Log": 30,
-	"Activity Log": 90,
-	"Email Queue": 30,
-	"Scheduled Job Log": 90,
-	"Route History": 90,
-	"Submission Queue": 30,
-	"Prepared Report": 30,
-	"Webhook Request Log": 30,
-	"Integration Request": 90,
-	"Unhandled Email": 30,
-	"Reminder": 30,
-}
-
 
 @runtime_checkable
 class LogType(Protocol):
@@ -81,12 +67,14 @@ def _deduplicate_entries(self):
 	def add_default_logtypes(self):
 		existing_logtypes = {d.ref_doctype for d in self.logs_to_clear}
 		added_logtypes = set()
-		for logtype, retention in DEFAULT_LOGTYPES_RETENTION.items():
+		default_logtypes_retention = frappe.get_hooks("default_log_clearing_doctypes", {})
+
+		for logtype, retentions in default_logtypes_retention.items():
 			if logtype not in existing_logtypes and _supports_log_clearing(logtype):
 				if not frappe.db.exists("DocType", logtype):
 					continue
 
-				self.append("logs_to_clear", {"ref_doctype": logtype, "days": cint(retention)})
+				self.append("logs_to_clear", {"ref_doctype": logtype, "days": cint(retentions[-1])})
 				added_logtypes.add(logtype)
 
 		if added_logtypes:
diff --git a/frappe/hooks.py b/frappe/hooks.py
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -439,3 +439,18 @@
 ]
 
 export_python_type_annotations = True
+
+# log doctype cleanups to automatically add in log settings
+default_log_clearing_doctypes = {
+	"Error Log": 30,
+	"Activity Log": 90,
+	"Email Queue": 30,
+	"Scheduled Job Log": 90,
+	"Route History": 90,
+	"Submission Queue": 30,
+	"Prepared Report": 30,
+	"Webhook Request Log": 30,
+	"Integration Request": 90,
+	"Unhandled Email": 30,
+	"Reminder": 30,
+}
diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py
--- a/frappe/utils/boilerplate.py
+++ b/frappe/utils/boilerplate.py
@@ -551,6 +551,14 @@ def _create_parent_folder_if_not_exists(self):
 # auth_hooks = [
 #	"{app_name}.auth.validate"
 # ]
+
+# Automatically update python controller files with type annotations for this app.
+# export_python_type_annotations = True
+
+# default_log_clearing_doctypes = {{
+#	"Logging DocType Name": 30  # days to retain logs
+# }}
+
 """
 
 gitignore_template = """.DS_Store


Test patch:
Repository: frappe/frappe

Issue:
Route History shouldn‘t be editable
Editing or adding a new Route History:

![IMG_2238](https://github.com/frappe/frappe/assets/46800703/caf5ccf6-80c0-4d66-8ed8-1716eaf27f52)
![IMG_2240](https://github.com/frappe/frappe/assets/46800703/ff1d1ae5-f657-4607-a27e-e390096b5144)

… shouldn’t be possible, not even for the Administrator.


Discussion:

Resolving patch:
diff --git a/frappe/desk/doctype/route_history/route_history.py b/frappe/desk/doctype/route_history/route_history.py
--- a/frappe/desk/doctype/route_history/route_history.py
+++ b/frappe/desk/doctype/route_history/route_history.py
@@ -18,6 +18,7 @@ class RouteHistory(Document):
 		route: DF.Data | None
 		user: DF.Link | None
 	# end: auto-generated types
+
 	@staticmethod
 	def clear_old_logs(days=30):
 		from frappe.query_builder import Interval


Test patch: