Change all unicode() calls with six.text_type().
[mediagoblin.git] / mediagoblin / plugins / archivalook / tools.py
CommitLineData
b24c27d1 1# GNU MediaGoblin -- federated, autonomous media hosting
2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3#
4# This program is free software: you can redistribute it and/or modify
5# it 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
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
cc929223
BP
16
17import six
18
b24c27d1 19from mediagoblin.db.models import MediaEntry, User
20from mediagoblin.plugins.archivalook.models import FeaturedMedia
21from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
22from mediagoblin.plugins.archivalook.models import FeaturedMedia
23
24def get_media_entry_from_uploader_slug(uploader_username, slug):
25 """
26 Accepts two strings and searches to see if those strings identify a
27 MediaEntry
28
29 :param uploader_username A string representing the User.username
30 of the user who uploaded a piece of
31 media.
32 :param slug A string representing the slug of a
33 piece of media
34
35 :returns media A MediaEntry object or None if no entry
36 matches the specifications.
37 """
38 uploader = User.query.filter(
39 User.username == uploader_username).first()
40 media = MediaEntry.query.filter(
41 MediaEntry.get_uploader == uploader ).filter(
42 MediaEntry.slug == slug).first()
43 return media
44
45
46def parse_url(url):
47 """
48 A simple helper function that extracts the uploader and slug from a full url
49
50 :param url A string containing the url for a piece
51 of media. This should be in the format
52 of "/u/{user}/m/{media}/"
53
54 :returns (uploader_username, slug) Uploader_username is a unicode string
55 representing the username of the user
56 who uploaded the piece of media, slug is
57 the media entry's url slug.
58 """
cc929223 59 url = six.text_type(url)
b24c27d1 60 u_end, m_start, m_end, end = (url.find('/u/') + 3,
61 url.find('/m/'),
62 url.find('/m/') + 3,
63 url.rfind('/'))
64
65 uploader_username = url[u_end:m_start].strip()
66 slug = url[m_end:end].strip()
67
68 return uploader_username, slug
69
70
71def split_featured_media_list(featured_media):
72 """
73 This script is part of processing post request on the /mod/feature-media/
74 page. Post requests on these pages will only include the textbox, so this
75 script accepts the textbox's contents as its parameter.
76
77 :parameter featured_media A string from a submitted
78 textarea within the post request
79 on /mod/feature-media/
80
81 :returns all_featured_media A dictionary of the format
82 MediaEntry : 'string'
83 where MediaEntry is a featured
84 piece of media and 'string' is
85 a string representation of its
86 display type (primary, secondary
87 or tertiary)
88 """
89
cc929223 90 featured_media = six.text_type(featured_media)
b24c27d1 91 featured_media_list = featured_media.split("\n")
92 display_type = 0
93 media_already_featured = []
94 all_featured_media = []
95 for line in featured_media_list:
96 if line == '' or line.isspace(): continue
97 elif line.startswith(u'-'):
98 display_type += 1
99 elif display_type <= 0 or display_type > 3: continue
100 else:
101 uploader, slug = parse_url(line)
102 media = get_media_entry_from_uploader_slug(uploader, slug)
103 # Make sure the media entry referenced exists, and has not already
104 # been featured higher up the list
105 if media == None or media in media_already_featured: continue
106 media_already_featured.append(media)
107 all_featured_media.append((media,
108 [None,
109 u'primary',
110 u'secondary',
111 u'tertiary'][display_type]))
112
113 return all_featured_media
114
115
116def create_featured_media_textbox():
117 """
118 This script searches through the database of which media is featured and
119 returns a string of each entry in the proper format for use in the
120 /mod/feature-media/ page. This string will be used as the default text in
121 the textbox on that page.
122 """
123
124 primaries = FeaturedMedia.query.order_by(
125 FeaturedMedia.order.asc()).filter(
126 FeaturedMedia.display_type == u'primary').all()
127 secondaries = FeaturedMedia.query.order_by(
128 FeaturedMedia.order.asc()).filter(
129 FeaturedMedia.display_type == u'secondary').all()
130 tertiaries = FeaturedMedia.query.order_by(
131 FeaturedMedia.order.asc()).filter(
132 FeaturedMedia.display_type == u'tertiary').all()
133 output_text = u''
134 for display_type, feature_list in [
135 (_(u'Primary'),primaries),
136 (_(u'Secondary'),secondaries),
137 (_(u'Tertiary'),tertiaries)]:
138 output_text += _(
139 u"""-----------{display_type}-Features---------------------------
140""").format(display_type=display_type)
141 for feature in feature_list:
142 media_entry = feature.media_entry
143 output_text += u'/u/{uploader_username}/m/{media_slug}/\n'.format(
144 uploader_username = media_entry.get_uploader.username,
145 media_slug = media_entry.slug)
146
147
148 return output_text
149
150def automatically_add_new_feature(media_entry):
151 """
152 This function automates the addition of a new feature. New features will be
153 placed at the top of the feature stack as 'primary' features. All of the
154 current features are demoted one step to make room.
155
156 :param media_entry :type mediagoblin.db.MediaEntry
157 The media entry that will been
158 featured which this function
159 targets
160 """
161 # Set variables to determine which media entries should be pushed down to
162 # maintain the correct number of primary & secondary featured media. At this
163 # point the program assumes that there should be 1 primary feature and 2
164 # secondary features, but in the future this should be a variable editable
165 # by the site admin.
166 too_many_primaries = FeaturedMedia.query.filter(
167 FeaturedMedia.display_type==u'primary').count() >= 1
168 too_many_secondaries = FeaturedMedia.query.filter(
169 FeaturedMedia.display_type==u'secondary').count() >= 2
170 featured_first_to_last = FeaturedMedia.query.order_by(
171 FeaturedMedia.order.asc()).all()
172
173 for feature in featured_first_to_last:
174 # Some features have the option to demote or promote themselves to a
175 # different display_type, based on their position. But all features move
176 # up and down one step in the stack.
177 if (feature.is_last_of_type() and feature.display_type == u'primary'
178 and too_many_primaries):
179 feature.demote()
180 too_many_primaries = False
181 elif (feature.is_last_of_type() and feature.display_type == u'secondary'
182 and too_many_secondaries):
183 feature.demote()
184 too_many_secondaries = False
185 feature.move_down()
186 feature.save()
187
188 # Create the new feature at the top of the stack.
189 new_feature = FeaturedMedia(
190 media_entry=media_entry,
191 display_type=u"primary",
192 order=0)
193 new_feature.save()
194 return new_feature
195
196def automatically_remove_feature(media_entry):
197 """
198 This function automates the removal of a feature. All of the features below
199 them are promoted one step to close the gap.
200
201 :param media_entry :type mediagoblin.db.MediaEntry
202 The media entry that will been
203 removed which this function
204 targets
205 """
206 # Get the feature which will be deleted
207 target_feature = FeaturedMedia.query.filter(
208 FeaturedMedia.media_entry_id == media_entry.id).first()
209 # Find out which continuing features will have to be adjusted
210 featured_last_to_first = FeaturedMedia.query.filter(
211 FeaturedMedia.order>target_feature.order).order_by(
212 FeaturedMedia.order.desc()).all()
213
214 for feature in featured_last_to_first:
215 # Maintain the current arrangement of primary/secondary/tertiary
216 # features by moving all the features below the deleted one up on slot
217 # and promoting any features in the proper position.
218 if feature.is_first_of_type():
219 feature.promote()
220 feature.move_up()
221 feature.save()
222
223 # Delete the feature, now that it's space has been closed
224 target_feature.delete()
225
226def promote_feature(media_entry):
227 """
228 This function takes a current feature and moves it up the stack so that it
229 will be displayed higher up. It swaps the place of the selected feature for
230 the one above it, or if relevant raises the display_type of the feature up
231 one rung (ie. from 'tertiary' to 'secondary')
232
233 :param media_entry :type mediagoblin.db.MediaEntry
234 The media entry that has been
235 featured which this function
236 targets
237 """
238 # Get the FeaturedMedia object
239 target_feature = FeaturedMedia.query.filter(
240 FeaturedMedia.media_entry_id == media_entry.id).first()
241 # Get the first Feature with a lower order than the target
242 above_feature = FeaturedMedia.query.filter(
243 FeaturedMedia.order < target_feature.order).order_by(
244 FeaturedMedia.order.desc()).first()
245 # If the feature is not the uppermost one
246 if above_feature is not None:
247 # Swap the positions of the target feature with the one before it
248 (target_feature.order,
249 above_feature.order) = above_feature.order, target_feature.order
250 (target_feature.display_type,
251 above_feature.display_type) = (above_feature.display_type,
252 target_feature.display_type)
253 above_feature.save()
254 # Change the feature's display type to a more prominent one
255 elif target_feature.display_type == u'secondary':
256 target_feature.display_type = u'primary'
257 elif target_feature.display_type == u'tertiary':
258 target_feature.display_type = u'secondary'
259 target_feature.save()
260
261def demote_feature(media_entry):
262 """
263 This function takes a current feature and moves it down the stack so that it
264 will be displayed lower down. It swaps the place of the selected feature for
265 the one below it, or if relevant lowers the display_type of the feature down
266 one rung (ie. from 'secondary' to 'tertiary')
267
268 :param media_entry :type mediagoblin.db.MediaEntry
269 The media entry that has been
270 featured which this function
271 targets
272 """
273 # Get the FeaturedMedia object
274 target_feature = FeaturedMedia.query.filter(
275 FeaturedMedia.media_entry_id == media_entry.id).first()
276 # Get the first Feature with a higher order than the target
277 below_feature = FeaturedMedia.query.filter(
278 FeaturedMedia.order > target_feature.order).order_by(
279 FeaturedMedia.order.asc()).first()
280 # If the feature is not the lowest one
281 if below_feature != None:
282 # Swap the positions of the target feature with the one after it
283 (target_feature.order,
284 below_feature.order) = below_feature.order, target_feature.order
285 (target_feature.display_type,
286 below_feature.display_type) = (below_feature.display_type,
287 target_feature.display_type)
288 below_feature.save()
289 # Change the feature's display type to a less prominent one
290 elif target_feature.display_type == u'secondary':
291 target_feature.display_type = u'tertiary'
292 elif target_feature.display_type == u'primary':
293 target_feature.display_type = u'secondary'
294 target_feature.save()
295