1 from __future__
import unicode_literals
4 from datetime
import datetime
6 from django
.contrib
.contenttypes
.models
import ContentType
7 from django
.contrib
.auth
.models
import User
8 from django
.utils
.text
import slugify
9 from django
.db
import reset_queries
11 from mygpo
.podcasts
.models
import Tag
12 from mygpo
.users
.models
import Chapter
as C
, EpisodeUserState
, Client
13 from mygpo
.chapters
.models
import Chapter
14 from mygpo
.subscriptions
.models
import Subscription
, PodcastConfig
15 from mygpo
.podcastlists
.models
import PodcastList
, PodcastListEntry
16 from mygpo
.history
.models
import EpisodeHistoryEntry
17 from mygpo
.podcasts
.models
import Episode
, Podcast
, PodcastGroup
18 from mygpo
.directory
.models
import Category
as C
19 from mygpo
.categories
.models
import Category
, CategoryEntry
, CategoryTag
20 from mygpo
.favorites
.models
import FavoriteEpisode
21 from mygpo
.votes
.models
import Vote
24 logger
= logging
.getLogger(__name__
)
27 def to_maxlength(cls
, field
, val
):
28 """ Cut val to the maximum length of cls's field """
29 max_length
= cls
._meta
.get_field(field
).max_length
30 orig_length
= len(val
)
31 if orig_length
> max_length
:
32 val
= val
[:max_length
]
33 logger
.warn('%s.%s length reduced from %d to %d',
34 cls
.__name
__, field
, orig_length
, max_length
)
39 #class EpisodeUserState(Document, SettingsMixin):
40 # ref_url = StringProperty(required=True)
41 # podcast_ref_url = StringProperty(required=True)
44 def migrate_estate(state
):
45 """ migrate a podcast state """
48 user
= User
.objects
.get(profile__uuid
=state
.user
)
49 except User
.DoesNotExist
:
50 logger
.warn("User with ID '{id}' does not exist".format(
55 podcast
= Podcast
.objects
.all().get_by_any_id(state
.podcast
)
56 except Podcast
.DoesNotExist
:
57 logger
.warn("Podcast with ID '{id}' does not exist".format(
62 episode
= Episode
.objects
.filter(podcast
=podcast
).get_by_any_id(state
.episode
)
63 except Episode
.DoesNotExist
:
64 logger
.warn("Episode with ID '{id}' does not exist".format(
68 logger
.info('Migrating episode state ({id}) for user {user} and episode {episode}'
69 .format(id=state
._id
, user
=user
, episode
=episode
))
71 for chapter
in state
.chapters
:
72 migrate_chapter(user
, episode
, chapter
)
74 for action
in state
.actions
:
75 migrate_eaction(user
, episode
, state
, action
)
77 is_favorite
= state
.settings
.get('is_favorite', False)
79 logger
.info('Favorite episode')
80 FavoriteEpisode
.objects
.get_or_create(user
=user
, episode
=episode
)
82 FavoriteEpisode
.objects
.filter(user
=user
, episode
=episode
).delete()
85 def migrate_chapter(user
, episode
, c
):
87 chapter
, created
= Chapter
.objects
.get_or_create(
93 'label': c
.label
or '',
94 'advertisement': c
.advertisement
,
99 def migrate_eaction(user
, episode
, state
, ea
):
101 logger
.info('Migrating {action} action'.format(action
=ea
.action
))
103 if ea
.device
is None:
108 client
= user
.client_set
.get(id=ea
.device
)
109 except Client
.DoesNotExist
:
110 logger
.warn("Client '{cid}' does not exist; skipping".format(
114 created
= datetime
.utcfromtimestamp(ea
.upload_timestamp
) if ea
.upload_timestamp
else datetime
.utcnow()
115 entry
, created
= EpisodeHistoryEntry
.objects
.get_or_create(
120 timestamp
=ea
.timestamp
,
123 'started': ea
.started
,
124 'stopped': ea
.playmark
,
126 'podcast_ref_url': state
.podcast_ref_url
,
127 'episode_ref_url': state
.ref_url
,
132 logger
.info('Episode History Entry created: {user} {action} {episode}'
133 'on {client} @ {timestamp}'.format(user
=user
,
134 action
=entry
.action
, episode
=episode
, client
=client
,
135 timestamp
=entry
.timestamp
))
138 def migrate_category(cat
):
140 logger
.info('Migrating category {category}'.format(category
=cat
))
141 category
, created
= Category
.objects
.get_or_create(
142 title
=to_maxlength(Category
, 'title', cat
.label
)
145 for spelling
in cat
.spellings
+ [cat
.label
]:
146 s
, c
= CategoryTag
.objects
.get_or_create(
147 tag
=slugify(to_maxlength(CategoryTag
, 'tag', spelling
.strip())),
149 'category': category
,
153 for podcast_id
in cat
.podcasts
:
154 if isinstance(podcast_id
, dict):
155 podcast_id
= podcast_id
['podcast']
156 logger
.info(repr(podcast_id
))
159 podcast
= Podcast
.objects
.all().get_by_any_id(podcast_id
)
160 except Podcast
.DoesNotExist
:
161 logger
.warn("Podcast with ID '{podcast_id}' does not exist".format(
162 podcast_id
=podcast_id
))
165 entry
, c
= CategoryEntry
.objects
.get_or_create(category
=category
,
171 from couchdbkit
import Database
172 db
= Database('http://127.0.0.1:5984/mygpo_userdata_copy')
173 from couchdbkit
.changes
import ChangesStream
, fold
, foreach
177 'PodcastUserState': (None, None),
178 'User': (None, None),
179 'Suggestions': (None, None),
180 'EpisodeUserState': (EpisodeUserState
, migrate_estate
),
181 'PodcastList': (None, None),
182 'Category': (C
, migrate_category
),
185 def migrate_change(c
):
186 logger
.info('Migrate seq %s', c
['seq'])
189 if not 'doc_type' in doc
:
190 logger
.warn('Document contains no doc_type: %r', doc
)
193 doctype
= doc
['doc_type']
195 cls
, migrate
= MIGRATIONS
[doctype
]
198 logger
.warn("Skipping '%s'", doctype
)
206 def migrate(since
=0, db
=db
):
207 with
ChangesStream(db
,
213 for change
in stream
:
214 migrate_change(change
)