Example #1
0
class CommunityTrackEvent(models.Model):
    venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
    talk = BigForeignKey(TalkProposal,
                         on_delete=models.CASCADE,
                         null=True,
                         blank=True)
    sponsored_event = BigForeignKey(SponsoredEvent,
                                    on_delete=models.CASCADE,
                                    null=True,
                                    blank=True)
    order = models.IntegerField(default=0)

    class Meta:
        verbose_name = _('community track event')
        verbose_name_plural = _('community track events')
        ordering = ['order']

    def __str__(self):
        return "%d. %s (%s)" % (self.order, self.get_event(), self.venue.name)

    def clean(self):
        if self.talk and self.sponsored_event:
            raise ValidationError(
                _('You can only put either proposed_talk_event or sponsored_event at once.'
                  ))

        if not self.talk and not self.sponsored_event:
            raise ValidationError(
                _('You need to put proposed_talk_event or sponsored_event.'))

    def get_event(self):
        return self.talk or self.sponsored_event
Example #2
0
class CommunityTrackEvent(models.Model):
    venue = models.ForeignKey(Venue,
                              on_delete=models.CASCADE,
                              null=True,
                              blank=True)
    talk = BigForeignKey(TalkProposal,
                         on_delete=models.CASCADE,
                         null=True,
                         blank=True)
    sponsored_event = BigForeignKey(SponsoredEvent,
                                    on_delete=models.CASCADE,
                                    null=True,
                                    blank=True)
    custom_event = models.CharField(max_length=140, blank=True)
    order = models.IntegerField(default=0)

    begin_time = models.ForeignKey(
        to=Time,
        blank=True,
        null=True,
        related_name='beginning_%(class)s_set',
        verbose_name=_('begin time'),
        on_delete=models.CASCADE,
    )

    end_time = models.ForeignKey(
        to=Time,
        blank=True,
        null=True,
        related_name='ending_%(class)s_set',
        verbose_name=_('end time'),
        on_delete=models.CASCADE,
    )

    class Meta:
        verbose_name = _('community track event')
        verbose_name_plural = _('community track events')
        ordering = ['begin_time', 'order']

    def __str__(self):
        return "%d. %s" % (self.order, self.get_event())

    def clean(self):
        values = [self.talk, self.sponsored_event, self.custom_event]
        count = len(tuple(filter(None, values)))

        if count > 1:
            raise ValidationError(
                _('You can only put either proposed_talk_event, sponsored_event or custom_event at once.'
                  ))

        if count <= 0:
            raise ValidationError(
                _('You need to put proposed_talk_event sponsored_event, or custom_event.'
                  ))

    def get_event(self):
        return self.talk or self.sponsored_event or self.custom_event
Example #3
0
class ProposedTutorialEvent(BaseEvent):

    proposal = BigForeignKey(
        to=TutorialProposal,
        verbose_name=_('proposal'),
    )
    registration_link = models.URLField(
        verbose_name=_('registration link'),
        blank=True,
        default='',
    )

    objects = ProposedEventManager()

    class Meta:
        verbose_name = _('tutorial event')
        verbose_name_plural = _('tutorial events')

    def __str__(self):
        return self.proposal.title

    def get_absolute_url(self):
        return reverse('events_tutorial_detail', kwargs={
            'pk': self.proposal.pk,
        })
Example #4
0
class AbstractProposal(EventInfo):

    submitter = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('submitter'),
    )

    objective = EAWTextField(
        verbose_name=_('objective'),
        max_length=1000,
        help_text=_(
            "Who is the intended audience for your talk? (Be specific, "
            "\"Python users\" is not a good answer). "
            "And what will the attendees get out of your talk? When they "
            "leave the room, what will they learn that they didn't know "
            "before? This is NOT made public and for REVIEW ONLY."
        ),
    )

    supplementary = models.TextField(
        verbose_name=_('supplementary'),
        blank=True,
        default='',
        help_text=_(
            "Anything else you'd like the program committee to know when "
            "making their selection: your past speaking experience, community "
            "experience, etc. This is NOT made public and for REVIEW ONLY. "
            "Edit using "
            "<a href='http://daringfireball.net/projects/markdown/basics' "
            "target='_blank'>Markdown</a>."
        ),
    )

    cancelled = models.BooleanField(
        verbose_name=_('cancelled'),
        default=False,
        db_index=True,
    )

    additionalspeaker_set = GenericRelation(
        to=AdditionalSpeaker,
        content_type_field='proposal_type',
        object_id_field='proposal_id',
    )

    objects = ProposalQuerySet.as_manager()

    class Meta:
        abstract = True

    @property
    def speakers(self):
        yield PrimarySpeaker(self)
        additionals = self.additionalspeaker_set.filter(cancelled=False)
        for speaker in additionals.select_related('user'):
            yield speaker

    @property
    def speaker_count(self):
        return self.additionalspeaker_set.filter(cancelled=False).count() + 1
