brotherton-erpnext/erpnext/patches/v5_4/fix_missing_item_images.py

127 lines
4.2 KiB
Python
Raw Normal View History

from __future__ import unicode_literals
import frappe
import os
from frappe.utils import get_files_path
from frappe.utils.file_manager import get_content_hash
def execute():
files_path = get_files_path()
# get files that don't have attached_to_name but exist
unlinked_files = get_unlinked_files(files_path)
if not unlinked_files:
return
fixed_files = fix_files_for_item(files_path, unlinked_files)
# fix remaining files
for key, file_data in unlinked_files.items():
if key not in fixed_files:
rename_and_set_content_hash(files_path, unlinked_files, key)
frappe.db.commit()
def fix_files_for_item(files_path, unlinked_files):
fixed_files = []
# make a list of files/something and /files/something to check in child table's image column
file_urls = [key for key in unlinked_files.keys()] + ["/" + key for key in unlinked_files.keys()]
file_item_code = get_file_item_code(file_urls)
for (file_url, item_code), children in file_item_code.items():
new_file_url = "/files/{0}".format(unlinked_files[file_url]["file_name"])
for row in children:
# print file_url, new_file_url, item_code, row.doctype, row.name
# replace image in these rows with the new file url
frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False)
# set it as attachment of this item code
file_data = frappe.get_doc("File", unlinked_files[file_url]["file"])
file_data.attached_to_doctype = "Item"
file_data.attached_to_name = item_code
file_data.flags.ignore_folder_validate = True
try:
file_data.save()
except IOError:
print "File {0} does not exist".format(new_file_url)
# marking fix to prevent further errors
fixed_files.append(file_url)
continue
# set it as image in Item
if not frappe.db.get_value("Item", item_code, "image"):
frappe.db.set_value("Item", item_code, "image", new_file_url, update_modified=False)
rename_and_set_content_hash(files_path, unlinked_files, file_url)
fixed_files.append(file_url)
# commit
frappe.db.commit()
return fixed_files
def rename_and_set_content_hash(files_path, unlinked_files, file_url):
# rename this file
old_filename = os.path.join(files_path, unlinked_files[file_url]["file"])
new_filename = os.path.join(files_path, unlinked_files[file_url]["file_name"])
if not os.path.exists(new_filename):
os.rename(old_filename, new_filename)
# set content hash if missing
file_data_name = unlinked_files[file_url]["file"]
if not frappe.db.get_value("File", file_data_name, "content_hash"):
with open(new_filename, "r") as f:
content_hash = get_content_hash(f.read())
frappe.db.set_value("File", file_data_name, "content_hash", content_hash)
def get_unlinked_files(files_path):
# find files that have the same name as a File doc
# and the file_name mentioned in that File doc doesn't exist
# and it isn't already attached to a doc
unlinked_files = {}
files = os.listdir(files_path)
for file in files:
if not frappe.db.exists("File", {"file_name": file}):
file_data = frappe.db.get_value("File", {"name": file},
["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True)
if (file_data
and file_data.file_name
and file_data.file_name not in files
and not file_data.attached_to_doctype
and not file_data.attached_to_name):
file_data["file"] = file
unlinked_files["files/{0}".format(file)] = file_data
return unlinked_files
def get_file_item_code(file_urls):
# get a map of file_url, item_code and list of documents where file_url will need to be changed in image field
file_item_code = {}
doctypes = frappe.db.sql_list("""select name from `tabDocType` dt
where istable=1
and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='item_code')
and exists (select name from `tabDocField` df where df.parent=dt.name and df.fieldname='image')""")
for doctype in doctypes:
result = frappe.db.sql("""select name, image, item_code, '{0}' as doctype from `tab{0}`
where image in ({1})""".format(doctype, ", ".join(["%s"]*len(file_urls))),
file_urls, as_dict=True)
for r in result:
key = (r.image, r.item_code)
if key not in file_item_code:
file_item_code[key] = []
file_item_code[key].append(r)
return file_item_code