ruff rule DJ

Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
This commit is contained in:
thomas girod 2024-06-27 15:48:07 +02:00
parent f941435232
commit 2ac578c3ad
20 changed files with 537 additions and 516 deletions

View File

@ -61,6 +61,15 @@ class Company(models.Model):
class Meta: class Meta:
verbose_name = _("company") 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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 True
return False 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): class BankAccount(models.Model):
name = models.CharField(_("name"), max_length=30) name = models.CharField(_("name"), max_length=30)
@ -112,6 +112,12 @@ class BankAccount(models.Model):
verbose_name = _("Bank account") verbose_name = _("Bank account")
ordering = ["club", "name"] 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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 True
return False 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): class ClubAccount(models.Model):
name = models.CharField(_("name"), max_length=30) name = models.CharField(_("name"), max_length=30)
@ -151,6 +151,12 @@ class ClubAccount(models.Model):
verbose_name = _("Club account") verbose_name = _("Club account")
ordering = ["bank_account", "name"] 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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): def get_open_journal(self):
return self.journals.filter(closed=False).first() 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): def get_display_name(self):
return _("%(club_account)s on %(bank_account)s") % { return _("%(club_account)s on %(bank_account)s") % {
"club_account": self.name, "club_account": self.name,
@ -224,6 +224,12 @@ class GeneralJournal(models.Model):
verbose_name = _("General journal") verbose_name = _("General journal")
ordering = ["-start_date"] 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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): def can_be_viewed_by(self, user):
return self.club_account.can_be_viewed_by(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): def update_amounts(self):
self.amount = 0 self.amount = 0
self.effective_amount = 0 self.effective_amount = 0
@ -356,6 +356,18 @@ class Operation(models.Model):
unique_together = ("number", "journal") unique_together = ("number", "journal")
ordering = ["-number"] 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): def __getattribute__(self, attr):
if attr == "target": if attr == "target":
return self.get_target() return self.get_target()
@ -409,12 +421,6 @@ class Operation(models.Model):
tar = Company.objects.filter(id=self.target_id).first() tar = Company.objects.filter(id=self.target_id).first()
return tar 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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 True
return False 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): class AccountingType(models.Model):
""" """
@ -486,6 +481,12 @@ class AccountingType(models.Model):
verbose_name = _("accounting type") verbose_name = _("accounting type")
ordering = ["movement_type", "code"] 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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 True
return False 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): class SimplifiedAccountingType(models.Model):
""" """
@ -520,6 +515,15 @@ class SimplifiedAccountingType(models.Model):
verbose_name = _("simplified type") verbose_name = _("simplified type")
ordering = ["accounting_type__movement_type", "accounting_type__code"] 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 @property
def movement_type(self): def movement_type(self):
return self.accounting_type.movement_type return self.accounting_type.movement_type
@ -527,18 +531,6 @@ class SimplifiedAccountingType(models.Model):
def get_movement_type_display(self): def get_movement_type_display(self):
return self.accounting_type.get_movement_type_display() 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): class Label(models.Model):
"""Label allow a club to sort its operations""" """Label allow a club to sort its operations"""

View File

@ -15,7 +15,7 @@ class Migration(migrations.Migration):
name="owner_group", name="owner_group",
field=models.ForeignKey( field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, 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", related_name="owned_club",
to="core.Group", to="core.Group",
), ),

View File

