2017-12-26 17:46:27 +01:00
from django . db import models
from django . contrib . sites . models import Site
from django . contrib . auth . models import User , Group , AnonymousUser
2018-06-10 19:06:46 +02:00
from django . contrib . staticfiles . templatetags . staticfiles import static
2017-12-26 17:46:27 +01:00
from django . utils . translation import ugettext_lazy as _
2018-06-13 23:37:41 +02:00
from django . shortcuts import reverse
2018-04-04 04:26:11 +02:00
from django . utils import timezone
2018-03-17 21:05:13 +01:00
from django . conf import settings
2018-06-10 19:06:46 +02:00
from imagekit . models import ProcessedImageField , ImageSpecField
from imagekit . processors import ResizeToFill , ResizeToFit , Adjust , ColorOverlay
2018-05-20 18:35:52 +02:00
from rest_framework import serializers
2017-12-26 17:46:27 +01:00
from . locale import *
2018-04-02 04:22:30 +02:00
from . . import location
2018-04-21 16:28:04 +02:00
from . . utils import slugify
2017-12-26 17:46:27 +01:00
2018-04-02 04:22:30 +02:00
import uuid
2017-12-26 17:46:27 +01:00
import pytz
2018-01-09 04:35:44 +01:00
import datetime
2018-02-02 04:52:02 +01:00
import hashlib
2017-12-26 17:46:27 +01:00
class UserProfile ( models . Model ) :
" Store profile information about a user "
user = models . OneToOneField ( User , on_delete = models . CASCADE )
2018-02-26 17:53:50 +01:00
realname = models . CharField ( verbose_name = _ ( " Real Name " ) , max_length = 150 , blank = True )
2018-04-02 04:22:30 +02:00
tz = models . CharField ( max_length = 32 , verbose_name = _ ( ' Timezone ' ) , default = ' UTC ' , choices = location . TimezoneChoices ( ) , blank = False , null = False )
2018-03-17 21:05:13 +01:00
avatar = ProcessedImageField ( verbose_name = _ ( " Photo Image " ) ,
upload_to = ' avatars ' ,
processors = [ ResizeToFill ( 128 , 128 ) ] ,
2018-03-18 17:26:32 +01:00
format = ' PNG ' ,
blank = True )
2018-04-21 16:28:04 +02:00
city = models . ForeignKey ( City , verbose_name = _ ( ' Home city ' ) , null = True , blank = True , on_delete = models . CASCADE )
2017-12-26 17:46:27 +01:00
web_url = models . URLField ( verbose_name = _ ( ' Website URL ' ) , blank = True , null = True )
twitter = models . CharField ( verbose_name = _ ( ' Twitter Name ' ) , max_length = 32 , blank = True , null = True )
facebook = models . URLField ( verbose_name = _ ( ' Facebook URL ' ) , max_length = 32 , blank = True , null = True )
2018-02-26 17:53:50 +01:00
send_notifications = models . BooleanField ( verbose_name = _ ( ' Send notification emails ' ) , default = True )
2018-08-04 04:56:48 +02:00
do_not_track = models . BooleanField ( verbose_name = _ ( " Do not track " ) , default = False )
2018-02-26 17:53:50 +01:00
2018-04-02 04:22:30 +02:00
secret_key = models . UUIDField ( default = uuid . uuid4 , editable = True )
2018-03-04 20:10:37 +01:00
categories = models . ManyToManyField ( ' Category ' , blank = True )
topics = models . ManyToManyField ( ' Topic ' , blank = True )
2017-12-26 17:46:27 +01:00
class Meta :
ordering = ( ' user__username ' , )
def __str__ ( self ) :
try :
if self . realname :
2018-01-25 22:50:19 +01:00
return self . realname
2017-12-26 17:46:27 +01:00
return " %s " % self . user . username
except :
return " Unknown Profile "
2018-08-17 06:07:29 +02:00
@property
def personal_team ( self ) :
teams = Team . objects . filter ( access = Team . PERSONAL , owner_profile = self )
if teams . count ( ) > 0 :
return teams [ 0 ]
else :
return Team . objects . create ( name = str ( self ) , access = Team . PERSONAL , owner_profile = self , city = self . city )
2018-03-17 21:05:13 +01:00
def avatar_url ( self ) :
2018-03-18 17:48:03 +01:00
try :
2018-09-05 03:36:15 +02:00
if self . avatar is None or self . avatar . name is None or self . avatar . name == ' ' :
2018-03-18 17:48:03 +01:00
return settings . STATIC_URL + ' img/avatar_placeholder.png '
elif self . avatar . name . startswith ( ' http ' ) :
return self . avatar . name
else :
return self . avatar . url
except :
2018-03-18 17:26:32 +01:00
return settings . STATIC_URL + ' img/avatar_placeholder.png '
2018-03-17 21:05:13 +01:00
2017-12-26 17:46:27 +01:00
def get_timezone ( self ) :
try :
return pytz . timezone ( self . tz )
except :
return pytz . utc
timezone = property ( get_timezone )
def tolocaltime ( self , dt ) :
as_utc = pytz . utc . localize ( dt )
return as_utc . astimezone ( self . timezone )
def fromlocaltime ( self , dt ) :
local = self . timezone . localize ( dt )
return local . astimezone ( pytz . utc )
2018-08-06 17:08:51 +02:00
@property
def is_a_team_admin ( self ) :
2018-08-17 06:07:29 +02:00
return Member . objects . filter ( user = self , role = Member . ADMIN , team__access__in = ( Team . PUBLIC , Team . PRIVATE ) ) . count ( ) > 0
2018-08-06 17:08:51 +02:00
2018-03-20 23:35:02 +01:00
@property
def administering ( self ) :
2018-08-17 06:07:29 +02:00
return [ member . team for member in Member . objects . filter ( user = self , role = Member . ADMIN , team__access__in = ( Team . PUBLIC , Team . PRIVATE ) ) . order_by ( ' team__name ' ) ]
2018-08-06 17:08:51 +02:00
@property
def is_a_team_moderator ( self ) :
2018-08-17 06:07:29 +02:00
return Member . objects . filter ( user = self , role__in = ( Member . ADMIN , Member . MODERATOR ) , team__access__in = ( Team . PUBLIC , Team . PRIVATE ) ) . count ( ) > 0
2018-03-20 23:35:02 +01:00
@property
def moderating ( self ) :
2018-08-17 06:07:29 +02:00
return [ member . team for member in Member . objects . filter ( user = self , role__in = ( Member . ADMIN , Member . MODERATOR ) , team__access__in = ( Team . PUBLIC , Team . PRIVATE ) ) . order_by ( ' team__name ' ) ]
2018-03-20 23:35:02 +01:00
2018-01-04 05:44:27 +01:00
def can_create_event ( self , team ) :
2018-01-22 03:48:58 +01:00
try :
if self . user . is_superuser :
return True
except :
return False
2018-01-04 05:44:27 +01:00
if not self . user_id :
return False
if team . owner_profile == self :
return True
2018-03-20 23:35:02 +01:00
if self in team . moderators :
2018-01-04 05:44:27 +01:00
return True
return False
2018-04-14 17:14:40 +02:00
def can_edit_series ( self , series ) :
try :
if self . user . is_superuser :
return True
except :
return False
if series . created_by == self :
return True
if series . team . owner_profile == self :
return True
if self in series . team . moderators :
return True
return False
2018-01-21 18:23:13 +01:00
def can_edit_event ( self , event ) :
2018-01-22 03:48:58 +01:00
try :
if self . user . is_superuser :
return True
except :
return False
2018-01-21 18:23:13 +01:00
if event . created_by == self :
return True
if event . team . owner_profile == self :
return True
2018-03-20 23:35:02 +01:00
if self in event . team . moderators :
2018-01-21 18:23:13 +01:00
return True
return False
2018-07-29 04:31:59 +02:00
def can_edit_org ( self , org ) :
try :
if self . user . is_superuser :
return True
except :
return False
if not self . user_id :
return False
if org . owner_profile == self :
return True
return False
2018-03-22 05:15:09 +01:00
def can_create_common_event ( self , org ) :
try :
if self . user . is_superuser :
return True
except :
return False
if not self . user_id :
return False
if org . owner_profile == self :
return True
return False
2018-01-21 18:23:13 +01:00
def can_edit_team ( self , team ) :
2018-01-22 03:48:58 +01:00
try :
if self . user . is_superuser :
return True
except :
return False
2018-01-21 18:23:13 +01:00
if team . owner_profile == self :
return True
2018-09-07 22:26:27 +02:00
if self in team . administrators :
2018-01-21 18:23:13 +01:00
return True
return False
2018-09-07 22:26:27 +02:00
2017-12-30 04:27:05 +01:00
def get_user_timezone ( username ) :
# TODO: find a smarter way to get timezone
return ' UTC '
2017-12-26 17:46:27 +01:00
def _getUserProfile ( self ) :
2017-12-30 04:27:05 +01:00
if not self . is_authenticated :
2017-12-26 17:46:27 +01:00
return UserProfile ( )
profile , created = UserProfile . objects . get_or_create ( user = self )
if created :
profile . tz = get_user_timezone ( self . username )
2018-01-27 03:06:33 +01:00
if self . first_name :
if self . last_name :
profile . realname = ' %s %s ' % ( self . first_name , self . last_name )
else :
profile . realname = self . first_name
2018-02-02 04:52:02 +01:00
if self . email :
h = hashlib . md5 ( )
h . update ( bytearray ( profile . user . email , ' utf8 ' ) )
2018-11-21 18:13:13 +01:00
profile . avatar = ' https://www.gravatar.com/avatar/ %s .jpg?d=mm ' % h . hexdigest ( )
2018-02-02 04:52:02 +01:00
2017-12-26 17:46:27 +01:00
profile . save ( )
return profile
def _getAnonProfile ( self ) :
return UserProfile ( )
User . profile = property ( _getUserProfile )
AnonymousUser . profile = property ( _getAnonProfile )
class Organization ( models . Model ) :
name = models . CharField ( max_length = 256 , null = False , blank = False )
2018-03-22 05:15:09 +01:00
slug = models . CharField ( max_length = 256 , null = False , blank = False )
2017-12-26 17:46:27 +01:00
site = models . ForeignKey ( Site , on_delete = models . CASCADE )
2018-03-17 20:29:17 +01:00
owner_profile = models . ForeignKey ( UserProfile , related_name = ' owned_orgs ' , blank = False , null = True , on_delete = models . SET_NULL )
2018-07-29 04:31:59 +02:00
cover_img = models . ImageField ( verbose_name = _ ( ' Cover Image ' ) , upload_to = ' org_covers ' , null = True , blank = True )
tile_img = ImageSpecField ( source = ' cover_img ' ,
processors = [
Adjust ( contrast = 0.8 , color = 1 ) ,
ResizeToFill ( 338 , 200 ) ,
] ,
format = ' PNG ' )
banner_img = ImageSpecField ( source = ' cover_img ' ,
processors = [
Adjust ( contrast = 0.8 , color = 1 ) ,
ResizeToFill ( 825 , 200 ) ,
] ,
format = ' PNG ' )
description = models . TextField ( blank = True , null = True )
def save ( self , * args , * * kwargs ) :
new_slug = slugify ( self . name )
slug_matches = list ( Organization . objects . filter ( slug = new_slug ) )
if len ( slug_matches ) == 0 or ( len ( slug_matches ) == 1 and slug_matches [ 0 ] . id == self . id ) :
self . slug = new_slug
else :
self . slug = ' %s - %s ' % ( new_slug , self . id )
super ( ) . save ( * args , * * kwargs ) # Call the "real" save() method.
def get_absolute_url ( self ) :
return reverse ( ' show-org ' , kwargs = { ' org_slug ' : self . slug } )
2017-12-26 17:46:27 +01:00
def __str__ ( self ) :
return u ' %s ' % ( self . name )
2018-08-06 17:08:51 +02:00
class OrgTeamRequest ( models . Model ) :
ORG = 0
TEAM = 1
ORIGINS = [
( ORG , _ ( " Organization " ) ) ,
( TEAM , _ ( " Team " ) ) ,
]
organization = models . ForeignKey ( Organization , on_delete = models . CASCADE )
team = models . ForeignKey ( ' Team ' , on_delete = models . CASCADE )
request_origin = models . SmallIntegerField ( _ ( " Request from " ) , choices = ORIGINS , default = ORG , db_index = True )
request_key = models . UUIDField ( default = uuid . uuid4 , editable = True )
requested_by = models . ForeignKey ( UserProfile , related_name = ' requested_org_memberships ' , on_delete = models . SET_NULL , null = True , blank = False )
requested_date = models . DateTimeField ( default = datetime . datetime . now )
accepted_by = models . ForeignKey ( UserProfile , related_name = ' accepted_org_memberships ' , on_delete = models . SET_NULL , null = True , blank = True )
joined_date = models . DateTimeField ( null = True , blank = True )
@property
def origin_name ( self ) :
return OrgTeamRequest . ORIGINS [ self . request_origin ] [ 1 ]
def __str__ ( self ) :
return ' %s in %s ' % ( self . team , self . organization )
2018-05-20 18:35:52 +02:00
class Sponsor ( models . Model ) :
name = models . CharField ( _ ( " Sponsor Name " ) , max_length = 256 , null = False , blank = False )
description = models . TextField ( blank = True , null = True )
web_url = models . URLField ( _ ( " Website " ) , null = True , blank = True )
logo = ProcessedImageField ( verbose_name = _ ( " Logo " ) , help_text = _ ( " Will be scaled and cropped to max 250x200 px. " ) ,
upload_to = ' sponsors ' ,
processors = [ ResizeToFit ( 250 , 200 ) ] ,
format = ' PNG ' ,
2018-09-27 00:15:32 +02:00
blank = False )
2018-05-20 18:35:52 +02:00
def __str__ ( self ) :
return self . name
class SponsorSerializer ( serializers . ModelSerializer ) :
display = serializers . CharField ( source = ' __str__ ' , read_only = True )
class Meta :
model = Sponsor
fields = (
' id ' ,
' name ' ,
' logo ' ,
' web_url ' ,
)
2018-08-17 06:07:29 +02:00
class PublicTeamsManager ( models . Manager ) :
def get_queryset ( self ) :
return super ( ) . get_queryset ( ) . filter ( access = Team . PUBLIC )
2017-12-26 17:46:27 +01:00
class Team ( models . Model ) :
2018-08-17 06:07:29 +02:00
PUBLIC = 0
PERSONAL = 1
PRIVATE = 2
TYPES = [
( PUBLIC , _ ( " Public " ) ) ,
( PERSONAL , _ ( " Personal " ) ) ,
( PRIVATE , _ ( " Private " ) ) ,
]
2018-01-20 23:00:41 +01:00
name = models . CharField ( _ ( " Team Name " ) , max_length = 256 , null = False , blank = False )
2018-06-09 04:46:07 +02:00
slug = models . CharField ( max_length = 256 , null = False , blank = False , unique = True )
2018-03-17 20:29:17 +01:00
organization = models . ForeignKey ( Organization , related_name = ' teams ' , null = True , blank = True , on_delete = models . CASCADE )
2018-08-17 06:07:29 +02:00
access = models . SmallIntegerField ( verbose_name = _ ( " Access " ) , choices = TYPES , default = PUBLIC )
2017-12-26 17:46:27 +01:00
2018-06-10 19:06:46 +02:00
cover_img = models . ImageField ( verbose_name = _ ( ' Cover Image ' ) , upload_to = ' team_covers ' , null = True , blank = True )
tile_img = ImageSpecField ( source = ' cover_img ' ,
processors = [
Adjust ( contrast = 0.8 , color = 1 ) ,
ResizeToFill ( 338 , 200 ) ,
] ,
format = ' PNG ' )
banner_img = ImageSpecField ( source = ' cover_img ' ,
processors = [
Adjust ( contrast = 0.8 , color = 1 ) ,
ResizeToFill ( 825 , 200 ) ,
] ,
format = ' PNG ' )
2018-04-17 03:52:24 +02:00
description = models . TextField ( blank = True , null = True )
2018-01-25 22:50:19 +01:00
2018-07-02 00:45:54 +02:00
about_page = models . TextField ( blank = True , null = True )
2018-08-19 19:24:07 +02:00
country = models . ForeignKey ( Country , null = True , blank = True , on_delete = models . CASCADE )
2017-12-26 17:46:27 +01:00
spr = models . ForeignKey ( SPR , null = True , blank = True , on_delete = models . CASCADE )
city = models . ForeignKey ( City , null = True , blank = True , on_delete = models . CASCADE )
web_url = models . URLField ( _ ( " Website " ) , null = True , blank = True )
email = models . EmailField ( _ ( " Email Address " ) , null = True , blank = True )
2018-04-04 04:26:11 +02:00
created_date = models . DateField ( _ ( " Date Created " ) , default = timezone . now , null = True , blank = True )
2017-12-26 17:46:27 +01:00
2018-03-17 20:29:17 +01:00
owner_profile = models . ForeignKey ( UserProfile , related_name = ' owned_teams ' , null = True , on_delete = models . CASCADE )
2017-12-30 04:27:05 +01:00
admin_profiles = models . ManyToManyField ( UserProfile , related_name = ' admins ' , blank = True )
contact_profiles = models . ManyToManyField ( UserProfile , related_name = ' contacts ' , blank = True )
2017-12-26 17:46:27 +01:00
2017-12-30 04:27:05 +01:00
languages = models . ManyToManyField ( Language , blank = True )
2017-12-26 17:46:27 +01:00
active = models . BooleanField ( _ ( " Active Team " ) , default = True )
2018-04-02 04:22:30 +02:00
tz = models . CharField ( max_length = 32 , verbose_name = _ ( ' Default Timezone ' ) , default = ' UTC ' , choices = location . TimezoneChoices ( ) , blank = False , null = False , help_text = _ ( ' The most commonly used timezone for this Team. ' ) )
2017-12-26 17:46:27 +01:00
2018-01-09 04:35:44 +01:00
members = models . ManyToManyField ( UserProfile , through = ' Member ' , related_name = " memberships " , blank = True )
2018-02-27 05:03:56 +01:00
category = models . ForeignKey ( ' Category ' , on_delete = models . SET_NULL , blank = False , null = True )
2018-02-25 23:03:10 +01:00
topics = models . ManyToManyField ( ' Topic ' , blank = True )
2018-08-05 19:06:22 +02:00
sponsors = models . ManyToManyField ( ' Sponsor ' , related_name = ' teams ' , blank = True )
2018-05-20 18:35:52 +02:00
2018-08-17 06:07:29 +02:00
objects = models . Manager ( )
public_objects = PublicTeamsManager ( )
2018-06-10 19:06:46 +02:00
@property
def card_img_url ( self ) :
if self . tile_img is not None and self . tile_img . name is not None :
return self . tile_img . url
2018-07-29 22:21:46 +02:00
elif self . organization and self . organization . tile_img and self . organization . tile_img . url is not None :
return self . organization . tile_img . url
2018-06-10 19:06:46 +02:00
elif self . category is not None :
return self . category . img_url
else :
return static ( ' img/team_placeholder.png ' )
2018-09-05 04:45:49 +02:00
@property
def full_img_url ( self ) :
if self . card_img_url . startswith ( ' http ' ) :
return self . card_img_url
else :
site = Site . objects . get ( id = 1 )
return " https:// %s %s " % ( site . domain , self . card_img_url )
2018-01-23 17:12:46 +01:00
@property
def location_name ( self ) :
if self . city :
return str ( self . city )
elif self . spr :
return str ( self . spr )
elif self . country :
return str ( self . country )
else :
return ' '
2018-03-20 23:35:02 +01:00
@property
def administrators ( self ) :
return [ member . user for member in Member . objects . filter ( team = self , role = Member . ADMIN ) ]
@property
def moderators ( self ) :
return [ member . user for member in Member . objects . filter ( team = self , role__in = ( Member . ADMIN , Member . MODERATOR ) ) ]
2018-06-13 23:37:41 +02:00
def get_absolute_url ( self ) :
return reverse ( ' show-team ' , kwargs = { ' team_id ' : self . id } )
2017-12-26 17:46:27 +01:00
def __str__ ( self ) :
return u ' %s ' % ( self . name )
2018-01-23 17:12:46 +01:00
def save ( self , * args , * * kwargs ) :
if self . city is not None :
self . spr = self . city . spr
self . country = self . spr . country
2018-06-09 04:46:07 +02:00
new_slug = slugify ( self . name )
slug_matches = list ( Team . objects . filter ( slug = new_slug ) )
if len ( slug_matches ) == 0 or ( len ( slug_matches ) == 1 and slug_matches [ 0 ] . id == self . id ) :
self . slug = new_slug
else :
self . slug = ' %s - %s ' % ( new_slug , self . id )
2018-01-23 17:12:46 +01:00
super ( ) . save ( * args , * * kwargs ) # Call the "real" save() method.
2018-07-02 00:45:54 +02:00
def get_absolute_url ( self ) :
return reverse ( ' show-team-by-slug ' , kwargs = { ' team_slug ' : self . slug } )
2018-01-23 17:12:46 +01:00
2018-01-09 04:35:44 +01:00
class Member ( models . Model ) :
NORMAL = 0
MODERATOR = 1
ADMIN = 2
ROLES = [
( NORMAL , _ ( " Normal " ) ) ,
( MODERATOR , _ ( " Moderator " ) ) ,
( ADMIN , _ ( " Administrator " ) )
]
team = models . ForeignKey ( Team , on_delete = models . CASCADE )
user = models . ForeignKey ( UserProfile , on_delete = models . CASCADE )
role = models . SmallIntegerField ( _ ( " Member Role " ) , choices = ROLES , default = NORMAL , db_index = True )
joined_date = models . DateTimeField ( default = datetime . datetime . now )
2018-01-24 05:15:14 +01:00
@property
def role_name ( self ) :
return Member . ROLES [ self . role ] [ 1 ]
def __str__ ( self ) :
return ' %s in %s ' % ( self . user , self . team )
2018-02-25 22:58:41 +01:00
class Category ( models . Model ) :
name = models . CharField ( max_length = 256 )
description = models . TextField ( )
2018-04-21 16:28:04 +02:00
slug = models . CharField ( max_length = 256 , blank = True )
2018-02-27 05:03:56 +01:00
img_url = models . URLField ( blank = False , null = False )
2018-04-21 16:28:04 +02:00
class Meta :
verbose_name_plural = ' Categories '
2018-02-27 05:03:56 +01:00
def __str__ ( self ) :
return self . name
2018-02-25 22:58:41 +01:00
2018-04-21 16:28:04 +02:00
def save ( self , * args , * * kwargs ) :
self . slug = slugify ( self . name )
super ( ) . save ( * args , * * kwargs )
2018-02-25 22:58:41 +01:00
class Topic ( models . Model ) :
category = models . ForeignKey ( Category , on_delete = models . CASCADE , null = False , blank = False )
name = models . CharField ( max_length = 256 )
2018-04-21 16:28:04 +02:00
slug = models . CharField ( max_length = 256 , blank = True )
description = models . TextField ( blank = True )
2018-02-27 05:03:56 +01:00
def __str__ ( self ) :
return self . name
2018-04-21 16:28:04 +02:00
def save ( self , * args , * * kwargs ) :
self . slug = slugify ( self . name )
super ( ) . save ( * args , * * kwargs )