def setUpTestData(cls):
        """Set up valid and invalid schemas for the test case once when the test case class is being prepared to run.

        Additionally, initialize a JSONSchemaValidator object for use in the individual unit tests.
        """
        cls.valid_schema = {
            'schema': 'http://json-schema.org/draft-07/schema#',
            'type': 'string',
            'minLength': 1
        }
        cls.invalid_schema = {
            'schema': 'http://json-schema.org/draft-07/schema#',
            'type': 'string',
            'minLength': -1
        }
        cls.validator = JSONSchemaValidator(cls.valid_schema)
class Announcement(models.Model):
    """A Django database model which represents a club announcement or update.

    In the PostgreSQL database, an announcement has a title, body of content, and the date and time that it was created.
    The schema used to validate an announcement's body content, as represented in JSON, is defined above by the global
    ANNOUNCEMENT_BODY_FIELD_JSON_SCHEMA.

    Attributes:  # noqa
        title: A CharField containing the announcement's title.

        body: A JSONField containing a JSON representation of the announcement's body or content.

        created: A DateTimeField that contains the date and time that the announcement was created.
    """

    title = models.CharField(
        max_length=250,
        null=False,
        blank=False,
        default='',
        editable=True,
        unique=False,
        verbose_name='Announcement Title'
    )
    body = models.JSONField(
        default=list,
        validators=[JSONSchemaValidator(limit_value=ANNOUNCEMENT_BODY_FIELD_JSON_SCHEMA)],
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Announcement Body'
    )
    created = models.DateTimeField(
        auto_now_add=True,
        null=False,
        blank=True,
        editable=False,
        unique=False,
        verbose_name='Announcement Creation Time/Date'
    )

    def __str__(self):
        """Defines the string representation of the Announcement class.

        The string representation of an Announcement class instance only contains the title of the announcement.

        Returns:
            A string containing the announcement's title.
        """
        return self.title

    class Meta:
        """This class contains meta-options for the Announcement model.

        Attributes:  # noqa
            ordering: A list of fields to order Announcement objects by. As-is, they are ordered by the date/time they
            were created and in descending order (i.e., the newest Announcement appears first and the oldest appears
            last).
        """
        ordering = ['-created']
Exemple #3
0
class Webhook(models.Model):
    """Model of webhooks.

    If webhook has not null project field -- it's project webhook
    """

    organization = models.ForeignKey('organizations.Organization',
                                     on_delete=models.CASCADE,
                                     related_name='webhooks')

    project = models.ForeignKey('projects.Project',
                                null=True,
                                on_delete=models.CASCADE,
                                related_name='webhooks',
                                default=None)

    url = models.URLField(_('URL of webhook'),
                          max_length=2048,
                          help_text=_('URL of webhook'))

    send_payload = models.BooleanField(
        _("does webhook send the payload"),
        default=True,
        help_text=('If value is False send only action'),
        db_index=True)

    send_for_all_actions = models.BooleanField(
        _("Use webhook for all actions"),
        default=True,
        help_text=
        'If value is False - used only for actions from WebhookAction',
        db_index=True)

    headers = models.JSONField(
        _("request extra headers of webhook"),
        validators=[JSONSchemaValidator(HEADERS_SCHEMA)],
        default=dict,
        help_text='Key Value Json of headers',
    )

    is_active = models.BooleanField(
        _("is webhook active"),
        default=True,
        help_text=('If value is False the webhook is disabled'),
        db_index=True)

    created_at = models.DateTimeField(_('created at'),
                                      auto_now_add=True,
                                      help_text=_('Creation time'),
                                      db_index=True)
    updated_at = models.DateTimeField(_('updated at'),
                                      auto_now=True,
                                      help_text=_('Last update time'),
                                      db_index=True)

    def get_actions(self):
        return WebhookAction.objects.filter(webhook=self).values_list(
            'action', flat=True)

    def validate_actions(self, actions):
        actions_meta = [WebhookAction.ACTIONS[action] for action in actions]
        if self.project and any(
            (meta.get('organization-only') for meta in actions_meta)):
            raise ValidationError(
                "Project webhook can't contain organization-only action.")
        return actions

    def set_actions(self, actions):
        if not actions:
            actions = set()
        actions = set(actions)
        old_actions = set(self.get_actions())

        for new_action in list(actions - old_actions):
            WebhookAction.objects.create(webhook=self, action=new_action)

        WebhookAction.objects.filter(webhook=self,
                                     action__in=(old_actions -
                                                 actions)).delete()

    def has_permission(self, user):
        return self.organization.has_user(user)

    class Meta:
        db_table = 'webhook'
