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
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2017-12-30 04:27:05 +01:00
|
|
|
from django.shortcuts import reverse
|
2017-12-26 17:46:27 +01:00
|
|
|
|
2018-01-20 20:09:57 +01:00
|
|
|
from rest_framework import serializers
|
2018-03-24 05:00:38 +01:00
|
|
|
from mptt.models import MPTTModel, TreeForeignKey
|
2018-01-20 20:09:57 +01:00
|
|
|
|
2018-03-17 22:37:56 +01:00
|
|
|
from imagekit.models import ImageSpecField
|
|
|
|
from imagekit.processors import ResizeToFill
|
|
|
|
|
2017-12-26 17:46:27 +01:00
|
|
|
from .locale import *
|
|
|
|
from .profiles import *
|
|
|
|
from .search import *
|
|
|
|
|
|
|
|
import re
|
|
|
|
import pytz
|
|
|
|
import datetime
|
|
|
|
import unicodedata
|
2018-03-11 08:13:57 +01:00
|
|
|
import hashlib
|
2017-12-26 17:46:27 +01:00
|
|
|
|
|
|
|
SLUG_OK = '-_~'
|
|
|
|
|
|
|
|
|
|
|
|
class Place(models.Model):
|
|
|
|
name = models.CharField(help_text=_('Name of the Place'), max_length=150)
|
|
|
|
city = models.ForeignKey(City, verbose_name=_('City'), on_delete=models.CASCADE)
|
|
|
|
address = models.CharField(help_text=_('Address with Street and Number'), max_length=150, null=True, blank=True)
|
|
|
|
longitude = models.FloatField(help_text=_('Longitude in Degrees East'), null=True, blank=True)
|
|
|
|
latitude = models.FloatField(help_text=_('Latitude in Degrees North'), null=True, blank=True)
|
|
|
|
tz = models.CharField(max_length=32, verbose_name=_('Timezone'), default='UTC', choices=[(tz, tz) for tz in pytz.all_timezones], blank=False, null=False)
|
|
|
|
place_url = models.URLField(help_text=_('URL for the Place Homepage'), verbose_name=_('URL of the Place'), max_length=200, blank=True, null=True)
|
|
|
|
cover_img = models.URLField(_("Place photo"), null=True, blank=True)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return u'%s, %s' % (self.name, self.city.name)
|
|
|
|
|
2018-01-09 04:35:44 +01:00
|
|
|
class PlaceSerializer(serializers.ModelSerializer):
|
|
|
|
city = serializers.CharField(read_only=True)
|
|
|
|
class Meta:
|
|
|
|
model = Place
|
|
|
|
fields = (
|
|
|
|
'id',
|
|
|
|
'name',
|
|
|
|
'city',
|
|
|
|
'address',
|
|
|
|
'longitude',
|
|
|
|
'latitude',
|
|
|
|
'tz',
|
|
|
|
'place_url',
|
|
|
|
'cover_img'
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2017-12-26 17:46:27 +01:00
|
|
|
class Event(models.Model):
|
|
|
|
name = models.CharField(max_length=150, verbose_name=_('Event Name'))
|
|
|
|
team = models.ForeignKey(Team, on_delete=models.CASCADE)
|
2018-03-17 20:29:17 +01:00
|
|
|
parent = models.ForeignKey('CommonEvent', related_name='participating_events', null=True, blank=True, on_delete=models.SET_NULL)
|
2017-12-26 17:46:27 +01:00
|
|
|
|
|
|
|
start_time = models.DateTimeField(help_text=_('Local date and time that the event starts'), verbose_name=_('Local Start Time'), db_index=True)
|
|
|
|
end_time = models.DateTimeField(help_text=_('Local date and time that the event ends'), verbose_name=_('Local End Time'), db_index=True)
|
|
|
|
summary = models.TextField(help_text=_('Summary of the Event'), blank=True, null=True)
|
|
|
|
|
|
|
|
place = models.ForeignKey(Place, blank=True, null=True, on_delete=models.CASCADE)
|
|
|
|
|
|
|
|
web_url = models.URLField(verbose_name=_('Website'), help_text=_('URL for the event'), max_length=200, blank=True, null=True)
|
|
|
|
announce_url = models.URLField(verbose_name=_('Announcement'), help_text=_('URL for the announcement'), max_length=200, blank=True, null=True)
|
|
|
|
|
|
|
|
created_by = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
|
|
|
created_time = models.DateTimeField(help_text=_('the date and time when the event was created'), default=datetime.datetime.now, db_index=True)
|
|
|
|
|
2018-02-27 05:03:56 +01:00
|
|
|
tags = models.CharField(verbose_name=_("Keyword Tags"), blank=True, null=True, max_length=128)
|
2017-12-26 17:46:27 +01:00
|
|
|
#image
|
|
|
|
#replies
|
|
|
|
|
2018-01-24 05:15:14 +01:00
|
|
|
attendees = models.ManyToManyField(UserProfile, through='Attendee', related_name="attending", blank=True)
|
|
|
|
|
2017-12-26 17:46:27 +01:00
|
|
|
def get_absolute_url(self):
|
2018-01-04 05:44:27 +01:00
|
|
|
return reverse('show-event', kwargs={'event_id': self.id, 'event_slug': self.slug})
|
|
|
|
|
2018-02-12 22:32:42 +01:00
|
|
|
def get_full_url(self):
|
|
|
|
site = Site.objects.get(id=1)
|
|
|
|
return "https://%s%s" % (site.domain, self.get_absolute_url())
|
|
|
|
|
2018-01-04 05:44:27 +01:00
|
|
|
@property
|
|
|
|
def slug(self):
|
|
|
|
return slugify(self.name)
|
2017-12-26 17:46:27 +01:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return u'%s by %s at %s' % (self.name, self.team.name, self.start_time)
|
|
|
|
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
super().save(*args, **kwargs) # Call the "real" save() method.
|
|
|
|
update_event_searchable(self)
|
|
|
|
|
|
|
|
def update_event_searchable(event):
|
|
|
|
site = Site.objects.get(id=1)
|
|
|
|
event_url = "https://%s%s" % (site.domain, event.get_absolute_url())
|
2018-01-25 22:41:12 +01:00
|
|
|
origin_url = "https://%s%s" % (site.domain, reverse('searchables'))
|
2018-03-11 08:13:57 +01:00
|
|
|
|
|
|
|
md5 = hashlib.md5()
|
|
|
|
federation_url = event_url.split('/')
|
|
|
|
federation_node = '/'.join(federation_url[:3])
|
|
|
|
federation_id = '/'.join(federation_url[:5])
|
|
|
|
md5.update(bytes(federation_id, 'utf8'))
|
|
|
|
event_uri = federation_node + '/' + md5.hexdigest()
|
|
|
|
|
2017-12-26 17:46:27 +01:00
|
|
|
try:
|
2018-03-11 08:13:57 +01:00
|
|
|
searchable = Searchable.objects.get(event_uri=event_uri)
|
2017-12-26 17:46:27 +01:00
|
|
|
except:
|
2018-03-11 08:13:57 +01:00
|
|
|
searchable = Searchable(event_uri)
|
2018-01-25 22:41:12 +01:00
|
|
|
searchable.origin_node = origin_url
|
|
|
|
searchable.federation_node = origin_url
|
2017-12-26 17:46:27 +01:00
|
|
|
searchable.federation_time = datetime.datetime.now()
|
|
|
|
|
2018-03-11 08:13:57 +01:00
|
|
|
searchable.event_url = event_url
|
|
|
|
|
2018-02-27 05:03:56 +01:00
|
|
|
if event.team.category:
|
|
|
|
searchable.img_url = event.team.category.img_url
|
|
|
|
else:
|
|
|
|
searchable.img_url = "https://%s%s" % (site.domain, '/static/img/team_placeholder.png')
|
2017-12-26 17:46:27 +01:00
|
|
|
searchable.event_title = event.name
|
|
|
|
searchable.group_name = event.team.name
|
|
|
|
searchable.start_time = event.start_time
|
|
|
|
searchable.end_time = event.end_time
|
|
|
|
searchable.cost = 0
|
|
|
|
searchable.tags = event.tags
|
|
|
|
if (event.place is not None):
|
|
|
|
searchable.location_name = str(event.place.city)
|
|
|
|
searchable.venue_name = event.place.name
|
|
|
|
searchable.longitude = event.place.longitude or None
|
2018-01-25 22:41:12 +01:00
|
|
|
searchable.latitude = event.place.latitude or None
|
2017-12-26 17:46:27 +01:00
|
|
|
else:
|
2018-01-24 16:58:47 +01:00
|
|
|
searchable.location_name = event.team.location_name
|
2018-01-25 22:41:12 +01:00
|
|
|
|
|
|
|
if event.team.city is not None and (searchable.longitude is None or searchable.latitude is None):
|
|
|
|
searchable.longitude = event.team.city.longitude
|
|
|
|
searchable.latitude = event.team.city.latitude
|
|
|
|
|
2017-12-26 17:46:27 +01:00
|
|
|
searchable.save()
|
|
|
|
|
|
|
|
def slugify(s, ok=SLUG_OK, lower=True, spaces=False):
|
|
|
|
# L and N signify letter/number.
|
|
|
|
# http://www.unicode.org/reports/tr44/tr44-4.html#GC_Values_Table
|
|
|
|
rv = []
|
|
|
|
for c in unicodedata.normalize('NFKC', s):
|
|
|
|
cat = unicodedata.category(c)[0]
|
|
|
|
if cat in 'LN' or c in ok:
|
|
|
|
rv.append(c)
|
|
|
|
if cat == 'Z': # space
|
|
|
|
rv.append(' ')
|
|
|
|
new = ''.join(rv).strip()
|
|
|
|
if not spaces:
|
|
|
|
new = re.sub('[-\s]+', '-', new)
|
|
|
|
return new.lower() if lower else new
|
2018-01-24 05:15:14 +01:00
|
|
|
|
|
|
|
class Attendee(models.Model):
|
|
|
|
NORMAL=0
|
|
|
|
CREW=1
|
|
|
|
HOST=2
|
|
|
|
ROLES = [
|
|
|
|
(NORMAL, _("Normal")),
|
|
|
|
(CREW, _("Crew")),
|
|
|
|
(HOST, _("Host"))
|
|
|
|
]
|
|
|
|
NO=-1
|
|
|
|
MAYBE=0
|
|
|
|
YES=1
|
|
|
|
STATUSES = [
|
|
|
|
(NO, _("No")),
|
|
|
|
(MAYBE, _("Maybe")),
|
|
|
|
(YES, _("Yes")),
|
|
|
|
]
|
|
|
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
|
|
|
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
|
|
|
role = models.SmallIntegerField(_("Role"), choices=ROLES, default=NORMAL, db_index=True)
|
2018-01-24 05:52:42 +01:00
|
|
|
status = models.SmallIntegerField(_("Attending?"), choices=STATUSES, default=YES, db_index=True)
|
2018-01-24 05:15:14 +01:00
|
|
|
joined_date = models.DateTimeField(default=datetime.datetime.now)
|
2018-01-24 05:52:42 +01:00
|
|
|
|
|
|
|
@property
|
|
|
|
def role_name(self):
|
|
|
|
return Attendee.ROLES[self.role][1]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def status_name(self):
|
|
|
|
return Attendee.STATUSES[self.status+1][1]
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return "%s at %s" % (self.user, self.event)
|
2018-03-17 22:37:56 +01:00
|
|
|
|
|
|
|
class EventPhoto(models.Model):
|
|
|
|
event = models.ForeignKey(Event, related_name='photos', on_delete=models.CASCADE)
|
|
|
|
title = models.CharField(max_length=256)
|
|
|
|
caption = models.TextField(null=True, blank=True)
|
|
|
|
src = models.ImageField(verbose_name=_('Photo'), upload_to='event_photos')
|
|
|
|
thumbnail = ImageSpecField(source='src',
|
|
|
|
processors=[ResizeToFill(250, 187)],
|
|
|
|
format='JPEG',
|
|
|
|
options={'quality': 60})
|
2018-03-17 20:29:17 +01:00
|
|
|
|
2018-03-24 05:00:38 +01:00
|
|
|
class EventComment(MPTTModel):
|
|
|
|
REMOVED=-1
|
|
|
|
PENDING=0
|
|
|
|
APPROVED=1
|
|
|
|
|
|
|
|
STATUSES = [
|
|
|
|
(REMOVED, _("Removed")),
|
|
|
|
(PENDING, _("Pending")),
|
|
|
|
(APPROVED, _("Approved")),
|
|
|
|
]
|
|
|
|
author = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
|
|
|
event = models.ForeignKey(Event, related_name='comments', on_delete=models.CASCADE)
|
|
|
|
body = models.TextField()
|
|
|
|
created_time = models.DateTimeField(default=datetime.datetime.now, db_index=True)
|
|
|
|
status = models.SmallIntegerField(choices=STATUSES, default=APPROVED, db_index=True)
|
|
|
|
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True, on_delete=models.SET_NULL)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return '%s at %s' % (self.author, self.created_time)
|
|
|
|
|
2018-03-17 20:29:17 +01:00
|
|
|
class CommonEvent(models.Model):
|
|
|
|
name = models.CharField(max_length=150, verbose_name=_('Event Name'))
|
|
|
|
organization = models.ForeignKey(Organization, null=True, blank=True, on_delete=models.CASCADE)
|
|
|
|
parent = models.ForeignKey('CommonEvent', related_name='sub_events', null=True, blank=True, on_delete=models.SET_NULL)
|
|
|
|
|
|
|
|
start_time = models.DateTimeField(help_text=_('Local date and time that the event starts'), verbose_name=_('Local Start Time'), db_index=True)
|
|
|
|
end_time = models.DateTimeField(help_text=_('Local date and time that the event ends'), verbose_name=_('Local End Time'), db_index=True)
|
|
|
|
summary = models.TextField(help_text=_('Summary of the Event'), blank=True, null=True)
|
|
|
|
|
|
|
|
country = models.ForeignKey(Country, null=True, blank=True, on_delete=models.SET_NULL)
|
|
|
|
spr = models.ForeignKey(SPR, null=True, blank=True, on_delete=models.SET_NULL)
|
|
|
|
city = models.ForeignKey(City, null=True, blank=True, on_delete=models.SET_NULL)
|
|
|
|
place = models.ForeignKey(Place, blank=True, null=True, on_delete=models.SET_NULL)
|
|
|
|
|
|
|
|
web_url = models.URLField(verbose_name=_('Website'), help_text=_('URL for the event'), max_length=200, blank=True, null=True)
|
|
|
|
announce_url = models.URLField(verbose_name=_('Announcement'), help_text=_('URL for the announcement'), max_length=200, blank=True, null=True)
|
|
|
|
|
|
|
|
created_by = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
|
|
|
|
created_time = models.DateTimeField(help_text=_('the date and time when the event was created'), default=datetime.datetime.now, db_index=True)
|
|
|
|
|
|
|
|
category = models.ForeignKey('Category', on_delete=models.SET_NULL, blank=False, null=True)
|
|
|
|
topics = models.ManyToManyField('Topic', blank=True)
|
|
|
|
tags = models.CharField(verbose_name=_("Keyword Tags"), blank=True, null=True, max_length=128)
|
|
|
|
|
2018-03-22 05:15:09 +01:00
|
|
|
def get_absolute_url(self):
|
|
|
|
return reverse('show-common-event', kwargs={'event_id': self.id, 'event_slug': self.slug})
|
|
|
|
|
|
|
|
def get_full_url(self):
|
|
|
|
site = self.organization.site
|
|
|
|
return "https://%s%s" % (site.domain, self.get_absolute_url())
|
|
|
|
|
|
|
|
@property
|
|
|
|
def slug(self):
|
|
|
|
return slugify(self.name)
|
|
|
|
|
2018-03-17 20:29:17 +01:00
|
|
|
def __str__(self):
|
|
|
|
return self.name
|
|
|
|
|