2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 from django
.core
.urlresolvers
import reverse
19 from django
.http
import HttpResponseRedirect
, Http404
20 from django
.contrib
.auth
.models
import User
21 from django
.template
import RequestContext
22 from mygpo
.core
import models
23 from mygpo
.api
.models
import Podcast
, Episode
, Device
, EpisodeAction
, SubscriptionAction
, ToplistEntry
, Subscription
, UserProfile
24 from mygpo
.data
.models
import PodcastTag
25 from django
.contrib
.auth
.decorators
import login_required
26 from django
.shortcuts
import render_to_response
27 from datetime
import datetime
, timedelta
28 from django
.contrib
.sites
.models
import Site
29 from mygpo
.constants
import PODCAST_LOGO_SIZE
, PODCAST_LOGO_BIG_SIZE
30 from mygpo
.web
import utils
31 from mygpo
.api
import backend
32 from mygpo
.migrate
import use_couchdb
40 if request
.user
.is_authenticated():
41 return dashboard(request
)
43 return welcome(request
)
46 def welcome(request
, toplist_entries
=10):
47 current_site
= Site
.objects
.get_current()
48 podcasts
= Podcast
.objects
.count()
49 users
= User
.objects
.filter(is_active
=True).count()
50 episodes
= Episode
.objects
.count()
51 hours_listened
= utils
.get_hours_listened()
54 lang
= utils
.process_lang_params(request
, '/toplist/')
55 except utils
.UpdatedException
, updated
:
59 entries
= ToplistEntry
.objects
.all()[:toplist_entries
]
61 entries
= backend
.get_toplist(toplist_entries
, lang
)
63 toplist
= [e
.get_podcast() for e
in entries
]
64 sponsored_podcast
= utils
.get_sponsored_podcast()
66 return render_to_response('home.html', {
67 'podcast_count': podcasts
,
69 'episode_count': episodes
,
71 'hours_listened': hours_listened
,
73 'sponsored_podcast': sponsored_podcast
,
74 }, context_instance
=RequestContext(request
))
78 def dashboard(request
, episode_count
=10):
79 site
= Site
.objects
.get_current()
80 devices
= Device
.objects
.filter(user
=request
.user
, deleted
=False)
81 subscribed_podcasts
= set([s
.podcast
for s
in Subscription
.objects
.filter(user
=request
.user
)])
83 tomorrow
= datetime
.today() + timedelta(days
=1)
84 newest_episodes
= Episode
.objects
.filter(podcast__in
=subscribed_podcasts
).filter(timestamp__lt
=tomorrow
).order_by('-timestamp')[:episode_count
]
86 lang
= utils
.get_accepted_lang(request
)
87 lang
= utils
.sanitize_language_codes(lang
)
89 random_podcasts
= backend
.get_random_picks(lang
)[:5]
90 sponsored_podcast
= utils
.get_sponsored_podcast()
92 return render_to_response('dashboard.html', {
95 'subscribed_podcasts': subscribed_podcasts
,
96 'newest_episodes': newest_episodes
,
97 'random_podcasts': random_podcasts
,
98 'sponsored_podcast': sponsored_podcast
,
99 }, context_instance
=RequestContext(request
))
102 def cover_art(request
, size
, filename
):
104 if size
not in (PODCAST_LOGO_SIZE
, PODCAST_LOGO_BIG_SIZE
):
105 raise Http404('Wrong size')
107 # XXX: Is there a "cleaner" way to get the root directory of the installation?
108 root
= os
.path
.join(os
.path
.dirname(__file__
), '..', '..', '..')
109 target
= os
.path
.join(root
, 'htdocs', 'media', 'logo', str(size
), filename
+'.jpg')
110 filepath
= os
.path
.join(root
, 'htdocs', 'media', 'logo', filename
)
112 if os
.path
.exists(target
):
113 return HttpResponseRedirect('/media/logo/%s/%s.jpg' % (str(size
), filename
))
115 if os
.path
.exists(filepath
):
116 target_dir
= os
.path
.dirname(target
)
117 if not os
.path
.isdir(target_dir
):
118 os
.makedirs(target_dir
)
121 im
= Image
.open(filepath
)
122 if im
.mode
not in ('RGB', 'RGBA'):
123 im
= im
.convert('RGB')
125 raise Http404('Cannot open cover file')
128 resized
= im
.resize((size
, size
), Image
.ANTIALIAS
)
130 # raised when trying to read an interlaced PNG; we use the original instead
131 return HttpResponseRedirect('/media/logo/%s' % filename
)
133 # If it's a RGBA image, composite it onto a white background for JPEG
134 if resized
.mode
== 'RGBA':
135 background
= Image
.new('RGB', resized
.size
)
136 draw
= ImageDraw
.Draw(background
)
137 draw
.rectangle((-1, -1, resized
.size
[0]+1, resized
.size
[1]+1), \
138 fill
=(255, 255, 255))
140 resized
= Image
.composite(resized
, background
, resized
)
142 io
= StringIO
.StringIO()
143 resized
.save(io
, 'JPEG', optimize
=True, progression
=True, quality
=80)
146 fp
= open(target
, 'wb')
150 return HttpResponseRedirect('/media/logo/%s/%s.jpg' % (str(size
), filename
))
152 raise Http404('Cover art not available')
155 def history(request
, len=15, device_id
=None):
157 devices
= Device
.objects
.filter(id=device_id
)
159 devices
= Device
.objects
.filter(user
=request
.user
)
161 history
= SubscriptionAction
.objects
.filter(device__in
=devices
).order_by('-timestamp')[:len]
162 episodehistory
= EpisodeAction
.objects
.filter(device__in
=devices
).order_by('-timestamp')[:len]
167 generalhistory
.append(row
)
168 for row
in episodehistory
:
169 generalhistory
.append(row
)
171 generalhistory
.sort(key
=lambda x
: x
.timestamp
,reverse
=True)
173 return render_to_response('history.html', {
174 'generalhistory': generalhistory
,
175 'singledevice': devices
[0] if device_id
else None
176 }, context_instance
=RequestContext(request
))
181 def blacklist(request
, podcast_id
):
182 blacklisted_podcast
= models
.Podcast
.for_oldid(podcast_id
)
183 suggestion
= models
.Suggestions
.for_user_oldid(request
.user
.id)
184 suggestion
.blacklist
.append(blacklisted_podcast
._id
)
187 p
, _created
= UserProfile
.objects
.get_or_create(user
=request
.user
)
188 p
.suggestion_up_to_date
= False
191 return HttpResponseRedirect(reverse('suggestions'))
196 def rate_suggestions(request
):
197 rating_val
= int(request
.GET
.get('rate', None))
199 if rating_val
in (1, -1):
200 suggestion
= models
.Suggestions
.for_user_oldid(request
.user
.id)
201 rating
= models
.Rating(rating
=rating_val
)
202 suggestion
.ratings
.append(rating
)
204 # TODO: when we use Django messaging system,
205 # add a message for successful rating here
208 return HttpResponseRedirect(reverse('suggestions'))
213 def suggestions(request
):
214 suggestion_obj
= models
.Suggestions
.for_user_oldid(request
.user
.id)
215 suggestions
= [p
.get_old_obj() for p
in suggestion_obj
.get_podcasts()]
216 current_site
= Site
.objects
.get_current()
217 return render_to_response('suggestions.html', {
218 'entries': suggestions
,
220 }, context_instance
=RequestContext(request
))
227 for tag
in PodcastTag
.objects
.filter(user
=request
.user
):
228 if not tag
.podcast
in tags_podcast
:
229 tags_podcast
[tag
.podcast
] = []
231 if not tag
.tag
in tags_tag
:
232 tags_tag
[tag
.tag
] = []
235 tags_podcast
[tag
.podcast
].append(tag
)
236 tags_tag
[tag
.tag
].append(tag
)
238 return render_to_response('mytags.html', {
239 'tags_podcast': tags_podcast
,
240 'tags_tag': tags_tag
,
241 }, context_instance
=RequestContext(request
))