Aller au contenu

# Models

## `Club`

Bases: `Model`

The Club class, made as a tree to allow nice tidy organization.

### `president()`

Fetch the membership of the current president of this club.

Source code in `club/models.py`

    138
    139
    140
    141
    @cached_property
    def president(self) -> Membership | None:
        """Fetch the membership of the current president of this club."""
        return self.members.filter(end_date=None).order_by("role__order").first()

### `check_loop()`

Raise a validation error when a loop is found within the parent list.

Source code in `club/models.py`

    143
    144
    145
    146
    147
    148
    149
    150
    151
    def check_loop(self):
        """Raise a validation error when a loop is found within the parent list."""
        objs = []
        cur = self
        while cur.parent is not None:
            if cur in objs:
                raise ValidationError(_("You can not make loops in clubs"))
            objs.append(cur)
            cur = cur.parent

### `create_default_roles()`

Create some roles that should exist by default for this club.

The created roles are : president, treasurer, active member and curious.

Source code in `club/models.py`

    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    def create_default_roles(self):
        """Create some roles that should exist by default for this club.

        The created roles are : president, treasurer, active member and curious.

        Warnings:
            When calling this method, no club must exist yet for this club.
        """
        if self.roles.exists():
            raise ProgrammingError(
                "Default roles can be created only for clubs "
                "that don't have associated roles yet"
            )
        # The names are written in French, because there is no gettext involved
        # for strings stored in database, and the majority of users are french.
        roles = [
            ClubRole(name="Président⸱e", is_board=True, is_presidency=True),
            ClubRole(name="Trésorier⸱e", is_board=True, is_presidency=False),
            ClubRole(name="Membre actif⸱ve", is_board=False, is_presidency=False),
            ClubRole(
                name="Curieux⸱euse",
                description=(
                    "Les gens qui suivent l'activité "
                    "du club sans forcément y participer"
                ),
                is_board=False,
                is_presidency=False,
            ),
        ]
        for i, role in enumerate(roles):
            role.club = self
            role.order = i
        ClubRole.objects.bulk_create(roles)

### `is_owned_by(user)`

Method to see if that object can be super edited by the given user.

Source code in `club/models.py`

    228
    229
    230
    231
    232
    def is_owned_by(self, user: User) -> bool:
        """Method to see if that object can be super edited by the given user."""
        if user.is_anonymous:
            return False
        return user.is_root or user.is_board_member

### `can_be_edited_by(user)`

Method to see if that object can be edited by the given user.

Source code in `club/models.py`

    237
    238
    239
    def can_be_edited_by(self, user: User) -> bool:
        """Method to see if that object can be edited by the given user."""
        return self.has_rights_in_club(user)

### `get_membership_for(user)`

Return the current membership of the given user.

Source code in `club/models.py`

    247
    248
    249
    250
    251
    def get_membership_for(self, user: User) -> Membership | None:
        """Return the current membership of the given user."""
        if user.is_anonymous:
            return None
        return next((m for m in self.current_members if m.user_id == user.id), None)

## `Sith`

Bases: `Model`

A one instance class storing all the modifiable infos.

## `NewsQuerySet`

Bases: `QuerySet`

### `waiting_moderation()`

Filter all non-finished non-published news

Source code in `com/models.py`

    61
    62
    63
    64
    65
    66
    67
    def waiting_moderation(self) -> Self:
        """Filter all non-finished non-published news"""
        # Because of the way News and NewsDates are created,
        # there may be some cases where this method is called before
        # the NewsDates linked to a Date are actually persisted in db.
        # Thus, it's important to filter by "not past date" rather than by "future date"
        return self.filter(~Q(dates__start_date__lt=timezone.now()), is_published=False)

### `viewable_by(user)`

Filter news that the given user can view.

If the user has the `com.view_unmoderated_news` permission, all news are viewable. Else the viewable news are those that are either moderated or authored by the user.

