diff --git a/.github/workflows/django-test.yml b/.github/workflows/django-test.yml index 0412bc7b..2ddbc21e 100644 --- a/.github/workflows/django-test.yml +++ b/.github/workflows/django-test.yml @@ -21,6 +21,7 @@ jobs: run: python -m pip install --upgrade pip - name: Install dependencies run: | + sudo apt update sudo apt install libsasl2-dev python3-dev libldap2-dev libssl-dev sudo locale-gen sv_FI.UTF-8 sudo update-locale diff --git a/teknologr/api/serializers.py b/teknologr/api/serializers.py index 86a5aa29..eb95ac9d 100644 --- a/teknologr/api/serializers.py +++ b/teknologr/api/serializers.py @@ -33,7 +33,7 @@ def get_minimal_id_name(self, instance): return {'id': instance.id, 'name': instance.name} def get_minimal_member(self, member): - return {'id': member.id, 'name': member.name if self.is_staff else member.public_full_name} + return {'id': member.id, 'name': member.full_name if self.is_staff else member.public_full_name} # Members diff --git a/teknologr/api/tests.py b/teknologr/api/tests.py index 149022d8..5e53cad5 100644 --- a/teknologr/api/tests.py +++ b/teknologr/api/tests.py @@ -124,7 +124,7 @@ def post(self): return self.client.post(self.api_path, self.post_data) def check_status_code(self, response, status_code): - self.assertEqual(response.status_code, status_code, response.data) + self.assertEqual(response.status_code, status_code, response.data if 'data' in response else None) ''' diff --git a/teknologr/katalogen/templates/year.html b/teknologr/katalogen/templates/year.html index b7b1e6d9..5c216316 100644 --- a/teknologr/katalogen/templates/year.html +++ b/teknologr/katalogen/templates/year.html @@ -19,7 +19,11 @@ Postinnehavare - {{ functionaries|length }} ({{ functionaries_unique_count }}) + {{ functionaries|length }} + {% if functionaries_unique_count %} + ({{ functionaries_unique_count }}) + {% endif %} + Grupper @@ -27,7 +31,11 @@ Gruppmedlemmar - {{ group_memberships_total }} ({{ group_memberships_unique }}) + {{ group_memberships_total|default:0 }} + {% if group_memberships_unique %} + ({{ group_memberships_unique }}) + {% endif %} + Nya ordinarie medlemmar diff --git a/teknologr/katalogen/tests.py b/teknologr/katalogen/tests_utils.py similarity index 100% rename from teknologr/katalogen/tests.py rename to teknologr/katalogen/tests_utils.py diff --git a/teknologr/katalogen/tests_views.py b/teknologr/katalogen/tests_views.py new file mode 100644 index 00000000..906e0a60 --- /dev/null +++ b/teknologr/katalogen/tests_views.py @@ -0,0 +1,91 @@ + +from rest_framework import status +from api.tests import BaseAPITest + +class GetPageTests(): + def test_get_for_anonymous_users(self): + response = self.get_all() + self.check_status_code(response, status.HTTP_302_FOUND) + self.assertTrue(response.url.startswith('/login/'), response.url) + + def test_get_for_user(self): + self.login_user() + response = self.get_all() + self.check_status_code(response, status.HTTP_200_OK) + + +class HomeViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '' + + +class MembersSearchViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/search/?q=test' + +class MembersStartsWithViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/A/' + +class MemberViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/members/{self.m1.id}/' + + +class DecorationOwnershipsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/decorations/{self.d.id}/' + + +class DecorationsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/decorations/' + +class DecorationOwnershipsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/decorations/{self.d.id}/' + + +class FunctionaryTypesViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/functionaries/' + +class FunctionariesViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/functionaries/{self.ft.id}/' + + +class GroupTypesViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/groups/' + +class GroupsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/groups/{self.gt.id}/' + + +class YearsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/years/' + +class YearViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/years/2023/' + +class Year0ViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/years/0/' diff --git a/teknologr/katalogen/views.py b/teknologr/katalogen/views.py index 27eefdc9..90554f3b 100644 --- a/teknologr/katalogen/views.py +++ b/teknologr/katalogen/views.py @@ -238,6 +238,12 @@ def add(obj, key, count_key=None): @login_required def year(request, year): + if int(year) <= 0: + return render(request, 'year.html', { + **_get_base_context(request), + 'year': year, + }) + ''' This could be enhanced, but curretnly it is done with 10 queries: 1. SELECT Functionary WHERE correct_year => COUNT diff --git a/teknologr/members/lookups.py b/teknologr/members/lookups.py index c6263699..27204486 100644 --- a/teknologr/members/lookups.py +++ b/teknologr/members/lookups.py @@ -18,8 +18,7 @@ def get_result(self, obj): def format_match(self, obj): """ (HTML) formatted item for display in the dropdown """ - preferred_name = obj.get_preferred_name() - return f'{obj.given_names.replace(preferred_name, f"{preferred_name}", 1)} {obj.surname}' + return obj.get_full_name_HTML() def format_item_display(self, obj): """ (HTML) formatted item for displaying item in the selected deck area """ diff --git a/teknologr/members/models.py b/teknologr/members/models.py index b1482a92..b42ceda1 100644 --- a/teknologr/members/models.py +++ b/teknologr/members/models.py @@ -4,8 +4,10 @@ from django.db.models import Q, Prefetch, Count from django_countries.fields import CountryField from django.shortcuts import get_object_or_404 +from django.utils.html import format_html from locale import strxfrm, strcoll from operator import attrgetter +from datetime import date from katalogen.utils import * from members.utils import * @@ -143,10 +145,12 @@ def get_surname_without_prefixes(self): surname = self.surname return surname.lstrip('abcdefghijklmnopqrstuvwxyzåäö ') or surname.split()[-1] - # Used for the side bar among other things - @property - def name(self): - return self.full_name + def get_full_name_HTML(self): + ''' + Return name with the preferred name undercored. This can go wrong if the preferred name is not set correctly to one of the given names. + ''' + preferred_name = self.get_preferred_name() + return format_html(f'{self.given_names.replace(preferred_name, f"{preferred_name}", 1)} {self.surname}') @property def common_name(self): @@ -420,7 +424,7 @@ def year(self, year): queryset=GroupMembership.objects.select_related('member') ), 'grouptype' - ).filter(begin_date__lte=datetime.date(int(year), 12, 31), end_date__gte=datetime.date(int(year), 1, 1), num_members__gt=0) + ).filter(begin_date__lte=date(int(year), 12, 31), end_date__gte=date(int(year), 1, 1), num_members__gt=0) def year_ordered_and_counts(self, year): queryset = self.year(year) @@ -537,7 +541,7 @@ def all_with_related(self): return self.get_queryset().select_related('functionarytype', 'member') def year(self, year): - return self.all_with_related().filter(begin_date__lte=datetime.date(int(year), 12, 31), end_date__gte=datetime.date(int(year), 1, 1)) + return self.all_with_related().filter(begin_date__lte=date(int(year), 12, 31), end_date__gte=date(int(year), 1, 1)) def year_ordered_and_unique(self, year): queryset = self.year(year) @@ -689,7 +693,7 @@ def order_by(cls, membertypes_list, by, reverse=False): if by == 'begin_date': key = lambda mt: mt.begin_date elif by == 'end_date': - key = lambda mt: mt.end_date or datetime.date(9999, 12, 31) + key = lambda mt: mt.end_date or date(9999, 12, 31) elif by == 'member': key = lambda mt: strxfrm(mt.member.full_name_for_sorting) else: diff --git a/teknologr/members/templates/decoration.html b/teknologr/members/templates/decoration.html index eff94603..6fdc9a68 100644 --- a/teknologr/members/templates/decoration.html +++ b/teknologr/members/templates/decoration.html @@ -37,7 +37,7 @@

