Skip to content

Commit ae16a1b

Browse files
authored
Merge pull request #821 from ae-utbm/taiste
Python upgrade and bugfixes
2 parents 878ee99 + e2b4214 commit ae16a1b

File tree

18 files changed

+561
-516
lines changed

18 files changed

+561
-516
lines changed

.github/actions/setup_project/action.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ runs:
1616
shell: bash
1717

1818
- name: Set up python
19-
uses: actions/setup-python@v4
19+
uses: actions/setup-python@v5
2020
with:
21-
python-version: "3.10"
21+
python-version: "3.12"
2222

2323
- name: Load cached Poetry installation
2424
id: cached-poetry
2525
uses: actions/cache@v3
2626
with:
2727
path: ~/.local
28-
key: poetry-0 # increment to reset cache
28+
key: poetry-1 # increment to reset cache
2929

3030
- name: Install Poetry
3131
if: steps.cached-poetry.outputs.cache-hit != 'true'

.github/workflows/deploy.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ jobs:
3636
export PATH="/home/sith/.local/bin:$PATH"
3737
pushd ${{secrets.SITH_PATH}}
3838
39-
git pull
39+
git fetch
40+
git reset --hard origin/master
4041
poetry install --with prod --without docs,tests
4142
poetry run ./manage.py install_xapian
4243
poetry run ./manage.py migrate

.github/workflows/deploy_docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
deploy:
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v3
12+
- uses: actions/checkout@v4
1313
- uses: ./.github/actions/setup_project
1414
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
1515
- uses: actions/cache@v3

.github/workflows/taiste.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ jobs:
3535
export PATH="$HOME/.poetry/bin:$PATH"
3636
pushd ${{secrets.SITH_PATH}}
3737
38-
git pull
38+
git fetch
39+
git reset --hard origin/taiste
3940
poetry install --with prod --without docs,tests
4041
poetry run ./manage.py install_xapian
4142
poetry run ./manage.py migrate

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
33
# Ruff version.
4-
rev: v0.5.5
4+
rev: v0.5.7
55
hooks:
66
- id: ruff # just check the code, and print the errors
77
- id: ruff # actually fix the fixable errors, but print nothing

club/models.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#
2424
from __future__ import annotations
2525

26+
from typing import Self
27+
2628
from django.conf import settings
2729
from django.core import validators
2830
from django.core.cache import cache
@@ -265,12 +267,11 @@ def has_rights_in_club(self, user):
265267

266268

267269
class MembershipQuerySet(models.QuerySet):
268-
def ongoing(self) -> "MembershipQuerySet":
270+
def ongoing(self) -> Self:
269271
"""Filter all memberships which are not finished yet."""
270-
# noinspection PyTypeChecker
271-
return self.filter(Q(end_date=None) | Q(end_date__gte=timezone.now()))
272+
return self.filter(Q(end_date=None) | Q(end_date__gt=timezone.now().date()))
272273

273-
def board(self) -> "MembershipQuerySet":
274+
def board(self) -> Self:
274275
"""Filter all memberships where the user is/was in the board.
275276
276277
Be aware that users who were in the board in the past
@@ -279,7 +280,6 @@ def board(self) -> "MembershipQuerySet":
279280
If you want to get the users who are currently in the board,
280281
mind combining this with the :meth:`ongoing` queryset method
281282
"""
282-
# noinspection PyTypeChecker
283283
return self.filter(role__gt=settings.SITH_MAXIMUM_FREE_ROLE)
284284

285285
def update(self, **kwargs):

club/tests.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
from club.forms import MailingForm
2626
from club.models import Club, Mailing, Membership
27+
from core.baker_recipes import subscriber_user
2728
from core.models import AnonymousUser, User
2829
from sith.settings import SITH_BAR_MANAGER, SITH_MAIN_CLUB_ID
2930

@@ -106,6 +107,18 @@ def test_ongoing(self):
106107
expected.sort(key=lambda i: i.id)
107108
assert current_members == expected
108109