Exemple #4
0
class MentorContactForm(ContactFormBase):
    """A concrete contact form model, intended for prospective mentors, which includes additional information
    such as the number of students to mentor, information about the mentor's background and field of expertise, and
    the availability of the prospective mentor to meet with club members.
    """
    students = models.PositiveSmallIntegerField(
        null=False,
        blank=False,
        default=1,
        editable=True,
        unique=False,
        verbose_name='Number of Students to Mentor',
        validators=[
            MinValueValidator(1, 'Number of students too small.'),
            MaxValueValidator(6, 'Number of students too large.'),
        ],
    )
    field_type = models.CharField(
        max_length=60,
        null=False,
        blank=False,
        default='',
        editable=True,
        unique=False,
        verbose_name='Type of Field',
    )
    field_name = models.CharField(
        max_length=200,
        null=False,
        blank=False,
        default='',
        editable=True,
        unique=False,
        verbose_name='Field/Sector Name',
    )
    field_description = models.TextField(
        null=True,
        blank=True,
        default='',
        editable=True,
        unique=False,
        verbose_name='Field/Sector Name',
    )
    availability_start = models.DateField(
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Start of Mentorship Availability',
    )
    availability_end = models.DateField(
        null=True,
        blank=True,
        editable=True,
        unique=False,
        verbose_name='End of Mentorship Availability',
    )
    meeting_information = models.JSONField(
        default=list,
        validators=[
            JSONSchemaValidator(
                limit_value=MENTOR_MEETING_INFORMATION_FIELD_SCHEMA)
        ],
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Weekly Meeting Availability',
    )
    weekly_minutes = models.PositiveSmallIntegerField(
        null=False,
        blank=False,
        default=1,
        editable=True,
        unique=False,
        verbose_name='Minutes of Availability Per Week',
        validators=[
            MinValueValidator(1, 'Number of minutes too small.'),
        ],
    )

    class Meta:
        """Defines the long-form name to label MentorContactForm objects.
        """
        verbose_name = 'Mentor Contact Form'
Exemple #5
0
class GuestSpeakerContactForm(ContactFormBase):
    """A concrete contact form model, intended for prospective guest speakers, which includes additional information
    such as the presentation topic and length, speaker availability, accommodations needed, and consent fields for
    audio/video recordings of the meeting.
    """
    topic = models.CharField(
        max_length=250,
        null=False,
        blank=False,
        default='',
        editable=True,
        unique=False,
        verbose_name='Presentation Topic',
    )
    availability = models.JSONField(
        default=list,
        validators=[
            JSONSchemaValidator(
                limit_value=GUEST_SPEAKER_AVAILABILITY_FIELD_SCHEMA)
        ],
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Availability',
    )
    length = models.PositiveSmallIntegerField(
        null=False,
        blank=False,
        default=1,
        editable=True,
        unique=False,
        verbose_name='Presentation Length',
    )
    visual_aids = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        default='',
        editable=True,
        unique=False,
        verbose_name='Prepared Visual Aids',
    )
    addl_visual_aids = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        default='',
        editable=True,
        unique=False,
        verbose_name='Additional Visual Aids Required',
    )
    addl_tech = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        default='',
        editable=True,
        unique=False,
        verbose_name='Additional Tech Setup Required',
    )
    consent_audio_rec = models.BooleanField(
        default=False,
        editable=True,
        verbose_name='Consent to Record Audio',
    )
    consent_video_rec = models.BooleanField(
        default=False,
        editable=True,
        verbose_name='Consent to Record Video',
    )
    consent_streaming = models.BooleanField(
        default=False,
        editable=True,
        verbose_name='Consent to Upload Recordings to Streaming Platform(s)',
    )
    consent_materials = models.BooleanField(
        default=False,
        editable=True,
        verbose_name=
        'Consent to Share Presentation Materials With Club Members',
    )

    class Meta:
        """Defines the long-form name to label GuestSpeakerContactForm objects.
        """
        verbose_name = 'Guest Speaker Contact Form'
