<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
import calendar
from frappe import _
from frappe.desk.form import assign_to
from frappe.utils.jinja import validate_template
from dateutil.relativedelta import relativedelta
from frappe.utils.user import get_system_managers
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
from frappe.model.document import Document
from frappe.core.doctype.communication.email import make
from frappe.utils.background_jobs import get_jobs

month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}


class AutoRepeat(Document):
	def validate(self):
		self.update_status()
		self.validate_reference_doctype()
		self.validate_dates()
		self.validate_email_id()
		self.set_dates()
		self.update_auto_repeat_id()
		self.unlink_if_applicable()

		validate_template(self.subject or "")
		validate_template(self.message or "")

	def before_insert(self):
		today_date = today()
		if self.start_date <= today_date:
			self.start_date = today_date

	def after_save(self):
		frappe.get_doc(self.reference_doctype, self.reference_document).notify_update()

	def on_trash(self):
		frappe.db.set_value(self.reference_doctype, self.reference_document, {
			'auto_repeat': self.name
		}, 'auto_repeat', '')

	def set_dates(self):
		if self.disabled:
			self.next_schedule_date = None
		else:
			self.next_schedule_date = get_next_schedule_date(self.start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, self.end_date)

	def unlink_if_applicable(self):
		if self.status == 'Completed' or self.disabled:
			frappe.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", '')

	def validate_reference_doctype(self):
		if not frappe.get_meta(self.reference_doctype).has_field('auto_repeat'):
			frappe.throw(_("Enable Allow Auto Repeat for the doctype {0} in Customize Form").format(self.reference_doctype))

	def validate_dates(self):
		self.validate_from_to_dates('start_date', 'end_date')

		if self.end_date == self.start_date:
			frappe.throw(_('{0} should not be same as {1}').format(frappe.bold('End Date'), frappe.bold('Start Date')))

	def validate_email_id(self):
		if self.notify_by_email:
			if self.recipients:
				email_list = split_emails(self.recipients.replace("\n", ""))
				from frappe.utils import validate_email_address

				for email in email_list:
					if not validate_email_address(email):
						frappe.throw(_("{0} is an invalid email address in 'Recipients'").format(email))
			else:
				frappe.throw(_("'Recipients' not specified"))

	def update_auto_repeat_id(self):
		#check if document is already on auto repeat
		auto_repeat = frappe.db.get_value(self.reference_doctype, self.reference_document, "auto_repeat")
		if auto_repeat and auto_repeat != self.name:
			frappe.throw(_("The {0} is already on auto repeat {1}").format(self.reference_document, auto_repeat))
		else:
			frappe.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", self.name)

	def update_status(self):
		if self.disabled:
			self.status = "Disabled"
		elif self.is_completed():
			self.status = "Completed"
		else:
			self.status = "Active"

	def is_completed(self):
		return self.end_date and getdate(self.end_date) < getdate(today())

	def get_auto_repeat_schedule(self):
		schedule_details = []
		start_date = getdate(self.start_date)
		end_date = getdate(self.end_date)
		today = frappe.utils.datetime.date.today()

		if start_date < today:
			start_date = today

		if not self.end_date:
			start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day)
			row = {
				"reference_document": self.reference_document,
				"frequency": self.frequency,
				"next_scheduled_date": start_date
			}
			schedule_details.append(row)
			start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day)

		if self.end_date:
			start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day)
			while (getdate(start_date) < getdate(end_date)):
				row = {
					"reference_document" : self.reference_document,
					"frequency" : self.frequency,
					"next_scheduled_date" : start_date
				}
				schedule_details.append(row)
				start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, end_date)


		return schedule_details


def get_next_schedule_date(start_date, frequency, repeat_on_day, repeat_on_last_day = False, end_date = None):
	mcount = month_map.get(frequency)
	if mcount and repeat_on_last_day:
		last_day = 31
		next_date = get_next_date(start_date, mcount, last_day)
	elif mcount and repeat_on_day:
		next_date = get_next_date(start_date, mcount, repeat_on_day)
	elif mcount:
		next_date = get_next_date(start_date, mcount)
	else:
		days = 7 if frequency == 'Weekly' else 1
		next_date = add_days(start_date, days)

	# if end_date and getdate(next_date) > getdate(end_date):
	# 	next_date = None
	return next_date

