Browse Source

Merge pull request #1331 from rmehta/thumbnail

[enhancement] added 'make_thumbnail' to File and will be saved as thumbnail_url
version-14
Anand Doshi 9 years ago
parent
commit
dbb2c3d917
8 changed files with 127 additions and 59 deletions
  1. +5
    -2
      frappe/__init__.py
  2. +24
    -2
      frappe/core/doctype/file/file.json
  3. +53
    -3
      frappe/core/doctype/file/file.py
  4. +28
    -40
      frappe/core/doctype/file/test_file.py
  5. +1
    -1
      frappe/model/naming.py
  6. +3
    -0
      frappe/utils/data.py
  7. +12
    -11
      frappe/utils/file_manager.py
  8. +1
    -0
      requirements.txt

+ 5
- 2
frappe/__init__.py View File

@@ -469,11 +469,14 @@ def get_precision(doctype, fieldname, currency=None, doc=None):
from frappe.model.meta import get_field_precision
return get_field_precision(get_meta(doctype).get_field(fieldname), doc, currency)

def generate_hash(txt=None):
def generate_hash(txt=None, length=None):
"""Generates random hash for given text + current timestamp + random string."""
import hashlib, time
from .utils import random_string
return hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest()
digest = hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest()
if length:
digest = digest[:length]
return digest

def reset_metadata_version():
"""Reset `metadata_version` (Client (Javascript) build ID) hash."""


+ 24
- 2
frappe/core/doctype/file/file.json View File

@@ -2,7 +2,7 @@
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "hash",
"autoname": "",
"creation": "2012-12-12 11:19:22",
"custom": 0,
"docstatus": 0,
@@ -205,6 +205,28 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "thumbnail_url",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Thumbnail URL",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -433,7 +455,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-09-18 06:22:10.902847",
"modified": "2015-10-07 05:52:52.922698",
"modified_by": "Administrator",
"module": "Core",
"name": "File",


+ 53
- 3
frappe/core/doctype/file/file.py View File

@@ -40,7 +40,7 @@ class File(NestedSet):
# home
self.name = self.file_name
else:
self.name = self.file_url
self.name = frappe.generate_hash("", 10)

def after_insert(self):
self.update_parent_folder_size()
@@ -53,7 +53,8 @@ class File(NestedSet):
return frappe.db.sql_list("select name from tabFile where folder='%s'"%self.name) or []

def validate(self):
self.validate_duplicate_entry()
if self.is_new():
self.validate_duplicate_entry()
self.validate_folder()
self.set_folder_size()

@@ -92,6 +93,8 @@ class File(NestedSet):

def validate_duplicate_entry(self):
if not self.flags.ignore_duplicate_entry_error and not self.is_folder:
# check duplicate name