Example #5
0
class ProposedTalkEvent(BaseEvent):

    proposal = BigForeignKey(
        to=TalkProposal,
        limit_choices_to={'accepted': True},
        verbose_name=_('proposal'),
        on_delete=models.CASCADE,
        unique=True,
    )
    is_remote = models.BooleanField(
        verbose_name=_('is remote'),
        default=False,
    )

    objects = ProposedEventManager()

    class Meta:
        verbose_name = _('talk event')
        verbose_name_plural = _('talk events')

    def __str__(self):
        return self.proposal.title

    def get_absolute_url(self):
        return reverse('events_talk_detail', kwargs={'pk': self.proposal.pk})
Example #6
0
class TalkProposalSnapshot(models.Model):
    """Snapshot for a talk proposal during a review stage.
    """
    proposal = BigForeignKey(
        to=TalkProposal,
        verbose_name=_('proposal'),
    )

    stage = models.IntegerField(verbose_name=_('stage'), )

    dumped_json = models.TextField(verbose_name=_('dumped JSON'), )

    dumped_at = models.DateTimeField(
        verbose_name=_('dumped at'),
        auto_now=True,
    )

    class Meta:
        verbose_name = _('talk proposal snapshot')
        verbose_name_plural = _('talk proposal snapshots')
        unique_together = ['proposal', 'stage']

    def __str__(self):
        return ugettext('Stage {stage} dump for {proposal}').format(
            stage=self.stage,
            proposal=self.proposal,
        )
Example #7
0
class OpenRole(models.Model):

    sponsor = BigForeignKey(
        to=Sponsor,
        verbose_name=_("sponsor"),
        on_delete=models.CASCADE,
    )

    name = models.CharField(
        verbose_name=_('open role name'),
        max_length=100,
    )

    description = models.TextField(verbose_name=_('open role descsription'), )
    url = models.URLField(
        verbose_name=_('open role URL'),
        max_length=255,
        blank=True,
    )

    class Meta:
        verbose_name = _('open role')
        verbose_name_plural = _('open Roles')

    def __str__(self):
        return self.name
Example #8
0
class SponsoredEvent(EventInfo, BaseEvent):

    host = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('host'),
        on_delete=models.CASCADE,
    )
    slug = models.SlugField(
        allow_unicode=True,
        verbose_name=_('slug'),
    )

    class Meta:
        verbose_name = _('sponsored event')
        verbose_name_plural = _('sponsored events')

    def get_absolute_url(self):
        return reverse('events_sponsored_event_detail',
                       kwargs={
                           'slug': self.slug,
                       })

    @property
    def speakers(self):
        yield PrimarySpeaker(user=self.host)
Example #9
0
class CommunityTrackEvent(models.Model):
    venue = models.ForeignKey(Venue, on_delete=models.CASCADE)
    talk = BigForeignKey(TalkProposal, on_delete=models.CASCADE)
    order = models.IntegerField(default=0)

    def __str__(self):
        return "%d. %s (%s)" % (self.order, self.talk.title, self.venue.name)
Example #10
0
class CocRecord(models.Model):
    user = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('user'),
        on_delete=models.CASCADE,
    )
    coc_version = models.CharField(verbose_name=_('latest agreed CoC version'),
                                   max_length=15)
    agreed_at = models.DateTimeField(
        verbose_name=_('agreed at'),
        auto_now_add=True,
        blank=True,
        null=True,
    )