110+
def test_ongoing_with_membership_ending_today(self):
111+
"""Test that a membership ending the present day is considered as ended."""
112+
today = timezone.now().date()
113+
self.richard.memberships.filter(club=self.club).update(end_date=today)
114+
current_members = list(self.club.members.ongoing().order_by("id"))
115+
expected = [
116+
self.skia.memberships.get(club=self.club),
117+
self.comptable.memberships.get(club=self.club),
118+
]
119+
expected.sort(key=lambda i: i.id)
120+
assert current_members == expected
121+
109122
def test_board(self):
110123
"""Test that the board queryset method returns the memberships
111124
of user in the club board.
@@ -422,11 +435,11 @@ def test_end_membership_as_main_club_board(self):
422435
of anyone.
423436
"""
424437
# make subscriber a board member
425-
self.subscriber.memberships.all().delete()
426-
Membership.objects.create(club=self.ae, user=self.subscriber, role=3)
438+
subscriber = subscriber_user.make()
439+
Membership.objects.create(club=self.ae, user=subscriber, role=3)
427440

428-
nb_memberships = self.club.members.count()
429-
self.client.force_login(self.subscriber)
441+
nb_memberships = self.club.members.ongoing().count()
442+
self.client.force_login(subscriber)
430443
response = self.client.post(
431444
self.members_url,
432445
{"users_old": self.comptable.id},
@@ -437,7 +450,7 @@ def test_end_membership_as_main_club_board(self):
437450

438451
def test_end_membership_as_root(self):
439452
"""Test that root users can end the membership of anyone."""
440-
nb_memberships = self.club.members.count()
453+
nb_memberships = self.club.members.ongoing().count()
441454
self.client.force_login(self.root)
442455
response = self.client.post(
443456
self.members_url,
@@ -446,7 +459,6 @@ def test_end_membership_as_root(self):
446459
self.assertRedirects(response, self.members_url)
447460
self.assert_membership_ended_today(self.comptable)
448461
assert self.club.members.ongoing().count() == nb_memberships - 1
449-
assert self.club.members.count() == nb_memberships
450462

451463
def test_end_membership_as_foreigner(self):
452464
"""Test that users who are not in this club cannot end its memberships."""

core/management/commands/install_xapian.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ tar xf "${BINDINGS}.tar.xz"
3232
# install
3333
echo "Installing Xapian-core..."
3434
cd "$VIRTUAL_ENV/packages/${CORE}" || exit 1
35-
./configure --prefix="$VIRTUAL_ENV" && make && make install
35+
./configure --prefix="$VIRTUAL_ENV" && make -j"$(nproc)" && make install
3636

3737
PYTHON_FLAG=--with-python3
3838

3939
echo "Installing Xapian-bindings..."
4040
cd "$VIRTUAL_ENV/packages/${BINDINGS}" || exit 1
41-
./configure --prefix="$VIRTUAL_ENV" $PYTHON_FLAG XAPIAN_CONFIG="$VIRTUAL_ENV/bin/xapian-config" && make && make install
41+
./configure --prefix="$VIRTUAL_ENV" $PYTHON_FLAG XAPIAN_CONFIG="$VIRTUAL_ENV/bin/xapian-config" && make -j"$(nproc)" && make install
4242

4343
# clean
4444
rm -rf "$VIRTUAL_ENV/packages"

core/models.py

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -944,40 +944,15 @@ def save(self, *args, **kwargs):
944944
param="1",
945945
).save()
946946

947-
def can_be_managed_by(self, user: User) -> bool:
948-
"""Tell if the user can manage the file (edit, delete, etc.) or not.
949-
Apply the following rules:
950-
- If the file is not in the SAS nor in the profiles directory, it can be "managed" by anyone -> return True
951-
- If the file is in the SAS, only the SAS admins (or roots) can manage it -> return True if the user is in the SAS admin group or is a root
952-
- If the file is in the profiles directory, only the roots can manage it -> return True if the user is a root.
953-
954-
Returns:
955-
True if the file is managed by the SAS or within the profiles directory, False otherwise
956-
"""
957-
# If the file is not in the SAS nor in the profiles directory, it can be "managed" by anyone
958-
profiles_dir = SithFile.objects.filter(name="profiles").first()
959-
if not self.is_in_sas and not profiles_dir in self.get_parent_list():
960-
return True
961-
962-
# If the file is in the SAS, only the SAS admins (or roots) can manage it
963-
if self.is_in_sas and (
964-
user.is_in_group(settings.SITH_GROUP_SAS_ADMIN_ID) or user.is_root
965-
):
966-
return True
967-
968-
# If the file is in the profiles directory, only the roots can manage it
969-
if profiles_dir in self.get_parent_list() and (
970-
user.is_root or user.is_board_member
971-
):
972-
return True
973-
974-
return False
975-
976947
def is_owned_by(self, user):
977948
if user.is_anonymous:
978949
return False
979-
if hasattr(self, "profile_of") and user.is_board_member:
950+
if user.is_root:
980951
return True
952+
if hasattr(self, "profile_of"):
953+
# if the `profile_of` attribute is set, this file is a profile picture
954+
# and profile pictures may only be edited by board members
955+
return user.is_board_member
981956
if user.is_com_admin:
982957
return True
983958
if self.is_in_sas and user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID):
@@ -993,7 +968,7 @@ def can_be_viewed_by(self, user):
993968
return user.can_view(self.scrub_of)
994969
return False
995970

