ajax decorator
[redakcja.git] / apps / catalogue / helpers.py
1 # -*- coding: utf-8 -*-
2 import filecmp
3 import json
4 import re
5 from datetime import date
6 from functools import wraps
7 from inspect import getargspec
8 from os import listdir
9 from os.path import join
10 from shutil import move, rmtree
11
12 from django.conf import settings
13 from django.http import HttpResponse
14 from django.template import RequestContext
15 from django.template.loader import render_to_string
16
17
18 def active_tab(tab):
19     """
20         View decorator, which puts tab info on a request.
21     """
22     def wrapper(f):
23         @wraps(f)
24         def wrapped(request, *args, **kwargs):
25             request.catalogue_active_tab = tab
26             return f(request, *args, **kwargs)
27         return wrapped
28     return wrapper
29
30
31 def cached_in_field(field_name):
32     def decorator(f):
33         @property
34         @wraps(f)
35         def wrapped(self, *args, **kwargs):
36             value = getattr(self, field_name)
37             if value is None:
38                 value = f(self, *args, **kwargs)
39                 type(self)._default_manager.filter(pk=self.pk).update(**{field_name: value})
40             return value
41         return wrapped
42     return decorator
43
44
45 class AjaxError(Exception):
46     pass
47
48
49 def ajax(method, template=None, login_required=False, permission_required=None):
50     def decorator(fun):
51         @wraps(fun)
52         def ajax_view(request):
53             kwargs = {}
54             request_params = None
55             if method == 'post':
56                 request_params = request.POST
57             elif method == 'get':
58                 request_params = request.GET
59             fun_params, xx, fun_kwargs, defaults = getargspec(fun)
60             if defaults:
61                 required_params = fun_params[1:-len(defaults)]
62             else:
63                 required_params = fun_params[1:]
64             missing_params = set(required_params) - set(request_params)
65             if missing_params:
66                 res = {
67                     'result': 'error',
68                     'msg': 'missing params: %s' % ', '.join(missing_params),
69                 }
70             else:
71                 if request_params:
72                     request_params = dict(
73                         (key, json.loads(value))
74                         for key, value in request_params.iteritems()
75                         if fun_kwargs or key in fun_params)
76                     kwargs.update(request_params)
77                 res = None
78                 if login_required and not request.user.is_authenticated():
79                     res = {'result': 'error', 'msg': 'logout'}
80                 if (permission_required and
81                         not request.user.has_perm(permission_required)):
82                     res = {'result': 'error', 'msg': 'access denied'}
83             if not res:
84                 try:
85                     res = fun(request, **kwargs)
86                     if res and template:
87                         res = {'html': render_to_string(template, res, RequestContext(request))}
88                 except AjaxError as e:
89                     res = {'result': e.args[0]}
90             if 'result' not in res:
91                 res['result'] = 'ok'
92             return HttpResponse(json.dumps(res), content_type='application/json; charset=utf-8')
93
94         return ajax_view
95
96     return decorator
97
98
99 def parse_isodate(isodate):
100     try:
101         return date(*[int(p) for p in isodate.split('-')])
102     except (AttributeError, TypeError, ValueError):
103         raise ValueError("Not a date in ISO format.")
104
105
106 class GalleryMerger(object):
107     def __init__(self, dest_gallery, src_gallery):
108         self.dest = dest_gallery
109         self.src = src_gallery
110         self.dest_size = None
111         self.src_size = None
112         self.num_deleted = 0
113
114     @staticmethod
115     def path(gallery):
116         return join(settings.MEDIA_ROOT, settings.IMAGE_DIR, gallery)
117
118     @staticmethod
119     def get_prefix(name):
120         m = re.match(r"^([0-9])-", name)
121         if m:
122             return int(m.groups()[0])
123         return None
124
125     @staticmethod
126     def set_prefix(name, prefix, always=False):
127         m = not always and re.match(r"^([0-9])-", name)
128         return "%1d-%s" % (prefix, m and name[2:] or name)
129
130     @property
131     def was_merged(self):
132         """Check if we have gallery size recorded"""
133         return self.dest_size is not None
134
135     def merge(self):
136         if not self.dest:
137             return self.src
138         if not self.src:
139             return self.dest
140
141         files = listdir(self.path(self.dest))
142         files.sort()
143         self.dest_size = len(files)
144         files_other = listdir(self.path(self.src))
145         files_other.sort()
146         self.src_size = len(files_other)
147
148         if files and files_other:
149             print "compare %s with %s" % (files[-1], files_other[0])
150             if filecmp.cmp(
151                     join(self.path(self.dest), files[-1]),
152                     join(self.path(self.src), files_other[0]),
153                     False
154                     ):
155                 files_other.pop(0)
156                 self.num_deleted = 1
157
158         prefixes = {}
159         renamed_files = {}
160         renamed_files_other = {}
161         last_pfx = -1
162
163         # check if all elements of my files have a prefix
164         files_prefixed = True
165         for f in files:
166             p = self.get_prefix(f)
167             if p:
168                 if p > last_pfx:
169                     last_pfx = p
170             else:
171                 files_prefixed = False
172                 break
173
174         # if not, add a 0 prefix to them
175         if not files_prefixed:
176             prefixes[0] = 0
177             for f in files:
178                 renamed_files[f] = self.set_prefix(f, 0, True)
179
180         # two cases here - either all are prefixed or not.
181         files_other_prefixed = True
182         for f in files_other:
183             pfx = self.get_prefix(f)
184             if pfx is not None:
185                 if pfx not in prefixes:
186                     last_pfx += 1
187                     prefixes[pfx] = last_pfx
188                 renamed_files_other[f] = self.set_prefix(f, prefixes[pfx])
189             else:
190                 # ops, not all files here were prefixed.
191                 files_other_prefixed = False
192                 break
193
194         # just set a 1- prefix to all of them
195         if not files_other_prefixed:
196             for f in files_other:
197                 renamed_files_other[f] = self.set_prefix(f, 1, True)
198
199         # finally, move / rename files.
200         for frm, to in renamed_files.items():
201             move(join(self.path(self.dest), frm), join(self.path(self.dest), to))
202         for frm, to in renamed_files_other.items():
203             move(join(self.path(self.src), frm), join(self.path(self.dest), to))
204
205         rmtree(join(self.path(self.src)))
206         return self.dest