2017-12-28 10:39:02 +00:00
# Episodes.Community - Community-Driven TV Show Episode Link Sharing Site
# Copyright (C) 2017 Evert "Diamond" Prants <evert@lunasqu.ee>, Taizo "Tsa6" Simpson <taizo@tsa6.net>
#
# 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 <https://www.gnu.org/licenses/>.
2017-08-25 18:03:37 +00:00
from django . db import models
2018-03-02 13:46:25 +00:00
from django . conf import settings
2017-11-14 12:56:44 +00:00
from django . contrib . auth . models import AbstractUser
2017-08-25 20:00:38 +00:00
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 ] )
2017-08-26 06:25:09 +00:00
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 ' )
2017-08-25 18:03:37 +00:00
# Create your models here.
2017-08-26 06:40:28 +00:00
class TimestampedModel ( models . Model ) :
2017-08-26 06:37:20 +00:00
timestamp = models . DateTimeField (
auto_now = True ,
help_text = ' The date and time this object was created '
)
class Meta :
abstract = True
class Show ( TimestampedModel ) :
2017-08-25 20:00:38 +00:00
name = models . CharField (
max_length = 40 ,
help_text = " The full name of the show " ,
verbose_name = " Full Name "
)
abbr = models . SlugField (
2017-12-28 15:02:41 +00:00
max_length = 16 ,
2017-08-25 20:00:38 +00:00
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 (
2017-08-25 23:37:45 +00:00
help_text = " Whether or not this show is user-moderated " ,
2017-08-25 20:00:38 +00:00
verbose_name = " User Moderated "
)
css = models . FileField (
storage = show_static_storage ,
upload_to = name_css ,
2017-12-28 15:03:16 +00:00
null = True ,
2017-12-28 15:20:22 +00:00
blank = True ,
2017-08-25 20:00:38 +00:00
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. " ,
2017-11-14 13:39:11 +00:00
verbose_name = " Banner "
2017-12-27 15:01:39 +00:00
)
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 ' ) ,
2018-02-28 16:34:10 +00:00
( ' can_moderate_board ' , ' Can delete and edit boards and replies of this show ' ) ,
2017-12-27 15:01:39 +00:00
)
2018-03-02 13:46:25 +00:00
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 )
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return ' %s [ %s ] ' % ( self . name , self . abbr )
2017-08-25 23:23:53 +00:00
2017-11-14 12:56:44 +00:00
class User ( AbstractUser ) :
2017-11-13 17:38:51 +00:00
icy_id = models . CharField (
2017-09-22 21:45:17 +00:00
max_length = 36 ,
help_text = ' The UUID assigned to this user by IcyNet \' s auth servers '
2017-08-26 19:08:14 +00:00
)
2017-08-25 23:23:53 +00:00
display_name = models . CharField (
max_length = 20 ,
help_text = " The name shown to other users " ,
verbose_name = " Display Name "
)
2017-08-26 06:25:09 +00:00
favorites = models . ManyToManyField (
' Episode ' ,
related_name = ' favorited_by ' ,
through = ' Favorite '
)
watches = models . ManyToManyField (
' Episode ' ,
related_name = ' watched_by ' ,
through = ' Watch '
2017-08-26 06:40:28 +00:00
)
2017-08-25 23:23:53 +00:00
2017-08-26 06:37:20 +00:00
class Ban ( TimestampedModel ) :
2017-08-25 23:23:53 +00:00
user = models . OneToOneField (
User ,
on_delete = models . CASCADE ,
help_text = " The user this ban applies to " ,
verbose_name = " Banned User "
)
admin = models . ForeignKey (
2017-11-16 19:13:13 +00:00
User ,
2017-08-25 23:23:53 +00:00
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 ,
2017-11-14 13:39:11 +00:00
blank = True ,
2017-08-25 23:23:53 +00:00
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 '
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return ( " Permanent " if self . permanent else " Temporary " ) + " ban of %s " % self . user
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class Report ( TimestampedModel ) :
2017-08-25 23:23:53 +00:00
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 '
)
2017-12-29 14:22:58 +00:00
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
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return " %s ' s report of %s " % ( self . reporter , self . url )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class ShowSubmission ( TimestampedModel ) :
2017-08-25 23:23:53 +00:00
user = models . ForeignKey (
User ,
on_delete = models . CASCADE ,
related_name = ' show_submissions ' ,
help_text = ' The user who submitted this show ' ,
verbose_name = ' Submitter '
)
2017-09-24 22:30:15 +00:00
name = models . CharField (
max_length = 40 ,
help_text = " The full name of the show " ,
verbose_name = " Full Name "
)
2017-08-25 23:23:53 +00:00
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 '
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return ' " %s " by %s ' % ( self . name , self . user )
2017-08-26 06:25:09 +00:00
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 (
2017-12-28 15:24:27 +00:00
help_text = ' A description of this season \' s happenings ' ,
blank = True
2017-08-26 06:25:09 +00:00
)
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
2018-03-02 13:46:25 +00:00
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return self . show . name + " S %d " % self . number
2017-08-26 06:25:09 +00:00
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 ' ,
2017-09-24 21:02:50 +00:00
verbose_name = ' Episode Name '
2017-08-26 06:25:09 +00:00
)
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 '
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return " [s %d ep %d ] %s — %s " % ( self . season . number , self . episode , self . show . name , self . name )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class Submission ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
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 (
2017-11-13 17:38:51 +00:00
User ,
2017-08-26 06:25:09 +00:00
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 ' ,
)
2017-12-28 15:09:39 +00:00
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 (
2017-12-28 15:16:20 +00:00
help_text = ' Whether or not this submission is pinned ' ,
default = False
2017-12-28 15:09:39 +00:00
)
2017-08-26 06:25:09 +00:00
tags = models . CharField (
help_text = ' Tags applied to this link submission ' ,
max_length = 200
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return ' %s \' s submission for %s — s %d ep %d ' % ( self . user , self . episode . show . name , self . episode . season . number , self . episode . episode )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class SubmissionVote ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
submission = models . ForeignKey (
Submission ,
on_delete = models . CASCADE ,
related_name = ' votes ' ,
help_text = ' What this submission was cast on '
)
user = models . ForeignKey (
2017-11-13 17:38:51 +00:00
User ,
2017-08-26 06:25:09 +00:00
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 '
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return " %s ' s vote on %s " % ( self . user , self . submission )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class Favorite ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
user = models . ForeignKey (
User ,
on_delete = models . CASCADE
)
episode = models . ForeignKey (
Episode ,
on_delete = models . CASCADE
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return " %s \u2665 %s " % ( self . user , self . episode )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class Watch ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
user = models . ForeignKey (
User ,
on_delete = models . CASCADE
)
episode = models . ForeignKey (
Episode ,
on_delete = models . CASCADE
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return " %s \U0001f441 %s " % ( self . user , self . episode )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class DiscussionBoard ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
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 '
)
2017-12-28 15:16:20 +00:00
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
)
2018-02-28 16:34:10 +00:00
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 ' )
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return ' [ %s ] " %s " by %s ' % ( self . show . abbr , self . title , self . user )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class DiscussionReply ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
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 (
2017-08-26 06:40:28 +00:00
help_text = ' The body of the response ' ,
2017-08-26 06:25:09 +00:00
verbose_name = ' Body '
)
2018-02-28 16:34:10 +00:00
deleted = models . BooleanField (
help_text = ' Whether or not the content has been deleted by a moderator ' ,
default = False
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
return ' [ %s ] %s \' s response to " %s " ' % ( self . board . show . abbr , self . user , self . board . title )
2017-08-26 06:25:09 +00:00
2017-08-26 06:37:20 +00:00
class DiscussionVote ( TimestampedModel ) :
2017-08-26 06:25:09 +00:00
user = models . ForeignKey (
User ,
2017-08-26 06:29:42 +00:00
on_delete = models . CASCADE ,
related_name = ' discussion_votes ' ,
help_text = ' The user which cast this vote '
2017-08-26 06:25:09 +00:00
)
2018-02-28 16:34:10 +00:00
reply = models . ForeignKey (
DiscussionReply ,
2017-08-26 06:29:42 +00:00
on_delete = models . CASCADE ,
related_name = ' votes ' ,
2018-02-28 16:34:10 +00:00
help_text = ' The reply this vote was cast on '
2017-08-26 06:25:09 +00:00
)
2017-09-25 19:49:15 +00:00
positive = models . BooleanField (
2017-08-26 06:25:09 +00:00
help_text = ' If true, the vote is an upvote. Otherwise, it is a downvote. Neutral votes are not recorded '
)
2017-09-24 22:30:15 +00:00
def __str__ ( self ) :
2018-03-01 13:48:20 +00:00
return " %s %s reply %d " % ( self . user , ' \U0001f592 ' if self . positive else ' \U0001f44e ' , self . reply . pk )