Innehavare av betygelsen

- {{ ownership.member.full_name }} + {{ ownership.member.get_full_name_HTML }} {{ ownership.acquired }} {% endfor %} diff --git a/teknologr/members/templates/functionary.html b/teknologr/members/templates/functionary.html index ef01ec36..518a764c 100644 --- a/teknologr/members/templates/functionary.html +++ b/teknologr/members/templates/functionary.html @@ -37,7 +37,7 @@

Innehavare av posten

- {{ functionary.member.full_name }} + {{ functionary.member.get_full_name_HTML }} {{ functionary.begin_date }} {{ functionary.end_date }} diff --git a/teknologr/members/templates/group.html b/teknologr/members/templates/group.html index 5c9dde2a..7fcd0eea 100644 --- a/teknologr/members/templates/group.html +++ b/teknologr/members/templates/group.html @@ -71,7 +71,7 @@

- {{ membership.member.full_name }} + {{ membership.member.get_full_name_HTML }} {% endfor %} diff --git a/teknologr/members/templates/member.html b/teknologr/members/templates/member.html index fefac2ed..f8299c45 100644 --- a/teknologr/members/templates/member.html +++ b/teknologr/members/templates/member.html @@ -11,7 +11,7 @@

{% if result %} diff --git a/teknologr/members/templates/side.html b/teknologr/members/templates/side.html index f8d57e05..1c1bf7cf 100644 --- a/teknologr/members/templates/side.html +++ b/teknologr/members/templates/side.html @@ -44,6 +44,6 @@ {{ obj.name|default:"Namnlös" }}{% if obj.count >= 0 %} ({{ obj.count }}) {% endif %} {% empty %} - Skriv i sökfältet för att visa medlemmar + Inga objekt hittades {% endfor %} diff --git a/teknologr/members/tests.py b/teknologr/members/tests_models.py similarity index 99% rename from teknologr/members/tests.py rename to teknologr/members/tests_models.py index 67dde22b..a1a843f9 100644 --- a/teknologr/members/tests.py +++ b/teknologr/members/tests_models.py @@ -121,9 +121,7 @@ def test_public_full_name_for_sorting(self): self.assertEqual(self.member3.public_full_name_for_sorting, 'Tester, F-B Biz-Baz') def test_name(self): - self.assertEqual(self.member1.name, 'Foo Bar Baz Tester') - self.assertEqual(self.member2.name, 'Foo Bar Baz Tester') - self.assertEqual(self.member3.name, 'Foo-Bar Biz-Baz von der Tester') + self.assertFalse(hasattr(self.member1, 'name')) def test_address(self): self.assertEquals('Otsvängen 22, 02150 Esbo, Finland', self.member1.full_address) diff --git a/teknologr/members/tests_views.py b/teknologr/members/tests_views.py new file mode 100644 index 00000000..a481f1f8 --- /dev/null +++ b/teknologr/members/tests_views.py @@ -0,0 +1,86 @@ + +from rest_framework import status +from api.tests import BaseAPITest + +class GetPageTests(): + def test_get_for_anonymous_users(self): + response = self.get_all() + self.check_status_code(response, status.HTTP_302_FOUND) + self.assertTrue(response.url.startswith('/login/'), response.url) + + def test_get_for_users(self): + response = self.get_all() + self.check_status_code(response, status.HTTP_302_FOUND) + self.assertTrue(response.url.startswith('/login/'), response.url) + + def test_get_for_superusers(self): + self.login_superuser() + response = self.get_all() + self.check_status_code(response, status.HTTP_200_OK) + + +class HomeViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/admin/members/' + + +class MemberViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/members/{self.m1.id}/' + + +class DecorationOwnershipsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/decorations/{self.d.id}/' + + +class DecorationsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/admin/decorations/' + +class DecorationOwnershipsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/decorations/{self.d.id}/' + + +class FunctionaryTypesViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/admin/functionarytypes/' + +class FunctionariesViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/functionarytypes/{self.ft.id}/' + + +class GroupTypesViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/admin/grouptypes/' + +class GroupsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/grouptypes/{self.gt.id}/' + +class GroupMembershipsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/grouptypes/{self.gt.id}/{self.g.id}/' + + +class ApplicantsViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = '/admin/applicants/' + +class ApplicantViewTest(BaseAPITest, GetPageTests): + def setUp(self): + super().setUp() + self.api_path = f'/admin/applicants/{self.a.id}/' diff --git a/teknologr/members/views.py b/teknologr/members/views.py index a987401f..ce969801 100644 --- a/teknologr/members/views.py +++ b/teknologr/members/views.py @@ -23,6 +23,7 @@ def set_side_context(context, category, active_obj=None): if active_obj and active_obj not in side['objects']: from itertools import chain side['objects'] = list(chain([active_obj], side['objects'])) + side['objects'] = [{'id': m.id, 'name': m.get_full_name_HTML()} for m in side['objects']] elif category == 'grouptypes': side['sname'] = 'grupp' side['form'] = GroupTypeForm(auto_id="gtmodal_%s") @@ -233,7 +234,7 @@ def decoration_ownership_form(request, decration_ownership_id): }) -@user_passes_test(lambda u: u.is_staff, login_url='/login') +@user_passes_test(lambda u: u.is_staff, login_url='/login/') def applicant(request, applicant_id): context = {}