@ -40,6 +40,11 @@ from core.models import Group, MetaGroup, Notification, Page, RealGroup, SithFil
# Create your models here. # 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): class Club(models.Model):
""" """
The Club class, made as a tree to allow nice tidy organization 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) 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( owner_group = models.ForeignKey(
Group, Group,
related_name="owned_club", related_name="owned_club",
@ -105,6 +106,34 @@ class Club(models.Model):
class Meta: class Meta:
ordering = ["name", "unix_name"] 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 @cached_property
def president(self): def president(self):
return self.members.filter( return self.members.filter(
@ -183,28 +212,6 @@ class Club(models.Model):
self.page.parent = self.parent.page self.page.parent = self.parent.page
self.page.save(force_lock=True) 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): def delete(self, *args, **kwargs):
# Invalidate the cache of this club and of its memberships # Invalidate the cache of this club and of its memberships
for membership in self.members.ongoing().select_related("user"): 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}") cache.delete(f"sith_club_{self.unix_name}")
super().delete(*args, **kwargs) 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): def get_display_name(self):
return self.name return self.name
@ -373,14 +374,21 @@ class Membership(models.Model):
def __str__(self): def __str__(self):
return ( return (
self.club.name f"{self.club.name} - {self.user.username} "
+ " - " f"- {settings.SITH_CLUB_ROLES[self.role]} "
+ self.user.username f"- {str(_('past member')) if self.end_date is not None else ''}"
+ " - "
+ str(settings.SITH_CLUB_ROLES[self.role])
+ str(" - " + 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): def is_owned_by(self, user):
""" """
Method to see if that object can be super edited by the given 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 True
return False 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): def delete(self, *args, **kwargs):
super().delete(*args, **kwargs) super().delete(*args, **kwargs)
cache.delete(f"membership_{self.club_id}_{self.user_id}") cache.delete(f"membership_{self.club_id}_{self.user_id}")
@ -451,6 +449,26 @@ class Mailing(models.Model):
on_delete=models.CASCADE, 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): def clean(self):
if Mailing.objects.filter(email=self.email).exists(): if Mailing.objects.filter(email=self.email).exists():
raise ValidationError(_("This mailing list already exists.")) raise ValidationError(_("This mailing list already exists."))
@ -488,26 +506,6 @@ class Mailing(models.Model):
resp += sub.fetch_format() resp += sub.fetch_format()
return resp 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): class MailingSubscription(models.Model):
""" """
@ -535,6 +533,9 @@ class MailingSubscription(models.Model):
class Meta: class Meta:
unique_together = (("user", "email", "mailing"),) unique_together = (("user", "email", "mailing"),)
def __str__(self):
return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email)
def clean(self): def clean(self):
if not self.user and not self.email: if not self.user and not self.email:
raise ValidationError(_("At least user or email is required")) raise ValidationError(_("At least user or email is required"))
@ -577,6 +578,3 @@ class MailingSubscription(models.Model):
def fetch_format(self): def fetch_format(self):
return self.get_email + " " return self.get_email + " "
def __str__(self):
return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email)

View File

