Attempt to add traceback logging
[redakcja.git] / apps / sorl / thumbnail / utils.py
1 import math
2 import os
3 import re
4
5
6 re_thumbnail_file = re.compile(r'(?P<source_filename>.+)_(?P<x>\d+)x(?P<y>\d+)'
7                                r'(?:_(?P<options>\w+))?_q(?P<quality>\d+)'
8                                r'(?:.[^.]+)?$')
9 re_new_args = re.compile('(?<!quality)=')
10
11
12 def all_thumbnails(path, recursive=True, prefix=None, subdir=None):
13     """
14     Return a dictionary referencing all files which match the thumbnail format.
15
16     Each key is a source image filename, relative to path.
17     Each value is a list of dictionaries as explained in `thumbnails_for_file`.
18     """
19     # Fall back to using thumbnail settings. These are local imports so that
20     # there is no requirement of Django to use the utils module.
21     if prefix is None:
22         from sorl.thumbnail.main import get_thumbnail_setting
23         prefix = get_thumbnail_setting('PREFIX')
24     if subdir is None:
25         from sorl.thumbnail.main import get_thumbnail_setting
26         subdir = get_thumbnail_setting('SUBDIR')
27     thumbnail_files = {}
28     if not path.endswith('/'):
29         path = '%s/' % path
30     len_path = len(path)
31     if recursive:
32         all = os.walk(path)
33     else:
34         files = []
35         for file in os.listdir(path):
36             if os.path.isfile(os.path.join(path, file)):
37                 files.append(file)
38         all = [(path, [], files)]
39     for dir_, subdirs, files in all:
40         rel_dir = dir_[len_path:]
41         for file in files:
42             thumb = re_thumbnail_file.match(file)
43             if not thumb:
44                 continue
45             d = thumb.groupdict()
46             source_filename = d.pop('source_filename')
47             if prefix:
48                 source_path, source_filename = os.path.split(source_filename)
49                 if not source_filename.startswith(prefix):
50                     continue
51                 source_filename = os.path.join(source_path,
52                     source_filename[len(prefix):])
53             d['options'] = d['options'] and d['options'].split('_') or []
54             if subdir and rel_dir.endswith(subdir):
55                 rel_dir = rel_dir[:-len(subdir)]
56             # Corner-case bug: if the filename didn't have an extension but did
57             # have an underscore, the last underscore will get converted to a
58             # '.'.
59             m = re.match(r'(.*)_(.*)', source_filename)
60             if m:
61                 source_filename = '%s.%s' % m.groups()
62             filename = os.path.join(rel_dir, source_filename)
63             thumbnail_file = thumbnail_files.setdefault(filename, [])
64             d['filename'] = os.path.join(dir_, file)
65             thumbnail_file.append(d)
66     return thumbnail_files
67
68
69 def thumbnails_for_file(relative_source_path, root=None, basedir=None,
70                         subdir=None, prefix=None):
71     """
72     Return a list of dictionaries, one for each thumbnail belonging to the
73     source image.
74
75     The following list explains each key of the dictionary:
76
77       `filename`  -- absolute thumbnail path
78       `x` and `y` -- the size of the thumbnail
79       `options`   -- list of options for this thumbnail
80       `quality`   -- quality setting for this thumbnail
81     """
82     # Fall back to using thumbnail settings. These are local imports so that
83     # there is no requirement of Django to use the utils module.
84     if root is None:
85         from django.conf import settings
86         root = settings.MEDIA_ROOT
87     if prefix is None:
88         from sorl.thumbnail.main import get_thumbnail_setting
89         prefix = get_thumbnail_setting('PREFIX')
90     if subdir is None:
91         from sorl.thumbnail.main import get_thumbnail_setting
92         subdir = get_thumbnail_setting('SUBDIR')
93     if basedir is None:
94         from sorl.thumbnail.main import get_thumbnail_setting
95         basedir = get_thumbnail_setting('BASEDIR')
96     source_dir, filename = os.path.split(relative_source_path)
97     thumbs_path = os.path.join(root, basedir, source_dir, subdir)
98     if not os.path.isdir(thumbs_path):
99         return []
100     files = all_thumbnails(thumbs_path, recursive=False, prefix=prefix,
101                            subdir='')
102     return files.get(filename, [])
103
104
105 def delete_thumbnails(relative_source_path, root=None, basedir=None,
106                       subdir=None, prefix=None):
107     """
108     Delete all thumbnails for a source image.
109     """
110     thumbs = thumbnails_for_file(relative_source_path, root, basedir, subdir,
111                                  prefix)
112     return _delete_using_thumbs_list(thumbs)
113
114
115 def _delete_using_thumbs_list(thumbs):
116     deleted = 0
117     for thumb_dict in thumbs:
118         filename = thumb_dict['filename']
119         try:
120             os.remove(filename)
121         except:
122             pass
123         else:
124             deleted += 1
125     return deleted
126
127
128 def delete_all_thumbnails(path, recursive=True):
129     """
130     Delete all files within a path which match the thumbnails pattern.
131
132     By default, matching files from all sub-directories are also removed. To
133     only remove from the path directory, set recursive=False.
134     """
135     total = 0
136     for thumbs in all_thumbnails(path, recursive=recursive).values():
137         total += _delete_using_thumbs_list(thumbs)
138     return total
139
140
141 def split_args(args):
142     """
143     Split a list of argument strings into a dictionary where each key is an
144     argument name.
145
146     An argument looks like ``crop``, ``crop="some option"`` or ``crop=my_var``.
147     Arguments which provide no value get a value of ``None``.
148     """
149     if not args:
150         return {}
151     # Handle the old comma separated argument format.
152     if len(args) == 1 and not re_new_args.search(args[0]):
153         args = args[0].split(',')
154     # Separate out the key and value for each argument.
155     args_dict = {}
156     for arg in args:
157         split_arg = arg.split('=', 1)
158         value = len(split_arg) > 1 and split_arg[1] or None
159         args_dict[split_arg[0]] = value
160     return args_dict
161
162
163 def image_entropy(im):
164     """
165     Calculate the entropy of an image. Used for "smart cropping".
166     """
167     hist = im.histogram()
168     hist_size = float(sum(hist))
169     hist = [h / hist_size for h in hist]
170     return -sum([p * math.log(p, 2) for p in hist if p != 0])