Example #11
0
class AdditionalSpeaker(ConferenceRelated):

    user = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('user'),
        on_delete=models.CASCADE,
    )

    proposal_type = models.ForeignKey(
        to='contenttypes.ContentType',
        verbose_name=_('proposal model type'),
        on_delete=models.CASCADE,
    )
    proposal_id = models.BigIntegerField(
        verbose_name=_('proposal ID'),
    )
    proposal = GenericForeignKey('proposal_type', 'proposal_id')

    SPEAKING_STATUS_PENDING = 'pending'
    SPEAKING_STATUS_ACCEPTED = 'accepted'
    SPEAKING_STATUS_DECLINED = 'declined'
    SPEAKING_STATUS = (
        (SPEAKING_STATUS_PENDING,  _('Pending')),
        (SPEAKING_STATUS_ACCEPTED, _('Accepted')),
        (SPEAKING_STATUS_DECLINED, _('Declined')),
    )
    status = models.CharField(
        max_length=8,
        choices=SPEAKING_STATUS,
        default=SPEAKING_STATUS_PENDING,
    )

    cancelled = models.BooleanField(
        verbose_name=_('cancelled'),
        default=False,
        db_index=True,
    )

    class Meta:
        unique_together = ['user', 'proposal_type', 'proposal_id']
        ordering = ['proposal_type', 'proposal_id', 'pk']
        verbose_name = _('additional speaker')
        verbose_name_plural = _('additional speakers')

    def __str__(self):
        return '{name} ({status})'.format(
            name=self.user.speaker_name,
            status=self.get_status_display(),
        )
Example #12
0
class JobListingsEvent(BaseEvent):

    sponsor = BigForeignKey(
        to=Sponsor,
        verbose_name=_("sponsor"),
        on_delete=models.CASCADE,
    )

    class Meta:
        verbose_name = _('Job Listings')
        verbose_name_plural = _('Job Listings')

    def __str__(self):
        return gettext('Open Role of Sponsor: {sponsor}'.format(
            sponsor=self.sponsor, ))
Example #13
0
class ProposedTalkEvent(BaseEvent):

    proposal = BigForeignKey(
        to=TalkProposal,
        limit_choices_to={'accepted': True},
        verbose_name=_('proposal'),
    )

    objects = ProposedEventManager()

    class Meta:
        verbose_name = _('talk event')
        verbose_name_plural = _('talk events')

    def __str__(self):
        return self.proposal.title

    def get_absolute_url(self):
        return reverse('events_talk_detail', kwargs={'pk': self.proposal.pk})
class Migration(migrations.Migration):

    dependencies = [
        ('proposals', '0036_add_proposal_update_timestamp'),
        ('reviews', '0015_review_appropriateness'),
    ]

    operations = [
        migrations.CreateModel(
            name='TalkProposalSnapshot',
            fields=[
                ('id',
                 models.AutoField(
                     auto_created=True,
                     primary_key=True,
                     serialize=False,
                     verbose_name='ID',
                 )),
                ('stage', models.IntegerField(verbose_name='stage', )),
                ('dumped_json',
                 models.TextField(verbose_name='dumped JSON', )),
                ('dumped_at',
                 models.DateTimeField(
                     auto_now=True,
                     verbose_name='dumped at',
                 )),
                ('proposal',
                 BigForeignKey(
                     on_delete=models.deletion.CASCADE,
                     to='proposals.TalkProposal',
                     verbose_name='proposal',
                 )),
            ],
            options={
                'verbose_name': 'talk proposal snapshot',
                'verbose_name_plural': 'talk proposal snapshots',
            },
        ),
        migrations.AlterUniqueTogether(
            name='talkproposalsnapshot',
            unique_together={('proposal', 'stage')},
        ),
    ]
Example #15
0
class SponsoredEvent(EventInfo):

    host = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('host'),
    )
    slug = models.SlugField(
        allow_unicode=True,
        verbose_name=_('slug'),
    )

    class Meta:
        verbose_name = _('sponsored event')
        verbose_name_plural = _('sponsored events')

    def get_absolute_url(self):
        return reverse('events_sponsored_event_detail',
                       kwargs={
                           'slug': self.slug,
                       })