@ -46,14 +46,14 @@ class Sith(models.Model):
weekmail_destinations = models.TextField(_("weekmail destinations"), default="") weekmail_destinations = models.TextField(_("weekmail destinations"), default="")
version = utils.get_git_revision_short_hash() version = utils.get_git_revision_short_hash()
def __str__(self):
return "⛩ Sith ⛩"
def is_owned_by(self, user): def is_owned_by(self, user):
if user.is_anonymous: if user.is_anonymous:
return False return False
return user.is_com_admin return user.is_com_admin
def __str__(self):
return "⛩ Sith ⛩"
NEWS_TYPES = [ NEWS_TYPES = [
("NOTICE", _("Notice")), ("NOTICE", _("Notice")),
@ -90,23 +90,6 @@ class News(models.Model):
on_delete=models.CASCADE, 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): def __str__(self):
return "%s: %s" % (self.type, self.title) return "%s: %s" % (self.type, self.title)
@ -124,6 +107,23 @@ class News(models.Model):
param="1", param="1",
).save() ).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): def news_notification_callback(notif):
count = ( count = (
@ -185,6 +185,9 @@ class Weekmail(models.Model):
class Meta: class Meta:
ordering = ["-id"] ordering = ["-id"]
def __str__(self):
return f"Weekmail {self.id} (sent: {self.sent}) - {self.title}"
def send(self): def send(self):
""" """
Send the weekmail to all users with the receive weekmail option opt-in. 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") 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): def is_owned_by(self, user):
if user.is_anonymous: if user.is_anonymous:
return False return False
@ -273,18 +273,21 @@ class WeekmailArticle(models.Model):
) )
rank = models.IntegerField(_("rank"), default=-1) 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): def is_owned_by(self, user):
if user.is_anonymous: if user.is_anonymous:
return False return False
return user.is_com_admin return user.is_com_admin
def __str__(self):
return "%s - %s (%s)" % (self.title, self.author, self.club)
class Screen(models.Model): class Screen(models.Model):
name = models.CharField(_("name"), max_length=128) name = models.CharField(_("name"), max_length=128)
def __str__(self):
return self.name
def active_posters(self): def active_posters(self):
now = timezone.now() now = timezone.now()
return self.posters.filter(is_moderated=True, date_begin__lte=now).filter( return self.posters.filter(is_moderated=True, date_begin__lte=now).filter(
@ -296,9 +299,6 @@ class Screen(models.Model):
return False return False
return user.is_com_admin return user.is_com_admin
def __str__(self):
return "%s" % (self.name)
class Poster(models.Model): class Poster(models.Model):
name = models.CharField( name = models.CharField(
@ -328,6 +328,9 @@ class Poster(models.Model):
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
def __str__(self):
return self.name
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.is_moderated: if not self.is_moderated:
for u in ( for u in (
@ -360,6 +363,3 @@ class Poster(models.Model):
@property @property
def page(self): def page(self):
return self.club.page return self.club.page
def __str__(self):
return self.name

View File

@ -16,7 +16,7 @@ class Migration(migrations.Migration):
field=models.ForeignKey( field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
verbose_name="owner group", verbose_name="owner group",
default=core.models.Page.get_default_owner_group, default=core.models.get_default_owner_group,
related_name="owned_page", related_name="owned_page",
to="core.Group", to="core.Group",
), ),

View File

@ -845,12 +845,15 @@ class Preferences(models.Model):
_("get a notification for every refilling"), default=False _("get a notification for every refilling"), default=False
) )
def get_display_name(self): def __str__(self):
return self.user.get_display_name() return f"Preferences of {self.user}"
def get_absolute_url(self): def get_absolute_url(self):
return self.user.get_absolute_url() return self.user.get_absolute_url()
def get_display_name(self):
return self.user.get_display_name()
def get_directory(instance, filename): def get_directory(instance, filename):
return ".{0}/{1}".format(instance.get_parent_path(), filename) return ".{0}/{1}".format(instance.get_parent_path(), filename)
@ -928,6 +931,31 @@ class SithFile(models.Model):
class Meta: class Meta:
verbose_name = _("file") 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: def can_be_managed_by(self, user: User) -> bool:
""" """
Tell if the user can manage the file (edit, delete, etc.) or not. 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 == ""): if self.is_file and (self.file is None or self.file == ""):
raise ValidationError(_("You must provide a 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): def apply_rights_recursively(self, *, only_folders=False):
children = self.children.all() children = self.children.all()
if only_folders: if only_folders:
@ -1189,9 +1195,6 @@ class SithFile(models.Model):
def get_download_url(self): def get_download_url(self):
return reverse("core:download", kwargs={"file_id": self.id}) return reverse("core:download", kwargs={"file_id": self.id})
def __str__(self):
return self.get_parent_path() + "/" + self.name
class LockError(Exception): class LockError(Exception):
"""There was a lock error on the object""" """There was a lock error on the object"""
@ -1211,6 +1214,11 @@ class NotLocked(LockError):
pass pass
# This function prevents generating migration upon settings change
def get_default_owner_group():
return settings.SITH_GROUP_ROOT_ID
class Page(models.Model): class Page(models.Model):
""" """
The page class to build a Wiki 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! # playing with a Page object, use get_full_name() instead!
_full_name = models.CharField(_("page name"), max_length=255, blank=True) _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( owner_group = models.ForeignKey(
Group, Group,
related_name="owned_page", related_name="owned_page",
@ -1286,6 +1290,38 @@ class Page(models.Model):
("change_prop_page", "Can change the page's properties (groups, ...)"), ("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 @staticmethod
def get_page_by_full_name(name): def get_page_by_full_name(name):
""" """
@ -1293,9 +1329,6 @@ class Page(models.Model):
""" """
return Page.objects.filter(_full_name=name).first() return Page.objects.filter(_full_name=name).first()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def clean(self): def clean(self):
""" """
Cleans up only the name for the moment, but this can be used to make any treatment before saving the object 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 p = p.parent
return l 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): def is_locked(self):
""" """
Is True if the page is locked, False otherwise Is True if the page is locked, False otherwise
@ -1415,15 +1425,6 @@ class Page(models.Model):
return self.lock_user return self.lock_user
raise NotLocked("The page is not locked and thus can not return its 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): def get_full_name(self):
""" """
Computes the real full_name of the page based on its name and its parent's name 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: class Meta:
ordering = ["date"] 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): def get_absolute_url(self):
""" """
This is needed for black magic powered UpdateView's children This is needed for black magic powered UpdateView's children
""" """
return reverse("core:page", kwargs={"page_name": self.page._full_name}) return reverse("core:page", kwargs={"page_name": self.page._full_name})
def __str__(self):
return str(self.__dict__)
def __getattribute__(self, attr): def __getattribute__(self, attr):
if attr == "owner_group": if attr == "owner_group":
return self.page.owner_group return self.page.owner_group
@ -1504,13 +1512,6 @@ class PageRev(models.Model):
def can_be_edited_by(self, user): def can_be_edited_by(self, user):
return self.page.can_be_edited_by(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): class Notification(models.Model):
user = models.ForeignKey( user = models.ForeignKey(
@ -1529,15 +1530,6 @@ class Notification(models.Model):
return self.get_type_display() % self.param return self.get_type_display() % self.param
return self.get_type_display() 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): def save(self, *args, **kwargs):
if not self.id and self.type in settings.SITH_PERMANENT_NOTIFICATIONS: if not self.id and self.type in settings.SITH_PERMANENT_NOTIFICATIONS:
old_notif = self.user.notifications.filter(type=self.type).last() old_notif = self.user.notifications.filter(type=self.type).last()
@ -1547,6 +1539,15 @@ class Notification(models.Model):
return return
super().save(*args, **kwargs) 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): class Gift(models.Model):
label = models.CharField(_("label"), max_length=255) 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 _("operation type"), max_length=40, choices=settings.SITH_LOG_OPERATION_TYPE
) )
def is_owned_by(self, user):
return user.is_root
def __str__(self): def __str__(self):
return "%s - %s - %s" % (self.operation_type, self.label, self.operator) return "%s - %s - %s" % (self.operation_type, self.label, self.operator)
def is_owned_by(self, user):
return user.is_root

View File

@ -93,7 +93,7 @@ def search_user(query):
return [] return []
def search_club(query,* , as_json=False): def search_club(query, *, as_json=False):
clubs = [] clubs = []
if query: if query:
clubs = Club.objects.filter(name__icontains=query).all() clubs = Club.objects.filter(name__icontains=query).all()

View File

@ -18,7 +18,15 @@ from counter.models import (
class BillingInfoForm(forms.ModelForm): class BillingInfoForm(forms.ModelForm):
class Meta: class Meta:
model = BillingInfo model = BillingInfo
exclude = ["customer"] fields = [
"first_name",
"last_name",
"address_1",
"address_2",
"zip_code",
"city",
"country",
]
class StudentCardForm(forms.ModelForm): class StudentCardForm(forms.ModelForm):

View File

@ -62,6 +62,19 @@ class Customer(models.Model):
def __str__(self): def __str__(self):
return "%s - %s" % (self.user.username, self.account_id) 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 @property
def can_record(self): def can_record(self):
return self.recorded_products > -settings.SITH_ECOCUP_LIMIT 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) account = cls.objects.create(user=user, account_id=account_id)
return account, True 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): def recompute_amount(self):
refillings = self.refillings.aggregate(sum=Sum(F("amount")))["sum"] refillings = self.refillings.aggregate(sum=Sum(F("amount")))["sum"]
self.amount = refillings if refillings is not None else 0 self.amount = refillings if refillings is not None else 0
@ -150,9 +153,6 @@ class Customer(models.Model):
self.amount -= purchases self.amount -= purchases
self.save() self.save()
def get_absolute_url(self):
return reverse("core:user_account", kwargs={"user_id": self.user.pk})
def get_full_url(self): def get_full_url(self):
return "".join(["https://", settings.SITH_URL, self.get_absolute_url()]) 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) city = models.CharField(_("City"), max_length=50)
country = CountryField(blank_label=_("Country")) country = CountryField(blank_label=_("Country"))
def __str__(self):
return f"{self.first_name} {self.last_name}"
def to_3dsv2_xml(self) -> str: def to_3dsv2_xml(self) -> str:
""" """
Convert the data from this model into a xml usable 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) xml = dict2xml(data, wrap="Billing", newlines=False)
return '<?xml version="1.0" encoding="UTF-8" ?>' + xml return '<?xml version="1.0" encoding="UTF-8" ?>' + xml
def __str__(self):
return f"{self.first_name} {self.last_name}"
class ProductType(models.Model): class ProductType(models.Model):
""" """
@ -222,6 +222,12 @@ class ProductType(models.Model):
verbose_name = _("product type") verbose_name = _("product type")
ordering = ["-priority", "name"] 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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 True
return False return False
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("counter:producttype_list")
class Product(models.Model): class Product(models.Model):
""" """
@ -282,6 +282,12 @@ class Product(models.Model):
class Meta: class Meta:
verbose_name = _("product") verbose_name = _("product")
def __str__(self):
return "%s (%s)" % (self.name, self.code)
def get_absolute_url(self):
return reverse("counter:product_list")
@property @property
def is_record_product(self): def is_record_product(self):
return settings.SITH_ECOCUP_CONS == self.id return settings.SITH_ECOCUP_CONS == self.id
@ -302,9 +308,6 @@ class Product(models.Model):
return True return True
return False return False
def get_absolute_url(self):
return reverse("counter:product_list")
def can_be_sold_to(self, user: User) -> bool: def can_be_sold_to(self, user: User) -> bool:
""" """
Check if whether the user given in parameter has the right to buy Check if whether the user given in parameter has the right to buy
@ -329,9 +332,6 @@ class Product(models.Model):
def profit(self): def profit(self):
return self.selling_price - self.purchase_price return self.selling_price - self.purchase_price
def __str__(self):
return "%s (%s)" % (self.name, self.code)
class CounterQuerySet(models.QuerySet): class CounterQuerySet(models.QuerySet):
def annotate_has_barman(self, user: User) -> CounterQuerySet: def annotate_has_barman(self, user: User) -> CounterQuerySet:
@ -388,13 +388,6 @@ class Counter(models.Model):
class Meta: class Meta:
verbose_name = _("counter") 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): def __str__(self):
return self.name return self.name
@ -403,6 +396,13 @@ class Counter(models.Model):
return reverse("eboutic:main") return reverse("eboutic:main")
return reverse("counter:details", kwargs={"counter_id": self.id}) 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): def is_owned_by(self, user):
if user.is_anonymous: if user.is_anonymous:
return False return False
@ -629,16 +629,6 @@ class Refilling(models.Model):
self.customer.user.get_display_name(), 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): def save(self, *args, **kwargs):
if not self.date: if not self.date:
self.date = timezone.now() self.date = timezone.now()
@ -663,6 +653,16 @@ class Refilling(models.Model):
).save() ).save()
super().save(*args, **kwargs) 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): class Selling(models.Model):
""" """
@ -723,59 +723,6 @@ class Selling(models.Model):
self.customer.user.get_display_name(), 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(
(
'<a href="',
self.customer.get_full_url(),
'">',
self.customer.get_full_url(),
"</a>",
)
),
"eticket": "".join(
(
'<a href="',
self.get_eticket_full_url(),
'">',
self.get_eticket_full_url(),
"</a>",
)
),
}
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): def save(self, *args, allow_negative=False, **kwargs):
""" """
allow_negative : Allow this selling to use more money than available for this user allow_negative : Allow this selling to use more money than available for this user
@ -858,6 +805,59 @@ class Selling(models.Model):
except: except:
pass 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(
(
'<a href="',
self.customer.get_full_url(),
'">',
self.customer.get_full_url(),
"</a>",
)
),
"eticket": "".join(
(
'<a href="',
self.get_eticket_full_url(),
'">',
self.get_eticket_full_url(),
"</a>",
)
),
}
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): def get_eticket_full_url(self):
eticket_url = reverse("counter:eticket_pdf", kwargs={"selling_id": self.id}) eticket_url = reverse("counter:eticket_pdf", kwargs={"selling_id": self.id})
return "".join(["https://", settings.SITH_URL, eticket_url]) return "".join(["https://", settings.SITH_URL, eticket_url])
@ -926,6 +926,14 @@ class CashRegisterSummary(models.Model):
def __str__(self): def __str__(self):
return "At %s by %s - Total: %s" % (self.counter, self.user, self.get_total()) 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): def __getattribute__(self, name):
if name[:5] == "check": if name[:5] == "check":
checks = self.items.filter(check=True).order_by("value").all() checks = self.items.filter(check=True).order_by("value").all()
@ -978,14 +986,6 @@ class CashRegisterSummary(models.Model):
t += it.quantity * it.value t += it.quantity * it.value
return t 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): class CashRegisterSummaryItem(models.Model):
cash_summary = models.ForeignKey( cash_summary = models.ForeignKey(
@ -1005,6 +1005,9 @@ class CashRegisterSummaryItem(models.Model):
class Meta: class Meta:
verbose_name = _("cash register summary item") verbose_name = _("cash register summary item")
def __str__(self):
return str(self.value)
class Eticket(models.Model): class Eticket(models.Model):
""" """
@ -1027,16 +1030,16 @@ class Eticket(models.Model):
secret = models.CharField(_("secret"), max_length=64, unique=True) secret = models.CharField(_("secret"), max_length=64, unique=True)
def __str__(self): def __str__(self):
return "%s" % (self.product.name) return self.product.name
def get_absolute_url(self):
return reverse("counter:eticket_list")
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.id: if not self.id:
self.secret = base64.b64encode(os.urandom(32)) self.secret = base64.b64encode(os.urandom(32))
return super().save(*args, **kwargs) return super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("counter:eticket_list")
def is_owned_by(self, user): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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_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 @staticmethod
def is_valid(uid): def is_valid(uid):
return ( return (
@ -1080,15 +1098,3 @@ class StudentCard(models.Model):
if isinstance(obj, User): if isinstance(obj, User):
return StudentCard.can_create(self.customer, obj) return StudentCard.can_create(self.customer, obj)
return False 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,
)

View File

@ -56,6 +56,9 @@ class Basket(models.Model):
) )
date = models.DateTimeField(_("date"), auto_now=True) 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): def add_product(self, p: Product, q: int = 1):
""" """
Given p an object of the Product model and q an integer, 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())) data.append(("PBX_HMAC", pbx_hmac.hexdigest().upper()))
return data return data
def __str__(self):
return "%s's basket (%d items)" % (self.user, self.items.all().count())
class Invoice(models.Model): class Invoice(models.Model):
""" """
@ -226,6 +226,9 @@ class Invoice(models.Model):
date = models.DateTimeField(_("date"), auto_now=True) date = models.DateTimeField(_("date"), auto_now=True)
validated = models.BooleanField(_("validated"), default=False) validated = models.BooleanField(_("validated"), default=False)
def __str__(self):
return f"{self.user} - {self.get_total()} - {self.date}"
def get_total(self) -> float: def get_total(self) -> float:
total = self.items.aggregate( total = self.items.aggregate(
total=Sum(F("quantity") * F("product_unit_price")) total=Sum(F("quantity") * F("product_unit_price"))
@ -268,9 +271,6 @@ class Invoice(models.Model):
self.validated = True self.validated = True
self.save() self.save()
def __str__(self):
return "%s - %s - %s" % (self.user, self.get_total(), self.date)
class AbstractBaseItem(models.Model): class AbstractBaseItem(models.Model):
product_id = models.IntegerField(_("product id")) product_id = models.IntegerField(_("product id"))

View File

@ -163,16 +163,16 @@ class ElectionList(models.Model):
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
def __str__(self):
return self.title
def can_be_edited_by(self, user): def can_be_edited_by(self, user):
return user.can_edit(self.election) return user.can_edit(self.election)
def delete(self): def delete(self, *args, **kwargs):
for candidature in self.candidatures.all(): for candidature in self.candidatures.all():
candidature.delete() candidature.delete()
super().delete() super().delete(*args, **kwargs)
def __str__(self):
return self.title
class Candidature(models.Model): class Candidature(models.Model):
@ -201,6 +201,9 @@ class Candidature(models.Model):
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
def __str__(self):
return f"{self.role.title} : {self.user.username}"
def delete(self): def delete(self):
for vote in self.votes.all(): for vote in self.votes.all():
vote.delete() vote.delete()
@ -209,9 +212,6 @@ class Candidature(models.Model):
def can_be_edited_by(self, user): def can_be_edited_by(self, user):
return (user == self.user) or user.can_edit(self.role.election) 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): class Vote(models.Model):
""" """

View File

@ -14,7 +14,7 @@ class Migration(migrations.Migration):
name="edit_groups", name="edit_groups",
field=models.ManyToManyField( field=models.ManyToManyField(
blank=True, blank=True,
default=forum.models.Forum.get_default_edit_group, default=forum.models.get_default_edit_group,
related_name="editable_forums", related_name="editable_forums",
to="core.Group", to="core.Group",
), ),
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
name="view_groups", name="view_groups",
field=models.ManyToManyField( field=models.ManyToManyField(
blank=True, blank=True,
default=forum.models.Forum.get_default_view_group, default=forum.models.get_default_view_group,
related_name="viewable_forums", related_name="viewable_forums",
to="core.Group", to="core.Group",
), ),

View File

@ -20,6 +20,7 @@
# Place - Suite 330, Boston, MA 02111-1307, USA. # Place - Suite 330, Boston, MA 02111-1307, USA.
# #
# #
from __future__ import annotations
from datetime import datetime from datetime import datetime
from itertools import chain from itertools import chain
@ -37,6 +38,15 @@ from club.models import Club
from core.models import Group, User 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): class Forum(models.Model):
""" """
The Forum class, made as a tree to allow nice tidy organization 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 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) id = models.AutoField(primary_key=True, db_index=True)
name = models.CharField(_("name"), max_length=64) name = models.CharField(_("name"), max_length=64)
description = models.CharField(_("description"), max_length=512, default="") description = models.CharField(_("description"), max_length=512, default="")
@ -98,8 +101,8 @@ class Forum(models.Model):
class Meta: class Meta:
ordering = ["number"] ordering = ["number"]
def clean(self): def __str__(self):
self.check_loop() return self.name
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
copy_rights = False copy_rights = False
@ -109,6 +112,12 @@ class Forum(models.Model):
if copy_rights: if copy_rights:
self.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): def set_topic_number(self):
self._topic_number = self.get_topic_number() self._topic_number = self.get_topic_number()
self.save() self.save()
@ -166,11 +175,11 @@ class Forum(models.Model):
return True return True
try: try:
m = Forum._club_memberships[self.id][user.id] m = Forum._club_memberships[self.id][user.id]
except: except KeyError:
m = self.owner_club.get_membership_for(user) m = self.owner_club.get_membership_for(user)
try: try:
Forum._club_memberships[self.id][user.id] = m Forum._club_memberships[self.id][user.id] = m
except: except KeyError:
Forum._club_memberships[self.id] = {} Forum._club_memberships[self.id] = {}
Forum._club_memberships[self.id][user.id] = m Forum._club_memberships[self.id][user.id] = m
if m: if m:
@ -187,9 +196,6 @@ class Forum(models.Model):
objs.append(cur) objs.append(cur)
cur = cur.parent cur = cur.parent
def __str__(self):
return "%s" % (self.name)
def get_full_name(self): def get_full_name(self):
return "/".join( return "/".join(
chain.from_iterable( 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 @cached_property
def parent_list(self): def parent_list(self):
return self.get_parent_list() return self.get_parent_list()
@ -256,11 +259,17 @@ class ForumTopic(models.Model):
class Meta: class Meta:
ordering = ["-_last_message__date"] ordering = ["-_last_message__date"]
def __str__(self):
return self.title
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
self.forum.set_topic_number() # Recompute the cached value self.forum.set_topic_number() # Recompute the cached value
self.forum.set_last_message() 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): def is_owned_by(self, user):
return self.forum.is_owned_by(user) return self.forum.is_owned_by(user)
@ -270,23 +279,15 @@ class ForumTopic(models.Model):
def can_be_viewed_by(self, user): def can_be_viewed_by(self, user):
return user.can_view(self.forum) return user.can_view(self.forum)
def __str__(self): def get_first_unread_message(self, user: User) -> ForumMessage | None:
return "%s" % (self.title) if not hasattr(user, "forum_infos"):
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:
return None return None
return (
self.messages.exclude(readers=user)
.filter(date__gte=user.forum_infos.last_read_date)
.order_by("id")
.first()
)
@cached_property @cached_property
def last_message(self): def last_message(self):
@ -332,6 +333,9 @@ class ForumMessage(models.Model):
self.topic._message_number = self.topic.messages.count() self.topic._message_number = self.topic.messages.count()
self.topic.save() self.topic.save()
def get_absolute_url(self):
return reverse("forum:view_message", kwargs={"message_id": self.id})
def is_first_in_topic(self): def is_first_in_topic(self):
return bool(self.id == self.topic.messages.order_by("date").first().id) 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): def can_be_moderated_by(self, user):
return self.topic.forum.is_owned_by(user) or user.id == self.author.id 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): def get_url(self):
return ( return (
self.topic.get_absolute_url() self.topic.get_absolute_url()
@ -378,11 +379,10 @@ class ForumMessage(models.Model):
) )
def mark_as_read(self, user): def mark_as_read(self, user):
try: # Need the try/except because of AnonymousUser if user.is_anonymous:
if not self.is_read(user): return
self.readers.add(user) if not self.is_read(user):
except: self.readers.add(user)
pass
def is_read(self, user): def is_read(self, user):
return (self.date < user.forum_infos.last_read_date) or ( 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) date = models.DateTimeField(_("date"), default=timezone.now)
action = models.CharField(_("action"), choices=MESSAGE_META_ACTIONS, max_length=16) 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): def save(self, *args, **kwargs):
super().save(*args, **kwargs) super().save(*args, **kwargs)
self.message._deleted = self.message.is_deleted() self.message._deleted = self.message.is_deleted()

