diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9c08960e..b657ff2e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,7 +4,7 @@ You are more than welcome to contribute to the system. This guide documents how ## Getting a local setup -- Installing Docker: Download and install [docker-compose][docker-guide]. If +- Installing Docker: Download and install [Docker Compose][docker-guide]. If you use Ubuntu 18.04 (LTS), you can use [this guide][docker-ubuntu-guide] to set up Docker. @@ -13,15 +13,15 @@ You are more than welcome to contribute to the system. This guide documents how - The setup adheres to the [twelve-factor-app][12f] principles. To get a local development configuration, copy the file `.env.example` to `.env` -- Run `docker-compose up` to start your local system. +- Run `docker compose up` to start your local system. -- Run `docker-compose run web ./manage.py get_live_data` to download public +- Run `docker compose run web ./manage.py get_live_data` to download public data and insert it into your local database. - To get some dummy members, families, etc. you can use the [factories][factories] to create them. ```bash - docker-compose run web ./manage.py shell + docker compose run web ./manage.py shell from members.tests.factories import MemberFactory MemberFactory.create_batch(20) ``` @@ -31,7 +31,7 @@ You are more than welcome to contribute to the system. This guide documents how with the real world. For instance each member belongs to their own department. - To create a super user for the admin interface you can run - `docker-compose run web ./manage.py createsuperuser` + `docker compose run web ./manage.py createsuperuser` - A pgAdmin container is configured as part of Docker Compose, and can be accessed on . Log in with credentials `admin@example.com`/`admin`. Connection to database has been configured in @@ -65,15 +65,15 @@ You are more than welcome to contribute to the system. This guide documents how - [Django][django]: The base web framework used. The link is to their great tutorial which takes an hour or two to complete. -- [Docker][docker-tutorial]: We use `docker-compose` to setup database, +- [Docker][docker-tutorial]: We use `docker compose` to setup database, environment and dependencies. The following commands is all that's required to work on the system. - - `docker-compose build` -- Builds the system. - - `docker-compose up` -- Starts the systems. - - `docker-compose down && docker volume rm backend_database` + - `docker compose build` -- Builds the system. + - `docker compose up` -- Starts the systems. + - `docker compose down && docker volume rm backend_database` \-- Deletes your local database - - `docker-compose run web command` -- Replace `command` with what you want + - `docker compose run web command` -- Replace `command` with what you want to run in the system. - [SASS][sass]: CSS files belong in `members/static/members/sass`, @@ -82,7 +82,7 @@ You are more than welcome to contribute to the system. This guide documents how following command in a separate terminal: ```bash - docker-compose run web node_modules/.bin/sass --watch members/static/members/sass/main.scss members/static/members/css/main.css + docker compose run web node_modules/.bin/sass --watch members/static/members/sass/main.scss members/static/members/css/main.css ``` It will compile SASS when you save a file. @@ -94,7 +94,7 @@ You are more than welcome to contribute to the system. This guide documents how - [Selenium][selenium]: runs the functional tests. To run a specific test run ```bash - docker-compose run web ./manage.py test members.tests.test_functional.test_create_family + docker compose run web ./manage.py test members.tests.test_functional.test_create_family ``` where the name of your tests replaces the last part. @@ -102,7 +102,7 @@ You are more than welcome to contribute to the system. This guide documents how - [Unit tests][unittest]: runs the unittests. You run the unit tests the same way as the selenium tests. To run a specific test run ```bash - docker-compose run web ./manage.py test members.tests.test_dump_data + docker compose run web ./manage.py test members.tests.test_dump_data ``` where the name of your tests replaces the last part. @@ -122,7 +122,7 @@ Pragmatic development is to use docker for database and run server and/or tests - Install npm dependencies: `npm install` - Copy the sample environment file: `cp .env.example .env` -- boot the database with `docker-compose start database` +- boot the database with `docker compose start database` - boot a selenium docker with `docker run -it -p 4444:4444 -p 7900:7900 --network="host" -v /dev/shm:/dev/shm selenium/standalone-chrome` - start the virtual env shell and work from there further on with `poetry shell` - Run sass: `./node_modules/.bin/sass members/static/members/sass/main.scss` @@ -159,13 +159,13 @@ p.user.username # show login email discussion happens before the code and limits duplicate work. 4. Help us specify the requirements specification. 5. Code the features with tests, see the [testing guide][test_guide] -6. Run the entire test suite with: `docker-compose run web ./manage.py test` +6. Run the entire test suite with: `docker compose run web ./manage.py test` 7. Check that the following requirements are meet: - The code has tests, code without tests is not accepted. (Except for minimal CSS and text changes). Use the existing test as inspiration and the [factories][factories] to create dummy data. - The code conforms to the [black][black] formatting rules. To format your - code run `docker-compose run web black .`. Consider looking for an + code run `docker compose run web black .`. Consider looking for an editor integration. - The code passes [flake8][flake8] checks. 8. Submit the pull request. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c44e7e3..3ad2f789 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,20 +11,20 @@ jobs: - name: Setup Enviroment run: cp .env.example .env - name: Builds the stack - run: docker-compose build + run: docker compose build - name: Check formatting (Black) - run: docker-compose run web black --check . + run: docker compose run web black --check . - name: Static error check (Flake8) - run: docker-compose run web flake8 + run: docker compose run web flake8 - name: Test - run: docker-compose run web ./manage.py test + run: docker compose run web ./manage.py test - uses: actions/upload-artifact@v2.1.4 if: always() with: name: selenium-screens path: ./test-screens - name: Create and upload UML diagram - run: mkdir -p UML && docker-compose run web ./manage.py graph_models members -o UML/UML_diagram.png + run: mkdir -p UML && docker compose run web ./manage.py graph_models members -o UML/UML_diagram.png - uses: actions/upload-artifact@v2.1.4 with: name: UML_diagram.png diff --git a/members/admin/activity_admin.py b/members/admin/activity_admin.py index 34b4a6bd..adaed33d 100644 --- a/members/admin/activity_admin.py +++ b/members/admin/activity_admin.py @@ -138,6 +138,8 @@ class ActivityAdmin(admin.ModelAdmin): ] save_as = True + ordering = ("-start_date", "department__name", "name") + class Media: css = {"all": ("members/css/custom_admin.css",)} # Include extra css js = ("members/js/copy_to_clipboard.js",) diff --git a/members/admin/union_admin.py b/members/admin/union_admin.py index 804ad409..0e7a9fee 100644 --- a/members/admin/union_admin.py +++ b/members/admin/union_admin.py @@ -81,7 +81,6 @@ def has_delete_permission(self, request, obj=None): class UnionAdmin(admin.ModelAdmin): inlines = [AdminUserUnionInline] list_display = ( - "id", "union_link", "address", "union_email", diff --git a/members/admin/waitinglist_admin.py b/members/admin/waitinglist_admin.py index 42dc72a9..5ae5af31 100644 --- a/members/admin/waitinglist_admin.py +++ b/members/admin/waitinglist_admin.py @@ -79,7 +79,6 @@ def get_form(self, request, obj=None, change=False, **kwargs): return form list_display = ( - "union_link", "department_link", "person_link", "person_age_years", @@ -89,6 +88,7 @@ def get_form(self, request, obj=None, change=False, **kwargs): "user_waiting_list_number", "user_created", "user_added_waiting_list", + "union_link", ) list_filter = ( @@ -101,10 +101,11 @@ def get_form(self, request, obj=None, change=False, **kwargs): "department__name", "department__union__name", "person__name", - "zipcode", + "person__zipcode", + "person__municipality", ] search_help_text = mark_safe( - """Du kan søge på forening, afdeling eller person.
+ """Du kan søge på forening (navn), afdeling (navn) eller person (navn, postnummer eller kommune).
'Nummer på venteliste' er relateret til personernes oprettelsestidspunkt""" ) diff --git a/members/tests/test_admin_admin_actions.py b/members/tests/test_admin_admin_actions.py index c9e71c54..42352de7 100644 --- a/members/tests/test_admin_admin_actions.py +++ b/members/tests/test_admin_admin_actions.py @@ -74,6 +74,9 @@ def setUp(self): self.person_above_max_age = self.create_person_and_waiting_list_entry( name="person_above_max_age", age=18 ) + self.person_without_age = self.create_person_and_waiting_list_entry( + name="person_without_age" + ) # activity starts in two days, person has birthday two weeks after self.person_too_young = self.create_person_and_waiting_list_entry( @@ -108,6 +111,7 @@ def setUp(self): ) def create_person_and_waiting_list_entry(self, name=None, age=None, birthday=None): + person_birthday = None if age is not None: person_birthday = ( datetime.now() - relativedelta(years=age) - relativedelta(weeks=1) @@ -117,16 +121,20 @@ def create_person_and_waiting_list_entry(self, name=None, age=None, birthday=Non person_birthday = birthday person_name = f"Testperson født {person_birthday}" else: - raise ValueError("Either age or birthday must be specified") + person_name = "Testperson uden fødselsdato" if name is not None: person_name = name - person = Person.objects.create( - name=person_name, - family=self.family, - birthday=person_birthday, - ) + if person_birthday is not None: + person = Person.objects.create( + name=person_name, + family=self.family, + birthday=person_birthday, + ) + else: + person = Person.objects.create(name=person_name, family=self.family) + WaitingList( person=person, department=self.department, diff --git a/members/tests/test_functional/test_account_login.py b/members/tests/test_functional/test_account_login.py index 749b4100..bb14ce0b 100644 --- a/members/tests/test_functional/test_account_login.py +++ b/members/tests/test_functional/test_account_login.py @@ -31,7 +31,7 @@ def setUp(self): def tearDown(self): if not os.path.exists("test-screens"): os.mkdir("test-screens") - self.browser.save_screenshot("test-screens/activities_list_final.png") + self.browser.save_screenshot("test-screens/account_login_final.png") self.browser.quit() def test_account_login(self): diff --git a/members/tests/test_functional/test_entry_page.py b/members/tests/test_functional/test_entry_page.py index cdba0abf..2c650491 100644 --- a/members/tests/test_functional/test_entry_page.py +++ b/members/tests/test_functional/test_entry_page.py @@ -32,7 +32,7 @@ def setUp(self): def tearDown(self): if not os.path.exists("test-screens"): os.mkdir("test-screens") - self.browser.save_screenshot("test-screens/activities_list_final.png") + self.browser.save_screenshot("test-screens/entry_page_final.png") self.browser.quit() def test_entry_page_as_person(self): diff --git a/members/utils/age_check.py b/members/utils/age_check.py index 4f0d1a80..b064b6bf 100644 --- a/members/utils/age_check.py +++ b/members/utils/age_check.py @@ -3,9 +3,14 @@ def check_is_person_too_young(activity, person): - # Check for two things: - # 1. if we are before start of activity: is person too young when activity starts ? - # 2. if we are at or after activity starts: is person too young now ? + # Check for three things: + # 1. Does the person have a birthday set (if none, then return false) + # 2. if we are before start of activity: is person too young when activity starts ? + # 3. if we are at or after activity starts: is person too young now ? + + if person.birthday is None: + return True + if timezone.now().date() <= activity.start_date: return ( person.birthday + relativedelta(years=activity.min_age) @@ -19,9 +24,13 @@ def check_is_person_too_young(activity, person): def check_is_person_too_old(activity, person): - # Check for two things: - # 1. if we are before start of activity: is person too old when activity starts ? - # 2. if we are at or after activity starts: is person too old now ? + # Check for three things: + # 1. Does the person have a birthday set (if none, then return false) + # 2. if we are before start of activity: is person too old when activity starts ? + # 3. if we are at or after activity starts: is person too old now ? + + if person.birthday is None: + return True if timezone.now().date() <= activity.start_date: return ( diff --git a/poetry.lock b/poetry.lock index cffad07b..6285a1b8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -362,13 +362,13 @@ files = [ [[package]] name = "django" -version = "4.2.14" +version = "4.2.15" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.8" files = [ - {file = "Django-4.2.14-py3-none-any.whl", hash = "sha256:3ec32bc2c616ab02834b9cac93143a7dc1cdcd5b822d78ac95fc20a38c534240"}, - {file = "Django-4.2.14.tar.gz", hash = "sha256:fc6919875a6226c7ffcae1a7d51e0f2ceaf6f160393180818f6c95f51b1e7b96"}, + {file = "Django-4.2.15-py3-none-any.whl", hash = "sha256:61ee4a130efb8c451ef3467c67ca99fdce400fedd768634efc86a68c18d80d30"}, + {file = "Django-4.2.15.tar.gz", hash = "sha256:c77f926b81129493961e19c0e02188f8d07c112a1162df69bfab178ae447f94a"}, ] [package.dependencies]