def make_auto_repeat_entry():
	enqueued_method = 'frappe.automation.doctype.auto_repeat.auto_repeat.create_repeated_entries'
	jobs = get_jobs()

	if not jobs or enqueued_method not in jobs[frappe.local.site]:
		date = today()
		data = get_auto_repeat_entries(date)
		frappe.enqueue(enqueued_method, data=data)

def create_repeated_entries(data):
	for d in data:
		doc = frappe.get_doc('Auto Repeat', d.name)

		current_date = getdate(today())
		schedule_date = getdate(doc.next_schedule_date)

		while schedule_date <= current_date and not doc.disabled:
			create_documents(doc, schedule_date)
			schedule_date = get_next_schedule_date(schedule_date, doc.frequency, doc.repeat_on_day, doc.repeat_on_last_day, doc.end_date)

			if schedule_date and not doc.disabled:
				frappe.db.set_value('Auto Repeat', doc.name, 'next_schedule_date', schedule_date)
				frappe.db.commit()

def get_auto_repeat_entries(date=None):
	date = today()
	return frappe.db.get_all('Auto Repeat', filters=[
		['next_schedule_date', '<=', date],
		['status', '=', 'Active']
	])
	# return frappe.db.sql(""" select * from `tabAuto Repeat`
	# 		where next_schedule_date <=%s
	# 		and reference_document is not null and reference_document != ''
	# 		and next_schedule_date <= ifnull(end_date, '2199-12-31')
	# 		and disabled = 0 and status != 'Disabled' """, (date), as_dict=1)

def set_auto_repeat_as_completed():
	auto_repeat = frappe.get_all("Auto Repeat", filters = {'status': ['!=', 'Disabled']})
	for entry in auto_repeat:
		doc = frappe.get_doc("Auto Repeat", entry.name)
		if doc.is_completed():
			doc.status = 'Completed'
			doc.save()

def create_documents(data, schedule_date):
	try:
		doc = make_new_document(data, schedule_date)
		if data.notify_by_email and data.recipients:
			print_format = data.print_format or "Standard"
			send_notification(doc, data, print_format=print_format)

		frappe.db.commit()
	except Exception:<commit_msg>fix: next schedule date logic<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe
import calendar
from frappe import _
from frappe.desk.form import assign_to
from frappe.utils.jinja import validate_template
from dateutil.relativedelta import relativedelta
from frappe.utils.user import get_system_managers
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
from frappe.model.document import Document
from frappe.core.doctype.communication.email import make
from frappe.utils.background_jobs import get_jobs

month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}


class AutoRepeat(Document):
	def validate(self):
		self.update_status()
		self.validate_reference_doctype()
		self.validate_dates()
		self.validate_email_id()
		self.set_dates()
		self.update_auto_repeat_id()
		self.unlink_if_applicable()

		validate_template(self.subject or "")
		validate_template(self.message or "")

	def before_insert(self):
		today_date = today()
		if self.start_date <= today_date:
			self.start_date = today_date

	def after_save(self):
		frappe.get_doc(self.reference_doctype, self.reference_document).notify_update()

	def on_trash(self):
		frappe.db.set_value(self.reference_doctype, self.reference_document, {
			'auto_repeat': self.name
		}, 'auto_repeat', '')

	def set_dates(self):
		if self.disabled:
			self.next_schedule_date = None
		else:
			self.next_schedule_date = get_next_schedule_date(self.start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, self.end_date)

	def unlink_if_applicable(self):
		if self.status == 'Completed' or self.disabled:
			frappe.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", '')

	def validate_reference_doctype(self):
		if not frappe.get_meta(self.reference_doctype).has_field('auto_repeat'):
			frappe.throw(_("Enable Allow Auto Repeat for the doctype {0} in Customize Form").format(self.reference_doctype))

	def validate_dates(self):
		self.validate_from_to_dates('start_date', 'end_date')

		if self.end_date == self.start_date:
			frappe.throw(_('{0} should not be same as {1}').format(frappe.bold('End Date'), frappe.bold('Start Date')))

	def validate_email_id(self):
		if self.notify_by_email:
			if self.recipients:
				email_list = split_emails(self.recipients.replace("\n", ""))
				from frappe.utils import validate_email_address

				for email in email_list:
					if not validate_email_address(email):
						frappe.throw(_("{0} is an invalid email address in 'Recipients'").format(email))
			else:
				frappe.throw(_("'Recipients' not specified"))

	def update_auto_repeat_id(self):
		#check if document is already on auto repeat
		auto_repeat = frappe.db.get_value(self.reference_doctype, self.reference_document, "auto_repeat")
		if auto_repeat and auto_repeat != self.name:
			frappe.throw(_("The {0} is already on auto repeat {1}").format(self.reference_document, auto_repeat))
		else:
			frappe.db.set_value(self.reference_doctype, self.reference_document, "auto_repeat", self.name)

	def update_status(self):
		if self.disabled:
			self.status = "Disabled"
		elif self.is_completed():
			self.status = "Completed"
		else:
			self.status = "Active"

	def is_completed(self):
		return self.end_date and getdate(self.end_date) < getdate(today())

	def get_auto_repeat_schedule(self):
		schedule_details = []
		start_date = getdate(self.start_date)
		end_date = getdate(self.end_date)
		today = frappe.utils.datetime.date.today()

		if start_date < today:
			start_date = today

		if not self.end_date:
			start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day)
			row = {
				"reference_document": self.reference_document,
				"frequency": self.frequency,
				"next_scheduled_date": start_date
			}
			schedule_details.append(row)
			start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day)

		if self.end_date:
			start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day)
			while (getdate(start_date) < getdate(end_date)):
				row = {
					"reference_document" : self.reference_document,
					"frequency" : self.frequency,
					"next_scheduled_date" : start_date
				}
				schedule_details.append(row)
				start_date = get_next_schedule_date(start_date, self.frequency, self.repeat_on_day, self.repeat_on_last_day, end_date)


		return schedule_details