View File

@ -127,6 +127,9 @@ class GalaxyLane(models.Model):
default=0, default=0,
) )
def __str__(self):
return f"{self.star1} -> {self.star2} ({self.distance})"
class StarDict(TypedDict): class StarDict(TypedDict):
id: int id: int

View File

@ -37,6 +37,12 @@ class Launderette(models.Model):
class Meta: class Meta:
verbose_name = _("Launderette") 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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): def can_be_viewed_by(self, user):
return user.is_subscribed 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): def get_machine_list(self):
return Machine.objects.filter(launderette_id=self.id) return Machine.objects.filter(launderette_id=self.id)
@ -98,6 +98,15 @@ class Machine(models.Model):
class Meta: class Meta:
verbose_name = _("Machine") 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): def is_owned_by(self, user):
""" """
Method to see if that object can be edited by the given 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 True
return False 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): class Token(models.Model):
name = models.CharField(_("name"), max_length=5) name = models.CharField(_("name"), max_length=5)
@ -148,6 +148,12 @@ class Token(models.Model):
unique_together = ("name", "launderette", "type") unique_together = ("name", "launderette", "type")
ordering = ["type", "name"] 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): def save(self, *args, **kwargs):
if self.name == "": if self.name == "":
raise DataError(_("Token name can not be blank")) raise DataError(_("Token name can not be blank"))
@ -168,18 +174,6 @@ class Token(models.Model):
return True return True
return False return False
def __str__(self):
return (
self.__class__._meta.verbose_name
+ " "
+ self.get_type_display()
+ " #"
+ self.name
+ " ("
+ self.launderette.name
+ ")"
)
def is_avaliable(self): def is_avaliable(self):
if not self.borrow_date and not self.user: if not self.borrow_date and not self.user:
return True return True
@ -214,9 +208,6 @@ class Slot(models.Model):
verbose_name = _("Slot") verbose_name = _("Slot")
ordering = ["start_date"] ordering = ["start_date"]
def is_owned_by(self, user):
return user == self.user
def __str__(self): def __str__(self):
return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % ( return "User: %s - Date: %s - Type: %s - Machine: %s - Token: %s" % (
self.user, self.user,
@ -225,3 +216,6 @@ class Slot(models.Model):
self.machine.name, self.machine.name,
self.token, self.token,
) )
def is_owned_by(self, user):
return user == self.user

