__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" from django.db import models from django.utils import timezone class FellowshipNomination(models.Model): college = models.ForeignKey( 'colleges.College', on_delete=models.PROTECT, related_name='nominations' ) profile = models.ForeignKey( 'profiles.Profile', on_delete=models.CASCADE, related_name='fellowship_nominations' ) nominated_by = models.ForeignKey( 'scipost.Contributor', on_delete=models.CASCADE, related_name='fellowship_nominations_initiated' ) nominated_on = models.DateTimeField(default=timezone.now) nominator_comments = models.TextField( help_text=('You can use plain text, Markdown or reStructuredText; see our ' '<a href="/markup/help/" target="_blank">markup help</a> pages.'), blank=True ) fellowship = models.OneToOneField( 'colleges.Fellowship', on_delete=models.CASCADE, related_name='nomination', blank=True, null=True ) class Meta: ordering = [ 'profile', 'college', ] verbose_name_plural = 'Fellowship Nominations' def __str__(self): return (f'{self.profile} to {self.college} ' f'on {self.nominated_on.strftime("%Y-%m-%d")}') class FellowshipNominationEvent(models.Model): nomination = models.ForeignKey( 'colleges.FellowshipNomination', on_delete=models.CASCADE, related_name='events' ) description = models.TextField() on = models.DateTimeField(default=timezone.now) class Meta: ordering = [ '-on' ] verbose_name_plural = 'Fellowhip Nomination Events' def __str__(self): return f'Event for {nomination}' class FellowshipNominationVotingRound(models.Model): nomination = models.ForeignKey( 'colleges.FellowshipNomination', on_delete=models.CASCADE, related_name='voting_rounds' ) eligible_to_vote = models.ManyToManyField( 'colleges.Fellowship', related_name='voting_rounds_eligible_to_vote_in', blank=True ) voting_opens = models.DateTimeField() voting_deadline = models.DateTimeField() class Meta: ordering = [ 'nomination__profile__last_name' ] verbose_name_plural = 'Fellowship Nomination Voting Rounds' def __str__(self): return (f'Voting round ({voting_opens.strftime("%Y-%m-%d")} -' f' {voting_deadline.strftime("%Y-%m-%d")}) for {nomination}') class FellowshipNominationVote(models.Model): VOTE_AGREE = 'agree' VOTE_ABSTAIN = 'abstain' VOTE_DISAGREE = 'disagree' VOTE_CHOICES = ( (VOTE_AGREE, 'Agree'), (VOTE_ABSTAIN, 'Abstain'), (VOTE_DISAGREE, 'Disagree') ) voting_round = models.ForeignKey( 'colleges.FellowshipNominationVotingRound', on_delete=models.CASCADE, related_name='votes' ) fellow = models.ForeignKey( 'colleges.Fellowship', on_delete=models.CASCADE, related_name='fellowship_nomination_votes' ) vote = models.CharField( max_length=16, choices=VOTE_CHOICES ) on = models.DateTimeField(blank=True, null=True) comments = models.TextField( help_text=('You can use plain text, Markdown or reStructuredText; see our ' '<a href="/markup/help/" target="_blank">markup help</a> pages.'), blank=True ) class Meta: ordering = ['voting_round',] verbose_name_plural = 'Fellowship Nomination Votes' class FellowshipNominationDecision(models.Model): nomination = models.OneToOneField( 'colleges.FellowshipNomination', on_delete=models.CASCADE, related_name='decision' ) OUTCOME_ELECTED = 'elected' OUTCOME_NOT_ELECTED = 'notelected' OUTCOME_CHOICES = ( (OUTCOME_ELECTED, 'Elected'), (OUTCOME_NOT_ELECTED, 'Not elected') ) outcome = models.CharField( max_length=16, choices=OUTCOME_CHOICES ) fixed_on = models.DateTimeField(default=timezone.now) comments = models.TextField( help_text=('You can use plain text, Markdown or reStructuredText; see our ' '<a href="/markup/help/" target="_blank">markup help</a> pages.'), blank=True ) class Meta: ordering = ['nomination',] verbose_name_plural = 'Fellowship Nomination Decisions' def __str__(self): return f'Decision for {nomination}: {self.get_outcome_display()}' @property def elected(self): return self.outcome == self.OUTCOME_ELECTED class FellowshipInvitation(models.Model): nomination = models.OneToOneField( 'colleges.FellowshipNomination', on_delete=models.CASCADE, related_name='invitation' ) invited_on = models.DateTimeField(blank=True, null=True) RESPONSE_NOT_YET_INVITED = 'notyetinvited' RESPONSE_INVITED = 'invited' RESPONSE_REINVITED = 'reinvited' RESPONSE_MULTIPLY_REINVITED = 'multireinvited' RESPONSE_UNRESPONSIVE = 'unresponsive' RESPONSE_ACCEPTED = 'accepted' RESPONSE_POSTPONED = 'postponed' RESPONSE_DECLINED = 'declined' RESPONSE_CHOICES = ( (RESPONSE_NOT_YET_INVITED, 'Not yet invited'), (RESPONSE_INVITED, 'Invited'), (RESPONSE_REINVITED, 'Reinvited'), (RESPONSE_MULTIPLY_REINVITED, 'Multiply reinvited'), (RESPONSE_UNRESPONSIVE, 'Unresponsive'), (RESPONSE_ACCEPTED, 'Accepted, for immediate start'), (RESPONSE_POSTPONED, 'Accepted, but start date postponed'), (RESPONSE_DECLINED, 'Declined') ) response = models.CharField( max_length=16, choices=RESPONSE_CHOICES, blank=True ) postpone_start_to = models.DateField(blank=True) comments = models.TextField( help_text=('You can use plain text, Markdown or reStructuredText; see our ' '<a href="/markup/help/" target="_blank">markup help</a> pages.'), blank=True ) class Meta: ordering = ['nomination',] verbose_name_plural = 'Fellowship Invitations' def __str__(self): return f'Invitation for {nomination}' @property def declined(self): return self.response == self.RESPONSE_DECLINED