def group(request: HttpRequest, g_id: int) -> HttpResponse: """ View that serves a single group's page. :param request: The original request. :param g_id: The ID of the group. :return: A response with the rendered ``group.html`` template. :raises Http404: If the group does not exist. """ if g_id == 0: raise Http404('Group ID cannot be 0') try: _group = Group.objects.get(id=g_id) except Group.DoesNotExist as e: raise Http404 from e members = _group.members \ .prefetch_related('roles') \ .order_by('name').distinct() url = request.path p_url = url.rsplit('/', 2)[0] + '/' crumbs = breadcrumbs([('Groups', request.build_absolute_uri(p_url)), (_group.name, request.build_absolute_uri(url))]) return render(request, 'group.html', { 'group': _group, 'members': list(members), 'breadcrumbs': crumbs })
def profile(request: 'HttpRequest') -> HttpResponse: """ View that serves the profile page of a user. A :class:`UserProfile` will be created if it doesn't exist. It serves the logged in user's profile by default, but accepts an ``id`` query parameter to view an arbitrary user's profile. :param request: The original request. :return: A response with the rendered ``profile.html`` template. :raises Http404: If there is no user with the specified ``id``. """ try: uid = int(request.GET.get('id', request.user.id)) prof = UserProfile.objects.get_or_create(user_id=uid)[0] except (ValueError, IntegrityError) as e: raise Http404 from e if uid != request.user.id and prof.user.is_superuser: raise Http404('Cannot view profile of superuser') uri = request.build_absolute_uri(request.path) crumbs = breadcrumbs([('User', uri)]) return render(request, 'profile.html', { 'profile': prof, 'breadcrumbs': crumbs })
def directory(request: HttpRequest) -> HttpResponse: """ View that serves a page which lists all the series. :param request: The original request. :return: A response with the rendered ``all_series.html`` template. """ chapters = Chapter.objects.filter( published__lte=tz.now()).order_by('-published').defer( 'file', 'views', 'modified') groups = Group.objects.only('name') q = Q(chapters__published__lte=tz.now()) series = list( Series.objects.alias(chapter_count=Count('chapters', filter=q)).filter( chapter_count__gt=0).prefetch_related( Prefetch('chapters', queryset=chapters), Prefetch('chapters__groups', queryset=groups)).distinct().order_by('title').only( 'title', 'slug', 'format', 'cover').exclude(licensed=True)) uri = request.build_absolute_uri(request.path) crumbs = jsonld.breadcrumbs([('Reader', uri)]) library = jsonld.carousel([s.get_absolute_url() for s in series]) return render(request, 'directory.html', { 'all_series': series, 'library': library, 'breadcrumbs': crumbs })
def breadcrumbs_ld(request: HttpRequest, page: FlatPage) -> str: """ Create a JSON-LD ``<script>`` with the page's breadcrumbs. :param request: The original request. :param page: A :class:`FlatPage` object instance. :return: An HTML script tag. """ uri = request.build_absolute_uri(page.url) crumbs = breadcrumbs([(page.title, uri)]) return jsonld(crumbs, 'breadcrumbs')
def all_groups(request: 'HttpRequest') -> 'HttpResponse': """ View that serves a page with all the groups. :param request: The original request. :return: A response with the rendered ``all_groups.html`` template. """ uri = request.build_absolute_uri(request.path) crumbs = breadcrumbs([('Groups', uri)]) return render(request, 'all_groups.html', { 'groups': Group.objects.all(), 'breadcrumbs': crumbs })
def directory(request: 'HttpRequest') -> 'HttpResponse': """ View that serves a page which lists all the series. :param request: The original request. :return: A response with the rendered ``all_series.html`` template. """ _series = Series.objects.prefetch_related('chapters').order_by('title') uri = request.build_absolute_uri(request.path) crumbs = jsonld.breadcrumbs([('Reader', uri)]) library = jsonld.carousel([s.get_absolute_url() for s in _series]) return render(request, 'directory.html', { 'all_series': _series, 'library': library, 'breadcrumbs': crumbs })
def setup(self, request: 'HttpRequest', *args, **kwargs): """ Initialize attributes shared by all view methods. A :class:`~users.models.UserProfile` will be created if the request user does not yet have one. :param request: The original request. """ super().setup(request) if request.user.is_authenticated: self.profile = UserProfile.objects \ .get_or_create(user_id=request.user.id)[0] url = request.path p_url = url.rsplit('/', 2)[0] + '/' crumbs = breadcrumbs([('User', request.build_absolute_uri(p_url)), ('Edit', request.build_absolute_uri(url))]) self.extra_context = {'breadcrumbs': crumbs}
def chapter_page(request: 'HttpRequest', slug: str, vol: int, num: float, page: int) -> 'HttpResponse': """ View that serves a chapter page. :param request: The original request. :param slug: The slug of the series. :param vol: The volume of the chapter. :param num: The number of the chapter. :param page: The number of the page. :return: A response with the rendered ``chapter.html`` template. :raises Http404: If there is no matching chapter or page. """ if page == 0: raise Http404('Page cannot be 0') chapters = Chapter.objects.filter(series__slug=slug, published__lte=tz.now()) try: current = chapters.select_related('series') \ .prefetch_related('pages').get(volume=vol, number=num) all_pages = current.pages.all() curr_page = next(p for p in all_pages if p.number == page) except (Chapter.DoesNotExist, Page.DoesNotExist, StopIteration) as e: raise Http404 from e url = request.path p_url = url.rsplit('/', 4)[0] + '/' p2_url = url.rsplit('/', 5)[0] + '/' crumbs = jsonld.breadcrumbs([ ('Reader', request.build_absolute_uri(p2_url)), (current.series.title, request.build_absolute_uri(p_url)), (current.title, request.build_absolute_uri(url)) ]) return render( request, 'chapter.html', { 'all_chapters': chapters.reverse(), 'curr_chapter': current, 'next_chapter': current.next, 'prev_chapter': current.prev, 'all_pages': all_pages, 'curr_page': curr_page, 'breadcrumbs': crumbs })
def get(self, request: 'HttpRequest', *args, **kwargs) -> HttpResponse: """ Handle ``GET`` requests. :param request: The original request. :return: A response with the rendered :obj:`template <EditUser.template_name>`. """ url = request.path p_url = url.rsplit('/', 2)[0] + '/' crumbs = breadcrumbs([('User', request.build_absolute_uri(p_url)), ('Bookmarks', request.build_absolute_uri(url))]) chapters = Chapter.objects.filter(series_id__in=Subquery( Bookmark.objects.filter(user_id=request.user.id).values( 'series'))).order_by('-published') token = UserProfile.objects.only('token') \ .get_or_create(user_id=request.user.id)[0].token return self.render_to_response( self.get_context_data(releases=chapters, breadcrumbs=crumbs, token=token))
def series(request: 'HttpRequest', slug: str) -> 'HttpResponse': """ View that serves the page of a single series. If the series doesn't have any published chapters, only staff members will be able to see it. :param request: The original request. :param slug: The slug of the series. :return: A response with the rendered ``series.html`` template. :raises Http404: If there is no series with the specified ``slug``. """ try: _series = Series.objects.prefetch_related('chapters__groups', 'artists', 'categories', 'authors', 'aliases').get(slug=slug) except Series.DoesNotExist as e: raise Http404 from e chapters = _series.chapters.filter(published__lte=tz.now()).reverse() if not (request.user.is_staff or chapters): return render( request, 'error.html', { 'error_message': 'Sorry. This series is not yet available.', 'error_status': 403 }, status=403) marked = (request.user.is_authenticated and request.user.bookmarks.filter(series=_series).exists()) url = request.path p_url = url.rsplit('/', 2)[0] + '/' uri = request.build_absolute_uri(url) crumbs = jsonld.breadcrumbs([('Reader', request.build_absolute_uri(p_url)), (_series.title, uri)]) book = jsonld.schema( 'Book', { 'url': uri, 'name': _series.title, 'abstract': _series.description, 'author': [{ '@type': 'Person', 'name': au.name, 'alternateName': au.aliases.names() } for au in _series.authors.iterator()], 'illustrator': [{ '@type': 'Person', 'name': ar.name, 'alternateName': ar.aliases.names() } for ar in _series.artists.iterator()], 'alternateName': _series.aliases.names(), 'genre': list(_series.categories.values_list('name', flat=True)), 'creativeWorkStatus': ('Published' if _series.completed else 'Incomplete'), 'dateCreated': _series.created.strftime('%F'), 'dateModified': _series.modified.strftime('%F'), 'bookFormat': 'GraphicNovel', }) return render( request, 'series.html', { 'series': _series, 'chapters': chapters, 'marked': marked, 'breadcrumbs': crumbs, 'book_ld': book })
def chapter_page(request: HttpRequest, slug: str, vol: int, num: float, page: int) -> HttpResponse: """ View that serves a chapter page. :param request: The original request. :param slug: The slug of the series. :param vol: The volume of the chapter. :param num: The number of the chapter. :param page: The number of the page. :return: A response with the rendered ``chapter.html`` template. :raises Http404: If there is no matching chapter or page. """ if page == 0: raise Http404('Page cannot be 0') chapters = list( Chapter.objects.filter( series__slug=slug, series__licensed=False, published__lte=tz.now()).select_related('series').only( 'title', 'number', 'volume', 'published', 'final', 'series__slug', 'series__cover', 'series__title', 'series__format').reverse()) if not chapters: raise Http404('No chapters for this series') max_ = len(chapters) - 1 for idx, current in enumerate(chapters): if current == (vol, num): next_ = chapters[idx - 1] if idx > 0 else None prev_ = chapters[idx + 1] if idx < max_ else None break if page == 1: Chapter.track_view(id=current.id) all_pages = list(current.pages.all()) try: curr_page = next(p for p in all_pages if p.number == page) except StopIteration as e: raise Http404('No such page') from e preload = list( filter(lambda p: curr_page < p < curr_page.number + 4, all_pages)) tags = current.series.categories.values_list('name', flat=True) url = request.path p_url = url.rsplit('/', 4)[0] + '/' p2_url = url.rsplit('/', 5)[0] + '/' crumbs = jsonld.breadcrumbs([ ('Reader', request.build_absolute_uri(p2_url)), (current.series.title, request.build_absolute_uri(p_url)), (current.title, request.build_absolute_uri(url)) ]) return render( request, 'chapter.html', { 'all_chapters': chapters, 'curr_chapter': current, 'next_chapter': next_, 'prev_chapter': prev_, 'all_pages': all_pages, 'curr_page': curr_page, 'preload': preload, 'breadcrumbs': crumbs, 'tags': ','.join(tags) })