def attach_file(self, n): """attach a file from the `FileData` table""" from dataent.utils.file_manager import get_file res = get_file(n) if not res: return self.add_attachment(res[0], res[1])
def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None): """Prepare to make multipart MIME Email :param print_html: Send given value as HTML attachment. :param print_format: Attach print format of parent document.""" view_link = dataent.utils.cint( dataent.db.get_value("Print Settings", "Print Settings", "attach_view_link")) if print_format and view_link: doc.content += get_attach_link(doc, print_format) set_incoming_outgoing_accounts(doc) if not doc.sender: doc.sender = doc.outgoing_email_account.email_id if not doc.sender_full_name: doc.sender_full_name = doc.outgoing_email_account.name or _( "Notification") if doc.sender: # combine for sending to get the format 'Jane <*****@*****.**>' doc.sender = formataddr([doc.sender_full_name, doc.sender]) doc.attachments = [] if print_html or print_format: doc.attachments.append({ "print_format_attachment": 1, "doctype": doc.reference_doctype, "name": doc.reference_name, "print_format": print_format, "html": print_html }) if attachments: if isinstance(attachments, string_types): attachments = json.loads(attachments) for a in attachments: if isinstance(a, string_types): # is it a filename? try: # keep this for error handling file = get_file(a) # these attachments will be attached on-demand # and won't be stored in the message doc.attachments.append({"fid": a}) except IOError: dataent.throw(_("Unable to find attachment {0}").format(a)) else: doc.attachments.append(a)
def get_prepared_report_result(report, filters, dn="", user=None): latest_report_data = {} doc = None if dn: # Get specified dn doc = dataent.get_doc("Prepared Report", dn) else: # Only look for completed prepared reports with given filters. doc_list = dataent.get_all("Prepared Report", filters={ "status": "Completed", "filters": json.dumps(filters), "owner": user, "report_name": report.report_name }) if doc_list: # Get latest doc = dataent.get_doc("Prepared Report", doc_list[0]) if doc: try: # Prepared Report data is stored in a GZip compressed JSON file attached_file_name = dataent.db.get_value( "File", { "attached_to_doctype": doc.doctype, "attached_to_name": doc.name }, "name") compressed_content = get_file(attached_file_name)[1] uncompressed_content = gzip_decompress(compressed_content) data = json.loads(uncompressed_content) if data: columns = json.loads(doc.columns) if doc.columns else data[0] for column in columns: if isinstance(column, dict): column["label"] = _(column["label"]) latest_report_data = {"columns": columns, "result": data} except Exception: dataent.log_error(dataent.get_traceback()) dataent.delete_doc("Prepared Report", doc.name) dataent.db.commit() doc = None latest_report_data.update({"prepared_report": True, "doc": doc}) return latest_report_data
def read_csv_content_from_attached_file(doc): fileid = dataent.get_all("File", fields = ["name"], filters = {"attached_to_doctype": doc.doctype, "attached_to_name":doc.name}, order_by="creation desc") if fileid : fileid = fileid[0].name if not fileid: msgprint(_("File not attached")) raise Exception try: from dataent.utils.file_manager import get_file fname, fcontent = get_file(fileid) return read_csv_content(fcontent, dataent.form_dict.get('ignore_encoding_errors')) except Exception: dataent.throw(_("Unable to open attached file. Did you export it as CSV?"), title=_('Invalid CSV Format'))
def execute(): dataent.db.auto_commit_on_many_writes = True rename_replacing_files() for name, file_name, file_url in dataent.db.sql( """select name, file_name, file_url from `tabFile` where ifnull(file_name, '')!='' and ifnull(content_hash, '')=''"""): b = dataent.get_doc('File', name) old_file_name = b.file_name b.file_name = os.path.basename(old_file_name) if old_file_name.startswith('files/') or old_file_name.startswith( '/files/'): b.file_url = os.path.normpath('/' + old_file_name) else: b.file_url = os.path.normpath('/files/' + old_file_name) try: _file_name, content = get_file(name) b.content_hash = get_content_hash(content) except IOError: print('Warning: Error processing ', name) b.content_hash = None b.flags.ignore_duplicate_entry_error = True b.save() dataent.db.auto_commit_on_many_writes = False
def execute(): dataent.reload_doc('core', 'doctype', 'file_data') for name, file_name, file_url in dataent.db.sql( """select name, file_name, file_url from `tabFile` where file_name is not null"""): b = dataent.get_doc('File', name) old_file_name = b.file_name b.file_name = os.path.basename(old_file_name) if old_file_name.startswith('files/') or old_file_name.startswith('/files/'): b.file_url = os.path.normpath('/' + old_file_name) else: b.file_url = os.path.normpath('/files/' + old_file_name) try: _file_name, content = get_file(name) b.content_hash = get_content_hash(content) except IOError: print('Warning: Error processing ', name) _file_name = old_file_name b.content_hash = None try: b.save() except dataent.DuplicateEntryError: dataent.delete_doc(b.doctype, b.name)
def prepare_message(email, recipient, recipients_list): message = email.message if not message: return "" # Parse "Email Account" from "Email Sender" email_account = get_outgoing_email_account(raise_exception_not_set=False, sender=email.sender) if dataent.conf.use_ssl and email_account.track_email_status: # Using SSL => Publically available domain => Email Read Reciept Possible message = message.replace("<!--email open check-->", quopri.encodestring('<img src="https://{}/api/method/dataent.core.doctype.communication.email.mark_email_as_seen?name={}"/>'.format(dataent.local.site, email.communication).encode()).decode()) else: # No SSL => No Email Read Reciept message = message.replace("<!--email open check-->", quopri.encodestring("".encode()).decode()) if email.add_unsubscribe_link and email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url unsubscribe_url = get_unsubcribed_url(email.reference_doctype, email.reference_name, recipient, email.unsubscribe_method, email.unsubscribe_params) message = message.replace("<!--unsubscribe url-->", quopri.encodestring(unsubscribe_url.encode()).decode()) if email.expose_recipients == "header": pass else: if email.expose_recipients == "footer": if isinstance(email.show_as_cc, string_types): email.show_as_cc = email.show_as_cc.split(",") email_sent_to = [r.recipient for r in recipients_list] email_sent_cc = ", ".join([e for e in email_sent_to if e in email.show_as_cc]) email_sent_to = ", ".join([e for e in email_sent_to if e not in email.show_as_cc]) if email_sent_cc: email_sent_message = _("This email was sent to {0} and copied to {1}").format(email_sent_to,email_sent_cc) else: email_sent_message = _("This email was sent to {0}").format(email_sent_to) message = message.replace("<!--cc message-->", quopri.encodestring(email_sent_message.encode()).decode()) message = message.replace("<!--recipient-->", recipient) message = (message and message.encode('utf8')) or '' message = safe_decode(message) if not email.attachments: return message # On-demand attachments from email.parser import Parser msg_obj = Parser().parsestr(message) attachments = json.loads(email.attachments) for attachment in attachments: if attachment.get('fcontent'): continue fid = attachment.get("fid") if fid: fname, fcontent = get_file(fid) attachment.update({ 'fname': fname, 'fcontent': fcontent, 'parent': msg_obj }) attachment.pop("fid", None) add_attachment(**attachment) elif attachment.get("print_format_attachment") == 1: attachment.pop("print_format_attachment", None) print_format_file = dataent.attach_print(**attachment) print_format_file.update({"parent": msg_obj}) add_attachment(**print_format_file) return msg_obj.as_string()
def upload(rows=None, submit_after_import=None, ignore_encoding_errors=False, no_email=True, overwrite=None, update_only=None, ignore_links=False, pre_process=None, via_console=False, from_data_import="No", skip_errors=True, data_import_doc=None, validate_template=False, user=None): """upload data""" # for translations if user: dataent.cache().hdel("lang", user) dataent.set_user_lang(user) if data_import_doc and isinstance(data_import_doc, string_types): data_import_doc = dataent.get_doc("Data Import", data_import_doc) if data_import_doc and from_data_import == "Yes": no_email = data_import_doc.no_email ignore_encoding_errors = data_import_doc.ignore_encoding_errors update_only = data_import_doc.only_update submit_after_import = data_import_doc.submit_after_import overwrite = data_import_doc.overwrite skip_errors = data_import_doc.skip_errors else: # extra input params params = json.loads(dataent.form_dict.get("params") or '{}') if params.get("submit_after_import"): submit_after_import = True if params.get("ignore_encoding_errors"): ignore_encoding_errors = True if not params.get("no_email"): no_email = False if params.get('update_only'): update_only = True if params.get('from_data_import'): from_data_import = params.get('from_data_import') if not params.get('skip_errors'): skip_errors = params.get('skip_errors') dataent.flags.in_import = True dataent.flags.mute_emails = no_email def get_data_keys_definition(): return get_data_keys() def bad_template(): dataent.throw( _("Please do not change the rows above {0}").format( get_data_keys_definition().data_separator)) def check_data_length(): if not data: dataent.throw( _("No data found in the file. Please reattach the new file with data." )) def get_start_row(): for i, row in enumerate(rows): if row and row[0] == get_data_keys_definition().data_separator: return i + 1 bad_template() def get_header_row(key): return get_header_row_and_idx(key)[0] def get_header_row_and_idx(key): for i, row in enumerate(header): if row and row[0] == key: return row, i return [], -1 def filter_empty_columns(columns): empty_cols = list(filter(lambda x: x in ("", None), columns)) if empty_cols: if columns[-1 * len(empty_cols):] == empty_cols: # filter empty columns if they exist at the end columns = columns[:-1 * len(empty_cols)] else: dataent.msgprint(_( "Please make sure that there are no empty columns in the file." ), raise_exception=1) return columns def make_column_map(): doctype_row, row_idx = get_header_row_and_idx( get_data_keys_definition().doctype) if row_idx == -1: # old style return dt = None for i, d in enumerate(doctype_row[1:]): if d not in ("~", "-"): if d and doctype_row[i] in (None, '', '~', '-', _("DocType") + ":"): dt, parentfield = d, None # xls format truncates the row, so it may not have more columns if len(doctype_row) > i + 2: parentfield = doctype_row[i + 2] doctypes.append((dt, parentfield)) column_idx_to_fieldname[(dt, parentfield)] = {} column_idx_to_fieldtype[(dt, parentfield)] = {} if dt: column_idx_to_fieldname[(dt, parentfield)][i + 1] = rows[row_idx + 2][i + 1] column_idx_to_fieldtype[(dt, parentfield)][i + 1] = rows[row_idx + 4][i + 1] def get_doc(start_idx): if doctypes: doc = {} attachments = [] last_error_row_idx = None for idx in range(start_idx, len(rows)): last_error_row_idx = idx # pylint: disable=W0612 if (not doc) or main_doc_empty(rows[idx]): for dt, parentfield in doctypes: d = {} for column_idx in column_idx_to_fieldname[( dt, parentfield)]: try: fieldname = column_idx_to_fieldname[( dt, parentfield)][column_idx] fieldtype = column_idx_to_fieldtype[( dt, parentfield)][column_idx] if not fieldname or not rows[idx][column_idx]: continue d[fieldname] = rows[idx][column_idx] if fieldtype in ("Int", "Check"): d[fieldname] = cint(d[fieldname]) elif fieldtype in ("Float", "Currency", "Percent"): d[fieldname] = flt(d[fieldname]) elif fieldtype == "Date": if d[fieldname] and isinstance( d[fieldname], string_types): d[fieldname] = getdate( parse_date(d[fieldname])) elif fieldtype == "Datetime": if d[fieldname]: if " " in d[fieldname]: _date, _time = d[fieldname].split() else: _date, _time = d[ fieldname], '00:00:00' _date = parse_date(d[fieldname]) d[fieldname] = get_datetime(_date + " " + _time) else: d[fieldname] = None elif fieldtype in ("Image", "Attach Image", "Attach"): # added file to attachments list attachments.append(d[fieldname]) elif fieldtype in ("Link", "Dynamic Link", "Data") and d[fieldname]: # as fields can be saved in the number format(long type) in data import template d[fieldname] = cstr(d[fieldname]) except IndexError: pass # scrub quotes from name and modified if d.get("name") and d["name"].startswith('"'): d["name"] = d["name"][1:-1] if sum([0 if not val else 1 for val in d.values()]): d['doctype'] = dt if dt == doctype: doc.update(d) else: if not overwrite and doc.get("name"): d['parent'] = doc["name"] d['parenttype'] = doctype d['parentfield'] = parentfield doc.setdefault(d['parentfield'], []).append(d) else: break return doc, attachments, last_error_row_idx else: doc = dataent._dict(zip(columns, rows[start_idx][1:])) doc['doctype'] = doctype return doc, [], None # used in testing whether a row is empty or parent row or child row # checked only 3 first columns since first two columns can be blank for example the case of # importing the item variant where item code and item name will be blank. def main_doc_empty(row): if row: for i in range(3, 0, -1): if len(row) > i and row[i]: return False return True def validate_naming(doc): autoname = dataent.get_meta(doctype).autoname if autoname: if autoname[0:5] == 'field': autoname = autoname[6:] elif autoname == 'naming_series:': autoname = 'naming_series' else: return True if (autoname not in doc) or (not doc[autoname]): from dataent.model.base_document import get_controller if not hasattr(get_controller(doctype), "autoname"): dataent.throw( _("{0} is a mandatory field".format(autoname))) return True users = dataent.db.sql_list("select name from tabUser") def prepare_for_insert(doc): # don't block data import if user is not set # migrating from another system if not doc.owner in users: doc.owner = dataent.session.user if not doc.modified_by in users: doc.modified_by = dataent.session.user def is_valid_url(url): is_valid = False if url.startswith("/files") or url.startswith("/private/files"): url = get_url(url) try: r = requests.get(url) is_valid = True if r.status_code == 200 else False except Exception: pass return is_valid def attach_file_to_doc(doctype, docname, file_url): # check if attachment is already available # check if the attachement link is relative or not if not file_url: return if not is_valid_url(file_url): return files = dataent.db.sql( """Select name from `tabFile` where attached_to_doctype='{doctype}' and attached_to_name='{docname}' and (file_url='{file_url}' or thumbnail_url='{file_url}')""" .format(doctype=doctype, docname=docname, file_url=file_url)) if files: # file is already attached return save_url(file_url, None, doctype, docname, "Home/Attachments", 0) # header filename, file_extension = ['', ''] if not rows: from dataent.utils.file_manager import get_file # get_file_doc fname, fcontent = get_file(data_import_doc.import_file) filename, file_extension = os.path.splitext(fname) if file_extension == '.xlsx' and from_data_import == 'Yes': from dataent.utils.xlsxutils import read_xlsx_file_from_attached_file rows = read_xlsx_file_from_attached_file( file_id=data_import_doc.import_file) elif file_extension == '.csv': from dataent.utils.csvutils import read_csv_content rows = read_csv_content(fcontent, ignore_encoding_errors) else: dataent.throw(_("Unsupported File Format")) start_row = get_start_row() header = rows[:start_row] data = rows[start_row:] try: doctype = get_header_row(get_data_keys_definition().main_table)[1] columns = filter_empty_columns( get_header_row(get_data_keys_definition().columns)[1:]) except: dataent.throw(_("Cannot change header content")) doctypes = [] column_idx_to_fieldname = {} column_idx_to_fieldtype = {} if skip_errors: data_rows_with_error = header if submit_after_import and not cint( dataent.db.get_value("DocType", doctype, "is_submittable")): submit_after_import = False parenttype = get_header_row(get_data_keys_definition().parent_table) if len(parenttype) > 1: parenttype = parenttype[1] # check permissions if not dataent.permissions.can_import(parenttype or doctype): dataent.flags.mute_emails = False return { "messages": [_("Not allowed to Import") + ": " + _(doctype)], "error": True } # Throw expception in case of the empty data file check_data_length() make_column_map() total = len(data) if validate_template: if total: data_import_doc.total_rows = total return True if overwrite == None: overwrite = params.get('overwrite') # delete child rows (if parenttype) parentfield = None if parenttype: parentfield = get_parent_field(doctype, parenttype) if overwrite: delete_child_rows(data, doctype) import_log = [] def log(**kwargs): if via_console: print( (kwargs.get("title") + kwargs.get("message")).encode('utf-8')) else: import_log.append(kwargs) def as_link(doctype, name): if via_console: return "{0}: {1}".format(doctype, name) else: return getlink(doctype, name) # publish realtime task update 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) error_flag = rollback_flag = False batch_size = dataent.conf.data_import_batch_size or 1000 for batch_start in range(0, total, batch_size): batch = data[batch_start:batch_start + batch_size] for i, row in enumerate(batch): # bypass empty rows if main_doc_empty(row): continue row_idx = i + start_row doc = None publish_progress(i) try: doc, attachments, last_error_row_idx = get_doc(row_idx) validate_naming(doc) if pre_process: pre_process(doc) original = None if parentfield: parent = dataent.get_doc(parenttype, doc["parent"]) doc = parent.append(parentfield, doc) parent.save() else: if overwrite and doc.get("name") and dataent.db.exists( doctype, doc["name"]): original = dataent.get_doc(doctype, doc["name"]) original_name = original.name original.update(doc) # preserve original name for case sensitivity original.name = original_name original.flags.ignore_links = ignore_links original.save() doc = original else: if not update_only: doc = dataent.get_doc(doc) prepare_for_insert(doc) doc.flags.ignore_links = ignore_links doc.insert() if attachments: # check file url and create a File document for file_url in attachments: attach_file_to_doc(doc.doctype, doc.name, file_url) if submit_after_import: doc.submit() # log errors if parentfield: log( **{ "row": doc.idx, "title": 'Inserted row for "%s"' % (as_link(parenttype, doc.parent)), "link": get_absolute_url(parenttype, doc.parent), "message": 'Document successfully saved', "indicator": "green" }) elif submit_after_import: log( **{ "row": row_idx + 1, "title": 'Submitted row for "%s"' % (as_link(doc.doctype, doc.name)), "message": "Document successfully submitted", "link": get_absolute_url(doc.doctype, doc.name), "indicator": "blue" }) elif original: log( **{ "row": row_idx + 1, "title": 'Updated row for "%s"' % (as_link(doc.doctype, doc.name)), "message": "Document successfully updated", "link": get_absolute_url(doc.doctype, doc.name), "indicator": "green" }) elif not update_only: log( **{ "row": row_idx + 1, "title": 'Inserted row for "%s"' % (as_link(doc.doctype, doc.name)), "message": "Document successfully saved", "link": get_absolute_url(doc.doctype, doc.name), "indicator": "green" }) else: log( **{ "row": row_idx + 1, "title": 'Ignored row for %s' % (row[1]), "link": None, "message": "Document updation ignored", "indicator": "orange" }) except Exception as e: error_flag = True # build error message if dataent.local.message_log: err_msg = "\n".join([ '<p class="border-bottom small">{}</p>'.format( json.loads(msg).get('message')) for msg in dataent.local.message_log ]) else: err_msg = '<p class="border-bottom small">{}</p>'.format( cstr(e)) error_trace = dataent.get_traceback() if error_trace: error_log_doc = dataent.log_error(error_trace) error_link = get_absolute_url("Error Log", error_log_doc.name) else: error_link = None log( **{ "row": row_idx + 1, "title": 'Error for row %s' % (len(row) > 1 and dataent.safe_decode(row[1]) or ""), "message": err_msg, "indicator": "red", "link": error_link }) # data with error to create a new file # include the errored data in the last row as last_error_row_idx will not be updated for the last row if skip_errors: if last_error_row_idx == len(rows) - 1: last_error_row_idx = len(rows) data_rows_with_error += rows[row_idx:last_error_row_idx] else: rollback_flag = True finally: dataent.local.message_log = [] start_row += batch_size if rollback_flag: dataent.db.rollback() else: dataent.db.commit() dataent.flags.mute_emails = False dataent.flags.in_import = False log_message = {"messages": import_log, "error": error_flag} if data_import_doc: data_import_doc.log_details = json.dumps(log_message) import_status = None if error_flag and data_import_doc.skip_errors and len(data) != len( data_rows_with_error): import_status = "Partially Successful" # write the file with the faulty row from dataent.utils.file_manager import save_file file_name = 'error_' + filename + file_extension if file_extension == '.xlsx': from dataent.utils.xlsxutils import make_xlsx xlsx_file = make_xlsx(data_rows_with_error, "Data Import Template") file_data = xlsx_file.getvalue() else: from dataent.utils.csvutils import to_csv file_data = to_csv(data_rows_with_error) error_data_file = save_file(file_name, file_data, "Data Import", data_import_doc.name, "Home/Attachments") data_import_doc.error_file = error_data_file.file_url elif error_flag: import_status = "Failed" else: import_status = "Successful" data_import_doc.import_status = import_status data_import_doc.save() if data_import_doc.import_status in [ "Successful", "Partially Successful" ]: data_import_doc.submit() publish_progress(100, True) else: publish_progress(0, True) dataent.db.commit() else: return log_message
def download_attachment(dn): attachment = get_attachments("Prepared Report", dn)[0] dataent.local.response.filename = attachment.file_name[:-2] dataent.local.response.filecontent = gzip_decompress( get_file(attachment.name)[1]) dataent.local.response.type = "binary"
def test_saved_content(self): filename1, content1 = get_file(self.saved_file1.name) filename2, content2 = get_file(self.saved_file2.name) self.assertEqual(filename1, filename2) self.assertFalse(os.path.exists(get_files_path(self.dup_filename)))
def test_saved_content(self): filename1, content1 = get_file(self.saved_file1.name) self.assertEqual(content1, self.test_content1) filename2, content2 = get_file(self.saved_file2.name) self.assertEqual(content2, self.test_content2)
def test_save(self): filename, content = get_file(self.saved_file.name) self.assertEqual(content, self.test_content)