Exemple #6
0
class Event(models.Model):
    """A Django database model which represents a club event.

    Attributes:  # noqa
        type: A CharField containing the type of event. The available types are defined in the EventType class.

        topics: A JSONField containing a JSON representation of a list of topics for an event, if any.

        start: A DateTimeField containing the date and time of the start of the event.

        end: A DateTimeField containing the date and time of the end of the event.

        calendar_link: An optional URLField containing a calendar invite link to add the event to your calendar.

        meeting_link: An optional URLField containing a virtual meeting link to join an online, virtual meeting.

        meeting_address: A many-to-one relation to the MeetingAddress model, which relates an Event to its location.

        contacts: A generic relation to the ContactInfo model in the core directory.

        objects: A custom Manager which includes all base Manager functionality with the addition of the `upcoming`
        method which can be used in place of `Event.objects.all()` to retrieve a QuerySet containing only upcoming
        event objects.
    """
    class EventType(models.TextChoices):
        """Defines the supported types of events for the Event model's ``type`` field.

        Attributes:  # noqa
            GUEST_SPEAKER: A 2 character identifier and lazily-evaluated label representing the choice of a presentation
            by a guest speaker.

            WORKSHOP: A 2 character identifier and lazily-evaluated label representing the choice of a workshop.

            INTERVIEW_PREP: A 2 character identifier and lazily-evaluated label representing the choice of an interview
            preparation session.

            DISCUSSION: A 2 character identifier and lazily-evaluated label representing the choice of a free form
            discussion.

            PROJECT_MEETING: A 2 character identifier and lazily-evaluated label representing the choice of a group
            project meeting.

            HACKATHON_MEETING: A 2 character identifier and lazily-evaluated label representing the choice of a
            hackathon meeting.

            OTHER: A 2 character identifier and lazily-evaluated label representing the catch-all choice for all other
            types of events.
        """
        GUEST_SPEAKER = 'GS', _('Guest Speaker Presentation')
        WORKSHOP = 'WS', _('Workshop')
        INTERVIEW_PREP = 'IP', _('Interview Prep Session')
        DISCUSSION = 'DI', _('Free Form Discussion')
        PROJECT_MEETING = 'PM', _('Group Project Meeting')
        HACKATHON_MEETING = 'HM', _('Hackathon Meeting')
        OTHER = 'OT', _('Other Event')

    type = models.CharField(
        max_length=2,
        choices=EventType.choices,
        default=EventType.OTHER,
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Type of Event',
    )
    topics = models.JSONField(
        default=list,
        validators=[
            JSONSchemaValidator(limit_value=EVENT_TOPICS_FIELD_JSON_SCHEMA)
        ],
        null=False,
        blank=True,
        editable=True,
        unique=False,
        verbose_name='Event Topics',
    )
    start = models.DateTimeField(
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Start Date and Time',
    )
    end = models.DateTimeField(
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='End Date and Time',
    )
    calendar_link = models.URLField(
        null=True,
        blank=True,
        editable=True,
        unique=False,
        verbose_name='Calendar Invite Link',
        max_length=400,
    )
    meeting_link = models.URLField(
        null=True,
        blank=True,
        editable=True,
        unique=False,
        verbose_name='Virtual Meeting Link',
    )
    meeting_address = models.ForeignKey(
        MeetingAddress,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        default=None,
        editable=True,
        verbose_name='Meeting Location',
    )

    contacts = GenericRelation('core.ContactInfo')
    objects = EventQuerySet.as_manager()

    __original_start = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__original_start = self.start

    def clean(self):
        """Provides additional validation for Event model fields.

        Ensures that an Event object's start datetime must fall before its end datetime.
        """
        if self.start >= self.end:
            raise ValidationError(
                'Event start date and time must fall before its end date and time.'
            )

    def save(self, *args, **kwargs):
        """Overrides the default model save method to send a Celery task when a new Event object is saved, or an
        existing event is rescheduled.
        """
        if self.pk and self.__original_start != self.start:
            super(Event, self).save(*args, **kwargs)
            event_rescheduled.delay(
                Event.EventType(self.type).label, self.start)
        else:
            super(Event, self).save(*args, **kwargs)
            event_created.delay(Event.EventType(self.type).label, self.start)

    def __str__(self):
        """Defines the string representation of the Event class.

        The string representation of an Event class instance contains the event type and the date(s) for which the event
        is scheduled. If the event occurs in a single day (i.e., its start and end dates are the same), only the start
        date is included. If the event occurs over the span of more than one day (i.e., its start and end dates are
        different), both the start and dates are included.

        Returns:
            A string containing the event's type and the date(s) when it is scheduled to occur.
        """
        if self.start.date() == self.end.date():
            return f'{self.EventType(self.type).label} on {self.start.strftime("%m-%d-%Y")}'
        else:
            return f'{self.EventType(self.type).label} from {self.start.strftime("%m-%d-%Y")} ' \
                   + f'to {self.end.strftime("%m-%d-%Y")}'

    class Meta:
        """This class contains meta-options for the Event model.

        Attributes:  #noqa
            ordering: A list of fields to order Event objects by. As-is, they are ordered by the date/time they start
            and in ascending order (i.e., the Event that is starting the soonest appears first and the one starting
            latest appears last).
        """
        ordering = ['start']