Example #16
0
class ProposedTutorialEvent(BaseEvent):

    proposal = BigForeignKey(
        to=TutorialProposal,
        verbose_name=_('proposal'),
        on_delete=models.CASCADE,
        unique=True,
    )
    registration_link = models.URLField(
        verbose_name=_('registration link'),
        blank=True,
        default='',
    )
    is_remote = models.BooleanField(
        verbose_name=_('is remote'),
        default=False,
    )
    youtube_id = models.CharField(
        verbose_name=_('youtube id'),
        max_length=20,
        blank=True,
    )

    objects = ProposedEventManager()

    class Meta:
        verbose_name = _('tutorial event')
        verbose_name_plural = _('tutorial events')

    def __str__(self):
        return self.proposal.title

    def get_absolute_url(self):
        return reverse('events_tutorial_detail',
                       kwargs={
                           'pk': self.proposal.pk,
                       })
Example #17
0
class Review(models.Model):

    reviewer = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('reviewer'),
    )

    stage = models.IntegerField(verbose_name=_('stage'), )

    proposal = models.ForeignKey(
        to=TalkProposal,
        verbose_name=_('proposal'),
    )

    objects = ReviewManager()
    all_objects = ReviewQuerySet.as_manager()

    class Vote:
        PLUS_ONE = '+1'
        PLUS_ZERO = '+0'
        MINUS_ZERO = '-0'
        MINUS_ONE = '-1'

    VOTE_CHOICES = (
        (Vote.PLUS_ONE, _('+1 (strong accept)')),
        (Vote.PLUS_ZERO, _('+0 (weak accept)')),
        (Vote.MINUS_ZERO, _('-0 (weak reject)')),
        (Vote.MINUS_ONE, _('-1 (strong reject)')),
    )

    VOTE_ORDER = {vote: i for i, (vote, _) in enumerate(VOTE_CHOICES)}

    vote = models.CharField(
        max_length=2,
        blank=False,
        choices=VOTE_CHOICES,
        verbose_name=_("vote"),
        help_text=_(
            "Your vote to accept or reject this talk. "
            "More information about the scoring and acceptance criteria "
            "can be found at the GitBook "
            "<a href=\"https://pycontw.github.io/reviewer-guidebook\" "
            "target=\"_blank\">Review Guideline</a>."),
    )

    comment = models.TextField(
        verbose_name=_('comment'),
        help_text=_(
            "Comments to this proposal. This may be available for other "
            "reviewers in later review stages, and you can choose whether "
            "or not to disclose it to the proposal's submitter."),
    )

    DISCLOSE_CHOICES = (
        (True, _('Yes')),
        (False, _('No')),
    )
    discloses_comment = models.BooleanField(
        default=True,
        choices=DISCLOSE_CHOICES,
        verbose_name=_('discloses comment to proposal submitter'),
        help_text=_(
            "Whether the proposal submitter can read you comments. We will "
            "include your comments in the proposal acceptance/rejection "
            "notice sent to the submitter if you allow us to."))

    APPROPRIATENESS_CHOICES = (
        (True, _('Yes')),
        (False, _('No')),
    )
    appropriateness = models.BooleanField(
        default=False,
        choices=APPROPRIATENESS_CHOICES,
        verbose_name=_('is appropriate'),
        help_text=_(
            "Administrators can use this field to hide a review from "
            "submitters, even if the reviewer enables disclosure. The review "
            "may be shown to the submitter only if this is set to True."),
    )

    note = models.TextField(
        blank=True,
        verbose_name=_('note'),
        help_text=_(
            "Personal notes about this proposal. You can use this field to "
            "record anything you like during the review process. We promise "
            "to never disclose them to anyone."),
    )

    updated = models.DateTimeField(
        auto_now=True,
        verbose_name=_('updated'),
        db_index=True,
    )

    class Meta:
        verbose_name = _('review')
        verbose_name_plural = _('reviews')
        unique_together = ['reviewer', 'stage', 'proposal']
        ordering = ['-updated']

    def __str__(self):
        return ugettext('Review {proposal} by {reviewer}: {vote}').format(
            reviewer=self.reviewer,
            proposal=self.proposal,
            vote=self.get_vote_display(),
        )

    def save(self, *args, **kwargs):
        if self.stage is None:
            self.stage = settings.REVIEWS_STAGE
        return super().save(*args, **kwargs)

    def is_comment_visible_to_submitter(self):
        return self.discloses_comment and self.appropriateness

    def is_outdated(self):
        return self.updated < self.proposal.last_updated_at