def get_next_schedule_date(start_date, frequency, repeat_on_day, repeat_on_last_day = False, end_date = None):
	mcount = month_map.get(frequency)
	if mcount and repeat_on_last_day:
		last_day = 31
		next_date = get_next_date(start_date, mcount, last_day)
	elif mcount and repeat_on_day:
		next_date = get_next_date(start_date, mcount, repeat_on_day)
	elif mcount:
		next_date = get_next_date(start_date, mcount)
	else:
		days = 7 if frequency == 'Weekly' else 1
		next_date = add_days(start_date, days)

	return next_date

def make_auto_repeat_entry():
	enqueued_method = 'frappe.automation.doctype.auto_repeat.auto_repeat.create_repeated_entries'
	jobs = get_jobs()

	if not jobs or enqueued_method not in jobs[frappe.local.site]:
		date = today()
		data = get_auto_repeat_entries(date)
		frappe.enqueue(enqueued_method, data=data)

def create_repeated_entries(data):
	for d in data:
		doc = frappe.get_doc('Auto Repeat', d.name)

		current_date = getdate(today())
		schedule_date = getdate(doc.next_schedule_date)

		while schedule_date <= current_date and not doc.disabled:
			create_documents(doc, schedule_date)
			schedule_date = get_next_schedule_date(schedule_date, doc.frequency, doc.repeat_on_day, doc.repeat_on_last_day, doc.end_date)

			if schedule_date and not doc.disabled:
				frappe.db.set_value('Auto Repeat', doc.name, 'next_schedule_date', schedule_date)
				frappe.db.commit()

def get_auto_repeat_entries(date=None):
	date = today()
	return frappe.db.get_all('Auto Repeat', filters=[
		['next_schedule_date', '<=', date],
		['status', '=', 'Active']
	])

def set_auto_repeat_as_completed():
	auto_repeat = frappe.get_all("Auto Repeat", filters = {'status': ['!=', 'Disabled']})
	for entry in auto_repeat:
		doc = frappe.get_doc("Auto Repeat", entry.name)
		if doc.is_completed():
			doc.status = 'Completed'
			doc.save()

def create_documents(data, schedule_date):
	try:
		doc = make_new_document(data, schedule_date)
		if data.notify_by_email and data.recipients:
			print_format = data.print_format or "Standard"
			send_notification(doc, data, print_format=print_format)

		frappe.db.commit()
	except Exception:
{'fraction_non_alphanumeric': 0.10983323749281196, 'fraction_numerical': 0.00359401955146636, 'mean_word_length': 5.35296803652968, 'pattern_counts': {'":': 6, '<': 17, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6075502', 'n_tokens_mistral': 5232, 'n_tokens_neox': 4766, 'n_words': 980}