# check duplicate assignement
n_records = frappe.db.sql("""select name from `tabFile`
where content_hash=%s
@@ -111,6 +114,50 @@ class File(NestedSet):
super(File, self).on_trash()
self.delete_file()

def make_thumbnail(self):
from PIL import Image, ImageOps
import os

if self.file_url:
if self.file_url.startswith("/files"):
try:
image = Image.open(frappe.get_site_path("public", self.file_url))
filename, extn = self.file_url.rsplit(".", 1)
except IOError:
frappe.msgprint("Unable to read file format for {0}".format(self.file_url))

else:
# downlaod
import requests, StringIO
file_url = frappe.utils.get_url(self.file_url)
r = requests.get(file_url, stream=True)
r.raise_for_status()
image = Image.open(StringIO.StringIO(r.content))
filename, extn = self.file_url.rsplit("/", 1)[1].rsplit(".", 1)
filename = "/files/" + filename

thumbnail = ImageOps.fit(
image,
(300, 300),
Image.ANTIALIAS
)

thumbnail_url = filename + "_small." + extn

if thumbnail_url[0]=="/":
thumbnail_url = thumbnail_url[1:]

path = os.path.abspath(frappe.get_site_path("public", thumbnail_url))

try:
thumbnail.save(path)
self.db_set("thumbnail_url", thumbnail_url)
except IOError:
frappe.msgprint("Unable to write file format for {0}".format(path))

return thumbnail_url


def after_delete(self):
self.update_parent_folder_size()

@@ -178,7 +225,10 @@ def create_new_folder(file_name, folder):

@frappe.whitelist()
def move_file(file_list, new_parent, old_parent):
for file_obj in json.loads(file_list):
if isinstance(file_list, basestring):
file_list = json.loads(file_list)

for file_obj in file_list:
setup_folder_path(file_obj.get("name"), new_parent)

# recalculate sizes


+ 28
- 40
frappe/core/doctype/file/test_file.py View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals

import frappe
import unittest
from frappe.utils.file_manager import save_file, get_file, get_files_path
from frappe.utils.file_manager import save_file, get_files_path
from frappe import _
from frappe.core.doctype.file.file import move_file
import json
@@ -15,27 +15,19 @@ class TestFile(unittest.TestCase):
def setUp(self):
self.delete_test_data()
self.upload_file()
def delete_test_data(self):
for file_name in ["folder_copy.txt", "file_copy.txt", "Test Folder 2"]:
for file_name in ["folder_copy.txt", "file_copy.txt", "Test Folder 3", "Test Folder 2", "Test Folder 1"]:
file_name = frappe.db.get_value("File", {"file_name": file_name}, "name")
if file_name:
file = frappe.get_doc("File", file_name)
ancestors = file.get_ancestors()
file.delete()
self.delete_ancestors(ancestors)

def delete_ancestors(self, ancestors):
for folder in ancestors:
if folder != "Home":
folder = frappe.get_doc("File", folder)
folder.delete()

def upload_file(self):
self.saved_file = save_file('file_copy.txt', "Testing file copy example.",\
"", "", self.get_folder("Test Folder 1", "Home").name)
self.saved_filename = get_files_path(self.saved_file.file_name)

def get_folder(self, folder_name, parent_folder="Home"):
return frappe.get_doc({
"doctype": "File",
@@ -46,61 +38,57 @@ class TestFile(unittest.TestCase):

def tests_after_upload(self):
self.assertEqual(self.saved_file.folder, _("Home/Test Folder 1"))
folder_size = frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size")
saved_file_size = frappe.db.get_value("File", self.saved_file.name, "file_size")
self.assertEqual(folder_size, saved_file_size)
def test_file_copy(self):
folder = self.get_folder("Test Folder 2", "Home")
file = frappe.get_doc("File", "/files/file_copy.txt")
file_dict = [{"name": file.name}]
move_file(json.dumps(file_dict), folder.name, file.folder)
file = frappe.get_doc("File", "/files/file_copy.txt")

file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
move_file([{"name": file.name}], folder.name, file.folder)
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})

self.assertEqual(_("Home/Test Folder 2"), file.folder)
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), file.file_size)
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), None)

def test_folder_copy(self):
folder = self.get_folder("Test Folder 2", "Home")
folder = self.get_folder("Test Folder 3", "Home/Test Folder 2")
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name)
file_dict = [{"name": folder.name}]
move_file(json.dumps(file_dict), 'Home/Test Folder 1', folder.folder)
file = frappe.get_doc("File", "/files/folder_copy.txt")

move_file([{"name": folder.name}], 'Home/Test Folder 1', folder.folder)

file = frappe.get_doc("File", {"file_name":"folder_copy.txt"})
file_copy_txt = frappe.get_value("File", {"file_name":"file_copy.txt"})
if file_copy_txt:
frappe.get_doc("File", file_copy_txt).delete()

self.assertEqual(_("Home/Test Folder 1/Test Folder 3"), file.folder)
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), file.file_size)
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), None)
def test_non_parent_folder(self):
d = frappe.get_doc({
"doctype": "File",
"file_name": _("Test_Folder"),
"is_folder": 1
})
self.assertRaises(frappe.ValidationError, d.save)
def test_on_delete(self):
file = frappe.get_doc("File", "/files/file_copy.txt")
file = frappe.get_doc("File", {"file_name":"file_copy.txt"})
file.delete()
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), None)
folder = self.get_folder("Test Folder 3", "Home/Test Folder 1")
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name)

folder = frappe.get_doc("File", "Home/Test Folder 1/Test Folder 3")
self.assertRaises(frappe.ValidationError, folder.delete)

+ 1
- 1
frappe/model/naming.py View File

@@ -82,7 +82,7 @@ def make_autoname(key, doctype=''):
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
"""
if key=="hash":
return frappe.generate_hash(doctype)[:10]
return frappe.generate_hash(doctype, 10)

if not "#" in key:
key = key + ".#####"


+ 3
- 0
frappe/utils/data.py View File

@@ -513,6 +513,9 @@ def get_url(uri=None, full_address=False):
"""get app url from request"""
host_name = frappe.local.conf.host_name

if uri and (uri.startswith("http://") or uri.startswith("https://")):
return uri

if not host_name:
if hasattr(frappe.local, "request") and frappe.local.request and frappe.local.request.host:
protocol = 'https' == frappe.get_request_header('X-Forwarded-Proto', "") and 'https://' or 'http://'


+ 12
- 11
frappe/utils/file_manager.py View File

@@ -246,17 +246,18 @@ def delete_file_data_content(doc):
method(doc)

def delete_file_from_filesystem(doc):
path = doc.file_name

if path.startswith("files/"):
path = frappe.utils.get_site_path("public", doc.file_name)
else:
path = frappe.utils.get_site_path("public", "files", doc.file_name)

path = encode(path)

if os.path.exists(path):
os.remove(path)
"""Delete file, thumbnail from File document"""
delete_file(doc.file_url)
delete_file(doc.thumbnail_url)

def delete_file(path):
"""Delete file from `public folder`"""
if path and path.startswith("/files/"):
parts = os.path.split(path)
path = frappe.utils.get_site_path("public", "files", parts[-1])
path = encode(path)
if os.path.exists(path):
os.remove(path)

def get_file(fname):
f = frappe.db.sql("""select file_name from `tabFile`


+ 1
- 0
requirements.txt View File

@@ -30,3 +30,4 @@ click
num2words
watchdog==0.8.0
bleach
Pillow

Loading…
Cancel
Save