enable new failpodder error pages (404, 500)
[mygpo.git] / mygpo / migrate.py
blobe38a521648e3346c8fbe0fa61813f02d45191a50
1 from datetime import datetime
2 from couchdbkit import Server, Document
4 from mygpo.core.models import Podcast, PodcastGroup, Episode, SubscriberData
5 from mygpo.users.models import Rating, EpisodeAction, User, Device, SubscriptionAction
6 from mygpo.log import log
7 from mygpo import utils
8 from mygpo.decorators import repeat_on_conflict
10 """
11 This module contains methods for converting objects from the old
12 ORM-based backend to the CouchDB-based backend
13 """
16 def save_podcast_signal(sender, instance=False, **kwargs):
17 """
18 Signal-handler for creating/updating a CouchDB-based podcast when
19 an ORM-based podcast has been saved
20 """
21 if not instance:
22 return
24 try:
25 newp = Podcast.for_oldid(instance.id)
26 if newp:
27 update_podcast(oldp=instance, newp=newp)
28 else:
29 create_podcast(instance)
31 except Exception, e:
32 log('error while updating CouchDB-Podcast: %s' % repr(e))
35 def delete_podcast_signal(sender, instance=False, **kwargs):
36 """
37 Signal-handler for deleting a CouchDB-based podcast when an ORM-based
38 podcast is deleted
39 """
40 if not instance:
41 return
43 try:
44 newp = Podcast.for_oldid(instance.id)
45 if newp:
46 newp.delete()
48 except Exception, e:
49 log('error while deleting CouchDB-Podcast: %s' % repr(e))
52 def save_episode_signal(sender, instance=False, **kwargs):
53 """
54 Signal-handler for creating/updating a CouchDB-based episode when
55 an ORM-based episode has been saved
56 """
57 if not instance:
58 return
60 try:
61 newe = Episode.for_oldid(instance.id)
62 newp = Podcast.for_id(newe.podcast)
64 if newe:
65 update_episode(instance, newe, newp)
66 else:
67 create_episode(instance)
69 except Exception, e:
70 log('error while updating CouchDB Episode: %s' % repr(e))
74 @repeat_on_conflict(['oldp'])
75 def update_podcast(oldp, newp):
76 """
77 Updates newp based on oldp and returns True if an update was necessary
78 """
79 updated = False
81 # Update related podcasts
82 from mygpo.data.models import RelatedPodcast
83 if newp._id:
84 rel_podcast = set([r.rel_podcast for r in RelatedPodcast.objects.filter(ref_podcast=oldp)])
85 rel = list(podcasts_to_ids(rel_podcast))
86 if newp.related_podcasts != rel:
87 newp.related_podcasts = rel
88 updated = True
90 # Update Group-assignment
91 if oldp.group:
92 group = get_group(oldp.group.id)
93 if not newp in list(group.podcasts):
94 newp = group.add_podcast(newp)
95 updated = True
97 # Update subscriber-data
98 from mygpo.data.models import HistoricPodcastData
99 sub = HistoricPodcastData.objects.filter(podcast=oldp).order_by('date')
100 if sub.count() and len(newp.subscribers) != sub.count():
101 transf = lambda s: SubscriberData(
102 timestamp = datetime(s.date.year, s.date.month, s.date.day),
103 subscriber_count = s.subscriber_count)
104 check = lambda s: s.date.weekday() == 6
106 newp.subscribers = newp.subscribers + map(transf, filter(check, sub))
107 newp.subscribers = utils.set_cmp(newp.subscribers, lambda x: x.timestamp)
108 newp.subscribers = list(sorted(set(newp.subscribers), key=lambda s: s.timestamp))
109 updated = True
111 PROPERTIES = ('language', 'content_types', 'title',
112 'description', 'link', 'last_update', 'logo_url',
113 'author')
115 for p in PROPERTIES:
116 if getattr(newp, p, None) != getattr(oldp, p, None):
117 setattr(newp, p, getattr(oldp, p, None))
118 updated = True
120 if not oldp.url in newp.urls:
121 newp.urls.append(oldp.url)
122 updated = True
124 if updated:
125 newp.save()
127 return updated
130 def create_podcast(oldp, sparse=False):
132 Creates a (CouchDB) Podcast document from a (ORM) Podcast object
134 p = Podcast()
135 p.oldid = oldp.id
136 p.save()
137 if not sparse:
138 update_podcast(oldp=oldp, newp=p)
140 return p
143 def get_group(oldid):
144 group = PodcastGroup.for_oldid(oldid)
145 if not group:
146 group = create_podcastgroup(oldid)
148 return group
151 def create_podcastgroup(oldid):
153 Creates a (CouchDB) PodcastGroup document from a
154 (ORM) PodcastGroup object
156 g = PodcastGroup()
157 g.oldid = oldid
158 g.save()
159 return g
162 def get_blacklist(blacklist):
164 Returns a list of Ids of all blacklisted podcasts
166 blacklisted = [b.podcast for b in blacklist]
167 blacklist_ids = []
168 for p in blacklisted:
169 newp = Podcast.for_oldid(p.id)
170 if not newp:
171 newp = create_podcast(p)
173 blacklist_ids.append(newp._id)
174 return blacklist_ids
177 def get_ratings(ratings):
179 Returns a list of Rating-objects, based on the relational Ratings
181 conv = lambda r: Rating(rating=r.rating, timestamp=r.timestamp)
182 return map(conv, ratings)
185 def podcasts_to_ids(podcasts):
186 for p in podcasts:
187 podcast = Podcast.for_oldid(p.id)
188 if not podcast:
189 podcast = create_podcast(p, sparse=True)
190 yield podcast.get_id()
193 def get_or_migrate_podcast(oldp):
194 return Podcast.for_oldid(oldp.id) or create_podcast(oldp)
197 def create_episode_action(action):
198 a = EpisodeAction()
199 a.action = action.action
200 a.timestamp = action.timestamp
201 a.device_oldid = action.device.id if action.device else None
202 a.started = action.started
203 a.playmark = action.playmark
204 return a
206 def create_episode(olde, sparse=False):
207 podcast = get_or_migrate_podcast(olde.podcast)
208 e = Episode()
209 e.oldid = olde.id
210 e.urls.append(olde.url)
211 e.podcast = podcast.get_id()
213 if not sparse:
214 update_episode(olde, e, podcast)
216 e.save()
218 return e
221 def get_or_migrate_episode(olde):
222 return Episode.for_oldid(olde.id) or create_episode(olde)
225 def update_episode(olde, newe, podcast):
226 updated = False
228 if not olde.url in newe.urls:
229 newe.urls.append(olde.url)
230 updated = False
232 PROPERTIES = ('title', 'description', 'link',
233 'author', 'duration', 'filesize', 'language',
234 'last_update', 'outdated')
236 for p in PROPERTIES:
237 if getattr(newe, p, None) != getattr(olde, p, None):
238 setattr(newe, p, getattr(olde, p, None))
239 updated = True
242 if newe.released != olde.timestamp:
243 newe.released = olde.timestamp
244 updated = True
246 if olde.mimetype and not olde.mimetype in newe.mimetypes:
247 newe.mimetypes.append(olde.mimetype)
248 updated = True
250 @repeat_on_conflict(['newe'])
251 def save(newe):
252 newe.save()
254 if updated:
255 save(newe=newe)
257 return updated
260 def get_or_migrate_user(user):
261 u = User.for_oldid(user.id)
262 if u:
263 return u
265 u = User()
266 u.oldid = user.id
267 u.username = user.username
268 u.save()
269 return u
272 def get_or_migrate_device(device, user=None):
273 d = Device.for_user_uid(device.user, device.uid)
274 if d:
275 return d
277 d = Device()
278 d.oldid = device.id
279 d.uid = device.uid
280 d.name = device.name
281 d.type = device.type
282 d.deleted = device.deleted
283 u = user or get_or_migrate_user(device.user)
284 u.devices.append(d)
285 u.save()
286 return d
289 def migrate_subscription_action(old_action):
290 action = SubscriptionAction()
291 action.timestamp = old_action.timestamp
292 action.action = 'subscribe' if old_action.action == 1 else 'unsubscribe'
293 action.device = get_or_migrate_device(old_action.device).id
294 return action