class Project(models.Model):
    """A Django database model which represents a group project.

    In the PostgreSQL database, a project has a name, a list of authors, a description, an image, a URL for an external
    website where the project (or its code) is hosted, a status (complete, in progress, etc.), and the date and time the
    project was last edited.

    Attributes:  # noqa
        name: A CharField containing the name of the project.

        authors: A JSONField containing a JSON representation of a list of project authors as strings.

        description: A TextField containing a long-form description of the project.

        image: An ImageField representing the project's image in the database.

        url: An optional URLField containing a link to an external website where the project is hosted (e.g., github).

        status: A CharField containing the status of the project. The available statuses are defined in the
        ProjectStatus class.

        modified: A DateTimeField containing the date and time when the project was last edited.
    """
    class ProjectStatus(models.TextChoices):
        """Defines the supported statuses of projects for the Project model's ``status`` field.

        Attributes:  # noqa
            COMPLETE: A 2 character identifier and lazily-evaluated label representing the choice of the project being
            finished/finalized.

            IN_PROGRESS: A 2 character identifier and lazily-evaluated label representing the choice of the project
            being in progress and still being actively worked on.

            PLANNED: A 2 character identifier and lazily-evaluated label representing the choice of the project being
            planned for the future.

            OTHER: A 2 character identifier and lazily-evaluated label representing the catch-all choice for all other
            statuses of projects.
        """
        COMPLETE = 'CO', _('Complete')
        IN_PROGRESS = 'IP', _('In Progress')
        PLANNED = 'PL', _('Planned')
        OTHER = 'OT', _('Other')

    name = models.CharField(max_length=250,
                            null=False,
                            blank=False,
                            default='',
                            editable=True,
                            unique=True,
                            verbose_name='Project Title')
    authors = models.JSONField(
        default=list,
        validators=[
            JSONSchemaValidator(limit_value=PROJECT_AUTHORS_FIELD_JSON_SCHEMA)
        ],
        null=False,
        blank=False,
        editable=True,
        unique=False,
        verbose_name='Project Authors')
    description = models.TextField(null=False,
                                   blank=False,
                                   default='',
                                   editable=True,
                                   unique=False,
                                   verbose_name='Project Description')
    image = models.ImageField(null=True,
                              blank=True,
                              editable=True,
                              unique=True,
                              verbose_name='Project Image',
                              upload_to=image_path)
    url = models.URLField(null=True,
                          blank=True,
                          editable=True,
                          unique=True,
                          verbose_name='External Project URL',
                          max_length=200)
    status = models.CharField(max_length=2,
                              choices=ProjectStatus.choices,
                              default=ProjectStatus.PLANNED,
                              null=False,
                              blank=False,
                              editable=True,
                              unique=False,
                              verbose_name='Project Status or Phase')
    modified = models.DateTimeField(auto_now=True,
                                    null=False,
                                    blank=True,
                                    editable=False,
                                    unique=False,
                                    verbose_name='Date and Time of Last Edit')

    def save(self, *args, **kwargs):
        """Overrides the default model save method to send a Celery task when a new Project object is created/saved.
        """
        if not self.pk:
            project_created.delay(self.name, self.authors, self.description,
                                  self.url)

        super(Project, self).save(*args, **kwargs)

    def __str__(self):
        """Defines the string representation of the Project model.

        The string representation of a Project object only contains the project's name.

        Returns:
            A string containing the project's name.
        """
        return self.name