1 from itertools
import count
3 from couchdbkit
.ext
.django
.schema
import *
5 from django
.template
.defaultfilters
import slugify
7 from mygpo
.utils
import multi_request_view
8 from mygpo
.decorators
import repeat_on_conflict
11 def assign_slug(obj
, generator
):
15 slug
= generator(obj
).get_slug()
16 _set_slug(obj
=obj
, slug
=slug
)
19 def assign_missing_episode_slugs(podcast
):
20 common_title
= podcast
.get_common_episode_title()
22 episodes
= EpisodesMissingSlugs(podcast
.get_id())
25 for episode
in episodes
:
26 slug
= EpisodeSlug(episode
, common_title
).get_slug()
27 _set_slug(obj
=episode
, slug
=slug
)
30 @repeat_on_conflict(['obj'])
31 def _set_slug(obj
, slug
):
38 class SlugGenerator(object):
39 """ Generates a unique slug for an object """
42 def __init__(self
, obj
):
44 raise ValueError('%(obj)s already has slug %(slug)s' % \
45 dict(obj
=obj
, slug
=obj
.slug
))
47 self
.base_slug
= self
._get
_base
_slug
(obj
)
51 def _get_base_slug(obj
):
54 base_slug
= slugify(obj
.title
)
59 def _get_existing_slugs():
64 """ Gets existing slugs and appends numbers until slug is unique """
65 if not self
.base_slug
:
68 existing_slugs
= self
._get
_existing
_slugs
()
70 if not self
.base_slug
in existing_slugs
:
71 return str(self
.base_slug
)
74 tmp_slug
= '%s-%d' % (self
.base_slug
, n
)
75 if not tmp_slug
in existing_slugs
:
76 # slugify returns SafeUnicode, we need a plain string
81 class PodcastGroupSlug(SlugGenerator
):
82 """ Generates slugs for Podcast Groups """
84 def _get_existing_slugs(self
):
85 from mygpo
.core
.models
import Podcast
87 res
= Podcast
.view('podcasts/by_slug',
88 startkey
= [self
.base_slug
, None],
89 endkey
= [self
.base_slug
+ 'ZZZZZ', None],
92 return [r
['key'][0] for r
in res
]
96 class PodcastSlug(PodcastGroupSlug
):
97 """ Generates slugs for Podcasts """
100 def _get_base_slug(podcast
):
101 base_slug
= SlugGenerator
._get
_base
_slug
(podcast
)
106 # append group_member_name to slug
107 if podcast
.group_member_name
:
108 member_slug
= slugify(podcast
.group_member_name
)
109 if member_slug
and not member_slug
in base_slug
:
110 base_slug
= '%s-%s' % (base_slug
, member_slug
)
116 class EpisodeSlug(SlugGenerator
):
117 """ Generates slugs for Episodes """
119 def __init__(self
, episode
, common_title
):
120 self
.common_title
= common_title
121 super(EpisodeSlug
, self
).__init
__(episode
)
122 self
.podcast_id
= episode
.podcast
125 def _get_base_slug(self
, obj
):
127 number
= obj
.get_episode_number(self
.common_title
)
131 short_title
= obj
.get_short_title(self
.common_title
)
133 return slugify(short_title
)
136 return slugify(obj
.title
)
141 def _get_existing_slugs(self
):
142 """ Episode slugs have to be unique within the Podcast """
143 from mygpo
.core
.models
import Episode
145 res
= Episode
.view('episodes/by_slug',
146 startkey
= [self
.podcast_id
, self
.base_slug
],
147 endkey
= [self
.podcast_id
, self
.base_slug
+ 'ZZZZZ'],
150 return [r
['key'][1] for r
in res
]
153 class ObjectsMissingSlugs(object):
154 """ A collections of objects missing a slug """
156 def __init__(self
, cls
, wrapper
=None, start
=[None], end
=[{}]):
158 self
.doc_type
= cls
._doc
_type
159 self
.wrapper
= wrapper
165 res
= self
.cls
.view('slugs/missing',
166 startkey
= [self
.doc_type
] + self
.end
,
167 endkey
= [self
.doc_type
] + self
.start
,
173 return res
.first()['value'] if res
else 0
178 return multi_request_view(self
.cls
, 'slugs/missing',
179 startkey
= [self
.doc_type
] + self
.end
,
180 endkey
= [self
.doc_type
] + self
.start
,
184 wrapper
= self
.wrapper
,
185 auto_advance
= False,
190 class PodcastsMissingSlugs(ObjectsMissingSlugs
):
191 """ Podcasts that don't have a slug (but could have one) """
194 from mygpo
.core
.models
import Podcast
195 super(PodcastsMissingSlugs
, self
).__init
__(Podcast
, self
._podcast
_wrapper
)
196 self
.kwargs
= {'wrap': False}
199 def _podcast_wrapper(r
):
200 from mygpo
.core
.models
import Podcast
, PodcastGroup
204 if doc
['doc_type'] == 'Podcast':
205 return Podcast
.wrap(doc
)
208 pg
= PodcastGroup
.wrap(doc
)
209 return pg
.get_podcast_by_id(pid
)
212 for r
in super(PodcastsMissingSlugs
, self
).__iter
__():
213 yield self
._podcast
_wrapper
(r
)
216 class EpisodesMissingSlugs(ObjectsMissingSlugs
):
217 """ Episodes that don't have a slug (but could have one) """
219 def __init__(self
, podcast_id
=None):
220 from mygpo
.core
.models
import Episode
223 start
= [podcast_id
, None]
224 end
= [podcast_id
, {}]
229 super(EpisodesMissingSlugs
, self
).__init
__(Episode
,
230 self
._episode
_wrapper
, start
, end
)
233 def _episode_wrapper(doc
):
234 from mygpo
.core
.models
import Episode
236 return Episode
.wrap(doc
)
239 class PodcastGroupsMissingSlugs(ObjectsMissingSlugs
):
240 """ Podcast Groups that don't have a slug (but could have one) """
243 from mygpo
.core
.models
import PodcastGroup
244 super(PodcastGroupsMissingSlugs
, self
).__init
__(PodcastGroup
,
248 def _group_wrapper(doc
):
249 from mygpo
.core
.models
import PodcastGroup
250 return PodcastGroup
.wrap(doc
)
253 class SlugMixin(DocumentSchema
):
254 slug
= StringProperty()
255 merged_slugs
= StringListProperty()
257 def set_slug(self
, slug
):
258 """ Set the main slug of the Podcast """
260 if not isinstance(slug
, basestring
):
261 raise ValueError('slug must be a string')
264 raise ValueError('slug cannot be empty')
267 self
.merged_slugs
.append(self
.slug
)