class Page(TimeStampedModel): title = I18nCharField(_("title"), max_length=200) slug = I18nCharField(_("slug"), max_length=200, blank=True) content = I18nTextField(_("content"), blank=False) published = models.BooleanField(_("published"), default=False) image = models.ImageField(_("image"), null=True, blank=True, upload_to="pages") conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE, verbose_name=_("conference"), related_name="pages", ) objects = models.Manager() published_pages = PageManager() def __str__(self): return str(self.title) def save(self, *args, **kwargs): if not self.slug: self.slug = copy(self.title) self.slug.map(slugify) super().save(*args, **kwargs) class Meta: unique_together = ["slug", "conference"]
class JobListing(TimeStampedModel): title = I18nCharField(_("title"), max_length=200) slug = I18nCharField(_("slug"), max_length=200, blank=True) company = models.CharField(_("company"), max_length=100) company_logo = models.ImageField(_("company logo"), null=True, blank=True, upload_to="job-listings") description = I18nTextField(_("description"), blank=True) apply_url = models.URLField(_("URL where you can apply"), blank=True) objects = JobListingManager() class Meta: ordering = ["created"] def __str__(self): return f"[{self.company}] - {self.title}" def save(self, *args, **kwargs): if not self.slug: self.slug = copy(self.title) self.slug.map(slugify) super().save(*args, **kwargs)
class Event(GeoLocalizedModel, TimeFramedModel, TimeStampedModel): slug = I18nCharField(_("slug"), blank=False) content = I18nTextField(_("content"), blank=False) title = I18nCharField(_("title"), blank=False) conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE, verbose_name=_("conference"), related_name="events", ) image = models.ImageField(_("image"), null=True, blank=True, upload_to="events") location_name = models.CharField(_("location name"), max_length=200, blank=True) def __str__(self): return f"{self.title} ({self.conference.name})" class Meta: unique_together = ["slug", "conference"] verbose_name = _("Event") verbose_name_plural = _("Events")
class Post(TimeStampedModel): author = models.ForeignKey("users.User", on_delete=models.CASCADE) title = I18nCharField(_("title"), max_length=200) slug = I18nCharField(_("slug"), max_length=200, blank=True) content = I18nTextField(_("content"), blank=True) excerpt = I18nTextField(_("excerpt"), blank=False) published = models.DateTimeField(_("published"), default=timezone.now) image = models.ImageField(_("image"), null=True, blank=True, upload_to="blog") objects = models.Manager() published_posts = PostManager() def __str__(self): return str(self.title) def save(self, *args, **kwargs): if not self.slug: self.slug = copy(self.title) self.slug.map(slugify) super().save(*args, **kwargs) class Meta: ordering = ["-published"]
class Keynote(OrderedModel, TimeStampedModel): conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE, verbose_name=_("conference"), related_name="keynotes", null=False, ) slug = I18nCharField(_("slug"), max_length=200, unique=True) title = I18nCharField(_("keynote title"), blank=False, max_length=512, default="") description = I18nTextField(_("keynote description"), blank=False, default="") topic = models.ForeignKey( "conferences.Topic", blank=True, null=True, on_delete=models.SET_NULL, default=None, ) order_with_respect_to = "conference" objects = KeynoteManager() def __str__(self) -> str: return f"{self.title} at {self.conference.code}" class Meta(OrderedModel.Meta): verbose_name = _("Keynote") verbose_name_plural = _("Keynotes")
class Deadline(TimeFramedModel): TYPES = Choices( ("cfp", _("Call for proposal")), ("voting", _("Voting")), ("refund", _("Ticket refund")), ("grants", _("Grants")), ("custom", _("Custom deadline")), ) conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE, verbose_name=_("conference"), related_name="deadlines", ) name = I18nCharField(_("name"), max_length=100) description = I18nTextField(_("description"), blank=True, null=True) type = models.CharField(_("type"), choices=TYPES, max_length=10) def __str__(self): return f"{self.type} ({self.name}) <{self.conference.code}>" @property def status(self) -> DeadlineStatus: now = timezone.now() if now >= self.start and now <= self.end: return DeadlineStatus.HAPPENING_NOW if self.start > now: return DeadlineStatus.IN_THE_FUTURE return DeadlineStatus.IN_THE_PAST
class HotelRoom(models.Model): conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE, verbose_name=_("conference"), related_name="hotel_rooms", ) name = I18nCharField(_("name"), max_length=200) description = I18nTextField(_("description"), blank=True) total_capacity = models.PositiveIntegerField(_("total capacity")) price = models.DecimalField(_("price"), max_digits=7, decimal_places=2) @cached_property def capacity_left(self): return (self.total_capacity - HotelRoomReservation.objects.filter(room_id=self.pk).count()) @property def is_sold_out(self): # TODO: run the check in the query instead of python return (HotelRoomReservation.objects.filter(room_id=self.pk).count() >= self.total_capacity) def __str__(self): return f"{self.name} ({self.conference.code})" class Meta: verbose_name = _("hotel room") verbose_name_plural = _("hotel rooms")
class KeynoteSpeaker(TimeStampedModel, OrderedModel): keynote = models.ForeignKey( "conferences.Keynote", on_delete=models.CASCADE, verbose_name=_("keynote"), related_name="speakers", null=False, ) name = models.CharField( _("fullname"), max_length=512, blank=False, ) photo = models.ImageField(_("photo"), null=False, blank=False, upload_to="keynotes") bio = I18nTextField( _("bio"), blank=False, ) pronouns = I18nCharField( _("pronouns"), max_length=512, ) highlight_color = models.CharField(choices=COLORS, max_length=15, blank=True, verbose_name=_("highlight color")) twitter_handle = models.CharField( _("twitter handle"), max_length=1024, default="", blank=True, ) instagram_handle = models.CharField( _("instagram handle"), max_length=1024, default="", blank=True, ) website = models.URLField(_("website"), blank=True, default="", max_length=2049) order_with_respect_to = "keynote" class Meta(OrderedModel.Meta): verbose_name = _("Keynote Speaker") verbose_name_plural = _("Keynote Speakers")
def test_i18n_textinput(): widget = I18nTextInput( locales=[("en", "English"), ("it", "Italian")], field=I18nCharField() ) output = widget.render("msg", "value") assert ( output == """<div class="i18n-form-group"><div> <label style="width: auto; margin-right: 20px;"> <strong style="display:block; margin-bottom: 10px;">English:</strong>\n <input type="text" name="msg_0" lang="en">\n </label>\n</div><div> <label style="width: auto; margin-right: 20px;"> <strong style="display:block; margin-bottom: 10px;">Italian:</strong>\n <input type="text" name="msg_1" value="value" lang="it">\n </label> </div></div>""" )
class Deadline(TimeFramedModel): TYPES = Choices( ("cfp", _("Call for proposal")), ("voting", _("Voting")), ("refund", _("Ticket refund")), ("custom", _("Custom deadline")), ) conference = models.ForeignKey( "conferences.Conference", on_delete=models.CASCADE, verbose_name=_("conference"), related_name="deadlines", ) name = I18nCharField(_("name"), max_length=100) description = I18nTextField(_("description"), blank=True, null=True) type = models.CharField(_("type"), choices=TYPES, max_length=10) def clean(self): super().clean() if self.start > self.end: raise exceptions.ValidationError(_("Start date cannot be after end")) if self.type != Deadline.TYPES.custom: if ( Deadline.objects.filter(conference=self.conference, type=self.type) .exclude(id=self.id) .exists() ): raise exceptions.ValidationError( _("You can only have one deadline of type %(type)s") % {"type": self.type} ) def __str__(self): return f"{self.type} ({self.name}) <{self.conference.code}>"
class Conference(GeoLocalizedModel, TimeFramedModel, TimeStampedModel): name = I18nCharField(_("name"), max_length=100) code = models.CharField(_("code"), max_length=10, unique=True) timezone = TimeZoneField() topics = models.ManyToManyField( "conferences.Topic", verbose_name=_("topics"), blank=True ) languages = models.ManyToManyField( "languages.Language", verbose_name=_("languages"), blank=True ) audience_levels = models.ManyToManyField( "conferences.AudienceLevel", verbose_name=_("audience levels"), blank=True ) submission_types = models.ManyToManyField( "submissions.SubmissionType", verbose_name=_("submission types"), blank=True ) pretix_organizer_id = models.CharField( _("pretix organizer id"), max_length=200, blank=True, default="" ) pretix_event_id = models.CharField( _("pretix event id"), max_length=200, blank=True, default="" ) pretix_event_url = models.URLField(_("pretix event url"), blank=True, default="") pretix_hotel_ticket_id = models.IntegerField( _("pretix hotel ticket id"), blank=True, null=True ) pretix_hotel_room_type_question_id = models.IntegerField( _("pretix hotel room type question id"), blank=True, null=True ) pretix_hotel_checkin_question_id = models.IntegerField( _("pretix hotel check-in question id"), blank=True, null=True ) pretix_hotel_checkout_question_id = models.IntegerField( _("pretix hotel checkout question id"), blank=True, null=True ) introduction = I18nTextField(_("introduction"), blank=False) @property def is_cfp_open(self): try: cfp_deadline = self.deadlines.get(type=Deadline.TYPES.cfp) now = timezone.now() return cfp_deadline.start <= now <= cfp_deadline.end except Deadline.DoesNotExist: return False @property def is_voting_open(self): try: voting_deadline = self.deadlines.get(type=Deadline.TYPES.voting) now = timezone.now() return voting_deadline.start <= now <= voting_deadline.end except Deadline.DoesNotExist: return False @cached_property def is_voting_closed(self): try: voting_deadline = self.deadlines.get(type=Deadline.TYPES.voting) now = timezone.now() return voting_deadline.end <= now except Deadline.DoesNotExist: return False @cached_property def is_grants_open(self): try: grants_deadline = self.deadlines.get(type=Deadline.TYPES.grants) return grants_deadline.status == DeadlineStatus.HAPPENING_NOW except Deadline.DoesNotExist: return False def __str__(self): return f"{self.name} <{self.code}>" class Meta: verbose_name = _("Conference") verbose_name_plural = _("Conferences")