def format_podcast_list(obj_list, format, title, get_podcast=None, json_map=lambda x: x.url, jsonp_padding=None, xml_template=None, request=None, template_args={}): """ Formats a list of podcasts for use in a API response obj_list is a list of podcasts or objects that contain podcasts format is one if txt, opml or json title is a label of the list if obj_list is a list of objects containing podcasts, get_podcast is the function used to get the podcast out of the each of these objects json_map is a function returning the contents of an object (from obj_list) that should be contained in the result (only used for format='json') """ def default_get_podcast(p): return p get_podcast = get_podcast or default_get_podcast if format == 'txt': podcasts = map(get_podcast, obj_list) s = '\n'.join([p.url for p in podcasts] + ['']) return HttpResponse(s, content_type='text/plain') elif format == 'opml': podcasts = map(get_podcast, obj_list) exporter = Exporter(title) opml = exporter.generate(podcasts) return HttpResponse(opml, content_type='text/xml') elif format == 'json': objs = list(map(json_map, obj_list)) return JsonResponse(objs) elif format == 'jsonp': ALLOWED_FUNCNAME = string.ascii_letters + string.digits + '_' if not jsonp_padding: return HttpResponseBadRequest('For a JSONP response, specify the name of the callback function in the jsonp parameter') if any(x not in ALLOWED_FUNCNAME for x in jsonp_padding): return HttpResponseBadRequest('JSONP padding can only contain the characters %(char)s' % {'char': ALLOWED_FUNCNAME}) objs = map(json_map, obj_list) return JsonResponse(objs, jsonp_padding=jsonp_padding) elif format == 'xml': if None in (xml_template, request): return HttpResponseBadRequest('XML is not a valid format for this request') podcasts = map(json_map, obj_list) template_args.update({'podcasts': podcasts}) return render(request, xml_template, template_args, content_type='application/xml') else: return None
def tag_podcasts(request, tag, count): count = parse_range(count, 1, 100, 100) try: category = Category.objects.get(tags__tag=tag) except Category.DoesNotExist: return JsonResponse([]) domain = RequestSite(request).domain entries = category.entries.all().prefetch_related('podcast', 'podcast__slugs', 'podcast__urls')[:count] resp = [podcast_data(entry.podcast, domain) for entry in entries] return JsonResponse(resp)
def get(self, request, username, device_uid): now = datetime.utcnow() now_ = get_timestamp(now) user = request.user try: device = user.client_set.get(uid=device_uid) except Client.DoesNotExist as e: return HttpResponseNotFound(str(e)) try: since = self.get_since(request) except ValueError as e: return HttpResponseBadRequest(str(e)) include_actions = parse_bool(request.GET.get("include_actions", False)) domain = RequestSite(request).domain add, rem, subscriptions = self.get_subscription_changes( user, device, since, now, domain) updates = self.get_episode_changes(user, subscriptions, domain, include_actions, since) return JsonResponse({ "add": add, "rem": rem, "updates": updates, "timestamp": get_timestamp(now), })
def post(self, request, version, username, device_uid): """ Client sends subscription updates """ now = get_timestamp(datetime.utcnow()) logger.info( "Subscription Upload @{username}/{device_uid}".format( username=request.user.username, device_uid=device_uid ) ) d = get_device( request.user, device_uid, request.META.get("HTTP_USER_AGENT", "") ) actions = self.parsed_body(request) add = list(filter(None, actions.get("add", []))) rem = list(filter(None, actions.get("remove", []))) logger.info( "Subscription Upload @{username}/{device_uid}: add " "{num_add}, remove {num_remove}".format( username=request.user.username, device_uid=device_uid, num_add=len(add), num_remove=len(rem), ) ) update_urls = self.update_subscriptions(request.user, d, add, rem) return JsonResponse({"timestamp": now, "update_urls": update_urls})
def post(self, request, version, username, device_uid): """ Client sends subscription updates """ now = get_timestamp(datetime.utcnow()) logger.info('Subscription Upload @{username}/{device_uid}'.format( username=request.user.username, device_uid=device_uid)) d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) actions = self.parsed_body(request) add = list(filter(None, actions.get('add', []))) rem = list(filter(None, actions.get('remove', []))) logger.info('Subscription Upload @{username}/{device_uid}: add ' '{num_add}, remove {num_remove}'.format( username=request.user.username, device_uid=device_uid, num_add=len(add), num_remove=len(rem))) update_urls = self.update_subscriptions(request.user, d, add, rem) return JsonResponse({ 'timestamp': now, 'update_urls': update_urls, })
def get(self, request, version, username, device_uid): """ Client retrieves subscription updates """ now = datetime.utcnow() user = request.user device = get_object_or_404(Client, user=user, uid=device_uid) since = self.get_since(request) add, rem, until = self.get_changes(user, device, since, now) return JsonResponse({"add": add, "remove": rem, "timestamp": until})
def post(self, request, username, scope): """ Update settings for scope object """ user = request.user scope = self.get_scope(request, scope) actions = self.parsed_body(request) settings = UserSettings.objects.get_for_scope(user, scope) resp = self.update_settings(settings, actions) return JsonResponse(resp)
def podcast_info(request): url = normalize_feed_url(request.GET.get('url', '')) # 404 before we query for url, because query would complain # about missing param if not url: raise Http404 podcast = get_object_or_404(Podcast, urls__url=url) domain = RequestSite(request).domain resp = podcast_data(podcast, domain) return JsonResponse(resp)
def main(request, username): """ API Endpoint for Device Synchronisation """ if request.method == "GET": return JsonResponse(get_sync_status(request.user)) else: try: actions = parse_request_body(request) except ValueError as e: return HttpResponseBadRequest(str(e)) synclist = actions.get("synchronize", []) stopsync = actions.get("stop-synchronize", []) try: update_sync_status(request.user, synclist, stopsync) except ValueError as e: return HttpResponseBadRequest(str(e)) except Client.DoesNotExist as e: return HttpResponseNotFound(str(e)) return JsonResponse(get_sync_status(request.user))
def add_podcast_status(request, job_id): url = request.GET.get('url', '') result = update_podcasts.AsyncResult(job_id) resp = {'id': str(job_id), 'type': 'create-podcast', 'url': url} if not result.ready(): resp['status'] = 'pending' elif result.successful(): resp['status'] = 'successful' resp['podcast'] = f'/api/2/data/podcast.json?url={url}' elif result.failed(): resp['status'] = 'unsuccessful' resp['error'] = 'feed could not be parsed' return JsonResponse(resp)
def add_podcast_status(request, job_id): url = request.GET.get("url", "") result = update_podcasts.AsyncResult(job_id) resp = {"id": str(job_id), "type": "create-podcast", "url": url} if not result.ready(): resp["status"] = "pending" elif result.successful(): resp["status"] = "successful" resp["podcast"] = f"/api/2/data/podcast.json?url={url}" elif result.failed(): resp["status"] = "unsuccessful" resp["error"] = "feed could not be parsed" return JsonResponse(resp)
def post(self, request, username): """ Add / remove Chapters to/from an episode """ user = request.user now_ = get_timestamp(datetime.utcnow()) body = self.parsed_body(request) podcast_url, episode_url, update_urls = self.get_urls(body) body['podcast'] = podcast_url body['episode'] = episode_url if not podcast_url or not episode_url: raise RequestException('Invalid Podcast or Episode URL') self.update_chapters(body, user) return JsonResponse({'update_url': update_urls, 'timestamp': now_})
def get_lists(request, username): """ Returns a list of all podcast lists by the given user """ User = get_user_model() user = User.objects.get(username=username) if not user: raise Http404 lists = PodcastList.objects.filter(user=user) site = RequestSite(request) get_data = partial(_get_list_data, username=user.username, domain=site.domain) lists_data = list(map(get_data, lists)) return JsonResponse(lists_data)
def get(self, request, username): """ Get chapters for an episode """ user = request.user now_ = get_timestamp(datetime.utcnow()) podcast_url, episode_url, _update_urls = self.get_urls(request) episode = Episode.objects.filter(podcast__urls__url=podcast_url, urls__url=episode_url).get() chapters = Chapter.objects.filter(user=user, episode=episode) since = self.get_since(request) if since: chapters = chapters.filter(created__gte=since) chapters_json = map(self.chapter_to_json, chapters) return JsonResponse({'chapters': chapters_json, 'timestamp': now_})
def episode_info(request): podcast_url = normalize_feed_url(request.GET.get('podcast', '')) episode_url = normalize_feed_url(request.GET.get('url', '')) # 404 before we query for url, because query would complain # about missing parameters if not podcast_url or not episode_url: raise Http404 try: query = Episode.objects.filter(podcast__urls__url=podcast_url, urls__url=episode_url) episode = query.select_related('podcast').get() except Episode.DoesNotExist: raise Http404 domain = RequestSite(request).domain resp = episode_data(episode, domain) return JsonResponse(resp)
def episodes(request, username, version=1): version = int(version) now = datetime.utcnow() now_ = get_timestamp(now) ua_string = request.META.get('HTTP_USER_AGENT', '') if request.method == 'POST': try: actions = parse_request_body(request) except (UnicodeDecodeError, ValueError) as e: msg = ('Could not decode episode update POST data for ' + 'user %s: %s') % (username, request.body.decode('ascii', errors='replace')) logger.warn(msg, exc_info=True) return HttpResponseBadRequest(msg) logger.info('start: user %s: %d actions from %s' % (request.user, len(actions), ua_string)) # handle in background if len(actions) > dsettings.API_ACTIONS_MAX_NONBG: bg_handler = dsettings.API_ACTIONS_BG_HANDLER if bg_handler is not None: modname, funname = bg_handler.rsplit('.', 1) mod = import_module(modname) fun = getattr(mod, funname) fun(request.user, actions, now, ua_string) # TODO: return 202 Accepted return JsonResponse({'timestamp': now_, 'update_urls': []}) try: update_urls = update_episodes(request.user, actions, now, ua_string) except ValidationError as e: logger.warning( 'Validation Error while uploading episode actions ' 'for user %s: %s', username, str(e)) return HttpResponseBadRequest(str(e)) except InvalidEpisodeActionAttributes as e: msg = 'invalid episode action attributes while uploading episode actions for user %s' % ( username, ) logger.warn(msg, exc_info=True) return HttpResponseBadRequest(str(e)) logger.info('done: user %s: %d actions from %s' % (request.user, len(actions), ua_string)) return JsonResponse({'timestamp': now_, 'update_urls': update_urls}) elif request.method == 'GET': podcast_url = request.GET.get('podcast', None) device_uid = request.GET.get('device', None) since_ = request.GET.get('since', None) aggregated = parse_bool(request.GET.get('aggregated', False)) try: since = int(since_) if since_ else None if since is not None: since = datetime.utcfromtimestamp(since) except ValueError: return HttpResponseBadRequest( 'since-value is not a valid timestamp') if podcast_url: podcast = get_object_or_404(Podcast, urls__url=podcast_url) else: podcast = None if device_uid: try: user = request.user device = user.client_set.get(uid=device_uid) except Client.DoesNotExist as e: return HttpResponseNotFound(str(e)) else: device = None changes = get_episode_changes(request.user, podcast, device, since, now, aggregated, version) return JsonResponse(changes)
def get(self, request, username, scope): """ Get settings for scope object """ user = request.user scope = self.get_scope(request, scope) settings = UserSettings.objects.get_for_scope(user, scope) return JsonResponse(settings.as_dict())
def top_tags(request, count): count = parse_range(count, 1, 100, 100) tag_cloud = Topics(count, num_cat=0) resp = list(map(category_data, tag_cloud.tagcloud)) return JsonResponse(resp)
def episodes(request, username, version=1): version = int(version) now = datetime.utcnow() now_ = get_timestamp(now) ua_string = request.META.get("HTTP_USER_AGENT", "") if request.method == "POST": try: actions = parse_request_body(request) except (UnicodeDecodeError, ValueError) as e: msg = ("Could not decode episode update POST data for " + "user %s: %s") % ( username, request.body.decode("ascii", errors="replace"), ) logger.warning(msg, exc_info=True) return HttpResponseBadRequest(msg) logger.info("start: user %s: %d actions from %s" % (request.user, len(actions), ua_string)) # handle in background if (dsettings.API_ACTIONS_MAX_NONBG is not None and len(actions) > dsettings.API_ACTIONS_MAX_NONBG): bg_handler = dsettings.API_ACTIONS_BG_HANDLER if bg_handler is not None: modname, funname = bg_handler.rsplit(".", 1) mod = import_module(modname) fun = getattr(mod, funname) fun(request.user, actions, now, ua_string) # TODO: return 202 Accepted return JsonResponse({"timestamp": now_, "update_urls": []}) try: update_urls = update_episodes(request.user, actions, now, ua_string) except ValidationError as e: logger.warning( "Validation Error while uploading episode actions " "for user %s: %s", username, str(e), ) return HttpResponseBadRequest(str(e)) except InvalidEpisodeActionAttributes as e: msg = ( "invalid episode action attributes while uploading episode actions for user %s" % (username, )) logger.warning(msg, exc_info=True) return HttpResponseBadRequest(str(e)) logger.info("done: user %s: %d actions from %s" % (request.user, len(actions), ua_string)) return JsonResponse({"timestamp": now_, "update_urls": update_urls}) elif request.method == "GET": podcast_url = request.GET.get("podcast", None) device_uid = request.GET.get("device", None) since_ = request.GET.get("since", None) aggregated = parse_bool(request.GET.get("aggregated", False)) try: since = int(since_) if since_ else None if since is not None: since = datetime.utcfromtimestamp(since) except ValueError: return HttpResponseBadRequest( "since-value is not a valid timestamp") if podcast_url: podcast = get_object_or_404(Podcast, urls__url=podcast_url) else: podcast = None if device_uid: try: user = request.user device = user.client_set.get(uid=device_uid) except Client.DoesNotExist as e: return HttpResponseNotFound(str(e)) else: device = None changes = get_episode_changes(request.user, podcast, device, since, now, aggregated, version) return JsonResponse(changes)
def favorites(request, username): favorites = FavoriteEpisode.episodes_for_user(request.user) domain = RequestSite(request).domain e_data = lambda e: episode_data(e, domain) ret = list(map(e_data, favorites)) return JsonResponse(ret)
def devices(request, username, version=None): user = request.user clients = user.client_set.filter(deleted=False) client_data = [get_client_data(user, client) for client in clients] return JsonResponse(client_data)
def get(self, request): cs = ClientStats() clients = cs.get_entries() return JsonResponse(map(self.to_dict, clients.most_common()))
def get(self, request): stats = self._get_stats() return JsonResponse(stats)