Source code in `com/models.py`

    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    def viewable_by(self, user: User) -> Self:
        """Filter news that the given user can view.

        If the user has the `com.view_unmoderated_news` permission,
        all news are viewable.
        Else the viewable news are those that are either moderated
        or authored by the user.
        """
        if user.has_perm("com.view_unmoderated_news"):
            return self
        q_filter = Q(is_published=True)
        if user.is_authenticated:
            q_filter |= Q(author_id=user.id)
        return self.filter(q_filter)

## `News`

Bases: `Model`

News about club events.

## `NewsDateQuerySet`

Bases: `QuerySet`

### `viewable_by(user)`

Filter the event dates that the given user can view.

- If the can view non moderated news, he can view all news dates
- else, he can view the dates of news that are either authored by him or moderated.

Source code in `com/models.py`

    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    def viewable_by(self, user: User) -> Self:
        """Filter the event dates that the given user can view.

        - If the can view non moderated news, he can view all news dates
        - else, he can view the dates of news that are either
          authored by him or moderated.
        """
        if user.has_perm("com.view_unmoderated_news"):
            return self
        q_filter = Q(news__is_published=True)
        if user.is_authenticated:
            q_filter |= Q(news__author_id=user.id)
        return self.filter(q_filter)

## `NewsDate`

Bases: `Model`

A date associated with news.

A News can have multiple dates, for example if it is a recurring event.

## `Weekmail`

Bases: `Model`

The weekmail class.

:ivar title: Title of the weekmail :ivar intro: Introduction of the weekmail :ivar joke: Joke of the week :ivar protip: Tip of the week :ivar conclusion: Conclusion of the weekmail :ivar sent: Track if the weekmail has been sent

### `send()`

Send the weekmail to all users with the receive weekmail option opt-in.

Also send the weekmail to the mailing list in settings.SITH_COM_EMAIL.

Source code in `com/models.py`

    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    def send(self):
        """Send the weekmail to all users with the receive weekmail option opt-in.

        Also send the weekmail to the mailing list in settings.SITH_COM_EMAIL.
        """
        dest = [
            i[0]
            for i in Preferences.objects.filter(receive_weekmail=True).values_list(
                "user__email"
            )
        ]
        with transaction.atomic():
            email = EmailMultiAlternatives(
                subject=self.title,
                body=self.render_text(),
                from_email=settings.SITH_COM_EMAIL,
                to=Sith.objects.first().weekmail_destinations.split(" "),
                bcc=dest,
            )
            email.attach_alternative(self.render_html(), "text/html")
            email.send()
            self.sent = True
            self.save()
            Weekmail().save()

### `render_text()`

Renders a pure text version of the mail for readers without HTML support.

Source code in `com/models.py`

    286
    287
    288
    289
    290
    def render_text(self):
        """Renders a pure text version of the mail for readers without HTML support."""
        return render(
            None, "com/weekmail_renderer_text.jinja", context={"weekmail": self}
        ).content.decode("utf-8")

### `render_html()`

Renders an HTML version of the mail with images and fancy CSS.

Source code in `com/models.py`

    292
    293
    294
    295
    296
    def render_html(self):
        """Renders an HTML version of the mail with images and fancy CSS."""
        return render(
            None, "com/weekmail_renderer_html.jinja", context={"weekmail": self}
        ).content.decode("utf-8")

### `get_banner()`

Return an absolute link to the banner.

Source code in `com/models.py`

    298
    299
    300
    301
    302
    def get_banner(self):
        """Return an absolute link to the banner."""
        return (
            "http://" + settings.SITH_URL + static("com/img/weekmail_bannerV2P22.png")
        )

### `get_footer()`

Return an absolute link to the footer.

Source code in `com/models.py`

    304
    305
    306
    def get_footer(self):
        """Return an absolute link to the footer."""
        return "http://" + settings.SITH_URL + static("com/img/weekmail_footerP22.png")

## `WeekmailArticle`

Bases: `Model`

## `Screen`

Bases: `Model`

## `Poster`

Bases: `Model`