We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
AttributeError: 'SystemSettings' object has no attribute 'enable_password_policy' during `bench restore`
Hello,
I ran `bench update` then tried to restore a backup and this error starts popping up.

It seems it might have come in from 7ccbbce5720bf16d5d3cc94c627e22ef0541e53b

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `frappe/utils/scheduler.py`
Content:
```
1 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
2 # MIT License. See license.txt
3 """
4 Events:
5 	always
6 	daily
7 	monthly
8 	weekly
9 """
10 
11 from __future__ import unicode_literals, print_function
12 
13 import frappe
14 import json
15 import schedule
16 import time
17 import MySQLdb
18 import frappe.utils
19 from frappe.utils import get_sites
20 from datetime import datetime
21 from background_jobs import enqueue, get_jobs, queue_timeout
22 from frappe.limits import has_expired
23 from frappe.utils.data import get_datetime, now_datetime
24 from frappe.core.doctype.user.user import STANDARD_USERS
25 from frappe.installer import update_site_config
26 
27 DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
28 
29 def start_scheduler():
30 	'''Run enqueue_events_for_all_sites every 2 minutes (default).
31 	Specify scheduler_interval in seconds in common_site_config.json'''
32 
33 	interval = frappe.get_conf().scheduler_interval or 240
34 	schedule.every(interval).seconds.do(enqueue_events_for_all_sites)
35 
36 	while True:
37 		schedule.run_pending()
38 		time.sleep(1)
39 
40 def enqueue_events_for_all_sites():
41 	'''Loop through sites and enqueue events that are not already queued'''
42 	with frappe.init_site():
43 		jobs_per_site = get_jobs()
44 		sites = get_sites()
45 
46 	for site in sites:
47 		try:
48 			enqueue_events_for_site(site=site, queued_jobs=jobs_per_site[site])
49 		except:
50 			# it should try to enqueue other sites
51 			print(frappe.get_traceback())
52 
53 def enqueue_events_for_site(site, queued_jobs):
54 	try:
55 		frappe.init(site=site)
56 		if frappe.local.conf.maintenance_mode:
57 			return
58 
59 		if frappe.local.conf.pause_scheduler:
60 			return
61 
62 		frappe.connect()
63 		if is_scheduler_disabled():
64 			return
65 
66 		enqueue_events(site=site, queued_jobs=queued_jobs)
67 
68 		frappe.logger(__name__).debug('Queued events for site {0}'.format(site))
69 
70 	except:
71 		frappe.logger(__name__).error('Exception in Enqueue Events for Site {0}'.format(site) +
72 			'\n' + frappe.get_traceback())
73 		raise
74 
75 	finally:
76 		frappe.destroy()
77 
78 def enqueue_events(site, queued_jobs):
79 	nowtime = frappe.utils.now_datetime()
80 	last = frappe.db.get_value('System Settings', 'System Settings', 'scheduler_last_event')
81 
82 	# set scheduler last event
83 	frappe.db.set_value('System Settings', 'System Settings',
84 		'scheduler_last_event', nowtime.strftime(DATETIME_FORMAT),
85 		update_modified=False)
86 	frappe.db.commit()
87 
88 	out = []
89 	if last:
90 		last = datetime.strptime(last, DATETIME_FORMAT)
91 		out = enqueue_applicable_events(site, nowtime, last, queued_jobs)
92 
93 	return '\n'.join(out)
94 
95 def enqueue_applicable_events(site, nowtime, last, queued_jobs=()):
96 	nowtime_str = nowtime.strftime(DATETIME_FORMAT)
97 	out = []
98 
99 	enabled_events = get_enabled_scheduler_events()
100 
101 	def trigger_if_enabled(site, event):
102 		if event in enabled_events:
103 			trigger(site, event, queued_jobs)
104 			_log(event)
105 
106 	def _log(event):
107 		out.append("{time} - {event} - queued".format(time=nowtime_str, event=event))
108 
109 	if nowtime.day != last.day:
110 		# if first task of the day execute daily tasks
111 		trigger_if_enabled(site, "daily")
112 		trigger_if_enabled(site, "daily_long")
113 
114 		if nowtime.month != last.month:
115 			trigger_if_enabled(site, "monthly")
116 			trigger_if_enabled(site, "monthly_long")
117 
118 		if nowtime.weekday()==0:
119 			trigger_if_enabled(site, "weekly")
120 			trigger_if_enabled(site, "weekly_long")
121 
122 		if "all" not in enabled_events:
123 			trigger(site, "all", queued_jobs)
124 
125 		if "hourly" not in enabled_events:
126 			trigger(site, "hourly", queued_jobs)
127 
128 	if nowtime.hour != last.hour:
129 		trigger_if_enabled(site, "hourly")
130 		trigger_if_enabled(site, "hourly_long")
131 
132 		if "all" not in enabled_events:
133 			trigger(site, "all", queued_jobs)
134 
135 	trigger_if_enabled(site, "all")
136 
137 	return out
138 
139 def trigger(site, event, queued_jobs=(), now=False):
140 	"""trigger method in hooks.scheduler_events"""
141 	queue = 'long' if event.endswith('_long') else 'short'
142 	timeout = queue_timeout[queue]
143 	if not queued_jobs and not now:
144 		queued_jobs = get_jobs(site=site, queue=queue)
145 
146 	if frappe.flags.in_test:
147 		frappe.flags.ran_schedulers.append(event)
148 
149 	events = get_scheduler_events(event)
150 	if not events:
151 		return
152 
153 	for handler in events:
154 		if not now:
155 			if handler not in queued_jobs:
156 				enqueue(handler, queue, timeout, event)
157 		else:
158 			scheduler_task(site=site, event=event, handler=handler, now=True)
159 
160 def get_scheduler_events(event):
161 	'''Get scheduler events from hooks and integrations'''
162 	scheduler_events = frappe.cache().get_value('scheduler_events')
163 	if not scheduler_events:
164 		scheduler_events = frappe.get_hooks("scheduler_events")
165 		frappe.cache().set_value('scheduler_events', scheduler_events)
166 
167 	return scheduler_events.get(event) or []
168 
169 def log(method, message=None):
170 	"""log error in patch_log"""
171 	message = frappe.utils.cstr(message) + "\n" if message else ""
172 	message += frappe.get_traceback()
173 
174 	if not (frappe.db and frappe.db._conn):
175 		frappe.connect()
176 
177 	frappe.db.rollback()
178 	frappe.db.begin()
179 
180 	d = frappe.new_doc("Error Log")
181 	d.method = method
182 	d.error = message
183 	d.insert(ignore_permissions=True)
184 
185 	frappe.db.commit()
186 
187 	return message
188 
189 def get_enabled_scheduler_events():
190 	if 'enabled_events' in frappe.flags:
191 		return frappe.flags.enabled_events
192 
193 	enabled_events = frappe.db.get_global("enabled_scheduler_events")
194 	if enabled_events:
195 		if isinstance(enabled_events, basestring):
196 			enabled_events = json.loads(enabled_events)
197 
198 		return enabled_events
199 
200 	return ["all", "hourly", "hourly_long", "daily", "daily_long",
201 		"weekly", "weekly_long", "monthly", "monthly_long"]
202 
203 def is_scheduler_disabled():
204 	if frappe.conf.disable_scheduler:
205 		return True
206 
207 	return not frappe.utils.cint(frappe.db.get_single_value("System Settings", "enable_scheduler"))
208 
209 def toggle_scheduler(enable):
210 	ss = frappe.get_doc("System Settings")
211 	ss.enable_scheduler = 1 if enable else 0
212 	ss.flags.ignore_mandatory = True
213 	ss.flags.ignore_permissions = True
214 	ss.save()
215 
216 def enable_scheduler():
217 	toggle_scheduler(True)
218 
219 def disable_scheduler():
220 	toggle_scheduler(False)
221 
222 def get_errors(from_date, to_date, limit):
223 	errors = frappe.db.sql("""select modified, method, error from `tabError Log`
224 		where date(modified) between %s and %s
225 		and error not like '%%[Errno 110] Connection timed out%%'
226 		order by modified limit %s""", (from_date, to_date, limit), as_dict=True)
227 	return ["""<p>Time: {modified}</p><pre><code>Method: {method}\n{error}</code></pre>""".format(**e)
228 		for e in errors]
229 
230 def get_error_report(from_date=None, to_date=None, limit=10):
231 	from frappe.utils import get_url, now_datetime, add_days
232 
233 	if not from_date:
234 		from_date = add_days(now_datetime().date(), -1)
235 	if not to_date:
236 		to_date = add_days(now_datetime().date(), -1)
237 
238 	errors = get_errors(from_date, to_date, limit)
239 
240 	if errors:
241 		return 1, """<h4>Error Logs (max {limit}):</h4>
242 			<p>URL: <a href="{url}" target="_blank">{url}</a></p><hr>{errors}""".format(
243 			limit=limit, url=get_url(), errors="<hr>".join(errors))
244 	else:
245 		return 0, "<p>No error logs</p>"
246 
247 def scheduler_task(site, event, handler, now=False):
248 	'''This is a wrapper function that runs a hooks.scheduler_events method'''
249 	frappe.logger(__name__).info('running {handler} for {site} for event: {event}'.format(handler=handler, site=site, event=event))
250 	try:
251 		if not now:
252 			frappe.connect(site=site)
253 
254 		frappe.flags.in_scheduler = True
255 		frappe.get_attr(handler)()
256 
257 	except Exception:
258 		frappe.db.rollback()
259 		traceback = log(handler, "Method: {event}, Handler: {handler}".format(event=event, handler=handler))
260 		frappe.logger(__name__).error(traceback)
261 		raise
262 
263 	else:
264 		frappe.db.commit()
265 
266 	frappe.logger(__name__).info('ran {handler} for {site} for event: {event}'.format(handler=handler, site=site, event=event))
267 
268 
269 def reset_enabled_scheduler_events(login_manager):
270 	if login_manager.info.user_type == "System User":
271 		try:
272 			frappe.db.set_global('enabled_scheduler_events', None)
273 		except MySQLdb.OperationalError as e:
274 			if e.args[0]==1205:
275 				frappe.log_error(frappe.get_traceback(), "Error in reset_enabled_scheduler_events")
276 			else:
277 				raise
278 		else:
279 			is_dormant = frappe.conf.get('dormant')
280 			if is_dormant:
281 				update_site_config('dormant', 'None')
282 
283 def disable_scheduler_on_expiry():
284 	if has_expired():
285 		disable_scheduler()
286 
287 def restrict_scheduler_events_if_dormant():
288 	if is_dormant():
289 		restrict_scheduler_events()
290 		update_site_config('dormant', True)
291 
292 def restrict_scheduler_events(*args, **kwargs):
293 	val = json.dumps(["hourly", "hourly_long", "daily", "daily_long", "weekly", "weekly_long", "monthly", "monthly_long"])
294 	frappe.db.set_global('enabled_scheduler_events', val)
295 
296 def is_dormant(since = 345600):
297 	last_active = get_datetime(get_last_active())
298 	# Get now without tz info
299 	now = now_datetime().replace(tzinfo=None)
300 	time_since_last_active = now - last_active
301 	if time_since_last_active.total_seconds() > since:  # 4 days
302 		return True
303 	return False
304 
305 def get_last_active():
306 	return frappe.db.sql("""select max(ifnull(last_active, "2000-01-01 00:00:00")) from `tabUser`
307 		where user_type = 'System User' and name not in ({standard_users})"""\
308 		.format(standard_users=", ".join(["%s"]*len(STANDARD_USERS))),
309 		STANDARD_USERS)[0][0]
310
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py
--- a/frappe/utils/scheduler.py
+++ b/frappe/utils/scheduler.py
@@ -207,11 +207,7 @@
 	return not frappe.utils.cint(frappe.db.get_single_value("System Settings", "enable_scheduler"))
 
 def toggle_scheduler(enable):
-	ss = frappe.get_doc("System Settings")
-	ss.enable_scheduler = 1 if enable else 0
-	ss.flags.ignore_mandatory = True
-	ss.flags.ignore_permissions = True
-	ss.save()
+	frappe.db.set_value("System Settings", None, "enable_scheduler", 1 if enable else 0)
 
 def enable_scheduler():
 	toggle_scheduler(True)

{"golden_diff": "diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py\n--- a/frappe/utils/scheduler.py\n+++ b/frappe/utils/scheduler.py\n@@ -207,11 +207,7 @@\n \treturn not frappe.utils.cint(frappe.db.get_single_value(\"System Settings\", \"enable_scheduler\"))\n \n def toggle_scheduler(enable):\n-\tss = frappe.get_doc(\"System Settings\")\n-\tss.enable_scheduler = 1 if enable else 0\n-\tss.flags.ignore_mandatory = True\n-\tss.flags.ignore_permissions = True\n-\tss.save()\n+\tfrappe.db.set_value(\"System Settings\", None, \"enable_scheduler\", 1 if enable else 0)\n \n def enable_scheduler():\n \ttoggle_scheduler(True)\n", "issue": "AttributeError: 'SystemSettings' object has no attribute 'enable_password_policy' during `bench restore`\nHello,\r\nI ran `bench update` then tried to restore a backup and this error starts popping up.\r\n\r\nIt seems it might have come in from 7ccbbce5720bf16d5d3cc94c627e22ef0541e53b\n", "before_files": [{"content": "# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors\n# MIT License. See license.txt\n\"\"\"\nEvents:\n\talways\n\tdaily\n\tmonthly\n\tweekly\n\"\"\"\n\nfrom __future__ import unicode_literals, print_function\n\nimport frappe\nimport json\nimport schedule\nimport time\nimport MySQLdb\nimport frappe.utils\nfrom frappe.utils import get_sites\nfrom datetime import datetime\nfrom background_jobs import enqueue, get_jobs, queue_timeout\nfrom frappe.limits import has_expired\nfrom frappe.utils.data import get_datetime, now_datetime\nfrom frappe.core.doctype.user.user import STANDARD_USERS\nfrom frappe.installer import update_site_config\n\nDATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'\n\ndef start_scheduler():\n\t'''Run enqueue_events_for_all_sites every 2 minutes (default).\n\tSpecify scheduler_interval in seconds in common_site_config.json'''\n\n\tinterval = frappe.get_conf().scheduler_interval or 240\n\tschedule.every(interval).seconds.do(enqueue_events_for_all_sites)\n\n\twhile True:\n\t\tschedule.run_pending()\n\t\ttime.sleep(1)\n\ndef enqueue_events_for_all_sites():\n\t'''Loop through sites and enqueue events that are not already queued'''\n\twith frappe.init_site():\n\t\tjobs_per_site = get_jobs()\n\t\tsites = get_sites()\n\n\tfor site in sites:\n\t\ttry:\n\t\t\tenqueue_events_for_site(site=site, queued_jobs=jobs_per_site[site])\n\t\texcept:\n\t\t\t# it should try to enqueue other sites\n\t\t\tprint(frappe.get_traceback())\n\ndef enqueue_events_for_site(site, queued_jobs):\n\ttry:\n\t\tfrappe.init(site=site)\n\t\tif frappe.local.conf.maintenance_mode:\n\t\t\treturn\n\n\t\tif frappe.local.conf.pause_scheduler:\n\t\t\treturn\n\n\t\tfrappe.connect()\n\t\tif is_scheduler_disabled():\n\t\t\treturn\n\n\t\tenqueue_events(site=site, queued_jobs=queued_jobs)\n\n\t\tfrappe.logger(__name__).debug('Queued events for site {0}'.format(site))\n\n\texcept:\n\t\tfrappe.logger(__name__).error('Exception in Enqueue Events for Site {0}'.format(site) +\n\t\t\t'\\n' + frappe.get_traceback())\n\t\traise\n\n\tfinally:\n\t\tfrappe.destroy()\n\ndef enqueue_events(site, queued_jobs):\n\tnowtime = frappe.utils.now_datetime()\n\tlast = frappe.db.get_value('System Settings', 'System Settings', 'scheduler_last_event')\n\n\t# set scheduler last event\n\tfrappe.db.set_value('System Settings', 'System Settings',\n\t\t'scheduler_last_event', nowtime.strftime(DATETIME_FORMAT),\n\t\tupdate_modified=False)\n\tfrappe.db.commit()\n\n\tout = []\n\tif last:\n\t\tlast = datetime.strptime(last, DATETIME_FORMAT)\n\t\tout = enqueue_applicable_events(site, nowtime, last, queued_jobs)\n\n\treturn '\\n'.join(out)\n\ndef enqueue_applicable_events(site, nowtime, last, queued_jobs=()):\n\tnowtime_str = nowtime.strftime(DATETIME_FORMAT)\n\tout = []\n\n\tenabled_events = get_enabled_scheduler_events()\n\n\tdef trigger_if_enabled(site, event):\n\t\tif event in enabled_events:\n\t\t\ttrigger(site, event, queued_jobs)\n\t\t\t_log(event)\n\n\tdef _log(event):\n\t\tout.append(\"{time} - {event} - queued\".format(time=nowtime_str, event=event))\n\n\tif nowtime.day != last.day:\n\t\t# if first task of the day execute daily tasks\n\t\ttrigger_if_enabled(site, \"daily\")\n\t\ttrigger_if_enabled(site, \"daily_long\")\n\n\t\tif nowtime.month != last.month:\n\t\t\ttrigger_if_enabled(site, \"monthly\")\n\t\t\ttrigger_if_enabled(site, \"monthly_long\")\n\n\t\tif nowtime.weekday()==0:\n\t\t\ttrigger_if_enabled(site, \"weekly\")\n\t\t\ttrigger_if_enabled(site, \"weekly_long\")\n\n\t\tif \"all\" not in enabled_events:\n\t\t\ttrigger(site, \"all\", queued_jobs)\n\n\t\tif \"hourly\" not in enabled_events:\n\t\t\ttrigger(site, \"hourly\", queued_jobs)\n\n\tif nowtime.hour != last.hour:\n\t\ttrigger_if_enabled(site, \"hourly\")\n\t\ttrigger_if_enabled(site, \"hourly_long\")\n\n\t\tif \"all\" not in enabled_events:\n\t\t\ttrigger(site, \"all\", queued_jobs)\n\n\ttrigger_if_enabled(site, \"all\")\n\n\treturn out\n\ndef trigger(site, event, queued_jobs=(), now=False):\n\t\"\"\"trigger method in hooks.scheduler_events\"\"\"\n\tqueue = 'long' if event.endswith('_long') else 'short'\n\ttimeout = queue_timeout[queue]\n\tif not queued_jobs and not now:\n\t\tqueued_jobs = get_jobs(site=site, queue=queue)\n\n\tif frappe.flags.in_test:\n\t\tfrappe.flags.ran_schedulers.append(event)\n\n\tevents = get_scheduler_events(event)\n\tif not events:\n\t\treturn\n\n\tfor handler in events:\n\t\tif not now:\n\t\t\tif handler not in queued_jobs:\n\t\t\t\tenqueue(handler, queue, timeout, event)\n\t\telse:\n\t\t\tscheduler_task(site=site, event=event, handler=handler, now=True)\n\ndef get_scheduler_events(event):\n\t'''Get scheduler events from hooks and integrations'''\n\tscheduler_events = frappe.cache().get_value('scheduler_events')\n\tif not scheduler_events:\n\t\tscheduler_events = frappe.get_hooks(\"scheduler_events\")\n\t\tfrappe.cache().set_value('scheduler_events', scheduler_events)\n\n\treturn scheduler_events.get(event) or []\n\ndef log(method, message=None):\n\t\"\"\"log error in patch_log\"\"\"\n\tmessage = frappe.utils.cstr(message) + \"\\n\" if message else \"\"\n\tmessage += frappe.get_traceback()\n\n\tif not (frappe.db and frappe.db._conn):\n\t\tfrappe.connect()\n\n\tfrappe.db.rollback()\n\tfrappe.db.begin()\n\n\td = frappe.new_doc(\"Error Log\")\n\td.method = method\n\td.error = message\n\td.insert(ignore_permissions=True)\n\n\tfrappe.db.commit()\n\n\treturn message\n\ndef get_enabled_scheduler_events():\n\tif 'enabled_events' in frappe.flags:\n\t\treturn frappe.flags.enabled_events\n\n\tenabled_events = frappe.db.get_global(\"enabled_scheduler_events\")\n\tif enabled_events:\n\t\tif isinstance(enabled_events, basestring):\n\t\t\tenabled_events = json.loads(enabled_events)\n\n\t\treturn enabled_events\n\n\treturn [\"all\", \"hourly\", \"hourly_long\", \"daily\", \"daily_long\",\n\t\t\"weekly\", \"weekly_long\", \"monthly\", \"monthly_long\"]\n\ndef is_scheduler_disabled():\n\tif frappe.conf.disable_scheduler:\n\t\treturn True\n\n\treturn not frappe.utils.cint(frappe.db.get_single_value(\"System Settings\", \"enable_scheduler\"))\n\ndef toggle_scheduler(enable):\n\tss = frappe.get_doc(\"System Settings\")\n\tss.enable_scheduler = 1 if enable else 0\n\tss.flags.ignore_mandatory = True\n\tss.flags.ignore_permissions = True\n\tss.save()\n\ndef enable_scheduler():\n\ttoggle_scheduler(True)\n\ndef disable_scheduler():\n\ttoggle_scheduler(False)\n\ndef get_errors(from_date, to_date, limit):\n\terrors = frappe.db.sql(\"\"\"select modified, method, error from `tabError Log`\n\t\twhere date(modified) between %s and %s\n\t\tand error not like '%%[Errno 110] Connection timed out%%'\n\t\torder by modified limit %s\"\"\", (from_date, to_date, limit), as_dict=True)\n\treturn [\"\"\"<p>Time: {modified}</p><pre><code>Method: {method}\\n{error}</code></pre>\"\"\".format(**e)\n\t\tfor e in errors]\n\ndef get_error_report(from_date=None, to_date=None, limit=10):\n\tfrom frappe.utils import get_url, now_datetime, add_days\n\n\tif not from_date:\n\t\tfrom_date = add_days(now_datetime().date(), -1)\n\tif not to_date:\n\t\tto_date = add_days(now_datetime().date(), -1)\n\n\terrors = get_errors(from_date, to_date, limit)\n\n\tif errors:\n\t\treturn 1, \"\"\"<h4>Error Logs (max {limit}):</h4>\n\t\t\t<p>URL: <a href=\"{url}\" target=\"_blank\">{url}</a></p><hr>{errors}\"\"\".format(\n\t\t\tlimit=limit, url=get_url(), errors=\"<hr>\".join(errors))\n\telse:\n\t\treturn 0, \"<p>No error logs</p>\"\n\ndef scheduler_task(site, event, handler, now=False):\n\t'''This is a wrapper function that runs a hooks.scheduler_events method'''\n\tfrappe.logger(__name__).info('running {handler} for {site} for event: {event}'.format(handler=handler, site=site, event=event))\n\ttry:\n\t\tif not now:\n\t\t\tfrappe.connect(site=site)\n\n\t\tfrappe.flags.in_scheduler = True\n\t\tfrappe.get_attr(handler)()\n\n\texcept Exception:\n\t\tfrappe.db.rollback()\n\t\ttraceback = log(handler, \"Method: {event}, Handler: {handler}\".format(event=event, handler=handler))\n\t\tfrappe.logger(__name__).error(traceback)\n\t\traise\n\n\telse:\n\t\tfrappe.db.commit()\n\n\tfrappe.logger(__name__).info('ran {handler} for {site} for event: {event}'.format(handler=handler, site=site, event=event))\n\n\ndef reset_enabled_scheduler_events(login_manager):\n\tif login_manager.info.user_type == \"System User\":\n\t\ttry:\n\t\t\tfrappe.db.set_global('enabled_scheduler_events', None)\n\t\texcept MySQLdb.OperationalError as e:\n\t\t\tif e.args[0]==1205:\n\t\t\t\tfrappe.log_error(frappe.get_traceback(), \"Error in reset_enabled_scheduler_events\")\n\t\t\telse:\n\t\t\t\traise\n\t\telse:\n\t\t\tis_dormant = frappe.conf.get('dormant')\n\t\t\tif is_dormant:\n\t\t\t\tupdate_site_config('dormant', 'None')\n\ndef disable_scheduler_on_expiry():\n\tif has_expired():\n\t\tdisable_scheduler()\n\ndef restrict_scheduler_events_if_dormant():\n\tif is_dormant():\n\t\trestrict_scheduler_events()\n\t\tupdate_site_config('dormant', True)\n\ndef restrict_scheduler_events(*args, **kwargs):\n\tval = json.dumps([\"hourly\", \"hourly_long\", \"daily\", \"daily_long\", \"weekly\", \"weekly_long\", \"monthly\", \"monthly_long\"])\n\tfrappe.db.set_global('enabled_scheduler_events', val)\n\ndef is_dormant(since = 345600):\n\tlast_active = get_datetime(get_last_active())\n\t# Get now without tz info\n\tnow = now_datetime().replace(tzinfo=None)\n\ttime_since_last_active = now - last_active\n\tif time_since_last_active.total_seconds() > since:  # 4 days\n\t\treturn True\n\treturn False\n\ndef get_last_active():\n\treturn frappe.db.sql(\"\"\"select max(ifnull(last_active, \"2000-01-01 00:00:00\")) from `tabUser`\n\t\twhere user_type = 'System User' and name not in ({standard_users})\"\"\"\\\n\t\t.format(standard_users=\", \".join([\"%s\"]*len(STANDARD_USERS))),\n\t\tSTANDARD_USERS)[0][0]\n", "path": "frappe/utils/scheduler.py"}], "after_files": [{"content": "# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors\n# MIT License. See license.txt\n\"\"\"\nEvents:\n\talways\n\tdaily\n\tmonthly\n\tweekly\n\"\"\"\n\nfrom __future__ import unicode_literals, print_function\n\nimport frappe\nimport json\nimport schedule\nimport time\nimport MySQLdb\nimport frappe.utils\nfrom frappe.utils import get_sites\nfrom datetime import datetime\nfrom background_jobs import enqueue, get_jobs, queue_timeout\nfrom frappe.limits import has_expired\nfrom frappe.utils.data import get_datetime, now_datetime\nfrom frappe.core.doctype.user.user import STANDARD_USERS\nfrom frappe.installer import update_site_config\n\nDATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'\n\ndef start_scheduler():\n\t'''Run enqueue_events_for_all_sites every 2 minutes (default).\n\tSpecify scheduler_interval in seconds in common_site_config.json'''\n\n\tinterval = frappe.get_conf().scheduler_interval or 240\n\tschedule.every(interval).seconds.do(enqueue_events_for_all_sites)\n\n\twhile True:\n\t\tschedule.run_pending()\n\t\ttime.sleep(1)\n\ndef enqueue_events_for_all_sites():\n\t'''Loop through sites and enqueue events that are not already queued'''\n\twith frappe.init_site():\n\t\tjobs_per_site = get_jobs()\n\t\tsites = get_sites()\n\n\tfor site in sites:\n\t\ttry:\n\t\t\tenqueue_events_for_site(site=site, queued_jobs=jobs_per_site[site])\n\t\texcept:\n\t\t\t# it should try to enqueue other sites\n\t\t\tprint(frappe.get_traceback())\n\ndef enqueue_events_for_site(site, queued_jobs):\n\ttry:\n\t\tfrappe.init(site=site)\n\t\tif frappe.local.conf.maintenance_mode:\n\t\t\treturn\n\n\t\tif frappe.local.conf.pause_scheduler:\n\t\t\treturn\n\n\t\tfrappe.connect()\n\t\tif is_scheduler_disabled():\n\t\t\treturn\n\n\t\tenqueue_events(site=site, queued_jobs=queued_jobs)\n\n\t\tfrappe.logger(__name__).debug('Queued events for site {0}'.format(site))\n\n\texcept:\n\t\tfrappe.logger(__name__).error('Exception in Enqueue Events for Site {0}'.format(site) +\n\t\t\t'\\n' + frappe.get_traceback())\n\t\traise\n\n\tfinally:\n\t\tfrappe.destroy()\n\ndef enqueue_events(site, queued_jobs):\n\tnowtime = frappe.utils.now_datetime()\n\tlast = frappe.db.get_value('System Settings', 'System Settings', 'scheduler_last_event')\n\n\t# set scheduler last event\n\tfrappe.db.set_value('System Settings', 'System Settings',\n\t\t'scheduler_last_event', nowtime.strftime(DATETIME_FORMAT),\n\t\tupdate_modified=False)\n\tfrappe.db.commit()\n\n\tout = []\n\tif last:\n\t\tlast = datetime.strptime(last, DATETIME_FORMAT)\n\t\tout = enqueue_applicable_events(site, nowtime, last, queued_jobs)\n\n\treturn '\\n'.join(out)\n\ndef enqueue_applicable_events(site, nowtime, last, queued_jobs=()):\n\tnowtime_str = nowtime.strftime(DATETIME_FORMAT)\n\tout = []\n\n\tenabled_events = get_enabled_scheduler_events()\n\n\tdef trigger_if_enabled(site, event):\n\t\tif event in enabled_events:\n\t\t\ttrigger(site, event, queued_jobs)\n\t\t\t_log(event)\n\n\tdef _log(event):\n\t\tout.append(\"{time} - {event} - queued\".format(time=nowtime_str, event=event))\n\n\tif nowtime.day != last.day:\n\t\t# if first task of the day execute daily tasks\n\t\ttrigger_if_enabled(site, \"daily\")\n\t\ttrigger_if_enabled(site, \"daily_long\")\n\n\t\tif nowtime.month != last.month:\n\t\t\ttrigger_if_enabled(site, \"monthly\")\n\t\t\ttrigger_if_enabled(site, \"monthly_long\")\n\n\t\tif nowtime.weekday()==0:\n\t\t\ttrigger_if_enabled(site, \"weekly\")\n\t\t\ttrigger_if_enabled(site, \"weekly_long\")\n\n\t\tif \"all\" not in enabled_events:\n\t\t\ttrigger(site, \"all\", queued_jobs)\n\n\t\tif \"hourly\" not in enabled_events:\n\t\t\ttrigger(site, \"hourly\", queued_jobs)\n\n\tif nowtime.hour != last.hour:\n\t\ttrigger_if_enabled(site, \"hourly\")\n\t\ttrigger_if_enabled(site, \"hourly_long\")\n\n\t\tif \"all\" not in enabled_events:\n\t\t\ttrigger(site, \"all\", queued_jobs)\n\n\ttrigger_if_enabled(site, \"all\")\n\n\treturn out\n\ndef trigger(site, event, queued_jobs=(), now=False):\n\t\"\"\"trigger method in hooks.scheduler_events\"\"\"\n\tqueue = 'long' if event.endswith('_long') else 'short'\n\ttimeout = queue_timeout[queue]\n\tif not queued_jobs and not now:\n\t\tqueued_jobs = get_jobs(site=site, queue=queue)\n\n\tif frappe.flags.in_test:\n\t\tfrappe.flags.ran_schedulers.append(event)\n\n\tevents = get_scheduler_events(event)\n\tif not events:\n\t\treturn\n\n\tfor handler in events:\n\t\tif not now:\n\t\t\tif handler not in queued_jobs:\n\t\t\t\tenqueue(handler, queue, timeout, event)\n\t\telse:\n\t\t\tscheduler_task(site=site, event=event, handler=handler, now=True)\n\ndef get_scheduler_events(event):\n\t'''Get scheduler events from hooks and integrations'''\n\tscheduler_events = frappe.cache().get_value('scheduler_events')\n\tif not scheduler_events:\n\t\tscheduler_events = frappe.get_hooks(\"scheduler_events\")\n\t\tfrappe.cache().set_value('scheduler_events', scheduler_events)\n\n\treturn scheduler_events.get(event) or []\n\ndef log(method, message=None):\n\t\"\"\"log error in patch_log\"\"\"\n\tmessage = frappe.utils.cstr(message) + \"\\n\" if message else \"\"\n\tmessage += frappe.get_traceback()\n\n\tif not (frappe.db and frappe.db._conn):\n\t\tfrappe.connect()\n\n\tfrappe.db.rollback()\n\tfrappe.db.begin()\n\n\td = frappe.new_doc(\"Error Log\")\n\td.method = method\n\td.error = message\n\td.insert(ignore_permissions=True)\n\n\tfrappe.db.commit()\n\n\treturn message\n\ndef get_enabled_scheduler_events():\n\tif 'enabled_events' in frappe.flags:\n\t\treturn frappe.flags.enabled_events\n\n\tenabled_events = frappe.db.get_global(\"enabled_scheduler_events\")\n\tif enabled_events:\n\t\tif isinstance(enabled_events, basestring):\n\t\t\tenabled_events = json.loads(enabled_events)\n\n\t\treturn enabled_events\n\n\treturn [\"all\", \"hourly\", \"hourly_long\", \"daily\", \"daily_long\",\n\t\t\"weekly\", \"weekly_long\", \"monthly\", \"monthly_long\"]\n\ndef is_scheduler_disabled():\n\tif frappe.conf.disable_scheduler:\n\t\treturn True\n\n\treturn not frappe.utils.cint(frappe.db.get_single_value(\"System Settings\", \"enable_scheduler\"))\n\ndef toggle_scheduler(enable):\n\tfrappe.db.set_value(\"System Settings\", None, \"enable_scheduler\", 1 if enable else 0)\n\ndef enable_scheduler():\n\ttoggle_scheduler(True)\n\ndef disable_scheduler():\n\ttoggle_scheduler(False)\n\ndef get_errors(from_date, to_date, limit):\n\terrors = frappe.db.sql(\"\"\"select modified, method, error from `tabError Log`\n\t\twhere date(modified) between %s and %s\n\t\tand error not like '%%[Errno 110] Connection timed out%%'\n\t\torder by modified limit %s\"\"\", (from_date, to_date, limit), as_dict=True)\n\treturn [\"\"\"<p>Time: {modified}</p><pre><code>Method: {method}\\n{error}</code></pre>\"\"\".format(**e)\n\t\tfor e in errors]\n\ndef get_error_report(from_date=None, to_date=None, limit=10):\n\tfrom frappe.utils import get_url, now_datetime, add_days\n\n\tif not from_date:\n\t\tfrom_date = add_days(now_datetime().date(), -1)\n\tif not to_date:\n\t\tto_date = add_days(now_datetime().date(), -1)\n\n\terrors = get_errors(from_date, to_date, limit)\n\n\tif errors:\n\t\treturn 1, \"\"\"<h4>Error Logs (max {limit}):</h4>\n\t\t\t<p>URL: <a href=\"{url}\" target=\"_blank\">{url}</a></p><hr>{errors}\"\"\".format(\n\t\t\tlimit=limit, url=get_url(), errors=\"<hr>\".join(errors))\n\telse:\n\t\treturn 0, \"<p>No error logs</p>\"\n\ndef scheduler_task(site, event, handler, now=False):\n\t'''This is a wrapper function that runs a hooks.scheduler_events method'''\n\tfrappe.logger(__name__).info('running {handler} for {site} for event: {event}'.format(handler=handler, site=site, event=event))\n\ttry:\n\t\tif not now:\n\t\t\tfrappe.connect(site=site)\n\n\t\tfrappe.flags.in_scheduler = True\n\t\tfrappe.get_attr(handler)()\n\n\texcept Exception:\n\t\tfrappe.db.rollback()\n\t\ttraceback = log(handler, \"Method: {event}, Handler: {handler}\".format(event=event, handler=handler))\n\t\tfrappe.logger(__name__).error(traceback)\n\t\traise\n\n\telse:\n\t\tfrappe.db.commit()\n\n\tfrappe.logger(__name__).info('ran {handler} for {site} for event: {event}'.format(handler=handler, site=site, event=event))\n\n\ndef reset_enabled_scheduler_events(login_manager):\n\tif login_manager.info.user_type == \"System User\":\n\t\ttry:\n\t\t\tfrappe.db.set_global('enabled_scheduler_events', None)\n\t\texcept MySQLdb.OperationalError as e:\n\t\t\tif e.args[0]==1205:\n\t\t\t\tfrappe.log_error(frappe.get_traceback(), \"Error in reset_enabled_scheduler_events\")\n\t\t\telse:\n\t\t\t\traise\n\t\telse:\n\t\t\tis_dormant = frappe.conf.get('dormant')\n\t\t\tif is_dormant:\n\t\t\t\tupdate_site_config('dormant', 'None')\n\ndef disable_scheduler_on_expiry():\n\tif has_expired():\n\t\tdisable_scheduler()\n\ndef restrict_scheduler_events_if_dormant():\n\tif is_dormant():\n\t\trestrict_scheduler_events()\n\t\tupdate_site_config('dormant', True)\n\ndef restrict_scheduler_events(*args, **kwargs):\n\tval = json.dumps([\"hourly\", \"hourly_long\", \"daily\", \"daily_long\", \"weekly\", \"weekly_long\", \"monthly\", \"monthly_long\"])\n\tfrappe.db.set_global('enabled_scheduler_events', val)\n\ndef is_dormant(since = 345600):\n\tlast_active = get_datetime(get_last_active())\n\t# Get now without tz info\n\tnow = now_datetime().replace(tzinfo=None)\n\ttime_since_last_active = now - last_active\n\tif time_since_last_active.total_seconds() > since:  # 4 days\n\t\treturn True\n\treturn False\n\ndef get_last_active():\n\treturn frappe.db.sql(\"\"\"select max(ifnull(last_active, \"2000-01-01 00:00:00\")) from `tabUser`\n\t\twhere user_type = 'System User' and name not in ({standard_users})\"\"\"\\\n\t\t.format(standard_users=\", \".join([\"%s\"]*len(STANDARD_USERS))),\n\t\tSTANDARD_USERS)[0][0]\n", "path": "frappe/utils/scheduler.py"}]}