def process_workflow_actions(doc, state): workflow = get_workflow_name(doc.get('doctype')) if not workflow: return if state == "on_trash": clear_workflow_actions(doc.get('doctype'), doc.get('name')) return if is_workflow_action_already_created(doc): return clear_old_workflow_actions(doc) update_completed_workflow_actions(doc) clear_doctype_notifications('Workflow Action') next_possible_transitions = get_next_possible_transitions( workflow, get_doc_workflow_state(doc)) if not next_possible_transitions: return user_data_map = get_users_next_action_data(next_possible_transitions, doc) if not user_data_map: return create_workflow_actions_for_users(user_data_map.keys(), doc) if send_email_alert(workflow): enqueue(send_workflow_action_email, queue='short', users_data=list(user_data_map.values()), doc=doc)
def send_email(self): """send email with payment link""" email_args = { "recipients": self.email_to, "sender": None, "subject": self.subject, "message": self.get_message(), "now": True, "attachments": [ dataent.attach_print(self.reference_doctype, self.reference_name, file_name=self.reference_name, print_format=self.print_format) ] } enqueue(method=dataent.sendmail, queue='short', timeout=300, is_async=True, **email_args)
def import_data(data_import): dataent.db.set_value("Data Import", data_import, "import_status", "In Progress", update_modified=False) dataent.publish_realtime("data_import_progress", { "progress": "0", "data_import": data_import, "reload": True }, user=dataent.session.user) from dataent.core.page.background_jobs.background_jobs import get_info enqueued_jobs = [d.get("job_name") for d in get_info()] if data_import not in enqueued_jobs: enqueue(upload, queue='default', timeout=6000, event='data_import', job_name=data_import, data_import_doc=data_import, from_data_import="Yes", user=dataent.session.user)
def email_salary_slip(self): receiver = dataent.db.get_value("Employee", self.employee, "prefered_email") if receiver: email_args = { "recipients": [receiver], "message": _("Please see attachment"), "subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date), "attachments": [ dataent.attach_print(self.doctype, self.name, file_name=self.name) ], "reference_doctype": self.doctype, "reference_name": self.name } if not dataent.flags.in_test: enqueue(method=dataent.sendmail, queue='short', timeout=300, is_async=True, **email_args) else: dataent.sendmail(**email_args) else: msgprint( _("{0}: Employee email not found, hence email not sent"). format(self.employee_name))
def execute(): comm_records_count = dataent.db.count("Communication", {"comment_type": "Updated"}) if comm_records_count > 100000: enqueue(method=move_data_from_communication_to_activity_log, queue='short', now=True) else: move_data_from_communication_to_activity_log()
def notify(doc, print_html=None, print_format=None, attachments=None, recipients=None, cc=None, bcc=None, fetched_from_email_account=False): """Calls a delayed task 'sendmail' that enqueus email in Email Queue queue :param print_html: Send given value as HTML attachment :param print_format: Attach print format of parent document :param attachments: A list of filenames that should be attached when sending this email :param recipients: Email recipients :param cc: Send email as CC to :param bcc: Send email as BCC to :param fetched_from_email_account: True when pulling email, the notification shouldn't go to the main recipient """ recipients, cc, bcc = get_recipients_cc_and_bcc( doc, recipients, cc, bcc, fetched_from_email_account=fetched_from_email_account) if not recipients and not cc: return doc.emails_not_sent_to = set(doc.all_email_addresses) - set( doc.sent_email_addresses) if dataent.flags.in_test: # for test cases, run synchronously doc._notify(print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, cc=cc, bcc=None) else: check_email_limit(list(set(doc.sent_email_addresses))) enqueue(sendmail, queue="default", timeout=300, event="sendmail", communication_name=doc.name, print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, cc=cc, bcc=bcc, lang=dataent.local.lang, session=dataent.local.session, print_letterhead=dataent.flags.print_letterhead)
def send_workflow_action_email(users_data, doc): common_args = get_common_email_args(doc) message = common_args.pop('message', None) for d in users_data: email_args = { 'recipients': [d.get('email')], 'args': { 'actions': d.get('possible_actions'), 'message': message }, 'reference_name': doc.name, 'reference_doctype': doc.doctype } email_args.update(common_args) enqueue(method=dataent.sendmail, queue='short', **email_args)
def schedule_files_backup(user_email): from dataent.utils.background_jobs import enqueue, get_jobs queued_jobs = get_jobs(site=dataent.local.site, queue="long") method = 'dataent.desk.page.backups.backups.backup_files_and_notify_user' if method not in queued_jobs[dataent.local.site]: enqueue( "dataent.desk.page.backups.backups.backup_files_and_notify_user", queue='long', user_email=user_email) dataent.msgprint( _("Queued for backup. You will receive an email with the download link" )) else: dataent.msgprint( _("Backup job is already queued. You will receive an email with the download link" ))
def trigger(site, event, last=None, queued_jobs=(), now=False): """Trigger method in hooks.scheduler_events.""" queue = 'long' if event.endswith('_long') else 'short' timeout = queue_timeout[queue] if not queued_jobs and not now: queued_jobs = get_jobs(site=site, queue=queue) if dataent.flags.in_test: dataent.flags.ran_schedulers.append(event) events_from_hooks = get_scheduler_events(event) if not events_from_hooks: return events = events_from_hooks if not now: events = [] if event == "cron": for e in events_from_hooks: e = cron_map.get(e, e) if croniter.is_valid(e): if croniter(e, last).get_next( datetime) <= dataent.utils.now_datetime(): events.extend(events_from_hooks[e]) else: dataent.log_error("Cron string " + e + " is not valid", "Error triggering cron job") dataent.logger(__name__).error( 'Exception in Trigger Events for Site {0}, Cron String {1}' .format(site, e)) else: if croniter( cron_map[event], last).get_next(datetime) <= dataent.utils.now_datetime(): events.extend(events_from_hooks) for handler in events: if not now: if handler not in queued_jobs: enqueue(handler, queue, timeout, event) else: scheduler_task(site=site, event=event, handler=handler, now=True)
def test_job_timeout(self): job = enqueue(test_timeout, timeout=10) count = 5 while count > 0: count -= 1 time.sleep(5) if job.get_status() == 'failed': break self.assertTrue(job.is_failed)
def pull(now=False): """Will be called via scheduler, pull emails from all enabled Email accounts.""" if dataent.cache().get_value("workers:no-internet") == True: if test_internet(): dataent.cache().set_value("workers:no-internet", False) else: return queued_jobs = get_jobs(site=dataent.local.site, key='job_name')[dataent.local.site] for email_account in dataent.get_list("Email Account", filters={"enable_incoming": 1, "awaiting_password": 0}): if now: pull_from_email_account(email_account.name) else: # job_name is used to prevent duplicates in queue job_name = 'pull_from_email_account|{0}'.format(email_account.name) if job_name not in queued_jobs: enqueue(pull_from_email_account, 'short', event='all', job_name=job_name, email_account=email_account.name)
def queue_action(self, action, **kwargs): '''Run an action in background. If the action has an inner function, like _submit for submit, it will call that instead''' # call _submit instead of submit, so you can override submit to call # run_delayed based on some action # See: Stock Reconciliation if hasattr(self, '_' + action): action = '_' + action if file_lock.lock_exists(self.get_signature()): dataent.throw(_( 'This document is currently queued for execution. Please try again' ), title=_('Document Queued')) self.lock() enqueue('dataent.model.document.execute_action', doctype=self.doctype, name=self.name, action=action, **kwargs)
def send_emails(self): """send emails to leads and customers""" if self.email_sent: throw(_("Newsletter has already been sent")) self.recipients = self.get_recipients() if getattr(dataent.local, "is_ajax", False): self.validate_send() # using default queue with a longer timeout as this isn't a scheduled task enqueue(send_newsletter, queue='default', timeout=6000, event='send_newsletter', newsletter=self.name) else: self.queue_all() dataent.msgprint(_("Scheduled to send to {0} recipients").format(len(self.recipients))) dataent.db.set(self, "email_sent", 1) dataent.db.set(self, 'scheduled_to_send', len(self.recipients))
def take_backup_to_dropbox(retry_count=0, upload_db_backup=True): did_not_upload, error_log = [], [] try: if cint(dataent.db.get_value("Dropbox Settings", None, "enabled")): did_not_upload, error_log = backup_to_dropbox(upload_db_backup) if did_not_upload: raise Exception send_email(True, "Dropbox") except JobTimeoutException: if retry_count < 2: args = { "retry_count": retry_count + 1, "upload_db_backup": False #considering till worker timeout db backup is uploaded } enqueue("dataent.integrations.doctype.dropbox_settings.dropbox_settings.take_backup_to_dropbox", queue='long', timeout=1500, **args) except Exception: file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)] error_message = ("\n".join(file_and_error) + "\n" + dataent.get_traceback()) dataent.errprint(error_message) send_email(False, "Dropbox", error_message)
def create_fees(self): self.db_set("fee_creation_status", "In Process") dataent.publish_realtime("fee_schedule_progress", { "progress": "0", "reload": 1 }, user=dataent.session.user) total_records = sum( [int(d.total_students) for d in self.student_groups]) if total_records > 10: dataent.msgprint( _('''Fee records will be created in the background. In case of any error the error message will be updated in the Schedule.''') ) enqueue(generate_fee, queue='default', timeout=6000, event='generate_fee', fee_schedule=self.name) else: generate_fee(self.name)
def send_token_via_sms(otpsecret, token=None, phone_no=None): '''Send token as sms to user.''' try: from dataent.core.doctype.sms_settings.sms_settings import send_request except: return False if not phone_no: return False ss = dataent.get_doc('SMS Settings', 'SMS Settings') if not ss.sms_gateway_url: return False hotp = pyotp.HOTP(otpsecret) args = { ss.message_parameter: 'Your verification code is {}'.format(hotp.at(int(token))) } for d in ss.get("parameters"): args[d.parameter] = d.value args[ss.receiver_parameter] = phone_no sms_args = { 'params': args, 'gateway_url': ss.sms_gateway_url, 'use_post': ss.use_post } enqueue(method=send_request, queue='short', timeout=300, event=None, is_async=True, job_name=None, now=False, **sms_args) return True
def send_token_via_email(user, token, otp_secret, otp_issuer, subject=None, message=None): '''Send token to user as email.''' user_email = dataent.db.get_value('User', user, 'email') if not user_email: return False hotp = pyotp.HOTP(otp_secret) otp = hotp.at(int(token)) template_args = {'otp': otp, 'otp_issuer': otp_issuer} if not subject: subject = get_email_subject_for_2fa(template_args) if not message: message = get_email_body_for_2fa(template_args) email_args = { 'recipients': user_email, 'sender': None, 'subject': subject, 'message': message, 'header': [_('Verfication Code'), 'blue'], 'delayed': False, 'retry': 3 } enqueue(method=dataent.sendmail, queue='short', timeout=300, event=None, is_async=True, job_name=None, now=False, **email_args) return True
def reset_otp_secret(user): otp_issuer = dataent.db.get_value('System Settings', 'System Settings', 'otp_issuer_name') user_email = dataent.db.get_value('User', user, 'email') if dataent.session.user in ["Administrator", user]: dataent.defaults.clear_default(user + '_otplogin') dataent.defaults.clear_default(user + '_otpsecret') email_args = { 'recipients': user_email, 'sender': None, 'subject': 'OTP Secret Reset - {}'.format(otp_issuer or "Dataent Framework"), 'message': '<p>Your OTP secret on {} has been reset. If you did not perform this reset and did not request it, please contact your System Administrator immediately.</p>' .format(otp_issuer or "Dataent Framework"), 'delayed': False, 'retry': 3 } enqueue(method=dataent.sendmail, queue='short', timeout=300, event=None, is_async=True, job_name=None, now=False, **email_args) return dataent.msgprint( _("OTP Secret has been reset. Re-registration will be required on next login." )) else: return dataent.throw( _("OTP secret can only be reset by the Administrator."))
def take_backup(): "Enqueue longjob for taking backup to dropbox" enqueue("dataent.integrations.doctype.dropbox_settings.dropbox_settings.take_backup_to_dropbox", queue='long', timeout=1500) dataent.msgprint(_("Queued for backup. It may take a few minutes to an hour."))
def enqueue_report(self): enqueue(run_background, prepared_report=self.name, timeout=6000)