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 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 action
in state
.actions
:
72 migrate_eaction(user
, episode
, state
, action
)
75 def migrate_eaction(user
, episode
, state
, ea
):
77 logger
.info('Migrating {action} action'.format(action
=ea
.action
))
84 client
= user
.client_set
.get(id=ea
.device
)
85 except Client
.DoesNotExist
:
86 logger
.warn("Client '{cid}' does not exist; skipping".format(
90 created
= datetime
.utcfromtimestamp(ea
.upload_timestamp
) if ea
.upload_timestamp
else datetime
.utcnow()
91 entry
, created
= EpisodeHistoryEntry
.objects
.get_or_create(
96 timestamp
=ea
.timestamp
,
99 'started': ea
.started
,
100 'stopped': ea
.playmark
,
102 'podcast_ref_url': state
.podcast_ref_url
,
103 'episode_ref_url': state
.ref_url
,
108 logger
.info('Episode History Entry created: {user} {action} {episode}'
109 'on {client} @ {timestamp}'.format(user
=user
,
110 action
=entry
.action
, episode
=episode
, client
=client
,
111 timestamp
=entry
.timestamp
))
114 def migrate_category(cat
):
116 logger
.info('Migrating category {category}'.format(category
=cat
))
117 category
, created
= Category
.objects
.get_or_create(
118 title
=to_maxlength(Category
, 'title', cat
.label
)
121 for spelling
in cat
.spellings
+ [cat
.label
]:
122 s
, c
= CategoryTag
.objects
.get_or_create(
123 tag
=slugify(to_maxlength(CategoryTag
, 'tag', spelling
.strip())),
125 'category': category
,
129 for podcast_id
in cat
.podcasts
:
130 if isinstance(podcast_id
, dict):
131 podcast_id
= podcast_id
['podcast']
132 logger
.info(repr(podcast_id
))
135 podcast
= Podcast
.objects
.all().get_by_any_id(podcast_id
)
136 except Podcast
.DoesNotExist
:
137 logger
.warn("Podcast with ID '{podcast_id}' does not exist".format(
138 podcast_id
=podcast_id
))
141 entry
, c
= CategoryEntry
.objects
.get_or_create(category
=category
,
147 from couchdbkit
import Database
148 db
= Database('http://127.0.0.1:5984/mygpo_userdata_copy')
149 from couchdbkit
.changes
import ChangesStream
, fold
, foreach
153 'PodcastUserState': (None, None),
154 'User': (None, None),
155 'Suggestions': (None, None),
156 'EpisodeUserState': (EpisodeUserState
, migrate_estate
),
157 'PodcastList': (None, None),
158 'Category': (C
, migrate_category
),
161 def migrate_change(c
):
162 logger
.info('Migrate seq %s', c
['seq'])
165 if not 'doc_type' in doc
:
166 logger
.warn('Document contains no doc_type: %r', doc
)
169 doctype
= doc
['doc_type']
171 cls
, migrate
= MIGRATIONS
[doctype
]
174 logger
.warn("Skipping '%s'", doctype
)
182 def migrate(since
=0, db
=db
):
183 with
ChangesStream(db
,
189 for change
in stream
:
190 migrate_change(change
)