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)