[Migration] remove CouchDB fulltext index
[mygpo.git] / mygpo / core / slugs.py
blob4890f764b0e0fffd15314a50f2527fe91c68ba34
1 from collections import defaultdict
3 from itertools import count
5 from couchdbkit.ext.django.schema import *
7 from django.utils.text import slugify
9 from mygpo.podcasts.models import Slug
10 from mygpo.decorators import repeat_on_conflict
11 from mygpo.utils import partition
14 # TODO: move to feed-downloader?
15 def assign_missing_episode_slugs(podcast):
16 common_title = podcast.get_common_episode_title()
18 episodes = Episode.objects.filter(podcast=podcast, slug__isnull=True)
20 for episode in episodes:
21 slug = EpisodeSlug(episode, common_title).get_slug()
22 episode.set_slug(slug)
25 class SlugGenerator(object):
26 """ Generates a unique slug for an object """
29 def __init__(self, obj, override_existing=False):
30 if obj.slug and not override_existing:
31 raise ValueError('%(obj)s already has slug %(slug)s' % \
32 dict(obj=obj, slug=obj.slug))
34 self.base_slug = self._get_base_slug(obj)
37 @staticmethod
38 def _get_base_slug(obj):
39 if not obj.title:
40 return None
41 base_slug = slugify(obj.title)
42 return base_slug
45 @staticmethod
46 def _get_existing_slugs():
47 return []
50 def get_slug(self):
51 """ Gets existing slugs and appends numbers until slug is unique """
52 if not self.base_slug:
53 return None
55 existing_slugs = self._get_existing_slugs()
57 if not self.base_slug in existing_slugs:
58 return str(self.base_slug)
60 for n in count(1):
61 tmp_slug = '%s-%d' % (self.base_slug, n)
62 if not tmp_slug in existing_slugs:
63 # slugify returns SafeUnicode, we need a plain string
64 return str(tmp_slug)
68 class PodcastGroupSlug(SlugGenerator):
69 """ Generates slugs for Podcast Groups """
71 def _get_existing_slugs(self):
72 query = Slug.objects.filter(scope__isnull=True,
73 slug__startswith=self.base_slug)
74 return [s['slug'] for s in query]
78 class PodcastSlug(PodcastGroupSlug):
79 """ Generates slugs for Podcasts """
81 @staticmethod
82 def _get_base_slug(podcast):
83 base_slug = SlugGenerator._get_base_slug(podcast)
85 if not base_slug:
86 return None
88 # append group_member_name to slug
89 if podcast.group_member_name:
90 member_slug = slugify(podcast.group_member_name)
91 if member_slug and not member_slug in base_slug:
92 base_slug = '%s-%s' % (base_slug, member_slug)
94 return base_slug
98 class EpisodeSlug(SlugGenerator):
99 """ Generates slugs for Episodes """
101 def __init__(self, episode, common_title, override_existing=False):
102 self.common_title = common_title
103 super(EpisodeSlug, self).__init__(episode, override_existing)
104 self.podcast_id = episode.podcast
107 def _get_base_slug(self, obj):
109 number = obj.get_episode_number(self.common_title)
110 if number:
111 return str(number)
113 short_title = obj.get_short_title(self.common_title)
114 if short_title:
115 return slugify(short_title)
117 if obj.title:
118 return slugify(obj.title)
120 return None
123 def _get_existing_slugs(self):
124 """ Episode slugs have to be unique within the Podcast """
125 query = Slug.objects.filter(scope=self.podcast_id,
126 slug__startswith=self.base_slug)
127 return [s['slug'] for s in query]
130 class SlugMixin(DocumentSchema):
131 slug = StringProperty()
132 merged_slugs = StringListProperty()
134 def set_slug(self, slug):
135 """ Set the main slug of the object """
137 if self.slug:
138 self.merged_slugs.append(self.slug)
140 self.merged_slugs = list(set(self.merged_slugs) - set([slug]))
142 self.slug = slug
145 def remove_slug(self, slug):
146 """ Removes the slug from the object """
148 # remove main slug
149 if self.slug == slug:
150 self.slug = None
152 # remove from merged slugs
153 self.merged_slugs = list(set(self.merged_slugs) - set([slug]))