Added caching to book_text view.
[wolnelektury.git] / apps / catalogue / views.py
1 # -*- coding: utf-8 -*-
2 from django.template import RequestContext
3 from django.shortcuts import render_to_response, get_object_or_404
4 from django.http import HttpResponse, HttpResponseRedirect, Http404
5 from django.core.urlresolvers import reverse
6 from django.db.models import Q
7 from django.contrib.auth.decorators import login_required
8 from django.utils.datastructures import SortedDict
9 from django.views.decorators.http import require_POST
10 from django.contrib import auth
11 from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
12 from django.utils import simplejson
13 from django.utils.functional import Promise
14 from django.utils.encoding import force_unicode
15 from django.views.decorators.cache import cache_page
16
17 from catalogue import models
18 from catalogue import forms
19 from catalogue.utils import split_tags
20 from newtagging import views as newtagging_views
21
22
23 class LazyEncoder(simplejson.JSONEncoder):
24     def default(self, obj):
25         if isinstance(obj, Promise):
26             return force_unicode(obj)
27         return obj
28
29
30 def search(request):
31     query = request.GET.get('q', '')
32     tags = request.GET.get('tags', '')
33     if tags == '':
34         tags = []
35     
36     try:
37         tag_list = models.Tag.get_tag_list(tags)
38         tag = models.Tag.objects.get(name=query)
39     except models.Tag.DoesNotExist:
40         try:
41             book = models.Book.objects.get(title=query)
42             return HttpResponseRedirect(book.get_absolute_url())
43         except models.Book.DoesNotExist:
44             return HttpResponseRedirect(reverse('catalogue.views.main_page'))
45     else:
46         tag_list.append(tag)
47         return HttpResponseRedirect(reverse('catalogue.views.tagged_object_list', 
48             kwargs={'tags': '/'.join(tag.slug for tag in tag_list)}
49         ))
50
51
52 def tags_starting_with(request):
53     try:
54         prefix = request.GET['q']
55         if len(prefix) < 2:
56             raise KeyError
57             
58         books = models.Book.objects.filter(title__icontains=prefix)
59         tags = models.Tag.objects.filter(name__icontains=prefix)
60         if request.user.is_authenticated():
61             tags = tags.filter(~Q(category='set') | Q(user=request.user))
62         else:
63             tags = tags.filter(~Q(category='set'))
64         
65         completions = [book.title for book in books] + [tag.name for tag in tags]
66
67         return HttpResponse('\n'.join(completions))    
68     
69     except KeyError:
70         return HttpResponse('')
71
72
73 def main_page(request):    
74     if request.user.is_authenticated():
75         shelves = models.Tag.objects.filter(category='set', user=request.user)
76         new_set_form = forms.NewSetForm()
77     extra_where = 'NOT catalogue_tag.category = "set"'
78     tags = models.Tag.objects.usage_for_model(models.Book, counts=True, extra={'where': [extra_where]})
79     fragment_tags = models.Tag.objects.usage_for_model(models.Fragment, counts=True,
80         extra={'where': ['catalogue_tag.category = "theme"'] + [extra_where]})
81     categories = split_tags(tags)
82     
83     form = forms.SearchForm()
84     return render_to_response('catalogue/main_page.html', locals(),
85         context_instance=RequestContext(request))
86
87
88 def book_list(request):
89     books = models.Book.objects.all()
90     form = forms.SearchForm()
91     
92     books_by_first_letter = SortedDict()
93     for book in books:
94         books_by_first_letter.setdefault(book.title[0], []).append(book)
95     
96     return render_to_response('catalogue/book_list.html', locals(),
97         context_instance=RequestContext(request))
98
99
100 def tagged_object_list(request, tags=''):
101     # Prevent DoS attacks on our database
102     if len(tags.split('/')) > 6:
103         raise Http404
104         
105     try:
106         tags = models.Tag.get_tag_list(tags)
107     except models.Tag.DoesNotExist:
108         raise Http404
109     
110     model = models.Book
111     shelf_is_set = (len(tags) == 1 and tags[0].category == 'set')
112     theme_is_set = any(tag.category == 'theme' for tag in tags)
113     if theme_is_set:
114         model = models.Fragment
115
116     extra_where = 'NOT catalogue_tag.category = "set"'
117     related_tags = models.Tag.objects.related_for_model(tags, model, counts=True, extra={'where': [extra_where]})
118     categories = split_tags(related_tags)
119
120     if not theme_is_set:
121         model=models.Book.objects.filter(parent=None)
122     
123     return newtagging_views.tagged_object_list(
124         request,
125         tag_model=models.Tag,
126         queryset_or_model=model,
127         tags=tags,
128         template_name='catalogue/tagged_object_list.html',
129         extra_context = {'categories': categories, 'shelf_is_set': shelf_is_set },
130     )
131
132
133 def book_detail(request, slug):
134     book = get_object_or_404(models.Book, slug=slug)
135     tags = list(book.tags.filter(~Q(category='set')))
136     categories = split_tags(tags)
137     book_children = book.children.all().order_by('parent_number')
138     
139     form = forms.SearchForm()
140     return render_to_response('catalogue/book_detail.html', locals(),
141         context_instance=RequestContext(request))
142
143
144 @cache_page(60 * 60)
145 def book_text(request, slug):
146     book = get_object_or_404(models.Book, slug=slug)
147     book_themes = {}
148     for fragment in book.fragments.all():
149         for theme in fragment.tags.filter(category='theme'):
150             book_themes.setdefault(theme, []).append(fragment)
151     
152     book_themes = book_themes.items()
153     book_themes.sort(key=lambda s: s[0].sort_key)
154     return render_to_response('catalogue/book_text.html', locals(),
155         context_instance=RequestContext(request))
156
157
158 def logout_then_redirect(request):
159     auth.logout(request)
160     return HttpResponseRedirect(request.GET.get('next', '/'))
161
162
163 @require_POST
164 def register(request):
165     registration_form = UserCreationForm(request.POST, prefix='registration')
166     if registration_form.is_valid():
167         user = registration_form.save()
168         user = auth.authenticate(
169             username=registration_form.cleaned_data['username'], 
170             password=registration_form.cleaned_data['password1']
171         )
172         auth.login(request, user)
173         response_data = {'success': True, 'errors': {}}
174     else:
175         response_data = {'success': False, 'errors': registration_form.errors}
176     return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
177
178
179 @require_POST
180 def login(request):
181     form = AuthenticationForm(data=request.POST, prefix='login')
182     if form.is_valid():
183         auth.login(request, form.get_user())
184         response_data = {'success': True, 'errors': {}}
185     else:
186         response_data = {'success': False, 'errors': form.errors}
187     return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
188
189
190 def book_sets(request, slug):
191     book = get_object_or_404(models.Book, slug=slug)
192     user_sets = models.Tag.objects.filter(category='set', user=request.user)
193     book_sets = book.tags.filter(category='set', user=request.user)
194     
195     if not request.user.is_authenticated():
196         return HttpResponse('<p>Aby zarządzać swoimi półkami, musisz się zalogować.</p>')
197     
198     if request.method == 'POST':
199         form = forms.ObjectSetsForm(book, request.user, request.POST)
200         if form.is_valid():
201             book.tags = ([models.Tag.objects.get(pk=id) for id in form.cleaned_data['set_ids']] +
202                 list(book.tags.filter(~Q(category='set') | ~Q(user=request.user))))
203             if request.is_ajax():
204                 return HttpResponse('<p>Półki zostały zapisane.</p>')
205             else:
206                 return HttpResponseRedirect('/')
207     else:
208         form = forms.ObjectSetsForm(book, request.user)
209         new_set_form = forms.NewSetForm()
210     
211     return render_to_response('catalogue/book_sets.html', locals(),
212         context_instance=RequestContext(request))
213
214
215 def fragment_sets(request, id):
216     fragment = get_object_or_404(models.Fragment, pk=id)
217     user_sets = models.Tag.objects.filter(category='set', user=request.user)
218     fragment_sets = fragment.tags.filter(category='set', user=request.user)
219
220     if not request.user.is_authenticated():
221         return HttpResponse('<p>Aby zarządzać swoimi półkami, musisz się zalogować.</p>')
222
223     if request.method == 'POST':
224         form = forms.ObjectSetsForm(fragment, request.user, request.POST)
225         if form.is_valid():
226             fragment.tags = ([models.Tag.objects.get(pk=id) for id in form.cleaned_data['set_ids']] +
227                 list(fragment.tags.filter(~Q(category='set') | ~Q(user=request.user))))
228             if request.is_ajax():
229                 return HttpResponse('<p>Półki zostały zapisane.</p>')
230             else:
231                 return HttpResponseRedirect('/')
232     else:
233         form = forms.ObjectSetsForm(fragment, request.user)
234         new_set_form = forms.NewSetForm()
235
236     return render_to_response('catalogue/fragment_sets.html', locals(),
237         context_instance=RequestContext(request))
238
239
240 @login_required
241 @require_POST
242 def new_set(request):
243     new_set_form = forms.NewSetForm(request.POST)
244     if new_set_form.is_valid():
245         new_set = new_set_form.save(request.user)
246         
247         if request.is_ajax():
248             return HttpResponse(u'<p>Półka <strong>%s</strong> została utworzona</p>' % new_set)
249         else:
250             return HttpResponseRedirect('/')
251     
252     return render_to_response('catalogue/book_sets.html', locals(),
253             context_instance=RequestContext(request))
254
255
256 @login_required
257 @require_POST
258 def delete_shelf(request, slug):
259     user_set = get_object_or_404(models.Tag, slug=slug, category='set', user=request.user)
260     user_set.delete()
261     
262     if request.is_ajax():
263         return HttpResponse(u'<p>Półka <strong>%s</strong> została usunięta</p>' % user_set.name)
264     else:
265         return HttpResponseRedirect('/')
266
267
268 @login_required
269 def user_shelves(request):
270     shelves = models.Tag.objects.filter(category='set', user=request.user)
271     new_set_form = forms.NewSetForm()
272     return render_to_response('catalogue/user_shelves.html', locals(),
273             context_instance=RequestContext(request))
274