View File

@ -141,6 +141,12 @@ class UV(models.Model):
default=0, 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): def is_owned_by(self, user):
""" """
Can be created by superuser, root or pedagogy admin 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()) 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): def has_user_already_commented(self, user):
""" """
Help prevent multiples comments from the same user Help prevent multiples comments from the same user
@ -194,9 +197,6 @@ class UV(models.Model):
def grade_work_load_average(self): def grade_work_load_average(self):
return self.__grade_average_generic("grade_work_load") return self.__grade_average_generic("grade_work_load")
def __str__(self):
return self.code
class UVComment(models.Model): class UVComment(models.Model):
""" """
@ -252,6 +252,14 @@ class UVComment(models.Model):
) )
publish_date = models.DateTimeField(_("publish date"), blank=True) 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): def is_owned_by(self, user):
""" """
Is owned by a pedagogy admin, a superuser or the author himself Is owned by a pedagogy admin, a superuser or the author himself
@ -265,14 +273,6 @@ class UVComment(models.Model):
""" """
return self.reports.exists() 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): class UVResult(models.Model):
""" """
@ -303,6 +303,9 @@ class UVResult(models.Model):
validators=[validators.RegexValidator("[AP][0-9]{3}")], validators=[validators.RegexValidator("[AP][0-9]{3}")],
) )
def __str__(self):
return f"{self.user.username} ; {self.uv.code} ; {self.grade}"
class UVCommentReport(models.Model): class UVCommentReport(models.Model):
""" """
@ -323,6 +326,9 @@ class UVCommentReport(models.Model):
) )
reason = models.TextField(_("reason")) reason = models.TextField(_("reason"))
def __str__(self):
return f"{self.reporter.username} : {self.reason}"
@cached_property @cached_property
def uv(self): def uv(self):
return self.comment.uv return self.comment.uv

