From 2ac578c3aded6801d143ac7015236878d7c38b02 Mon Sep 17 00:00:00 2001 From: thomas girod Date: Thu, 27 Jun 2024 15:48:07 +0200 Subject: [PATCH] ruff rule DJ Co-authored-by: Bartuccio Antoine --- accounting/models.py | 116 +++++---- club/migrations/0011_auto_20180426_2013.py | 2 +- club/models.py | 140 ++++++----- com/models.py | 64 ++--- core/migrations/0029_auto_20180426_2013.py | 2 +- core/models.py | 177 +++++++------- core/views/site.py | 2 +- counter/forms.py | 10 +- counter/models.py | 250 ++++++++++---------- eboutic/models.py | 12 +- election/models.py | 16 +- forum/migrations/0006_auto_20180426_2013.py | 4 +- forum/models.py | 85 +++---- galaxy/models.py | 3 + launderette/models.py | 54 ++--- pedagogy/models.py | 34 +-- pyproject.toml | 14 +- stock/models.py | 8 +- subscription/models.py | 45 ++-- trombi/models.py | 15 +- 20 files changed, 537 insertions(+), 516 deletions(-) diff --git a/accounting/models.py b/accounting/models.py index 54cb890e..254a41ba 100644 --- a/accounting/models.py +++ b/accounting/models.py @@ -61,6 +61,15 @@ class Company(models.Model): class Meta: verbose_name = _("company") + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("accounting:co_edit", kwargs={"co_id": self.id}) + + def get_display_name(self): + return self.name + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -87,15 +96,6 @@ class Company(models.Model): return True return False - def get_absolute_url(self): - return reverse("accounting:co_edit", kwargs={"co_id": self.id}) - - def get_display_name(self): - return self.name - - def __str__(self): - return self.name - class BankAccount(models.Model): name = models.CharField(_("name"), max_length=30) @@ -112,6 +112,12 @@ class BankAccount(models.Model): verbose_name = _("Bank account") ordering = ["club", "name"] + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("accounting:bank_details", kwargs={"b_account_id": self.id}) + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -125,12 +131,6 @@ class BankAccount(models.Model): return True return False - def get_absolute_url(self): - return reverse("accounting:bank_details", kwargs={"b_account_id": self.id}) - - def __str__(self): - return self.name - class ClubAccount(models.Model): name = models.CharField(_("name"), max_length=30) @@ -151,6 +151,12 @@ class ClubAccount(models.Model): verbose_name = _("Club account") ordering = ["bank_account", "name"] + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("accounting:club_details", kwargs={"c_account_id": self.id}) + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -188,12 +194,6 @@ class ClubAccount(models.Model): def get_open_journal(self): return self.journals.filter(closed=False).first() - def get_absolute_url(self): - return reverse("accounting:club_details", kwargs={"c_account_id": self.id}) - - def __str__(self): - return self.name - def get_display_name(self): return _("%(club_account)s on %(bank_account)s") % { "club_account": self.name, @@ -224,6 +224,12 @@ class GeneralJournal(models.Model): verbose_name = _("General journal") ordering = ["-start_date"] + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("accounting:journal_details", kwargs={"j_id": self.id}) + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -249,12 +255,6 @@ class GeneralJournal(models.Model): def can_be_viewed_by(self, user): return self.club_account.can_be_viewed_by(user) - def get_absolute_url(self): - return reverse("accounting:journal_details", kwargs={"j_id": self.id}) - - def __str__(self): - return self.name - def update_amounts(self): self.amount = 0 self.effective_amount = 0 @@ -356,6 +356,18 @@ class Operation(models.Model): unique_together = ("number", "journal") ordering = ["-number"] + def __str__(self): + return f"{self.amount} € | {self.date} | {self.accounting_type} | {self.done}" + + def save(self, *args, **kwargs): + if self.number is None: + self.number = self.journal.operations.count() + 1 + super().save(*args, **kwargs) + self.journal.update_amounts() + + def get_absolute_url(self): + return reverse("accounting:journal_details", kwargs={"j_id": self.journal.id}) + def __getattribute__(self, attr): if attr == "target": return self.get_target() @@ -409,12 +421,6 @@ class Operation(models.Model): tar = Company.objects.filter(id=self.target_id).first() return tar - def save(self): - if self.number is None: - self.number = self.journal.operations.count() + 1 - super().save() - self.journal.update_amounts() - def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -443,17 +449,6 @@ class Operation(models.Model): return True return False - def get_absolute_url(self): - return reverse("accounting:journal_details", kwargs={"j_id": self.journal.id}) - - def __str__(self): - return "%d € | %s | %s | %s" % ( - self.amount, - self.date, - self.accounting_type, - self.done, - ) - class AccountingType(models.Model): """ @@ -486,6 +481,12 @@ class AccountingType(models.Model): verbose_name = _("accounting type") ordering = ["movement_type", "code"] + def __str__(self): + return self.code + " - " + self.get_movement_type_display() + " - " + self.label + + def get_absolute_url(self): + return reverse("accounting:type_list") + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -496,12 +497,6 @@ class AccountingType(models.Model): return True return False - def get_absolute_url(self): - return reverse("accounting:type_list") - - def __str__(self): - return self.code + " - " + self.get_movement_type_display() + " - " + self.label - class SimplifiedAccountingType(models.Model): """ @@ -520,6 +515,15 @@ class SimplifiedAccountingType(models.Model): verbose_name = _("simplified type") ordering = ["accounting_type__movement_type", "accounting_type__code"] + def __str__(self): + return ( + f"{self.get_movement_type_display()} " + f"- {self.accounting_type.code} - {self.label}" + ) + + def get_absolute_url(self): + return reverse("accounting:simple_type_list") + @property def movement_type(self): return self.accounting_type.movement_type @@ -527,18 +531,6 @@ class SimplifiedAccountingType(models.Model): def get_movement_type_display(self): return self.accounting_type.get_movement_type_display() - def get_absolute_url(self): - return reverse("accounting:simple_type_list") - - def __str__(self): - return ( - self.get_movement_type_display() - + " - " - + self.accounting_type.code - + " - " - + self.label - ) - class Label(models.Model): """Label allow a club to sort its operations""" diff --git a/club/migrations/0011_auto_20180426_2013.py b/club/migrations/0011_auto_20180426_2013.py index 3f2a123e..c10fe7b7 100644 --- a/club/migrations/0011_auto_20180426_2013.py +++ b/club/migrations/0011_auto_20180426_2013.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): name="owner_group", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - default=club.models.Club.get_default_owner_group, + default=club.models.get_default_owner_group, related_name="owned_club", to="core.Group", ), diff --git a/club/models.py b/club/models.py index 2e02da99..e315f1d2 100644 --- a/club/models.py +++ b/club/models.py @@ -40,6 +40,11 @@ from core.models import Group, MetaGroup, Notification, Page, RealGroup, SithFil # Create your models here. +# This function prevents generating migration upon settings change +def get_default_owner_group(): + return settings.SITH_GROUP_ROOT_ID + + class Club(models.Model): """ The Club class, made as a tree to allow nice tidy organization @@ -74,10 +79,6 @@ class Club(models.Model): ) address = models.CharField(_("address"), max_length=254) - # This function prevents generating migration upon settings change - def get_default_owner_group(): - return settings.SITH_GROUP_ROOT_ID - owner_group = models.ForeignKey( Group, related_name="owned_club", @@ -105,6 +106,34 @@ class Club(models.Model): class Meta: ordering = ["name", "unix_name"] + def __str__(self): + return self.name + + @transaction.atomic() + def save(self, *args, **kwargs): + old = Club.objects.filter(id=self.id).first() + creation = old is None + if not creation and old.unix_name != self.unix_name: + self._change_unixname(self.unix_name) + super().save(*args, **kwargs) + if creation: + board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX) + board.save() + member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX) + member.save() + subscribers = Group.objects.filter( + name=settings.SITH_MAIN_MEMBERS_GROUP + ).first() + self.make_home() + self.home.edit_groups.set([board]) + self.home.view_groups.set([member, subscribers]) + self.home.save() + self.make_page() + cache.set(f"sith_club_{self.unix_name}", self) + + def get_absolute_url(self): + return reverse("club:club_view", kwargs={"club_id": self.id}) + @cached_property def president(self): return self.members.filter( @@ -183,28 +212,6 @@ class Club(models.Model): self.page.parent = self.parent.page self.page.save(force_lock=True) - @transaction.atomic() - def save(self, *args, **kwargs): - old = Club.objects.filter(id=self.id).first() - creation = old is None - if not creation and old.unix_name != self.unix_name: - self._change_unixname(self.unix_name) - super().save(*args, **kwargs) - if creation: - board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX) - board.save() - member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX) - member.save() - subscribers = Group.objects.filter( - name=settings.SITH_MAIN_MEMBERS_GROUP - ).first() - self.make_home() - self.home.edit_groups.set([board]) - self.home.view_groups.set([member, subscribers]) - self.home.save() - self.make_page() - cache.set(f"sith_club_{self.unix_name}", self) - def delete(self, *args, **kwargs): # Invalidate the cache of this club and of its memberships for membership in self.members.ongoing().select_related("user"): @@ -212,12 +219,6 @@ class Club(models.Model): cache.delete(f"sith_club_{self.unix_name}") super().delete(*args, **kwargs) - def __str__(self): - return self.name - - def get_absolute_url(self): - return reverse("club:club_view", kwargs={"club_id": self.id}) - def get_display_name(self): return self.name @@ -373,14 +374,21 @@ class Membership(models.Model): def __str__(self): return ( - self.club.name - + " - " - + self.user.username - + " - " - + str(settings.SITH_CLUB_ROLES[self.role]) - + str(" - " + str(_("past member")) if self.end_date is not None else "") + f"{self.club.name} - {self.user.username} " + f"- {settings.SITH_CLUB_ROLES[self.role]} " + f"- {str(_('past member')) if self.end_date is not None else ''}" ) + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + if self.end_date is None: + cache.set(f"membership_{self.club_id}_{self.user_id}", self) + else: + cache.set(f"membership_{self.club_id}_{self.user_id}", "not_member") + + def get_absolute_url(self): + return reverse("club:club_members", kwargs={"club_id": self.club_id}) + def is_owned_by(self, user): """ Method to see if that object can be super edited by the given user @@ -400,16 +408,6 @@ class Membership(models.Model): return True return False - def get_absolute_url(self): - return reverse("club:club_members", kwargs={"club_id": self.club_id}) - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - if self.end_date is None: - cache.set(f"membership_{self.club_id}_{self.user_id}", self) - else: - cache.set(f"membership_{self.club_id}_{self.user_id}", "not_member") - def delete(self, *args, **kwargs): super().delete(*args, **kwargs) cache.delete(f"membership_{self.club_id}_{self.user_id}") @@ -451,6 +449,26 @@ class Mailing(models.Model): on_delete=models.CASCADE, ) + def __str__(self): + return "%s - %s" % (self.club, self.email_full) + + def save(self, *args, **kwargs): + if not self.is_moderated: + for user in ( + RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID) + .first() + .users.all() + ): + if not user.notifications.filter( + type="MAILING_MODERATION", viewed=False + ).exists(): + Notification( + user=user, + url=reverse("com:mailing_admin"), + type="MAILING_MODERATION", + ).save(*args, **kwargs) + super().save(*args, **kwargs) + def clean(self): if Mailing.objects.filter(email=self.email).exists(): raise ValidationError(_("This mailing list already exists.")) @@ -488,26 +506,6 @@ class Mailing(models.Model): resp += sub.fetch_format() return resp - def save(self): - if not self.is_moderated: - for user in ( - RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID) - .first() - .users.all() - ): - if not user.notifications.filter( - type="MAILING_MODERATION", viewed=False - ).exists(): - Notification( - user=user, - url=reverse("com:mailing_admin"), - type="MAILING_MODERATION", - ).save() - super().save() - - def __str__(self): - return "%s - %s" % (self.club, self.email_full) - class MailingSubscription(models.Model): """ @@ -535,6 +533,9 @@ class MailingSubscription(models.Model): class Meta: unique_together = (("user", "email", "mailing"),) + def __str__(self): + return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email) + def clean(self): if not self.user and not self.email: raise ValidationError(_("At least user or email is required")) @@ -577,6 +578,3 @@ class MailingSubscription(models.Model): def fetch_format(self): return self.get_email + " " - - def __str__(self): - return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email) diff --git a/com/models.py b/com/models.py index 7e6889a8..d9a4628c 100644 --- a/com/models.py +++ b/com/models.py @@ -46,14 +46,14 @@ class Sith(models.Model): weekmail_destinations = models.TextField(_("weekmail destinations"), default="") version = utils.get_git_revision_short_hash() + def __str__(self): + return "⛩ Sith ⛩" + def is_owned_by(self, user): if user.is_anonymous: return False return user.is_com_admin - def __str__(self): - return "⛩ Sith ⛩" - NEWS_TYPES = [ ("NOTICE", _("Notice")), @@ -90,23 +90,6 @@ class News(models.Model): on_delete=models.CASCADE, ) - def is_owned_by(self, user): - if user.is_anonymous: - return False - return user.is_com_admin or user == self.author - - def can_be_edited_by(self, user): - return user.is_com_admin - - def can_be_viewed_by(self, user): - return self.is_moderated or user.is_com_admin - - def get_absolute_url(self): - return reverse("com:news_detail", kwargs={"news_id": self.id}) - - def get_full_url(self): - return "https://%s%s" % (settings.SITH_URL, self.get_absolute_url()) - def __str__(self): return "%s: %s" % (self.type, self.title) @@ -124,6 +107,23 @@ class News(models.Model): param="1", ).save() + def get_absolute_url(self): + return reverse("com:news_detail", kwargs={"news_id": self.id}) + + def get_full_url(self): + return "https://%s%s" % (settings.SITH_URL, self.get_absolute_url()) + + def is_owned_by(self, user): + if user.is_anonymous: + return False + return user.is_com_admin or user == self.author + + def can_be_edited_by(self, user): + return user.is_com_admin + + def can_be_viewed_by(self, user): + return self.is_moderated or user.is_com_admin + def news_notification_callback(notif): count = ( @@ -185,6 +185,9 @@ class Weekmail(models.Model): class Meta: ordering = ["-id"] + def __str__(self): + return f"Weekmail {self.id} (sent: {self.sent}) - {self.title}" + def send(self): """ Send the weekmail to all users with the receive weekmail option opt-in. @@ -240,9 +243,6 @@ class Weekmail(models.Model): """ return "http://" + settings.SITH_URL + static("com/img/weekmail_footerP22.png") - def __str__(self): - return "Weekmail %s (sent: %s) - %s" % (self.id, self.sent, self.title) - def is_owned_by(self, user): if user.is_anonymous: return False @@ -273,18 +273,21 @@ class WeekmailArticle(models.Model): ) rank = models.IntegerField(_("rank"), default=-1) + def __str__(self): + return "%s - %s (%s)" % (self.title, self.author, self.club) + def is_owned_by(self, user): if user.is_anonymous: return False return user.is_com_admin - def __str__(self): - return "%s - %s (%s)" % (self.title, self.author, self.club) - class Screen(models.Model): name = models.CharField(_("name"), max_length=128) + def __str__(self): + return self.name + def active_posters(self): now = timezone.now() return self.posters.filter(is_moderated=True, date_begin__lte=now).filter( @@ -296,9 +299,6 @@ class Screen(models.Model): return False return user.is_com_admin - def __str__(self): - return "%s" % (self.name) - class Poster(models.Model): name = models.CharField( @@ -328,6 +328,9 @@ class Poster(models.Model): on_delete=models.CASCADE, ) + def __str__(self): + return self.name + def save(self, *args, **kwargs): if not self.is_moderated: for u in ( @@ -360,6 +363,3 @@ class Poster(models.Model): @property def page(self): return self.club.page - - def __str__(self): - return self.name diff --git a/core/migrations/0029_auto_20180426_2013.py b/core/migrations/0029_auto_20180426_2013.py index cc9a0dc0..bc86fb66 100644 --- a/core/migrations/0029_auto_20180426_2013.py +++ b/core/migrations/0029_auto_20180426_2013.py @@ -16,7 +16,7 @@ class Migration(migrations.Migration): field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, verbose_name="owner group", - default=core.models.Page.get_default_owner_group, + default=core.models.get_default_owner_group, related_name="owned_page", to="core.Group", ), diff --git a/core/models.py b/core/models.py index 0d5e1013..19e762db 100644 --- a/core/models.py +++ b/core/models.py @@ -845,12 +845,15 @@ class Preferences(models.Model): _("get a notification for every refilling"), default=False ) - def get_display_name(self): - return self.user.get_display_name() + def __str__(self): + return f"Preferences of {self.user}" def get_absolute_url(self): return self.user.get_absolute_url() + def get_display_name(self): + return self.user.get_display_name() + def get_directory(instance, filename): return ".{0}/{1}".format(instance.get_parent_path(), filename) @@ -928,6 +931,31 @@ class SithFile(models.Model): class Meta: verbose_name = _("file") + def __str__(self): + return self.get_parent_path() + "/" + self.name + + def save(self, *args, **kwargs): + sas = SithFile.objects.filter(id=settings.SITH_SAS_ROOT_DIR_ID).first() + self.is_in_sas = sas in self.get_parent_list() or self == sas + copy_rights = False + if self.id is None: + copy_rights = True + super().save(*args, **kwargs) + if copy_rights: + self.copy_rights() + if self.is_in_sas: + for u in ( + RealGroup.objects.filter(id=settings.SITH_GROUP_SAS_ADMIN_ID) + .first() + .users.all() + ): + Notification( + user=u, + url=reverse("sas:moderation"), + type="SAS_MODERATION", + param="1", + ).save() + def can_be_managed_by(self, user: User) -> bool: """ Tell if the user can manage the file (edit, delete, etc.) or not. @@ -1033,28 +1061,6 @@ class SithFile(models.Model): if self.is_file and (self.file is None or self.file == ""): raise ValidationError(_("You must provide a file")) - def save(self, *args, **kwargs): - sas = SithFile.objects.filter(id=settings.SITH_SAS_ROOT_DIR_ID).first() - self.is_in_sas = sas in self.get_parent_list() or self == sas - copy_rights = False - if self.id is None: - copy_rights = True - super().save(*args, **kwargs) - if copy_rights: - self.copy_rights() - if self.is_in_sas: - for u in ( - RealGroup.objects.filter(id=settings.SITH_GROUP_SAS_ADMIN_ID) - .first() - .users.all() - ): - Notification( - user=u, - url=reverse("sas:moderation"), - type="SAS_MODERATION", - param="1", - ).save() - def apply_rights_recursively(self, *, only_folders=False): children = self.children.all() if only_folders: @@ -1189,9 +1195,6 @@ class SithFile(models.Model): def get_download_url(self): return reverse("core:download", kwargs={"file_id": self.id}) - def __str__(self): - return self.get_parent_path() + "/" + self.name - class LockError(Exception): """There was a lock error on the object""" @@ -1211,6 +1214,11 @@ class NotLocked(LockError): pass +# This function prevents generating migration upon settings change +def get_default_owner_group(): + return settings.SITH_GROUP_ROOT_ID + + class Page(models.Model): """ The page class to build a Wiki @@ -1250,10 +1258,6 @@ class Page(models.Model): # playing with a Page object, use get_full_name() instead! _full_name = models.CharField(_("page name"), max_length=255, blank=True) - # This function prevents generating migration upon settings change - def get_default_owner_group(): - return settings.SITH_GROUP_ROOT_ID - owner_group = models.ForeignKey( Group, related_name="owned_page", @@ -1286,6 +1290,38 @@ class Page(models.Model): ("change_prop_page", "Can change the page's properties (groups, ...)"), ) + def __str__(self): + return self.get_full_name() + + def save(self, *args, **kwargs): + """ + Performs some needed actions before and after saving a page in database + """ + locked = kwargs.pop("force_lock", False) + if not locked: + locked = self.is_locked() + if not locked: + raise NotLocked("The page is not locked and thus can not be saved") + self.full_clean() + if not self.id: + super().save( + *args, **kwargs + ) # Save a first time to correctly set _full_name + # This reset the _full_name just before saving to maintain a coherent field quicker for queries than the + # recursive method + # It also update all the children to maintain correct names + self._full_name = self.get_full_name() + for c in self.children.all(): + c.save() + super().save(*args, **kwargs) + self.unset_lock() + + def get_absolute_url(self): + """ + This is needed for black magic powered UpdateView's children + """ + return reverse("core:page", kwargs={"page_name": self._full_name}) + @staticmethod def get_page_by_full_name(name): """ @@ -1293,9 +1329,6 @@ class Page(models.Model): """ return Page.objects.filter(_full_name=name).first() - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def clean(self): """ Cleans up only the name for the moment, but this can be used to make any treatment before saving the object @@ -1333,29 +1366,6 @@ class Page(models.Model): p = p.parent return l - def save(self, *args, **kwargs): - """ - Performs some needed actions before and after saving a page in database - """ - locked = kwargs.pop("force_lock", False) - if not locked: - locked = self.is_locked() - if not locked: - raise NotLocked("The page is not locked and thus can not be saved") - self.full_clean() - if not self.id: - super().save( - *args, **kwargs - ) # Save a first time to correctly set _full_name - # This reset the _full_name just before saving to maintain a coherent field quicker for queries than the - # recursive method - # It also update all the children to maintain correct names - self._full_name = self.get_full_name() - for c in self.children.all(): - c.save() - super().save(*args, **kwargs) - self.unset_lock() - def is_locked(self): """ Is True if the page is locked, False otherwise @@ -1415,15 +1425,6 @@ class Page(models.Model): return self.lock_user raise NotLocked("The page is not locked and thus can not return its user") - def get_absolute_url(self): - """ - This is needed for black magic powered UpdateView's children - """ - return reverse("core:page", kwargs={"page_name": self._full_name}) - - def __str__(self): - return self.get_full_name() - def get_full_name(self): """ Computes the real full_name of the page based on its name and its parent's name @@ -1480,15 +1481,22 @@ class PageRev(models.Model): class Meta: ordering = ["date"] + def __str__(self): + return str(self.__dict__) + + def save(self, *args, **kwargs): + if self.revision is None: + self.revision = self.page.revisions.all().count() + 1 + super().save(*args, **kwargs) + # Don't forget to unlock, otherwise, people will have to wait for the page's timeout + self.page.unset_lock() + def get_absolute_url(self): """ This is needed for black magic powered UpdateView's children """ return reverse("core:page", kwargs={"page_name": self.page._full_name}) - def __str__(self): - return str(self.__dict__) - def __getattribute__(self, attr): if attr == "owner_group": return self.page.owner_group @@ -1504,13 +1512,6 @@ class PageRev(models.Model): def can_be_edited_by(self, user): return self.page.can_be_edited_by(user) - def save(self, *args, **kwargs): - if self.revision is None: - self.revision = self.page.revisions.all().count() + 1 - super().save(*args, **kwargs) - # Don't forget to unlock, otherwise, people will have to wait for the page's timeout - self.page.unset_lock() - class Notification(models.Model): user = models.ForeignKey( @@ -1529,15 +1530,6 @@ class Notification(models.Model): return self.get_type_display() % self.param return self.get_type_display() - def callback(self): - # Get the callback defined in settings to update existing - # notifications - mod_name, func_name = settings.SITH_PERMANENT_NOTIFICATIONS[self.type].rsplit( - ".", 1 - ) - mod = importlib.import_module(mod_name) - getattr(mod, func_name)(self) - def save(self, *args, **kwargs): if not self.id and self.type in settings.SITH_PERMANENT_NOTIFICATIONS: old_notif = self.user.notifications.filter(type=self.type).last() @@ -1547,6 +1539,15 @@ class Notification(models.Model): return super().save(*args, **kwargs) + def callback(self): + # Get the callback defined in settings to update existing + # notifications + mod_name, func_name = settings.SITH_PERMANENT_NOTIFICATIONS[self.type].rsplit( + ".", 1 + ) + mod = importlib.import_module(mod_name) + getattr(mod, func_name)(self) + class Gift(models.Model): label = models.CharField(_("label"), max_length=255) @@ -1585,8 +1586,8 @@ class OperationLog(models.Model): _("operation type"), max_length=40, choices=settings.SITH_LOG_OPERATION_TYPE ) - def is_owned_by(self, user): - return user.is_root - def __str__(self): return "%s - %s - %s" % (self.operation_type, self.label, self.operator) + + def is_owned_by(self, user): + return user.is_root diff --git a/core/views/site.py b/core/views/site.py index b0aeb3e7..67c628d8 100644 --- a/core/views/site.py +++ b/core/views/site.py @@ -93,7 +93,7 @@ def search_user(query): return [] -def search_club(query,* , as_json=False): +def search_club(query, *, as_json=False): clubs = [] if query: clubs = Club.objects.filter(name__icontains=query).all() diff --git a/counter/forms.py b/counter/forms.py index 9b098ac3..1adbed68 100644 --- a/counter/forms.py +++ b/counter/forms.py @@ -18,7 +18,15 @@ from counter.models import ( class BillingInfoForm(forms.ModelForm): class Meta: model = BillingInfo - exclude = ["customer"] + fields = [ + "first_name", + "last_name", + "address_1", + "address_2", + "zip_code", + "city", + "country", + ] class StudentCardForm(forms.ModelForm): diff --git a/counter/models.py b/counter/models.py index dd7a9989..8d9db9b4 100644 --- a/counter/models.py +++ b/counter/models.py @@ -62,6 +62,19 @@ class Customer(models.Model): def __str__(self): return "%s - %s" % (self.user.username, self.account_id) + def save(self, *args, allow_negative=False, is_selling=False, **kwargs): + """ + is_selling : tell if the current action is a selling + allow_negative : ignored if not a selling. Allow a selling to put the account in negative + Those two parameters avoid blocking the save method of a customer if his account is negative + """ + if self.amount < 0 and (is_selling and not allow_negative): + raise ValidationError(_("Not enough money")) + super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("core:user_account", kwargs={"user_id": self.user.pk}) + @property def can_record(self): return self.recorded_products > -settings.SITH_ECOCUP_LIMIT @@ -128,16 +141,6 @@ class Customer(models.Model): account = cls.objects.create(user=user, account_id=account_id) return account, True - def save(self, *args, allow_negative=False, is_selling=False, **kwargs): - """ - is_selling : tell if the current action is a selling - allow_negative : ignored if not a selling. Allow a selling to put the account in negative - Those two parameters avoid blocking the save method of a customer if his account is negative - """ - if self.amount < 0 and (is_selling and not allow_negative): - raise ValidationError(_("Not enough money")) - super().save(*args, **kwargs) - def recompute_amount(self): refillings = self.refillings.aggregate(sum=Sum(F("amount")))["sum"] self.amount = refillings if refillings is not None else 0 @@ -150,9 +153,6 @@ class Customer(models.Model): self.amount -= purchases self.save() - def get_absolute_url(self): - return reverse("core:user_account", kwargs={"user_id": self.user.pk}) - def get_full_url(self): return "".join(["https://", settings.SITH_URL, self.get_absolute_url()]) @@ -178,6 +178,9 @@ class BillingInfo(models.Model): city = models.CharField(_("City"), max_length=50) country = CountryField(blank_label=_("Country")) + def __str__(self): + return f"{self.first_name} {self.last_name}" + def to_3dsv2_xml(self) -> str: """ Convert the data from this model into a xml usable @@ -199,9 +202,6 @@ class BillingInfo(models.Model): xml = dict2xml(data, wrap="Billing", newlines=False) return '' + xml - def __str__(self): - return f"{self.first_name} {self.last_name}" - class ProductType(models.Model): """ @@ -222,6 +222,12 @@ class ProductType(models.Model): verbose_name = _("product type") ordering = ["-priority", "name"] + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("counter:producttype_list") + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -232,12 +238,6 @@ class ProductType(models.Model): return True return False - def __str__(self): - return self.name - - def get_absolute_url(self): - return reverse("counter:producttype_list") - class Product(models.Model): """ @@ -282,6 +282,12 @@ class Product(models.Model): class Meta: verbose_name = _("product") + def __str__(self): + return "%s (%s)" % (self.name, self.code) + + def get_absolute_url(self): + return reverse("counter:product_list") + @property def is_record_product(self): return settings.SITH_ECOCUP_CONS == self.id @@ -302,9 +308,6 @@ class Product(models.Model): return True return False - def get_absolute_url(self): - return reverse("counter:product_list") - def can_be_sold_to(self, user: User) -> bool: """ Check if whether the user given in parameter has the right to buy @@ -329,9 +332,6 @@ class Product(models.Model): def profit(self): return self.selling_price - self.purchase_price - def __str__(self): - return "%s (%s)" % (self.name, self.code) - class CounterQuerySet(models.QuerySet): def annotate_has_barman(self, user: User) -> CounterQuerySet: @@ -388,13 +388,6 @@ class Counter(models.Model): class Meta: verbose_name = _("counter") - def __getattribute__(self, name): - if name == "edit_groups": - return Group.objects.filter( - name=self.club.unix_name + settings.SITH_BOARD_SUFFIX - ).all() - return object.__getattribute__(self, name) - def __str__(self): return self.name @@ -403,6 +396,13 @@ class Counter(models.Model): return reverse("eboutic:main") return reverse("counter:details", kwargs={"counter_id": self.id}) + def __getattribute__(self, name): + if name == "edit_groups": + return Group.objects.filter( + name=self.club.unix_name + settings.SITH_BOARD_SUFFIX + ).all() + return object.__getattribute__(self, name) + def is_owned_by(self, user): if user.is_anonymous: return False @@ -629,16 +629,6 @@ class Refilling(models.Model): self.customer.user.get_display_name(), ) - def is_owned_by(self, user): - if user.is_anonymous: - return False - return user.is_owner(self.counter) and self.payment_method != "CARD" - - def delete(self, *args, **kwargs): - self.customer.amount -= self.amount - self.customer.save() - super().delete(*args, **kwargs) - def save(self, *args, **kwargs): if not self.date: self.date = timezone.now() @@ -663,6 +653,16 @@ class Refilling(models.Model): ).save() super().save(*args, **kwargs) + def is_owned_by(self, user): + if user.is_anonymous: + return False + return user.is_owner(self.counter) and self.payment_method != "CARD" + + def delete(self, *args, **kwargs): + self.customer.amount -= self.amount + self.customer.save() + super().delete(*args, **kwargs) + class Selling(models.Model): """ @@ -723,59 +723,6 @@ class Selling(models.Model): self.customer.user.get_display_name(), ) - def is_owned_by(self, user): - if user.is_anonymous: - return False - return user.is_owner(self.counter) and self.payment_method != "CARD" - - def can_be_viewed_by(self, user): - if ( - not hasattr(self, "customer") or self.customer is None - ): # Customer can be set to Null - return False - return user == self.customer.user - - def delete(self, *args, **kwargs): - if self.payment_method == "SITH_ACCOUNT": - self.customer.amount += self.quantity * self.unit_price - self.customer.save() - super().delete(*args, **kwargs) - - def send_mail_customer(self): - event = self.product.eticket.event_title or _("Unknown event") - subject = _("Eticket bought for the event %(event)s") % {"event": event} - message_html = _( - "You bought an eticket for the event %(event)s.\nYou can download it directly from this link %(eticket)s.\nYou can also retrieve all your e-tickets on your account page %(url)s." - ) % { - "event": event, - "url": "".join( - ( - '', - self.customer.get_full_url(), - "", - ) - ), - "eticket": "".join( - ( - '', - self.get_eticket_full_url(), - "", - ) - ), - } - message_txt = _( - "You bought an eticket for the event %(event)s.\nYou can download it directly from this link %(eticket)s.\nYou can also retrieve all your e-tickets on your account page %(url)s." - ) % { - "event": event, - "url": self.customer.get_full_url(), - "eticket": self.get_eticket_full_url(), - } - self.customer.user.email_user(subject, message_txt, html_message=message_html) - def save(self, *args, allow_negative=False, **kwargs): """ allow_negative : Allow this selling to use more money than available for this user @@ -858,6 +805,59 @@ class Selling(models.Model): except: pass + def is_owned_by(self, user): + if user.is_anonymous: + return False + return user.is_owner(self.counter) and self.payment_method != "CARD" + + def can_be_viewed_by(self, user): + if ( + not hasattr(self, "customer") or self.customer is None + ): # Customer can be set to Null + return False + return user == self.customer.user + + def delete(self, *args, **kwargs): + if self.payment_method == "SITH_ACCOUNT": + self.customer.amount += self.quantity * self.unit_price + self.customer.save() + super().delete(*args, **kwargs) + + def send_mail_customer(self): + event = self.product.eticket.event_title or _("Unknown event") + subject = _("Eticket bought for the event %(event)s") % {"event": event} + message_html = _( + "You bought an eticket for the event %(event)s.\nYou can download it directly from this link %(eticket)s.\nYou can also retrieve all your e-tickets on your account page %(url)s." + ) % { + "event": event, + "url": "".join( + ( + '', + self.customer.get_full_url(), + "", + ) + ), + "eticket": "".join( + ( + '', + self.get_eticket_full_url(), + "", + ) + ), + } + message_txt = _( + "You bought an eticket for the event %(event)s.\nYou can download it directly from this link %(eticket)s.\nYou can also retrieve all your e-tickets on your account page %(url)s." + ) % { + "event": event, + "url": self.customer.get_full_url(), + "eticket": self.get_eticket_full_url(), + } + self.customer.user.email_user(subject, message_txt, html_message=message_html) + def get_eticket_full_url(self): eticket_url = reverse("counter:eticket_pdf", kwargs={"selling_id": self.id}) return "".join(["https://", settings.SITH_URL, eticket_url]) @@ -926,6 +926,14 @@ class CashRegisterSummary(models.Model): def __str__(self): return "At %s by %s - Total: %s €" % (self.counter, self.user, self.get_total()) + def save(self, *args, **kwargs): + if not self.id: + self.date = timezone.now() + return super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse("counter:cash_summary_list") + def __getattribute__(self, name): if name[:5] == "check": checks = self.items.filter(check=True).order_by("value").all() @@ -978,14 +986,6 @@ class CashRegisterSummary(models.Model): t += it.quantity * it.value return t - def save(self, *args, **kwargs): - if not self.id: - self.date = timezone.now() - return super().save(*args, **kwargs) - - def get_absolute_url(self): - return reverse("counter:cash_summary_list") - class CashRegisterSummaryItem(models.Model): cash_summary = models.ForeignKey( @@ -1005,6 +1005,9 @@ class CashRegisterSummaryItem(models.Model): class Meta: verbose_name = _("cash register summary item") + def __str__(self): + return str(self.value) + class Eticket(models.Model): """ @@ -1027,16 +1030,16 @@ class Eticket(models.Model): secret = models.CharField(_("secret"), max_length=64, unique=True) def __str__(self): - return "%s" % (self.product.name) - - def get_absolute_url(self): - return reverse("counter:eticket_list") + return self.product.name def save(self, *args, **kwargs): if not self.id: self.secret = base64.b64encode(os.urandom(32)) return super().save(*args, **kwargs) + def get_absolute_url(self): + return reverse("counter:eticket_list") + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -1064,6 +1067,21 @@ class StudentCard(models.Model): UID_SIZE = 14 + uid = models.CharField( + _("uid"), max_length=UID_SIZE, unique=True, validators=[MinLengthValidator(4)] + ) + customer = models.ForeignKey( + Customer, + related_name="student_cards", + verbose_name=_("student cards"), + null=False, + blank=False, + on_delete=models.CASCADE, + ) + + def __str__(self): + return self.uid + @staticmethod def is_valid(uid): return ( @@ -1080,15 +1098,3 @@ class StudentCard(models.Model): if isinstance(obj, User): return StudentCard.can_create(self.customer, obj) return False - - uid = models.CharField( - _("uid"), max_length=14, unique=True, validators=[MinLengthValidator(4)] - ) - customer = models.ForeignKey( - Customer, - related_name="student_cards", - verbose_name=_("student cards"), - null=False, - blank=False, - on_delete=models.CASCADE, - ) diff --git a/eboutic/models.py b/eboutic/models.py index c9b9f3da..610a6a19 100644 --- a/eboutic/models.py +++ b/eboutic/models.py @@ -56,6 +56,9 @@ class Basket(models.Model): ) date = models.DateTimeField(_("date"), auto_now=True) + def __str__(self): + return f"{self.user}'s basket ({self.items.all().count()} items)" + def add_product(self, p: Product, q: int = 1): """ Given p an object of the Product model and q an integer, @@ -207,9 +210,6 @@ class Basket(models.Model): data.append(("PBX_HMAC", pbx_hmac.hexdigest().upper())) return data - def __str__(self): - return "%s's basket (%d items)" % (self.user, self.items.all().count()) - class Invoice(models.Model): """ @@ -226,6 +226,9 @@ class Invoice(models.Model): date = models.DateTimeField(_("date"), auto_now=True) validated = models.BooleanField(_("validated"), default=False) + def __str__(self): + return f"{self.user} - {self.get_total()} - {self.date}" + def get_total(self) -> float: total = self.items.aggregate( total=Sum(F("quantity") * F("product_unit_price")) @@ -268,9 +271,6 @@ class Invoice(models.Model): self.validated = True self.save() - def __str__(self): - return "%s - %s - %s" % (self.user, self.get_total(), self.date) - class AbstractBaseItem(models.Model): product_id = models.IntegerField(_("product id")) diff --git a/election/models.py b/election/models.py index 34e93ea2..e52fe84e 100644 --- a/election/models.py +++ b/election/models.py @@ -163,16 +163,16 @@ class ElectionList(models.Model): on_delete=models.CASCADE, ) + def __str__(self): + return self.title + def can_be_edited_by(self, user): return user.can_edit(self.election) - def delete(self): + def delete(self, *args, **kwargs): for candidature in self.candidatures.all(): candidature.delete() - super().delete() - - def __str__(self): - return self.title + super().delete(*args, **kwargs) class Candidature(models.Model): @@ -201,6 +201,9 @@ class Candidature(models.Model): on_delete=models.CASCADE, ) + def __str__(self): + return f"{self.role.title} : {self.user.username}" + def delete(self): for vote in self.votes.all(): vote.delete() @@ -209,9 +212,6 @@ class Candidature(models.Model): def can_be_edited_by(self, user): return (user == self.user) or user.can_edit(self.role.election) - def __str__(self): - return "%s : %s" % (self.role.title, self.user.username) - class Vote(models.Model): """ diff --git a/forum/migrations/0006_auto_20180426_2013.py b/forum/migrations/0006_auto_20180426_2013.py index 14fdea81..029f407a 100644 --- a/forum/migrations/0006_auto_20180426_2013.py +++ b/forum/migrations/0006_auto_20180426_2013.py @@ -14,7 +14,7 @@ class Migration(migrations.Migration): name="edit_groups", field=models.ManyToManyField( blank=True, - default=forum.models.Forum.get_default_edit_group, + default=forum.models.get_default_edit_group, related_name="editable_forums", to="core.Group", ), @@ -24,7 +24,7 @@ class Migration(migrations.Migration): name="view_groups", field=models.ManyToManyField( blank=True, - default=forum.models.Forum.get_default_view_group, + default=forum.models.get_default_view_group, related_name="viewable_forums", to="core.Group", ), diff --git a/forum/models.py b/forum/models.py index 47e13e48..92a233aa 100644 --- a/forum/models.py +++ b/forum/models.py @@ -20,6 +20,7 @@ # Place - Suite 330, Boston, MA 02111-1307, USA. # # +from __future__ import annotations from datetime import datetime from itertools import chain @@ -37,6 +38,15 @@ from club.models import Club from core.models import Group, User +# Those functions prevent generating migration upon settings changes +def get_default_edit_group(): + return [settings.SITH_GROUP_OLD_SUBSCRIBERS_ID] + + +def get_default_view_group(): + return [settings.SITH_GROUP_PUBLIC_ID] + + class Forum(models.Model): """ The Forum class, made as a tree to allow nice tidy organization @@ -46,13 +56,6 @@ class Forum(models.Model): view_groups allows some groups to view a forum """ - # Those functions prevent generating migration upon settings changes - def get_default_edit_group(): - return [settings.SITH_GROUP_OLD_SUBSCRIBERS_ID] - - def get_default_view_group(): - return [settings.SITH_GROUP_PUBLIC_ID] - id = models.AutoField(primary_key=True, db_index=True) name = models.CharField(_("name"), max_length=64) description = models.CharField(_("description"), max_length=512, default="") @@ -98,8 +101,8 @@ class Forum(models.Model): class Meta: ordering = ["number"] - def clean(self): - self.check_loop() + def __str__(self): + return self.name def save(self, *args, **kwargs): copy_rights = False @@ -109,6 +112,12 @@ class Forum(models.Model): if copy_rights: self.copy_rights() + def get_absolute_url(self): + return reverse("forum:view_forum", kwargs={"forum_id": self.id}) + + def clean(self): + self.check_loop() + def set_topic_number(self): self._topic_number = self.get_topic_number() self.save() @@ -166,11 +175,11 @@ class Forum(models.Model): return True try: m = Forum._club_memberships[self.id][user.id] - except: + except KeyError: m = self.owner_club.get_membership_for(user) try: Forum._club_memberships[self.id][user.id] = m - except: + except KeyError: Forum._club_memberships[self.id] = {} Forum._club_memberships[self.id][user.id] = m if m: @@ -187,9 +196,6 @@ class Forum(models.Model): objs.append(cur) cur = cur.parent - def __str__(self): - return "%s" % (self.name) - def get_full_name(self): return "/".join( chain.from_iterable( @@ -197,9 +203,6 @@ class Forum(models.Model): ) ) - def get_absolute_url(self): - return reverse("forum:view_forum", kwargs={"forum_id": self.id}) - @cached_property def parent_list(self): return self.get_parent_list() @@ -256,11 +259,17 @@ class ForumTopic(models.Model): class Meta: ordering = ["-_last_message__date"] + def __str__(self): + return self.title + def save(self, *args, **kwargs): super().save(*args, **kwargs) self.forum.set_topic_number() # Recompute the cached value self.forum.set_last_message() + def get_absolute_url(self): + return reverse("forum:view_topic", kwargs={"topic_id": self.id}) + def is_owned_by(self, user): return self.forum.is_owned_by(user) @@ -270,23 +279,15 @@ class ForumTopic(models.Model): def can_be_viewed_by(self, user): return user.can_view(self.forum) - def __str__(self): - return "%s" % (self.title) - - def get_absolute_url(self): - return reverse("forum:view_topic", kwargs={"topic_id": self.id}) - - def get_first_unread_message(self, user): - try: - msg = ( - self.messages.exclude(readers=user) - .filter(date__gte=user.forum_infos.last_read_date) - .order_by("id") - .first() - ) - return msg - except: + def get_first_unread_message(self, user: User) -> ForumMessage | None: + if not hasattr(user, "forum_infos"): return None + return ( + self.messages.exclude(readers=user) + .filter(date__gte=user.forum_infos.last_read_date) + .order_by("id") + .first() + ) @cached_property def last_message(self): @@ -332,6 +333,9 @@ class ForumMessage(models.Model): self.topic._message_number = self.topic.messages.count() self.topic.save() + def get_absolute_url(self): + return reverse("forum:view_message", kwargs={"message_id": self.id}) + def is_first_in_topic(self): return bool(self.id == self.topic.messages.order_by("date").first().id) @@ -356,9 +360,6 @@ class ForumMessage(models.Model): def can_be_moderated_by(self, user): return self.topic.forum.is_owned_by(user) or user.id == self.author.id - def get_absolute_url(self): - return reverse("forum:view_message", kwargs={"message_id": self.id}) - def get_url(self): return ( self.topic.get_absolute_url() @@ -378,11 +379,10 @@ class ForumMessage(models.Model): ) def mark_as_read(self, user): - try: # Need the try/except because of AnonymousUser - if not self.is_read(user): - self.readers.add(user) - except: - pass + if user.is_anonymous: + return + if not self.is_read(user): + self.readers.add(user) def is_read(self, user): return (self.date < user.forum_infos.last_read_date) or ( @@ -413,6 +413,9 @@ class ForumMessageMeta(models.Model): date = models.DateTimeField(_("date"), default=timezone.now) action = models.CharField(_("action"), choices=MESSAGE_META_ACTIONS, max_length=16) + def __str__(self): + return f"{self.user.nick_name} ({self.date})" + def save(self, *args, **kwargs): super().save(*args, **kwargs) self.message._deleted = self.message.is_deleted() diff --git a/galaxy/models.py b/galaxy/models.py index 9eabac04..d477fad6 100644 --- a/galaxy/models.py +++ b/galaxy/models.py @@ -127,6 +127,9 @@ class GalaxyLane(models.Model): default=0, ) + def __str__(self): + return f"{self.star1} -> {self.star2} ({self.distance})" + class StarDict(TypedDict): id: int diff --git a/launderette/models.py b/launderette/models.py index 7e3bd4c8..0abae7ab 100644 --- a/launderette/models.py +++ b/launderette/models.py @@ -37,6 +37,12 @@ class Launderette(models.Model): class Meta: verbose_name = _("Launderette") + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("launderette:launderette_list") + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -63,12 +69,6 @@ class Launderette(models.Model): def can_be_viewed_by(self, user): return user.is_subscribed - def __str__(self): - return self.name - - def get_absolute_url(self): - return reverse("launderette:launderette_list") - def get_machine_list(self): return Machine.objects.filter(launderette_id=self.id) @@ -98,6 +98,15 @@ class Machine(models.Model): class Meta: verbose_name = _("Machine") + def __str__(self): + return "%s %s" % (self._meta.verbose_name, self.name) + + def get_absolute_url(self): + return reverse( + "launderette:launderette_admin", + kwargs={"launderette_id": self.launderette.id}, + ) + def is_owned_by(self, user): """ Method to see if that object can be edited by the given user @@ -112,15 +121,6 @@ class Machine(models.Model): return True return False - def __str__(self): - return "%s %s" % (self._meta.verbose_name, self.name) - - def get_absolute_url(self): - return reverse( - "launderette:launderette_admin", - kwargs={"launderette_id": self.launderette.id}, - ) - class Token(models.Model): name = models.CharField(_("name"), max_length=5) @@ -148,6 +148,12 @@ class Token(models.Model): unique_together = ("name", "launderette", "type") ordering = ["type", "name"] + def __str__(self): + return ( + f"{self.__class__._meta.verbose_name} {self.get_type_display()} " + f"#{self.name} ({self.launderette.name})" + ) + def save(self, *args, **kwargs): if self.name == "": raise DataError(_("Token name can not be blank")) @@ -168,18 +174,6 @@ class Token(models.Model): return True return False - def __str__(self): - return ( - self.__class__._meta.verbose_name - + " " - + self.get_type_display() - + " #" - + self.name - + " (" - + self.launderette.name - + ")" - ) - def is_avaliable(self): if not self.borrow_date and not self.user: return True @@ -214,9 +208,6 @@ class Slot(models.Model): verbose_name = _("Slot") ordering = ["start_date"] - def is_owned_by(self, user): - return user == self.user - def __str__(self): return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % ( self.user, @@ -225,3 +216,6 @@ class Slot(models.Model): self.machine.name, self.token, ) + + def is_owned_by(self, user): + return user == self.user diff --git a/pedagogy/models.py b/pedagogy/models.py index ccbfa565..2457970f 100644 --- a/pedagogy/models.py +++ b/pedagogy/models.py @@ -141,6 +141,12 @@ class UV(models.Model): default=0, ) + def __str__(self): + return self.code + + def get_absolute_url(self): + return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.id}) + def is_owned_by(self, user): """ Can be created by superuser, root or pedagogy admin user @@ -160,9 +166,6 @@ class UV(models.Model): return int(sum(comments.values_list(field, flat=True)) / comments.count()) - def get_absolute_url(self): - return reverse("pedagogy:uv_detail", kwargs={"uv_id": self.id}) - def has_user_already_commented(self, user): """ Help prevent multiples comments from the same user @@ -194,9 +197,6 @@ class UV(models.Model): def grade_work_load_average(self): return self.__grade_average_generic("grade_work_load") - def __str__(self): - return self.code - class UVComment(models.Model): """ @@ -252,6 +252,14 @@ class UVComment(models.Model): ) publish_date = models.DateTimeField(_("publish date"), blank=True) + def __str__(self): + return f"{self.uv} - {self.author}" + + def save(self, *args, **kwargs): + if self.publish_date is None: + self.publish_date = timezone.now() + super().save(*args, **kwargs) + def is_owned_by(self, user): """ Is owned by a pedagogy admin, a superuser or the author himself @@ -265,14 +273,6 @@ class UVComment(models.Model): """ return self.reports.exists() - def __str__(self): - return "%s - %s" % (self.uv, self.author) - - def save(self, *args, **kwargs): - if self.publish_date is None: - self.publish_date = timezone.now() - super().save(*args, **kwargs) - class UVResult(models.Model): """ @@ -303,6 +303,9 @@ class UVResult(models.Model): validators=[validators.RegexValidator("[AP][0-9]{3}")], ) + def __str__(self): + return f"{self.user.username} ; {self.uv.code} ; {self.grade}" + class UVCommentReport(models.Model): """ @@ -323,6 +326,9 @@ class UVCommentReport(models.Model): ) reason = models.TextField(_("reason")) + def __str__(self): + return f"{self.reporter.username} : {self.reason}" + @cached_property def uv(self): return self.comment.uv diff --git a/pyproject.toml b/pyproject.toml index a0c16ec2..a690bfea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,7 +70,19 @@ optional = true version = "1.4.25" [tool.ruff.lint] -select = ["I", "A", "F401", "UP008", "UP009"] +select = [ + "I", # isort + "B", + "A", # shadowing of Python builtins + "F401", # unused import + "DJ", # django-specific rules, + "UP008", # Use super() instead of super(__class__, self) + "UP009" # utf-8 encoding declaration is unnecessary +] + +ignore = [ + "DJ001", # null=True in CharField/TextField. this one would require a migration +] [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "sith.settings" diff --git a/stock/models.py b/stock/models.py index de55e8b4..89efdad7 100644 --- a/stock/models.py +++ b/stock/models.py @@ -149,10 +149,10 @@ class ShoppingListItem(models.Model): ) def __str__(self): - return "%s - %s" % (self.name, self.shopping_lists.first()) - - def can_be_viewed_by(self, user): - return user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID) + return f"{self.name} - {self.shopping_lists.first()}" def get_absolute_url(self): return reverse("stock:shoppinglist_list") + + def can_be_viewed_by(self, user): + return user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID) diff --git a/subscription/models.py b/subscription/models.py index f1cf12c7..18b3776a 100644 --- a/subscription/models.py +++ b/subscription/models.py @@ -22,6 +22,7 @@ from django.contrib.auth.forms import PasswordResetForm from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from core.models import User @@ -65,26 +66,11 @@ class Subscription(models.Model): class Meta: ordering = ["subscription_start"] - def clean(self): - try: - for s in ( - Subscription.objects.filter(member=self.member) - .exclude(pk=self.pk) - .all() - ): - if ( - s.is_valid_now() - and s.subscription_end - - timedelta(weeks=settings.SITH_SUBSCRIPTION_END) - > date.today() - ): - raise ValidationError( - _("You can not subscribe many time for the same period") - ) - except: # This should not happen, because the form should have handled the data before, but sadly, it still - # calls the model validation :'( - # TODO see SubscriptionForm's clean method - raise ValidationError(_("Subscription error")) + def __str__(self): + if hasattr(self, "member") and self.member is not None: + return f"{self.member.username} - {self.pk}" + else: + return f"No user - {self.pk}" def save(self, *args, **kwargs): super().save() @@ -105,11 +91,20 @@ class Subscription(models.Model): def get_absolute_url(self): return reverse("core:user_edit", kwargs={"user_id": self.member.pk}) - def __str__(self): - if hasattr(self, "member") and self.member is not None: - return self.member.username + " - " + str(self.pk) - else: - return "No user - " + str(self.pk) + def clean(self): + today = timezone.now().date() + active_subscriptions = Subscription.objects.exclude(pk=self.pk).filter( + subscription_start__gte=today, subscription_end__lte=today + ) + for s in active_subscriptions: + if ( + s.is_valid_now() + and s.subscription_end - timedelta(weeks=settings.SITH_SUBSCRIPTION_END) + > date.today() + ): + raise ValidationError( + _("You can not subscribe many time for the same period") + ) @staticmethod def compute_start(d: date = None, duration: int = 1, user: User = None) -> date: diff --git a/trombi/models.py b/trombi/models.py index 9a39579e..c171cf8d 100644 --- a/trombi/models.py +++ b/trombi/models.py @@ -83,6 +83,9 @@ class Trombi(models.Model): def __str__(self): return str(self.club.name) + def get_absolute_url(self): + return reverse("trombi:detail", kwargs={"trombi_id": self.id}) + def clean(self): if self.subscription_deadline > self.comments_deadline: raise ValidationError( @@ -92,9 +95,6 @@ class Trombi(models.Model): ) ) - def get_absolute_url(self): - return reverse("trombi:detail", kwargs={"trombi_id": self.id}) - def is_owned_by(self, user): return user.can_edit(self.club) @@ -192,6 +192,9 @@ class TrombiComment(models.Model): content = models.TextField(_("content"), default="") is_moderated = models.BooleanField(_("is the comment moderated"), default=False) + def __str__(self): + return f"{self.author} : {self.content}" + def can_be_viewed_by(self, user): if user.id == self.target.user.id: return False @@ -220,8 +223,8 @@ class TrombiClubMembership(models.Model): def __str__(self): return "%s - %s - %s (%s)" % (self.user, self.club, self.role, self.start) - def can_be_edited_by(self, user): - return user.id == self.user.user.id or user.can_edit(self.user.trombi) - def get_absolute_url(self): return reverse("trombi:profile") + + def can_be_edited_by(self, user): + return user.id == self.user.user.id or user.can_edit(self.user.trombi)