Example #18
0
class AbstractProposal(ConferenceRelated, EventInfo):

    submitter = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('submitter'),
        on_delete=models.CASCADE,
    )

    outline = models.TextField(
        verbose_name=_('outline'),
        blank=True,
    )

    objective = EAWTextField(
        verbose_name=_('objective'),
        max_length=1000,
    )

    supplementary = models.TextField(
        verbose_name=_('supplementary'),
        blank=True,
        default='',
    )

    cancelled = models.BooleanField(
        verbose_name=_('cancelled'),
        default=False,
        db_index=True,
    )

    ACCEPTED_CHOICES = (
        (None,  '----------'),
        (True,  _('Accepted')),
        (False, _('Rejected')),
    )
    accepted = models.NullBooleanField(
        verbose_name=_('accepted'),
        default=None,
        choices=ACCEPTED_CHOICES,
        db_index=True,
    )

    additionalspeaker_set = GenericRelation(
        to=AdditionalSpeaker,
        content_type_field='proposal_type',
        object_id_field='proposal_id',
    )

    objects = DefaultConferenceManager.from_queryset(ProposalQuerySet)()
    all_objects = ProposalQuerySet.as_manager()

    _must_fill_fields = [
        'abstract', 'objective', 'supplementary',
        'detailed_description', 'outline',
    ]

    class Meta:
        abstract = True

    @property
    def speakers(self):
        yield PrimarySpeaker(proposal=self)

        # Optimization: Callers of this method can annotate the queryset to
        # avoid lookups when a proposal doesn't have any additional speakers.
        with contextlib.suppress(AttributeError):
            if self._additional_speaker_count < 1:
                return

        # Optimization: Callers of this method can prefetch the additional
        # speaker queryset to avoid n+1 lookups when operating on multiple
        # proposals. Example::
        #
        #   proposals = TalkProposal.objects.prefetch_related(Prefetch(
        #       'additionalspeaker_set',
        #       to_attr='_additional_speakers',
        #       queryset=(
        #           AdditionalSpeaker.objects
        #           .filter(cancelled=False)
        #           .select_related('user')
        #       ),
        #   ))
        #   for p in proposals:   # Only two queries: proposals, and speakers.
        #       for s in p.speakers:
        #           print(s.user.email)
        try:
            additionals = self._additional_speakers
        except AttributeError:
            additionals = (
                self.additionalspeaker_set
                .filter(cancelled=False)
                .select_related('user')
            )

        for speaker in additionals:
            yield speaker

    @property
    def speaker_count(self):
        # Optimization: Callers of this method can annotate the queryset to
        # avoid n+1 lookups when operating on multiple proposals.
        try:
            count = self._additional_speaker_count
        except AttributeError:
            count = self.additionalspeaker_set.filter(cancelled=False).count()
        return count + 1

    @property
    def must_fill_fields_count(self):
        return len(self._must_fill_fields)

    @property
    def finished_fields_count(self):
        count = sum(1 for f in self._must_fill_fields if getattr(self, f))
        return count

    @property
    def finish_percentage(self):
        return 100 * self.finished_fields_count // self.must_fill_fields_count

    @property
    def unfinished_fields_count(self):
        return self.must_fill_fields_count - self.finished_fields_count
