# Episodes.Community - Community-Driven TV Show Episode Link Sharing Site # Copyright (C) 2017 Evert "Diamond" Prants , Taizo "Tsa6" Simpson # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from django.db import models from django.conf import settings from django.contrib.auth.models import AbstractUser from django.core.files.storage import FileSystemStorage from django.conf import settings import os def name_artwork(inst, name): return '%s/artwork.%s'%(inst.abbr,name.split('.')[-1]) def name_css(inst, name): return '%s/style.css'%inst.abbr def name_banner(inst, name): return '%s/banner.%s'%(inst.abbr,name.split('.')[-1]) def name_season_artwork(inst, name): return '%s/%d/artwork.%s'%(inst.show.abbr,inst.number,name.split('.')[-1]) show_static_storage = FileSystemStorage(location=os.path.join(os.path.dirname(settings.MEDIA_ROOT), 'uploaded_resources'), base_url='showstatic') # Create your models here. class TimestampedModel(models.Model): timestamp = models.DateTimeField( auto_now=True, help_text='The date and time this object was created' ) class Meta: abstract = True class Show(TimestampedModel): name = models.CharField( max_length=40, help_text="The full name of the show", verbose_name="Full Name" ) abbr = models.SlugField( max_length=16, unique=True, help_text="A short abbreviation of the show, for use in urls", verbose_name="Abbreviation" ) description = models.TextField( help_text="A description of the show", verbose_name="Description" ) release = models.DateField( help_text="The release date of the first episode of the show", verbose_name="Release Date" ) artwork = models.ImageField( storage=show_static_storage, upload_to = name_artwork, help_text="The artwork associated with the show. Should display the name of the show in a movie-poster esque format. Aspect ration should be about 2:3", verbose_name="Artwork" ) imdb = models.URLField( help_text="The url of the IMDb page for this show", verbose_name="IMDb Page" ) moderated = models.BooleanField( help_text="Whether or not this show is user-moderated", verbose_name="User Moderated" ) css = models.FileField( storage=show_static_storage, upload_to=name_css, null=True, blank=True, help_text="The CSS stylesheet applied to this show's page", verbose_name="Custom Style" ) banner = models.ImageField( storage=show_static_storage, upload_to = name_banner, help_text="A banner used for the show's page.", verbose_name="Banner" ) class Meta: permissions = ( ('can_create_show_ban', 'Can ban an user from submitting to this show'), ('can_moderate_show', 'Can add episodes, seasons and unrestricted submissions'), ('can_moderate_board', 'Can delete and edit boards and replies of this show'), ) def url(self): use_sdms = getattr(settings, "DOMAIN_SUBDOMAIN_SHOWS", False) domain = getattr(settings, "DOMAIN", 'localhost') if use_sdms: return domain.format(sub=self.abbr + '.',path='') return '/show/%s' % (self.abbr) def __str__(self): return '%s [%s]'%(self.name,self.abbr) class User(AbstractUser): icy_id = models.CharField( max_length=36, help_text='The UUID assigned to this user by IcyNet\'s auth servers' ) display_name=models.CharField( max_length=20, help_text="The name shown to other users", verbose_name="Display Name" ) favorites=models.ManyToManyField( 'Episode', related_name='favorited_by', through='Favorite' ) watches=models.ManyToManyField( 'Episode', related_name='watched_by', through='Watch' ) class Ban(TimestampedModel): user = models.OneToOneField( User, on_delete=models.CASCADE, help_text="The user this ban applies to", verbose_name="Banned User" ) admin = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, help_text='The admin which banned this user', verbose_name='Banned By', related_name='bans' ) reason = models.CharField( max_length=50, blank=True, help_text='The reason this user was banned', verbose_name='Ban Reason' ) expiration = models.DateField( help_text='The date this user will become unbanned', blank=True, verbose_name='Expiration Date' ) permanent = models.BooleanField( help_text='If checked, this user will never be unbanned, even if the expiration date passes', verbose_name='Permanent' ) scope = models.ManyToManyField( Show, blank=True, help_text='The shows this user is banned from interacting with', verbose_name='Banned From', related_name='bans' ) site_wide = models.BooleanField( help_text='If checked, this is a site-wide ban, and the user is automatically banned from all shows, not just those in the Banned From (scope) paramenter', verbose_name = 'Site Wide Ban' ) def __str__(self): return ("Permanent" if self.permanent else "Temporary") + " ban of %s"%self.user class Report(TimestampedModel): reporter = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, related_name='reports', help_text='The user who created this report', verbose_name='Reporter' ) title = models.CharField( max_length=50, help_text='A brief summary of the report', verbose_name='Title' ) details = models.TextField( help_text='The details of the report, preferably including why the content should be removed', verbose_name = 'Details' ) url = models.URLField( max_length=100, help_text='The URL of the content being reported', verbose_name = 'Content URL' ) show = models.ForeignKey( Show, on_delete=models.CASCADE, null=True, blank=True, help_text='The show associated with the reported url', ) read_by = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='reader', help_text='The administrator that read this report', verbose_name='Read by' ) read_at = models.DateTimeField( null=True, blank=True, help_text='The date and time this report was read' ) resolved = models.BooleanField( help_text='Whether or not this report has been marked as resolved', default=False ) def __str__(self): return "%s's report of %s"%(self.reporter, self.url) class ShowSubmission(TimestampedModel): user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='show_submissions', help_text='The user who submitted this show', verbose_name='Submitter' ) name = models.CharField( max_length=40, help_text="The full name of the show", verbose_name="Full Name" ) details = models.TextField( help_text='Some details about the show. Why it should be added, where information about it can be found, etc.', verbose_name='Details' ) def __str__(self): return '"%s" by %s'%(self.name, self.user) class Season(models.Model): show = models.ForeignKey( 'Show', on_delete=models.CASCADE, related_name='seasons', help_text='The show this season belongs to' ) name = models.CharField( max_length=40, blank=True, help_text='The name given to this season by its producers. Can be blank if no name was given', verbose_name='Season Name' ) number = models.IntegerField( help_text='The number of this season, starting at 1; For example, the first season to be aired would be number 1, and the second would be number 2' ) description = models.TextField( help_text='A description of this season\'s happenings', blank=True ) artwork = models.ImageField( storage=show_static_storage, upload_to = name_season_artwork, help_text="The artwork associated with the season. Should display the name of the show in a movie-poster esque format. Aspect ration should be about 2:3", verbose_name="Artwork", blank=True ) def __str__(self): return self.show.name + " S%d"%self.number class Episode(models.Model): show = models.ForeignKey( 'Show', on_delete=models.CASCADE, related_name='episodes', help_text='The show this episode belongs to' ) season = models.ForeignKey( Season, on_delete=models.CASCADE, related_name='episodes', help_text='The season this episode is from' ) episode = models.IntegerField( help_text='The position of this episode in the season. The first episode of the season to air would be episode number 1', verbose_name='Episode Number' ) name = models.CharField( max_length=40, help_text='The name given to this episode by its producers', verbose_name='Episode Name' ) summary = models.TextField( help_text='A summary of this episode' ) airdate = models.DateField( help_text='The date this episode officially aired for the first time', verbose_name='Original Air Date' ) def __str__(self): return "[s%dep%d] %s — %s"%(self.season.number,self.episode,self.show.name, self.name) class Submission(TimestampedModel): episode = models.ForeignKey( Episode, on_delete=models.CASCADE, related_name='submissions', help_text='What episode this link contains a mirror of', verbose_name='Submitted For' ) user = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, related_name='submissions', help_text='The user who submitted this link' ) url = models.URLField( help_text='The link that was submitted', ) title = models.TextField( help_text='Title of the page', blank=True ) embed = models.URLField( help_text='Embed player link for this episode', blank=True, verbose_name='Embed URL' ) pinned = models.BooleanField( help_text='Whether or not this submission is pinned', default=False ) tags = models.CharField( help_text='Tags applied to this link submission', max_length=200 ) def __str__(self): return '%s\'s submission for %s — s%dep%d'%(self.user,self.episode.show.name,self.episode.season.number, self.episode.episode) class SubmissionVote(TimestampedModel): submission = models.ForeignKey( Submission, on_delete=models.CASCADE, related_name='votes', help_text='What this submission was cast on' ) user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='votes', help_text='The user who cast this vote' ) positive = models.BooleanField( help_text='If this is true, the vote is an upvote. Otherwise, it is a downvote' ) def __str__(self): return "%s's vote on %s"%(self.user,self.submission) class Favorite(TimestampedModel): user = models.ForeignKey( User, on_delete=models.CASCADE ) episode = models.ForeignKey( Episode, on_delete=models.CASCADE ) def __str__(self): return "%s \u2665 %s"%(self.user, self.episode) class Watch(TimestampedModel): user = models.ForeignKey( User, on_delete=models.CASCADE ) episode = models.ForeignKey( Episode, on_delete=models.CASCADE ) def __str__(self): return "%s \U0001f441 %s"%(self.user, self.episode) class DiscussionBoard(TimestampedModel): show = models.ForeignKey( Show, on_delete=models.CASCADE, related_name='discussion_boards', help_text='The show this discussion was created for' ) user = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, related_name='discussion_boards', help_text='The user that created this discussion' ) title = models.CharField( max_length=100, help_text='The title of the discussion' ) views = models.IntegerField( help_text='The amount of times this board has been viewed', default=0 ) pinned = models.BooleanField( help_text='Whether or not this board is pinned', default=False ) locked = models.BooleanField( help_text='Whether or not this board is locked for further replies', default=False ) def latest_reply(self): return self.replies.latest('timestamp') def __str__(self): return '[%s] "%s" by %s'%(self.show.abbr, self.title, self.user) class DiscussionReply(TimestampedModel): board = models.ForeignKey( DiscussionBoard, on_delete=models.CASCADE, related_name='replies', help_text='The discussion board this was created in reply to' ) user = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, related_name='replies', help_text='The user that posted this reply' ) body = models.TextField( help_text='The body of the response', verbose_name='Body' ) deleted = models.BooleanField( help_text='Whether or not the content has been deleted by a moderator', default=False ) def __str__(self): return '[%s] %s\'s response to "%s"'%(self.board.show.abbr,self.user, self.board.title) class DiscussionVote(TimestampedModel): user = models.ForeignKey( User, on_delete=models.CASCADE, related_name='discussion_votes', help_text='The user which cast this vote' ) reply = models.ForeignKey( DiscussionReply, on_delete=models.CASCADE, related_name='votes', help_text='The reply this vote was cast on' ) positive = models.BooleanField( help_text='If true, the vote is an upvote. Otherwise, it is a downvote. Neutral votes are not recorded' ) def __str__(self): return "%s %s reply %d"%(self.user, '\U0001f592' if self.positive else '\U0001f44e', self.reply.pk) class UserAction(TimestampedModel): user = models.ForeignKey( User, on_delete=models.CASCADE, help_text='The user who committed this action', ) url = models.URLField( max_length=100, help_text='The URL of the content', verbose_name = 'Content URL' ) show = models.ForeignKey( Show, on_delete=models.CASCADE, null=True, blank=True, help_text='The show associated with the action', ) act_type = models.IntegerField( help_text='Type of this action', default=0, verbose_name = 'Action Type' ) def act_str(self): action = 'created' if self.act_type == 1: action = 'submitted' elif self.act_type == 2: action = 'replied' elif self.act_type == 3: action = 'modified' elif self.act_type == 4: action = 'deleted' elif self.act_type == 5: action = 'banned' elif self.act_type == 6: action = 'voted down' elif self.act_type == 7: action = 'voted up' return action def __str__(self): return "%s %s %s"%(self.user, self.act_str(), self.url)