View File

@ -70,7 +70,19 @@ optional = true
version = "1.4.25" version = "1.4.25"
[tool.ruff.lint] [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] [tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "sith.settings" DJANGO_SETTINGS_MODULE = "sith.settings"

View File

@ -149,10 +149,10 @@ class ShoppingListItem(models.Model):
) )
def __str__(self): def __str__(self):
return "%s - %s" % (self.name, self.shopping_lists.first()) return f"{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)
def get_absolute_url(self): def get_absolute_url(self):
return reverse("stock:shoppinglist_list") return reverse("stock:shoppinglist_list")
def can_be_viewed_by(self, user):
return user.is_in_group(pk=settings.SITH_GROUP_COUNTER_ADMIN_ID)

View File

@ -22,6 +22,7 @@ from django.contrib.auth.forms import PasswordResetForm
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.models import User from core.models import User
@ -65,26 +66,11 @@ class Subscription(models.Model):
class Meta: class Meta:
ordering = ["subscription_start"] ordering = ["subscription_start"]
def clean(self): def __str__(self):
try: if hasattr(self, "member") and self.member is not None:
for s in ( return f"{self.member.username} - {self.pk}"
Subscription.objects.filter(member=self.member) else:
.exclude(pk=self.pk) return f"No user - {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 save(self, *args, **kwargs): def save(self, *args, **kwargs):
super().save() super().save()
@ -105,11 +91,20 @@ class Subscription(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse("core:user_edit", kwargs={"user_id": self.member.pk}) return reverse("core:user_edit", kwargs={"user_id": self.member.pk})
def __str__(self): def clean(self):
if hasattr(self, "member") and self.member is not None: today = timezone.now().date()
return self.member.username + " - " + str(self.pk) active_subscriptions = Subscription.objects.exclude(pk=self.pk).filter(
else: subscription_start__gte=today, subscription_end__lte=today
return "No user - " + str(self.pk) )
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 @staticmethod
def compute_start(d: date = None, duration: int = 1, user: User = None) -> date: def compute_start(d: date = None, duration: int = 1, user: User = None) -> date:

View File

@ -83,6 +83,9 @@ class Trombi(models.Model):
def __str__(self): def __str__(self):
return str(self.club.name) return str(self.club.name)
def get_absolute_url(self):
return reverse("trombi:detail", kwargs={"trombi_id": self.id})
def clean(self): def clean(self):
if self.subscription_deadline > self.comments_deadline: if self.subscription_deadline > self.comments_deadline:
raise ValidationError( 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): def is_owned_by(self, user):
return user.can_edit(self.club) return user.can_edit(self.club)
@ -192,6 +192,9 @@ class TrombiComment(models.Model):
content = models.TextField(_("content"), default="") content = models.TextField(_("content"), default="")
is_moderated = models.BooleanField(_("is the comment moderated"), default=False) 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): def can_be_viewed_by(self, user):
if user.id == self.target.user.id: if user.id == self.target.user.id:
return False return False
@ -220,8 +223,8 @@ class TrombiClubMembership(models.Model):
def __str__(self): def __str__(self):
return "%s - %s - %s (%s)" % (self.user, self.club, self.role, self.start) 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): def get_absolute_url(self):
return reverse("trombi:profile") 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)