Example #19
0
class AbstractProposal(ConferenceRelated, EventInfo):

    submitter = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('submitter'),
    )

    outline = models.TextField(
        verbose_name=_('outline'),
        blank=True,
    )

    objective = EAWTextField(
        verbose_name=_('objective'),
        max_length=1000,
    )

    supplementary = models.TextField(
        verbose_name=_('supplementary'),
        blank=True,
        default='',
    )

    cancelled = models.BooleanField(
        verbose_name=_('cancelled'),
        default=False,
        db_index=True,
    )

    additionalspeaker_set = GenericRelation(
        to=AdditionalSpeaker,
        content_type_field='proposal_type',
        object_id_field='proposal_id',
    )

    objects = DefaultConferenceManager.from_queryset(ProposalQuerySet)()
    all_objects = ProposalQuerySet.as_manager()

    _must_fill_fields = [
        'abstract',
        'objective',
        'supplementary',
        'detailed_description',
        'outline',
    ]

    class Meta:
        abstract = True

    @property
    def speakers(self):
        yield PrimarySpeaker(proposal=self)
        additionals = self.additionalspeaker_set.filter(cancelled=False)
        for speaker in additionals.select_related('user'):
            yield speaker

    @property
    def speaker_count(self):
        return self.additionalspeaker_set.filter(cancelled=False).count() + 1

    @property
    def must_fill_fields_count(self):
        return len(self._must_fill_fields)

    @property
    def finished_fields_count(self):
        count = sum(1 for f in self._must_fill_fields if getattr(self, f))
        return count

    @property
    def finish_percentage(self):
        return 100 * self.finished_fields_count // self.must_fill_fields_count

    @property
    def unfinished_fields_count(self):
        return self.must_fill_fields_count - self.finished_fields_count
Example #20
0
class Review(models.Model):

    reviewer = BigForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('reviewer'),
    )

    stage = models.IntegerField(
        verbose_name=_('stage'),
    )

    proposal = models.ForeignKey(
        to=TalkProposal,
        verbose_name=_('proposal'),
    )

    class Vote(object):
        PLUS_ONE = "+1"
        PLUS_ZERO = "+0"
        MINUS_ZERO = "-0"
        MINUS_ONE = "-1"

    VOTE_CHOICES = (
        (Vote.PLUS_ONE, _('+1 (strong accept)')),
        (Vote.PLUS_ZERO, _('+0 (weakly accept)')),
        (Vote.MINUS_ZERO, _('-0 (weakly reject)')),
        (Vote.MINUS_ONE, _('-1 (strong reject)')),
    )

    vote = models.CharField(
        max_length=2,
        blank=True,
        choices=VOTE_CHOICES,
        verbose_name=_("vote"),
        help_text=_(
            "Your vote to accept or reject this talk. "
            "More information about the scoring and acceptance criteria "
            "can be found at the google doc "
            "<a href=\"https://goo.gl/EPlUZx\" "
            "target=\"_blank\">Review Guideline</a>."
        ),
    )

    comment = models.TextField(
        verbose_name=_('comment'),
        help_text=_(
            "Comments to this proposal. This may be available for other "
            "reviewers in later review stages, and you can choose whether "
            "or not to disclose it to the proposal's submitter."
        ),
    )

    DISCLOSE_CHOICES = (
        (True, _('Yes')),
        (False, _('No')),
    )
    discloses_comment = models.BooleanField(
        default=True,
        choices=DISCLOSE_CHOICES,
        verbose_name=_('discloses comment to proposal submitter'),
        help_text=_(
            "Whether the proposal submitter can read you comments. We will "
            "include your comments in the proposal acceptance/rejection "
            "notice sent to the submitter if you allow us to."
        )
    )

    note = models.TextField(
        blank=True,
        verbose_name=_('note'),
        help_text=_(
            "Personal notes about this proposal. You can use this field to "
            "record anything you like during the review process. We promise "
            "to never disclose them to anyone."
        ),
    )

    updated = models.DateTimeField(
        auto_now=True,
        verbose_name=_('updated'),
        db_index=True,
    )

    class Meta:
        verbose_name = _('review')
        verbose_name_plural = _('reviews')
        unique_together = ['reviewer', 'stage', 'proposal']
        ordering = ['-updated']

    def __str__(self):
        return _('Review {proposal} by {reviewer}: {vote}').format(
            reviewer=self.reviewer,
            proposal=self.proposal,
            vote=self.get_vote_display(),
        )

    def save(self, *args, **kwargs):
        if self.stage is None:
            self.stage = ReviewsConfig.stage
        return super().save(*args, **kwargs)