[Podcasts] fix linking to podcasts w/o title
[mygpo.git] / mygpo / maintenance / migrate.py
blob2a19d83620138f9027b6e6e0b7fb746be0eb949e
1 from __future__ import unicode_literals
3 import json
4 from datetime import datetime
6 from django.contrib.contenttypes.models import ContentType
7 from django.contrib.auth.models import User
8 from django.db import reset_queries
10 from mygpo.podcasts.models import Tag
11 from mygpo.users.models import Chapter as C, EpisodeUserState, Client
12 from mygpo.chapters.models import Chapter
13 from mygpo.subscriptions.models import Subscription, PodcastConfig
14 from mygpo.podcastlists.models import PodcastList, PodcastListEntry
15 from mygpo.history.models import EpisodeHistoryEntry
16 from mygpo.podcasts.models import Episode, Podcast, PodcastGroup
17 from mygpo.favorites.models import FavoriteEpisode
18 from mygpo.votes.models import Vote
20 import logging
21 logger = logging.getLogger(__name__)
24 def to_maxlength(cls, field, val):
25 """ Cut val to the maximum length of cls's field """
26 max_length = cls._meta.get_field(field).max_length
27 orig_length = len(val)
28 if orig_length > max_length:
29 val = val[:max_length]
30 logger.warn('%s.%s length reduced from %d to %d',
31 cls.__name__, field, orig_length, max_length)
33 return val
36 #class EpisodeUserState(Document, SettingsMixin):
37 # ref_url = StringProperty(required=True)
38 # podcast_ref_url = StringProperty(required=True)
41 def migrate_estate(state):
42 """ migrate a podcast state """
44 try:
45 user = User.objects.get(profile__uuid=state.user)
46 except User.DoesNotExist:
47 logger.warn("User with ID '{id}' does not exist".format(
48 id=state.user))
49 return
51 try:
52 podcast = Podcast.objects.all().get_by_any_id(state.podcast)
53 except Podcast.DoesNotExist:
54 logger.warn("Podcast with ID '{id}' does not exist".format(
55 id=state.podcast))
56 return
58 try:
59 episode = Episode.objects.filter(podcast=podcast).get_by_any_id(state.episode)
60 except Episode.DoesNotExist:
61 logger.warn("Episode with ID '{id}' does not exist".format(
62 id=state.episode))
63 return
65 logger.info('Migrating episode state ({id}) for user {user} and episode {episode}'
66 .format(id=state._id, user=user, episode=episode))
68 for chapter in state.chapters:
69 migrate_chapter(user, episode, chapter)
71 for action in state.actions:
72 migrate_eaction(user, episode, state, action)
74 is_favorite = state.settings.get('is_favorite', False)
75 if is_favorite:
76 logger.info('Favorite episode')
77 FavoriteEpisode.objects.get_or_create(user=user, episode=episode)
78 else:
79 FavoriteEpisode.objects.filter(user=user, episode=episode).delete()
82 def migrate_chapter(user, episode, c):
84 chapter, created = Chapter.objects.get_or_create(
85 user=user,
86 episode=episode,
87 start=c.start,
88 end=c.end,
89 defaults = {
90 'label': c.label or '',
91 'advertisement': c.advertisement,
96 def migrate_eaction(user, episode, state, ea):
98 logger.info('Migrating {action} action'.format(action=ea.action))
100 if ea.device is None:
101 client = None
103 else:
104 try:
105 client = user.client_set.get(id=ea.device)
106 except Client.DoesNotExist:
107 logger.warn("Client '{cid}' does not exist; skipping".format(
108 cid=ea.device))
109 return
111 created = datetime.utcfromtimestamp(ea.upload_timestamp) if ea.upload_timestamp else datetime.utcnow()
112 entry, created = EpisodeHistoryEntry.objects.get_or_create(
113 user=user,
114 client=client,
115 episode=episode,
116 action=ea.action,
117 timestamp=ea.timestamp,
118 defaults = {
119 'created': created,
120 'started': ea.started,
121 'stopped': ea.playmark,
122 'total': ea.total,
123 'podcast_ref_url': state.podcast_ref_url,
124 'episode_ref_url': state.ref_url,
128 if created:
129 logger.info('Episode History Entry created: {user} {action} {episode}'
130 'on {client} @ {timestamp}'.format(user=user,
131 action=entry.action, episode=episode, client=client,
132 timestamp=entry.timestamp))
135 from couchdbkit import Database
136 db = Database('http://127.0.0.1:5984/mygpo_userdata_copy')
137 from couchdbkit.changes import ChangesStream, fold, foreach
140 MIGRATIONS = {
141 'PodcastUserState': (None, None),
142 'User': (None, None),
143 'Suggestions': (None, None),
144 'EpisodeUserState': (EpisodeUserState, migrate_estate),
145 'PodcastList': (None, None),
148 def migrate_change(c):
149 logger.info('Migrate seq %s', c['seq'])
150 doc = c['doc']
152 if not 'doc_type' in doc:
153 logger.warn('Document contains no doc_type: %r', doc)
154 return
156 doctype = doc['doc_type']
158 cls, migrate = MIGRATIONS[doctype]
160 if cls is None:
161 logger.warn("Skipping '%s'", doctype)
162 return
164 obj = cls.wrap(doc)
165 migrate(obj)
166 reset_queries()
169 def migrate(since=0, db=db):
170 with ChangesStream(db,
171 feed="continuous",
172 heartbeat=True,
173 include_docs=True,
174 since=since,
175 ) as stream:
176 for change in stream:
177 migrate_change(change)