def enroll_student(source_name): """Creates a Student Record and returns a Program Enrollment. :param source_name: Student Applicant. """ dataent.publish_realtime('enroll_student_progress', {"progress": [1, 4]}, user=dataent.session.user) student = get_mapped_doc("Student Applicant", source_name, { "Student Applicant": { "doctype": "Student", "field_map": { "name": "student_applicant" } } }, ignore_permissions=True) student.save() program_enrollment = dataent.new_doc("Program Enrollment") program_enrollment.student = student.name program_enrollment.student_name = student.title program_enrollment.program = dataent.db.get_value("Student Applicant", source_name, "program") dataent.publish_realtime('enroll_student_progress', {"progress": [4, 4]}, user=dataent.session.user) return program_enrollment
def run_background(prepared_report): instance = dataent.get_doc("Prepared Report", prepared_report) report = dataent.get_doc("Report", instance.ref_report_doctype) try: report.custom_columns = [] if report.report_type == 'Custom Report': custom_report_doc = report reference_report = custom_report_doc.reference_report report = dataent.get_doc("Report", reference_report) report.custom_columns = custom_report_doc.json result = generate_report_result(report, filters=instance.filters, user=instance.owner) create_json_gz_file(result['result'], 'Prepared Report', instance.name) instance.status = "Completed" instance.columns = json.dumps(result["columns"]) instance.report_end_time = dataent.utils.now() instance.save(ignore_permissions=True) except Exception: dataent.log_error(dataent.get_traceback()) instance = dataent.get_doc("Prepared Report", prepared_report) instance.status = "Error" instance.error_message = dataent.get_traceback() instance.save(ignore_permissions=True) dataent.publish_realtime('report_generated', { "report_name": instance.report_name, "name": instance.name }, user=dataent.session.user)
def on_update(self): if not self.is_new(): before = self.get_doc_before_save() after = self diff = dictify(get_diff(before, after)) if diff: update = {} for changed in diff.changed: field, old, new = changed if field == 'last_message': new = chat_message.get(new) update.update({field: new}) if diff.added or diff.removed: update.update(dict(users=[u.user for u in self.users])) update = dict(room=self.name, data=update) dataent.publish_realtime('dataent.chat.room:update', update, room=self.name, after_commit=True)
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 send(user, room, content, type="Content"): mess = get_new_chat_message(user, room, content, type) dataent.publish_realtime('dataent.chat.message:create', mess, room=room, after_commit=True)
def setup_complete(args): """Calls hooks for `setup_wizard_complete`, sets home page as `desktop` and clears cache. If wizard breaks, calls `setup_wizard_exception` hook""" # Setup complete: do not throw an exception, let the user continue to desk if cint(dataent.db.get_single_value('System Settings', 'setup_complete')): return args = parse_args(args) stages = get_setup_stages(args) try: current_task = None for idx, stage in enumerate(stages): dataent.publish_realtime('setup_task', {"progress": [idx, len(stages)], "stage_status": stage.get('status')}, user=dataent.session.user) for task in stage.get('tasks'): current_task = task task.get('fn')(task.get('args')) except Exception: handle_setup_exception(args) return {'status': 'fail', 'fail': current_task.get('fail_msg')} else: run_setup_success(args) return {'status': 'ok'}
def generate_fee(fee_schedule): doc = dataent.get_doc("Fee Schedule", fee_schedule) error = False total_records = sum([int(d.total_students) for d in doc.student_groups]) created_records = 0 if not total_records: dataent.throw(_("Please setup Students under Student Groups")) for d in doc.student_groups: students = get_students(d.student_group, doc.academic_year, doc.academic_term, doc.student_category) for student in students: try: fees_doc = get_mapped_doc( "Fee Schedule", fee_schedule, { "Fee Schedule": { "doctype": "Fees", "field_map": { "name": "Fee Schedule" } } }) fees_doc.student = student.student fees_doc.student_name = student.student_name fees_doc.program = student.program fees_doc.student_batch = student.student_batch_name fees_doc.send_payment_request = doc.send_email fees_doc.save() fees_doc.submit() created_records += 1 dataent.publish_realtime("fee_schedule_progress", { "progress": str(int(created_records * 100 / total_records)) }, user=dataent.session.user) except Exception as e: error = True err_msg = dataent.local.message_log and "\n\n".join( dataent.local.message_log) or cstr(e) if error: dataent.db.rollback() dataent.db.set_value("Fee Schedule", fee_schedule, "fee_creation_status", "Failed") dataent.db.set_value("Fee Schedule", fee_schedule, "error_log", err_msg) else: dataent.db.set_value("Fee Schedule", fee_schedule, "fee_creation_status", "Successful") dataent.db.set_value("Fee Schedule", fee_schedule, "error_log", None) dataent.publish_realtime("fee_schedule_progress", { "progress": "100", "reload": 1 }, user=dataent.session.user)
def publish_progress(achieved, reload=False): if data_import_doc: dataent.publish_realtime( "data_import_progress", { "progress": str(int(100.0 * achieved / total)), "data_import": data_import_doc.name, "reload": reload }, user=dataent.session.user)
def make_invoices(self): names = [] mandatory_error_msg = _( "Row {0}: {1} is required to create the Opening {2} Invoices") if not self.company: dataent.throw(_("Please select the Company")) for row in self.invoices: if not row.qty: row.qty = 1.0 # always mandatory fields for the invoices if not row.temporary_opening_account: row.temporary_opening_account = get_temporary_opening_account( self.company) row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier" # Allow to create invoice even if no party present in customer or supplier. if not dataent.db.exists(row.party_type, row.party): if self.create_missing_party: self.add_party(row.party_type, row.party) else: dataent.throw( _("{0} {1} does not exist.").format( dataent.bold(row.party_type), dataent.bold(row.party))) if not row.item_name: row.item_name = _("Opening Invoice Item") if not row.posting_date: row.posting_date = nowdate() if not row.due_date: row.due_date = nowdate() for d in ("Party", "Outstanding Amount", "Temporary Opening Account"): if not row.get(scrub(d)): dataent.throw( mandatory_error_msg.format(row.idx, _(d), self.invoice_type)) args = self.get_invoice_dict(row=row) if not args: continue doc = dataent.get_doc(args).insert() doc.submit() names.append(doc.name) if len(self.invoices) > 5: dataent.publish_realtime( "progress", dict(progress=[row.idx, len(self.invoices)], title=_('Creating {0}').format(doc.doctype)), user=dataent.session.user) return names
def generate_csrf_token(): dataent.local.session.data.csrf_token = dataent.generate_hash() dataent.local.session_obj.update(force=True) # send sid and csrf token to the user # handles the case when a user logs in again from another tab # and it leads to invalid request in the current tab dataent.publish_realtime(event="csrf_generated", message={"sid": dataent.local.session.sid, "csrf_token": dataent.local.session.data.csrf_token}, user=dataent.session.user, after_commit=True)
def reload_linked_analysis(self): linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis'] required_fields = ['location', 'name', 'collection_datetime'] output = {} for doctype in linked_doctypes: output[doctype] = dataent.get_all(doctype, fields=required_fields) output['Location'] = [] for location in self.linked_location: output['Location'].append(dataent.get_doc('Location', location.location)) dataent.publish_realtime("List of Linked Docs", output, user=dataent.session.user)
def _close_the_doc(start_time, key, console_dump, status, user): time_taken = dataent.utils.time.time() - start_time final_console_dump = '' console_dump = console_dump.split('\n\r') for i in console_dump: i = i.split('\r') final_console_dump += '\n' + i[-1] dataent.set_value('Bench Manager Command', key, 'console', final_console_dump) dataent.set_value('Bench Manager Command', key, 'status', status) dataent.set_value('Bench Manager Command', key, 'time_taken', time_taken) dataent.publish_realtime(key, '\n\n' + status + '!\nThe operation took ' + str(time_taken) + ' seconds', user=user)
def seen(message, user=None): authenticate(user) has_message = dataent.db.exists('Chat Message', message) if has_message: mess = dataent.get_doc('Chat Message', message) mess.add_seen(user) room = mess.room resp = dict(message=message, data=dict(seen=json.loads(mess._seen))) dataent.publish_realtime('dataent.chat.message:update', resp, room=room, after_commit=True)
def on_trash(self): if (not self.flags.ignore_permissions and self.communication_type == "Comment" and self.comment_type != "Comment"): # prevent deletion of auto-created comments if not ignore_permissions dataent.throw( _("Sorry! You cannot delete auto-generated comments")) if self.communication_type in ("Communication", "Comment"): # send delete comment to listening clients dataent.publish_realtime('delete_communication', self.as_dict(), doctype=self.reference_doctype, docname=self.reference_name, after_commit=True) # delete the comments from _comment on_trash(self)
def publish_realtime(context, event, message, room, user, doctype, docname, after_commit): "Publish realtime event from bench" from dataent import publish_realtime for site in context.sites: try: dataent.init(site=site) dataent.connect() publish_realtime(event, message=message, room=room, user=user, doctype=doctype, docname=docname, after_commit=after_commit) dataent.db.commit() finally: dataent.destroy()
def on_update(self): if not self.is_new(): b, a = self.get_doc_before_save(), self diff = dictify(get_diff(a, b)) if diff: user = session.user fields = [changed[0] for changed in diff.changed] if 'status' in fields: rooms = chat_room.get(user, filters = ['Chat Room', 'type', '=', 'Direct']) update = dict(user = user, data = dict(status = self.status)) for room in rooms: dataent.publish_realtime('dataent.chat.profile:update', update, room = room.name, after_commit = True) if 'enable_chat' in fields: update = dict(user = user, data = dict(enable_chat = bool(self.enable_chat))) dataent.publish_realtime('dataent.chat.profile:update', update, user = user, after_commit = True)
def notify_update(self): """Publish realtime that the current document is modified""" dataent.publish_realtime("doc_update", { "modified": self.modified, "doctype": self.doctype, "name": self.name }, doctype=self.doctype, docname=self.name, after_commit=True) if not self.meta.get("read_only") and not self.meta.get("issingle") and \ not self.meta.get("istable"): data = { "doctype": self.doctype, "name": self.name, "user": dataent.session.user } dataent.publish_realtime("list_update", data, after_commit=True)
def clear_notifications(user=None): if dataent.flags.in_install: return config = get_notification_config() for_doctype = list( config.get('for_doctype')) if config.get('for_doctype') else [] for_module = list( config.get('for_module')) if config.get('for_module') else [] groups = for_doctype + for_module cache = dataent.cache() for name in groups: if user: cache.hdel("notification_count:" + name, user) else: cache.delete_key("notification_count:" + name) dataent.publish_realtime('clear_notifications')
def enqueue_next_page(self): mapping = self.get_mapping(self.current_mapping) percent_complete = self.percent_complete + (100.0 / self.total_pages) fields = dict( percent_complete = percent_complete ) if self.current_mapping_action == 'Insert': start = self.current_mapping_start + mapping.page_length fields['current_mapping_start'] = start elif self.current_mapping_action == 'Delete': delete_start = self.current_mapping_delete_start + mapping.page_length fields['current_mapping_delete_start'] = delete_start self.db_set(fields, notify=True, commit=True) if(percent_complete < 100): dataent.publish_realtime(self.trigger_name, {"progress_percent": percent_complete}, user=dataent.session.user) dataent.enqueue_doc(self.doctype, self.name, 'run_current_mapping', now=dataent.flags.in_test)
def complete(self): fields = dict() push_failed = self.get_log('push_failed', []) pull_failed = self.get_log('pull_failed', []) status = 'Partial Success' if not push_failed and not pull_failed: status = 'Success' fields['percent_complete'] = 100 fields['status'] = status self.db_set(fields, notify=True, commit=True) self.execute_postprocess(status) dataent.publish_realtime(self.trigger_name, {"progress_percent": 100}, user=dataent.session.user)
def create_student_groups(self): if not self.courses: dataent.throw(_("""No Student Groups created.""")) l = len(self.courses) for d in self.courses: if not d.student_group_name: dataent.throw( _("""Student Group Name is mandatory in row {0}""".format( d.idx))) if d.group_based_on == "Course" and not d.course: dataent.throw( _("""Course is mandatory in row {0}""".format(d.idx))) if d.group_based_on == "Batch" and not d.batch: dataent.throw( _("""Batch is mandatory in row {0}""".format(d.idx))) dataent.publish_realtime('student_group_creation_progress', {"progress": [d.idx, l]}, user=dataent.session.user) student_group = dataent.new_doc("Student Group") student_group.student_group_name = d.student_group_name student_group.group_based_on = d.group_based_on student_group.program = self.program student_group.course = d.course student_group.batch = d.batch student_group.max_strength = d.max_strength student_group.academic_term = self.academic_term student_group.academic_year = self.academic_year student_list = get_students(self.academic_year, d.group_based_on, self.academic_term, self.program, d.batch, d.course) for student in student_list: student_group.append('students', student) student_group.save() dataent.msgprint(_("{0} Student Groups created.".format(l)))
def after_insert(self): if not (self.reference_doctype and self.reference_name): return if self.reference_doctype == "Communication" and self.sent_or_received == "Sent" and \ self.communication_type != 'Comment': dataent.db.set_value("Communication", self.reference_name, "status", "Replied") if self.communication_type in ("Communication", "Comment"): # send new comment to listening clients dataent.publish_realtime('new_communication', self.as_dict(), doctype=self.reference_doctype, docname=self.reference_name, after_commit=True) if self.communication_type == "Comment": notify_mentions(self) elif self.communication_type in ("Chat", "Notification", "Bot"): if self.reference_name == dataent.session.user: message = self.as_dict() message['broadcast'] = True dataent.publish_realtime('new_message', message, after_commit=True) else: # reference_name contains the user who is addressed in the messages' page comment dataent.publish_realtime('new_message', self.as_dict(), user=self.reference_name, after_commit=True)
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 enroll_students(self): total = len(self.students) for i, stud in enumerate(self.students): dataent.publish_realtime("program_enrollment_tool", dict(progress=[i + 1, total]), user=dataent.session.user) if stud.student: prog_enrollment = dataent.new_doc("Program Enrollment") prog_enrollment.student = stud.student prog_enrollment.student_name = stud.student_name prog_enrollment.program = self.new_program prog_enrollment.academic_year = self.new_academic_year prog_enrollment.academic_term = self.new_academic_term prog_enrollment.student_batch_name = stud.student_batch_name if stud.student_batch_name else self.new_student_batch prog_enrollment.save() elif stud.student_applicant: prog_enrollment = enroll_student(stud.student_applicant) prog_enrollment.academic_year = self.academic_year prog_enrollment.academic_term = self.academic_term prog_enrollment.student_batch_name = stud.student_batch_name if stud.student_batch_name else self.new_student_batch prog_enrollment.save() dataent.msgprint(_("{0} Students have been enrolled").format(total))
def set_delivery_status(self, commit=False): '''Look into the status of Email Queue linked to this Communication and set the Delivery Status of this Communication''' delivery_status = None status_counts = Counter( dataent.db.sql_list( '''select status from `tabEmail Queue` where communication=%s''', self.name)) if self.sent_or_received == "Received": return if status_counts.get('Not Sent') or status_counts.get('Sending'): delivery_status = 'Sending' elif status_counts.get('Error'): delivery_status = 'Error' elif status_counts.get('Expired'): delivery_status = 'Expired' elif status_counts.get('Sent'): delivery_status = 'Sent' if delivery_status: self.db_set('delivery_status', delivery_status) dataent.publish_realtime('update_communication', self.as_dict(), doctype=self.reference_doctype, docname=self.reference_name, after_commit=True) # for list views and forms self.notify_update() if commit: dataent.db.commit()
def on_trash(self): # pylint: disable=no-self-use dataent.cache().delete_value('user_permissions') dataent.publish_realtime('update_user_permissions')
def create(kind, owner, users=None, name=None): authenticate(owner) users = safe_json_loads(users) create = True if kind == 'Visitor': room = squashify( dataent.db.sql(""" SELECT name FROM `tabChat Room` WHERE owner = "{owner}" """.format(owner=owner), as_dict=True)) if room: room = dataent.get_doc('Chat Room', room.name) create = False if create: room = dataent.new_doc('Chat Room') room.type = kind room.owner = owner room.room_name = name dusers = [] if kind != 'Visitor': if users: users = listify(users) for user in users: duser = dataent.new_doc('Chat Room User') duser.user = user dusers.append(duser) room.users = dusers else: dsettings = dataent.get_single('Website Settings') room.room_name = dsettings.chat_room_name users = [user for user in room.users] if hasattr(room, 'users') else [] for user in dsettings.chat_operators: if user.user not in users: # appending user to room.users will remove the user from chat_operators # this is undesirable, create a new Chat Room User instead chat_room_user = { "doctype": "Chat Room User", "user": user.user } room.append('users', chat_room_user) room.save(ignore_permissions=True) room = get(owner, rooms=room.name) users = [room.owner] + [u for u in room.users] for u in users: dataent.publish_realtime('dataent.chat.room:create', room, user=u, after_commit=True) return room
def run_command(commands, doctype, key, cwd='..', docname=' ', after_command=None): verify_whitelisted_call() start_time = dataent.utils.time.time() console_dump = "" logged_command = " && ".join(commands) logged_command += " " #to make sure passwords at the end of the commands are also hidden sensitive_data = [ "--mariadb-root-password", "--admin-password", "--root-password" ] for password in sensitive_data: logged_command = re.sub("{password} .*? ".format(password=password), '', logged_command, flags=re.DOTALL) doc = dataent.get_doc({ 'doctype': 'Bench Manager Command', 'key': key, 'source': doctype + ': ' + docname, 'command': logged_command, 'console': console_dump, 'status': 'Ongoing' }) doc.insert() dataent.db.commit() dataent.publish_realtime(key, "Executing Command:\n{logged_command}\n\n".format( logged_command=logged_command), user=dataent.session.user) try: for command in commands: terminal = Popen(shlex.split(command), stdin=PIPE, stdout=PIPE, stderr=STDOUT, cwd=cwd) for c in iter(lambda: safe_decode(terminal.stdout.read(1)), ''): dataent.publish_realtime(key, c, user=dataent.session.user) console_dump += c if terminal.wait(): _close_the_doc(start_time, key, console_dump, status='Failed', user=dataent.session.user) else: _close_the_doc(start_time, key, console_dump, status='Success', user=dataent.session.user) except Exception as e: _close_the_doc(start_time, key, "{} \n\n{}".format(e, console_dump), status='Failed', user=dataent.session.user) finally: dataent.db.commit() # hack: dataent.db.commit() to make sure the log created is robust, # and the _refresh throws an error if the doc is deleted dataent.enqueue('bench_manager.bench_manager.utils._refresh', doctype=doctype, docname=docname, commands=commands)
def migrate(verbose=True, rebuild_website=False): '''Migrate all apps to the latest version, will: - run before migrate hooks - run patches - sync doctypes (schema) - sync fixtures - sync desktop icons - sync web pages (from /www) - sync web pages (from /www) - run after migrate hooks ''' touched_tables_file = dataent.get_site_path('touched_tables.json') if os.path.exists(touched_tables_file): os.remove(touched_tables_file) try: dataent.flags.touched_tables = set() dataent.flags.in_migrate = True clear_global_cache() #run before_migrate hooks for app in dataent.get_installed_apps(): for fn in dataent.get_hooks('before_migrate', app_name=app): dataent.get_attr(fn)() # run patches dataent.modules.patch_handler.run_all() # sync dataent.model.sync.sync_all(verbose=verbose) dataent.translate.clear_cache() sync_fixtures() sync_customizations() sync_desktop_icons() sync_languages() dataent.get_doc('Portal Settings', 'Portal Settings').sync_menu() # syncs statics render.clear_cache() # add static pages to global search router.sync_global_search() #run after_migrate hooks for app in dataent.get_installed_apps(): for fn in dataent.get_hooks('after_migrate', app_name=app): dataent.get_attr(fn)() dataent.db.commit() clear_notifications() dataent.publish_realtime("version-update") dataent.flags.in_migrate = False finally: with open(touched_tables_file, 'w') as f: json.dump(list(dataent.flags.touched_tables), f, sort_keys=True, indent=4) dataent.flags.touched_tables.clear()
def after_command(self, commands=None): dataent.publish_realtime("Bench-Manager:reload-page")