996-
def delete(self):
971+
def delete(self, *args, **kwargs):
997972
for c in self.children.all():
998973
c.delete()
999974
self.file.delete()

core/views/files.py

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,6 @@ class FileEditView(CanEditMixin, UpdateView):
190190
template_name = "core/file_edit.jinja"
191191
context_object_name = "file"
192192

193-
def get(self, request, *args, **kwargs):
194-
self.object = self.get_object()
195-
if not self.object.can_be_managed_by(request.user):
196-
raise PermissionDenied
197-
198-
return super().get(request, *args, **kwargs)
199-
200193
def get_form_class(self):
201194
fields = ["name", "is_moderated"]
202195
if self.object.is_file:
@@ -242,13 +235,6 @@ class FileEditPropView(CanEditPropMixin, UpdateView):
242235
context_object_name = "file"
243236
form_class = FileEditPropForm
244237

245-
def get(self, request, *args, **kwargs):
246-
self.object = self.get_object()
247-
if not self.object.can_be_managed_by(request.user):
248-
raise PermissionDenied
249-
250-
return super().get(request, *args, **kwargs)
251-
252238
def get_form(self, form_class=None):
253239
form = super().get_form(form_class)
254240
form.fields["parent"].queryset = SithFile.objects.filter(is_folder=True)
@@ -322,9 +308,6 @@ def handle_clipboard(request, obj):
322308

323309
def get(self, request, *args, **kwargs):
324310
self.form = self.get_form()
325-
if not self.object.can_be_managed_by(request.user):
326-
raise PermissionDenied
327-
328311
if "clipboard" not in request.session.keys():
329312
request.session["clipboard"] = []
330313
return super().get(request, *args, **kwargs)
@@ -372,13 +355,6 @@ class FileDeleteView(CanEditPropMixin, DeleteView):
372355
template_name = "core/file_delete_confirm.jinja"
373356
context_object_name = "file"
374357

375-
def get(self, request, *args, **kwargs):
376-
self.object = self.get_object()
377-
if not self.object.can_be_managed_by(request.user):
378-
raise PermissionDenied
379-
380-
return super().get(request, *args, **kwargs)
381-
382358
def get_success_url(self):
383359
self.object.file.delete() # Doing it here or overloading delete() is the same, so let's do it here
384360
if "next" in self.request.GET.keys():
@@ -416,6 +392,7 @@ class FileModerateView(CanEditPropMixin, SingleObjectMixin):
416392
model = SithFile
417393
pk_url_kwarg = "file_id"
418394

395+
# FIXME : wrong http method. This should be a POST or a DELETE request
419396
def get(self, request, *args, **kwargs):
420397
self.object = self.get_object()
421398
self.object.is_moderated = True

core/views/user.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,8 @@ class UserListView(ListView, CanEditPropMixin):
561561
template_name = "core/user_list.jinja"
562562

563563

564+
# FIXME: the edit_once fields aren't displayed to the user (as expected).
565+
# However, if the user re-add them manually in the form, they are saved.
564566
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
565567
"""Edit a user's profile."""
566568

counter/models.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,6 @@ def annotate_has_barman(self, user: User) -> CounterQuerySet:
362362
```
363363
"""
364364
subquery = user.counters.filter(pk=OuterRef("pk"))
365-
# noinspection PyTypeChecker
366365
return self.annotate(has_annotated_barman=Exists(subquery))
367366

368367

0 commit comments

Comments
 (0)