205 Commits

Author SHA1 Message Date
jadera 8393875963 change olhev date 2026-01-15 15:03:37 +02:00
SimeonPursiainen d5f67f4cc1 Edit GDPR link in Kaehmy site 2025-11-21 22:48:49 +02:00
J4DER4 ebd0bd9fa2 Merge branch 'Testing-audit-fixes-pt1' into 'main'
PIP upgrades for audit safetyscheck

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!117
2025-11-20 17:13:26 +00:00
J4DER4 c0a9321341 Merge branch 'jäsen_searchbar' into 'main'
added searchbar to jäsenrekisteri (check description)

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!116
2025-11-20 17:02:59 +00:00
jadera 6693279348 use strict version 2025-11-20 18:45:13 +02:00
jadera d0557ffb79 added more pip upgrades 2025-11-20 00:59:30 +02:00
jadera 91ee3bea6d testing pip upgrade to bump packages 2025-11-20 00:43:47 +02:00
jadera b1d6bf359f added searchbar to jäsenrekisteri
added searchbar
2025-11-19 23:37:48 +02:00
Justus Ojala 61ac177ce3 Made manual submit_id check and IntegrityError messages the same (were different to distinguish which is blocking the request when running locally) 2025-10-13 20:37:09 +03:00
Justus Ojala 9a2168e47f Add manual submit_id uniqueness check as it is not enforced on server 2025-10-13 20:20:29 +03:00
Justus Ojala a5732669da Merge branch 'signup-duplicate-reduction' into 'main'
Add submit_id to signup model

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!115
2025-10-13 19:45:46 +03:00
Justus Ojala 75800ee9ee Add submit_id to signup model 2025-10-13 19:33:26 +03:00
Justus Ojala 5782c20b4b Fix more name errors 2025-09-23 08:08:39 +03:00
Justus Ojala 167b0bfabf Fix more name errors 2025-09-23 07:52:01 +03:00
Justus Ojala 43c9a6d328 Merge branch 'print-syntax' into 'main'
This isn't C

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!113
2025-09-22 18:50:27 +03:00
Justus Ojala b3a159b3d8 This isn't C 2025-09-22 18:46:57 +03:00
Justus Ojala a7ed188dc8 Merge branch 'debugprints' into 'main'
added debugprints

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!112
2025-09-22 18:25:55 +03:00
Justus Ojala 162759dcb2 added debugprints 2025-09-22 18:22:08 +03:00
Justus Ojala 05e6ba01d9 Merge branch 'time-name-fix' into 'main'
Import time

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!111
2025-09-22 18:04:59 +03:00
Justus Ojala 8a061381f4 Import time 2025-09-22 18:01:27 +03:00
Justus Ojala d2fa7084da Merge branch 'setuptools' into 'main'
Setuptools

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!110
2025-09-22 17:45:46 +03:00
Justus Ojala e7278c8893 Remove poetry.lock from version control 2025-09-22 17:38:47 +03:00
Justus Ojala 52bf21c8ba Add setuptools dependency 2025-09-22 17:38:00 +03:00
Justus Ojala 0cbc794c75 Merge branch 'signup_duplicate_prevention' into 'main'
Added submission key checking to backend

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!106
2025-09-16 21:43:28 +03:00
Justus Ojala 90a0550775 Added submission key checking to backend 2025-09-16 21:24:52 +03:00
Aarni Halinen 0d458cf2ea Merge branch 'deps' into 'main'
Update some deps

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!105
2025-03-26 21:37:14 +02:00
Aarni Halinen 5155f52f29 update djangorestframework-simplejwt 2025-03-26 21:33:23 +02:00
Aarni Halinen a2ccc43a36 update gunicorn 2025-03-26 21:16:22 +02:00
Aarni Halinen a045c6ac89 update sentry-sdk 2025-03-26 21:13:17 +02:00
Aarni Halinen dcb2115cb5 poetry update 2025-03-26 21:12:51 +02:00
Aarni Halinen 5c7528ca6a Merge branch 'dev-setup' into 'main'
Update poetry and fix development tooling and docs

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!104
2025-03-26 21:07:01 +02:00
Aarni Halinen a3ab12619a EOF new line 2025-03-26 21:03:52 +02:00
Aarni Halinen c637ffb3f6 stop using env file in docker-compose 2025-03-26 20:58:05 +02:00
Aarni Halinen 41fd3043d0 update poetry to v2.1.1 2025-03-26 20:51:02 +02:00
Aarni Halinen 1331eeb1d7 change trunk and production branches 2025-03-06 02:36:14 +02:00
Aarni Halinen 1fd329f0c1 pick changes from templates 2025-03-06 02:31:56 +02:00
Aarni Halinen 52eb9e370c Revert "production deployments manually from develop branch"
This reverts commit 00e5eff8db.
2025-03-06 02:31:02 +02:00
Aarni Halinen 00e5eff8db production deployments manually from develop branch 2025-03-06 02:10:19 +02:00
SimeonPursiainen 7554c1e7e8 testing 2025-02-28 22:46:40 +02:00
Simeon Pursiainen 50fd4ff9f7 Merge branch 'update-python' into 'develop'
Update python to 3.12

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!99
2025-02-09 19:53:50 +00:00
Aarni Halinen ff8230d9c0 update node packages 2025-02-09 21:20:38 +02:00
Aarni Halinen 3f73fbec62 update node to v22 2025-02-09 21:08:18 +02:00
Aarni Halinen 916e0bdaf0 allow audit failure 2025-02-09 21:05:20 +02:00
Aarni Halinen 9e4a7c8569 poetry update 2025-02-09 20:56:21 +02:00
Aarni Halinen b5839da135 rm deprecated compose version 2025-02-09 20:54:05 +02:00
Aarni Halinen f562912492 migrate to new dependecy format 2025-02-09 20:54:04 +02:00
Aarni Halinen f0a5b6e8e7 update to python3.12 2025-02-09 19:47:10 +02:00
Aarni Halinen 8fc3ee534d fix markdown lint issues in README 2025-02-09 18:27:18 +02:00
Aarni Halinen 9b4fa56add uppercase AS in Dockerfile 2025-02-09 18:22:15 +02:00
Simeon Pursiainen 90ca91970d Update .gitlab-ci.yml file 2025-01-23 19:11:25 +00:00
Simeon Pursiainen 955072370f temporary fix 2025-01-23 19:05:17 +00:00
Simeon Pursiainen 709275c4d3 Edit email.html 2025-01-23 18:52:54 +00:00
Simeon Pursiainen 6f61c9dc32 Do audit only on push to master 2025-01-23 18:51:46 +00:00
Simeon Pursiainen f5432a1ff9 Edited Hafv date 2025-01-21 21:11:01 +00:00
Johannes Viirimäki f23ce6b39e use docker:25 based images and services 2024-09-30 07:36:45 +00:00
Johannes Viirimäki 3a8c455031 Update kaehmy.html 2024-09-30 07:25:41 +00:00
Johannes a6a973f008 update pip & pillow 2024-01-24 23:22:39 +02:00
Johannes 01f7911352 update packages 2024-01-24 22:02:05 +02:00
johannes 55507f89d1 change hafv date 2024-01-24 20:49:34 +02:00
Tommi S 4e0a93631d Compiled translations 2023-10-05 16:20:28 +03:00
Tommi S be9e308587 update kaeyhmy categories and add english kaehmyguide 2023-10-05 14:25:22 +03:00
Tommi S 8550a9a02b prkl 2023-10-04 20:46:06 +03:00
Tommi S 703bb91bfd Edit signup email contact 2023-10-04 20:04:20 +03:00
Tommi S 6b4a00ebd8 Edit text 2023-10-03 23:30:34 +03:00
Tommi S 677c1400fa Fix lines 2023-10-03 00:08:08 +03:00
Tommi S a2e08d0ea6 Update kaehmy header dates 2023-10-02 23:23:32 +03:00
tommi s 901f2bed96 Merge branch 'feature/disable_kaehmybot_button' into 'develop'
Add kaehmybot disable button

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!94
2023-10-02 20:05:24 +00:00
Tommi S 6004156b6f Add kaehmybot disable button 2023-10-01 17:34:01 +03:00
Tommi S e00323bffe Update readme docker database name 2023-09-29 21:27:38 +03:00
Tommi S 6ef0dbf91b Update kaehmy dates 2023-09-29 20:10:39 +03:00
Tommi S 63a4781574 Edit accepting message 2023-09-15 12:02:31 +03:00
Tommi S e735ebe64a Merge branch 'develop' of gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend into develop 2023-09-14 17:54:48 +03:00
Tommi S f9db8476a1 Does it work now 2023-09-14 17:52:54 +03:00
tommi s 2a24544056 Update deprecated variables 2023-09-14 14:39:38 +00:00
Tommi S fb7bee5480 Update djangorestframework 2023-09-14 15:33:19 +03:00
Tommi S 8355d10635 Update django, requests, sentry-sdk, sqlparse 2023-09-14 11:35:59 +03:00
Tommi S 21892e277e Update certifi 2023-09-14 11:28:58 +03:00
Tommi S 81e1f994eb Update tg-channel link and remove freshmen groups from accepting email 2023-09-13 19:03:49 +03:00
Aarni Halinen fc6e02b71b Add log level to gunicorn 2023-03-27 19:16:08 +03:00
Ojakoo 8815ccf667 update django 2023-03-05 19:53:44 +02:00
Ojakoo b694370572 :D 2023-03-05 19:35:51 +02:00
Ojakoo fda9acfb2f update certifi 2023-02-07 16:43:02 +02:00
Ojakoo 30c6e4809b update hafv page 2023-02-07 16:35:07 +02:00
Ilari Ojakorpi 39754a5e63 Merge branch 'django-v4' into 'develop'
Draft: Django v4

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!72
2022-12-25 13:10:34 +00:00
Ojakoo 0be3ee69be add ordering to remove pagination error 2022-12-24 21:07:46 +02:00
Ojakoo c6f0f4615b lint 2022-12-24 20:59:07 +02:00
Ojakoo 3ac5400b79 updated pyexcel packages 2022-12-24 20:55:40 +02:00
Ojakoo 429d3a0602 make datetimes aware 2022-12-23 13:16:39 +02:00
Ojakoo 1b086843dc Update poetry 2022-12-21 19:16:55 +02:00
Ojakoo b9280ea026 update protobuf 2022-12-21 18:59:27 +02:00
Ojakoo b36022e546 Update django 2022-12-21 18:57:33 +02:00
Ojakoo 9bb57840a7 Update pillow 2022-12-21 18:55:49 +02:00
Ojakoo 36bd74c6cc Revert some merge stuff 2022-12-21 18:44:43 +02:00
Ojakoo a2615ae27d Merge branch 'develop' into django-v4 2022-12-21 18:34:39 +02:00
Ojakoo f7de9e32d3 remove last_paid column to not search database for unexisting entries 2022-12-21 18:13:54 +02:00
Ojakoo 57d8c4321f Update readme. 2022-12-21 16:20:59 +02:00
Aarni Halinen 632eedea9c Test scheduled job 2022-10-31 22:36:28 +02:00
Aarni Halinen 1405d89d9a Test scheduled job 2022-10-31 22:21:45 +02:00
Aarni Halinen da1ae8d721 Test scheduled job 2022-10-31 22:19:30 +02:00
Aarni Halinen 52a83b9336 Scheduled job for Docker cleanup 2022-10-24 21:58:39 +03:00
Aarni Halinen f67ce55d60 Fix webapp test as module 2022-10-24 21:42:41 +03:00
Ojakoo 5e1390ab6b Changes to kaehmy categories 2022-10-05 14:18:28 +03:00
Ojakoo 99348dc297 Update dparse 2022-10-05 12:58:29 +03:00
Ojakoo 715b309c89 Added new categories for kaehmy 2022-10-05 12:43:52 +03:00
Ojakoo 70676d5203 Lint 2022-09-23 20:53:25 +03:00
Ojakoo 037e4ae6e8 Use html templates for kaehmy emails. No translations for now. 2022-09-23 20:30:08 +03:00
Ojakoo 8d6f13b61d Update privacy policy link 2022-09-23 17:57:36 +03:00
Ojakoo 03982ee620 Update kaehmy dates and change header to html instead of picture. 2022-09-23 17:51:01 +03:00
Ojakoo a8923b63d6 wrap private key 2022-09-13 13:31:58 +03:00
Ojakoo 19975877cb Remove debug stuff 2022-09-13 09:59:55 +03:00
Ojakoo 2e0fad4bb2 Use POSIX format for source 2022-09-12 22:42:01 +03:00
Ojakoo f0179c1840 Change google creds format. Ugly but works. 2022-09-12 22:38:18 +03:00
Ojakoo 37a9750d4d Add group key and dev secrets to stack compose 2022-09-05 13:38:26 +03:00
Ojakoo 5575186570 lint 2022-09-05 12:03:31 +03:00
Ojakoo 9e179d5e06 Added error handling for incorrectly formatted google creds 2022-09-05 10:40:29 +03:00
Ojakoo ea9a732803 Modified paths 2022-08-17 23:00:11 +03:00
Ojakoo 0026b788b2 juuh eli 2022-08-16 23:00:36 +03:00
Ojakoo da3a484f6c wbn? 2022-08-16 22:24:20 +03:00
Ojakoo a310d51f5e or not 2022-08-16 21:43:04 +03:00
Ojakoo 6732e30213 #14 fix paths 2022-08-16 21:26:55 +03:00
Ojakoo 4e59eee200 Enable GOOGLE_SERVICE_ACCOUNT, should work now.. 2022-08-16 20:59:19 +03:00
Aarni Halinen bb0b2a2628 Fix kaehmy form 2022-08-11 11:28:30 +03:00
Aarni Halinen 32d636d3ee Disable GOOGLE_SERVICE_ACCOUNT for debugging 2022-08-11 09:52:58 +03:00
Ojakoo c91b99cdb1 lol 2022-08-09 21:44:01 +03:00
Aarni Halinen c1a1f6e534 Replace deprecated functions 2022-08-06 16:54:16 +03:00
Aarni Halinen f79d1467f7 Install Django v4 2022-08-06 16:38:28 +03:00
Aarni Halinen 40cf9121b6 Clean-up README and coveragerc 2022-08-06 16:35:58 +03:00
Aarni Halinen ca73eba609 Revert "Install django-suit v2"
This reverts commit fe46d57108.
2022-08-06 16:26:15 +03:00
Aarni Halinen fe46d57108 Install django-suit v2 2022-08-06 16:02:58 +03:00
Ojakoo 70e1835a4f Update dependencies 2022-08-06 12:28:43 +03:00
Ojakoo b7f17671d9 Resolve conlict 2022-08-06 12:09:50 +03:00
Ojakoo 34659403a8 lint 2022-08-06 11:03:39 +03:00
Ojakoo 4fbf5fe0a4 Jas list error notification only in prod 2022-08-06 10:17:43 +03:00
Ojakoo c6be0e6562 Add google envs to deploy 2022-08-06 10:07:00 +03:00
Aarni Halinen 3623c7e9f4 Fix TG message template 2022-08-04 00:51:45 +03:00
Aarni Halinen 298db5b78e Merge branch 'remove-baserole' into 'develop'
Remove webapp BaseRole model

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!67
2022-08-03 20:44:55 +00:00
Aarni Halinen 1ca6de3090 Fix lint 2022-08-03 23:35:41 +03:00
Aarni Halinen 07d0f2aa47 Remove comment 2022-08-03 23:19:42 +03:00
Aarni Halinen 93e122b8a8 Remove webapp BaseRole 2022-08-03 23:19:40 +03:00
Aarni Halinen 9678b663a0 Update kaehmy BaseRole 2022-08-03 23:18:32 +03:00
Aarni Halinen 992a2ec8e0 Add new BaseRole 2022-08-03 23:15:12 +03:00
Aarni Halinen b41bd41a54 Fix typo 2022-08-03 23:11:49 +03:00
Aarni Halinen 30f59c36fb Clean-up templates 2022-08-03 22:57:19 +03:00
Aarni Halinen f51d71e045 Fix most of html templates 2022-08-03 22:46:14 +03:00
Ojakoo 9c66238b82 Remove old json route 2022-08-03 00:34:42 +03:00
Ilari Ojakorpi 2f0143a9ae Merge branch 'update-django' into 'develop'
Update Django

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!68
2022-08-02 21:31:05 +00:00
Ojakoo 45ff2c3757 Format datetime to local timezone 2022-08-03 00:20:23 +03:00
Aarni Halinen 321d45b628 Fix unit tests 2022-08-01 22:31:47 +03:00
Aarni Halinen 2628d753f5 Fix JSONField deprecation warning 2022-08-01 22:22:30 +03:00
Aarni Halinen a603e2dff8 Add explicit primary keys 2022-08-01 22:22:02 +03:00
Aarni Halinen 96e05d908d Fix static files 2022-08-01 22:00:12 +03:00
Aarni Halinen a5bf5668eb Remove django-suit 2022-08-01 21:50:07 +03:00
Aarni Halinen 1ec5082faf Update Django packages 2022-08-01 21:33:31 +03:00
Aarni Halinen 6da5b97e19 Remove template tags removed in Django v3 2022-08-01 21:30:17 +03:00
Aarni Halinen 7fd30e3eba Update DB settings for Django v3 2022-08-01 21:24:10 +03:00
Aarni Halinen 3e9084ca1d Update jsonschema 2022-08-01 21:16:56 +03:00
Aarni Halinen a28f82d31e Update whitenoise 2022-08-01 21:15:08 +03:00
Aarni Halinen 04ecb8fc7e Update requests 2022-08-01 21:14:47 +03:00
Aarni Halinen 3e707e58a5 Run poetry update 2022-08-01 21:12:17 +03:00
Aarni Halinen 70d7f55996 Remove nose test runner 2022-08-01 21:09:17 +03:00
Aarni Halinen e408809e58 Update dev tools 2022-08-01 21:02:15 +03:00
Ojakoo 33fd4012f1 #13 fixed homepage link 2022-08-01 00:24:34 +03:00
Ojakoo 228938b695 lint 2022-07-31 16:33:28 +03:00
Ojakoo 72e91e3d62 Moved google creds to .env 2022-07-31 16:28:57 +03:00
Ojakoo 3f6a719e9d Added error handling, send email to user if adding fails 2022-07-31 11:12:51 +03:00
Aarni Halinen 490b99a848 Fix schema type 2022-07-28 22:13:01 +03:00
Aarni Halinen 0a899f5600 Fix schema 2022-07-28 21:45:48 +03:00
Aarni Halinen 7825cc7293 Add form validation on update 2022-07-28 21:16:22 +03:00
Aarni Halinen 8bb6e9e9a7 Add input validation schema for SignupFormViewSet create 2022-07-28 21:13:09 +03:00
Ojakoo 53c3acd39f directory api working state 2022-07-25 20:12:32 +03:00
Aarni Halinen dd0254a08e fix try-catch for jwt verification 2022-07-24 21:02:35 +03:00
Aarni Halinen 9b53fb4bc0 use jwt_access cookie in Filebrowser auth 2022-07-24 20:53:21 +03:00
Aarni Halinen e17c3ad92c Add --without-hashes to poetry export in Dockerfile 2022-07-22 01:08:36 +03:00
Aarni Halinen 362d981532 Disable pip dependency resolver in Dockerfile (handled by poetry) 2022-07-22 00:55:19 +03:00
Aarni Halinen e12be3c2f6 Revert "Add --without-hashes to poetry export in Dockerfile"
This reverts commit 3edae7f967.
2022-07-22 00:54:38 +03:00
Aarni Halinen 3edae7f967 Add --without-hashes to poetry export in Dockerfile 2022-07-22 00:52:04 +03:00
Aarni Halinen 4d159b2793 Add algorithms for FileBrowser JWT verification 2022-07-22 00:13:37 +03:00
Aarni Halinen cb3b831f7a Use husky for pre-commit hook 2022-07-21 23:47:56 +03:00
Aarni Halinen acba330694 Update CORS setting 2022-07-21 23:37:32 +03:00
Aarni Halinen eb22368055 Cleanup settings 2022-07-21 23:28:06 +03:00
Ojakoo fac2f9b367 Added pre-commit linter, lint 2022-07-04 10:33:38 +03:00
Ojakoo 7319c32d73 lint 2022-07-04 10:24:53 +03:00
Ojakoo b3a484ce55 Added verify path 2022-07-04 10:19:40 +03:00
Ojakoo 337b774074 lint 2022-06-21 21:47:54 +03:00
Ojakoo e70e598c57 #12 Changed djangorestframework-jwt to djangorestframework-simplejwt 2022-06-21 21:35:59 +03:00
Ojakoo 5eef2f685c Update pyjwt 2022-06-20 22:17:29 +03:00
Ojakoo d1953ef24c Changed application accept email formatting to html 2022-06-20 16:44:06 +03:00
Ojakoo ec4317d9e7 heevi email fix 2022-05-25 19:13:16 +03:00
Ojakoo cff84816fc Updated member application email links 2022-05-23 01:53:05 +03:00
Ojakoo 7881a24eb1 Updated heevi email message 2022-05-23 01:52:30 +03:00
Aarni Halinen a0765ca18b Merge branch 'python-dotenv' into 'develop'
Python dotenv

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!56
2022-05-19 19:23:14 +00:00
Aarni Halinen 913eb1cedf load .env file 2022-05-19 22:19:07 +03:00
Aarni Halinen 7035ebccca install python-dotenv 2022-05-19 22:18:50 +03:00
Aarni Halinen 2031146fc7 Merge branch 'add-dockerignore' into 'develop'
Add dockerignore file

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!59
2022-05-19 19:15:11 +00:00
Aarni Halinen 35f30300b3 Merge branch 'remove-about-page' into 'develop'
Remove about page

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!58
2022-05-19 19:13:58 +00:00
Aarni Halinen 342f2862a5 force empty logs and media folders 2022-05-19 22:11:36 +03:00
Aarni Halinen 3536ca5922 add dockerignore 2022-05-19 22:11:36 +03:00
Aarni Halinen 50ab7bc1f9 remove dealer 2022-05-19 22:09:47 +03:00
Aarni Halinen 102d8f82d6 remove about page 2022-05-19 22:08:18 +03:00
Aarni Halinen f302c0a17d Merge branch 'fix-audit' into 'develop'
Fix audit

See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!57
2022-05-19 18:52:17 +00:00
Aarni Halinen a2b7086e9a update poetry to 1.1.13 2022-05-19 21:44:15 +03:00
Aarni Halinen 704652c643 update linter 2022-05-19 21:37:54 +03:00
Aarni Halinen 8741f6b113 update Pillow 2022-05-19 21:33:19 +03:00
Aarni Halinen 9ffb79aa52 poetry update 2022-05-19 21:32:37 +03:00
Ojakoo a2551cc110 Changed heevi banner 2022-05-19 15:44:32 +03:00
169 changed files with 8198 additions and 7951 deletions
-4
View File
@@ -1,9 +1,5 @@
[report]
show_missing = True
omit =
*/migrations/*
*/admin.py
*/translation.py
[run]
omit =
*/migrations/*
+32
View File
@@ -0,0 +1,32 @@
.DS_Store
.dockerignore
.git/
.husky/
.venv/
.vscode/
collected_static/
logs/
!logs/README
media/
!media/REMOVE_ME
misc/
node_modules/
scripts/
.coverage
.coveragerc
.env*
.eslintignore
.eslintrc.json
.gitignore
.gitlab-ci.yml
.python-version
docker-compose.yml
!manage.py
package*.json
!poetry.lock
!production_entrypoint.sh
pycodestyle.cfg
!pyproject.toml
pyright.json
README.md
stack-compose*.yml
+4 -2
View File
@@ -1,11 +1,13 @@
DEPLOY_ENV=local
SENTRY_DSN=
HOST=api.dev.sahkoinsinoorikilta.fi
HOST=localhost
DEBUG=True
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
DB_NAME=postgres
DB_USER=postgres
DB_PASSWD=postgres
DB_HOST=db
DB_HOST=localhost
DB_PORT=5432
EMAIL_API_KEY=
GROUP_KEY=
GOOGLE_CREDS='{}'
+2
View File
@@ -9,3 +9,5 @@ DB_PASSWD=postgres
DB_HOST=db
DB_PORT=5432
EMAIL_API_KEY=
GROUP_KEY=
GOOGLE_CREDS='{}'
-6
View File
@@ -1,6 +0,0 @@
members/static/js/lib
infoscreen/static/js/lib
webapp/static/js/lib
static/js/lib
collected_static
venv
-251
View File
@@ -1,251 +0,0 @@
{
"env": {
"browser": true,
"jquery": true
},
"globals": {
"angular": true,
"noty": true,
"_": true,
"moment": true
},
"extends": "eslint:recommended",
"rules": {
"no-unused-vars": "warn",
"accessor-pairs": "error",
"array-bracket-spacing": "off",
"array-callback-return": "error",
"arrow-body-style": "error",
"arrow-parens": "error",
"arrow-spacing": "error",
"block-scoped-var": "off",
"block-spacing": "off",
"brace-style": "off",
"callback-return": "off",
"camelcase": "off",
"capitalized-comments": "off",
"class-methods-use-this": "error",
"comma-dangle": "off",
"comma-spacing": "off",
"comma-style": "off",
"complexity": "off",
"computed-property-spacing": "off",
"consistent-return": "off",
"consistent-this": "off",
"curly": "off",
"default-case": "off",
"dot-location": [
"error",
"property"
],
"dot-notation": "off",
"eol-last": "off",
"eqeqeq": "off",
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": "off",
"func-style": "off",
"generator-star-spacing": "error",
"global-require": "off",
"guard-for-in": "off",
"handle-callback-err": "off",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
"indent": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "off",
"keyword-spacing": "off",
"line-comment-position": "off",
"linebreak-style": "off",
"lines-around-comment": "off",
"lines-around-directive": "off",
"max-depth": "off",
"max-len": "off",
"max-lines": "off",
"max-nested-callbacks": "error",
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-ternary": "off",
"new-parens": "off",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "off",
"no-alert": "error",
"no-array-constructor": "off",
"no-await-in-loop": "error",
"no-bitwise": "off",
"no-caller": "error",
"no-catch-shadow": "off",
"no-confusing-arrow": "error",
"no-constant-condition": [
"error",
{
"checkLoops": false
}
],
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-else-return": "off",
"no-empty-function": "off",
"no-eq-null": "off",
"no-eval": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "off",
"no-implicit-coercion": [
"error",
{
"boolean": false,
"number": false,
"string": false
}
],
"no-implicit-globals": "off",
"no-implied-eval": "error",
"no-inline-comments": "off",
"no-inner-declarations": [
"error",
"functions"
],
"no-invalid-this": "off",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "off",
"no-loop-func": "error",
"no-magic-numbers": "off",
"no-mixed-operators": "off",
"no-mixed-requires": "error",
"no-multi-assign": "off",
"no-multi-spaces": "off",
"no-multi-str": "error",
"no-multiple-empty-lines": "off",
"no-native-reassign": "off",
"no-negated-condition": "off",
"no-negated-in-lhs": "error",
"no-nested-ternary": "off",
"no-new": "error",
"no-new-func": "off",
"no-new-object": "error",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "error",
"no-plusplus": "off",
"no-process-env": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-prototype-builtins": "off",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-properties": "error",
"no-restricted-syntax": "error",
"no-return-assign": "off",
"no-return-await": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "off",
"no-shadow": "off",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sync": "error",
"no-tabs": "off",
"no-template-curly-in-string": "error",
"no-ternary": "off",
"no-throw-literal": "off",
"no-trailing-spaces": "off",
"no-undef-init": "error",
"no-undefined": "off",
"no-underscore-dangle": "off",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": [
"error",
{
"defaultAssignment": true
}
],
"no-unused-expressions": "off",
"no-use-before-define": "off",
"no-useless-call": "off",
"no-useless-computed-key": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-escape": "off",
"no-useless-rename": "error",
"no-useless-return": "error",
"no-var": "off",
"no-void": "off",
"no-warning-comments": "off",
"no-whitespace-before-property": "error",
"no-with": "error",
"object-curly-newline": "off",
"object-curly-spacing": "off",
"object-property-newline": "off",
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": "off",
"operator-linebreak": "off",
"padded-blocks": "off",
"prefer-arrow-callback": "off",
"prefer-const": "error",
"prefer-destructuring": [
"error",
{
"array": false,
"object": false
}
],
"prefer-numeric-literals": "error",
"prefer-promise-reject-errors": "error",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": "off",
"radix": "off",
"require-await": "error",
"require-jsdoc": "off",
"rest-spread-spacing": "error",
"semi": "off",
"semi-spacing": "off",
"sort-imports": "error",
"sort-keys": "off",
"sort-vars": "off",
"space-before-blocks": "off",
"space-before-function-paren": "off",
"space-in-parens": "off",
"space-infix-ops": "off",
"space-unary-ops": [
"error",
{
"nonwords": false,
"words": false
}
],
"spaced-comment": "off",
"strict": "off",
"symbol-description": "error",
"template-curly-spacing": "error",
"unicode-bom": [
"error",
"never"
],
"valid-jsdoc": "off",
"vars-on-top": "off",
"wrap-iife": "off",
"wrap-regex": "off",
"yield-star-spacing": "error",
"yoda": "off"
}
}
+1
View File
@@ -12,3 +12,4 @@ node_modules/
*.code-workspace
venv/
.venv/
poetry.lock
+172 -112
View File
@@ -1,131 +1,191 @@
stages:
- setup
- audit
- lint
- test
- publish
- deploy
- setup
- audit
- lint
- test
- publish
- deploy
- cleanup
install:
image: node:14
stage: setup
script:
- npm ci
artifacts:
paths:
- node_modules
expire_in: 1 week
image: node:22
stage: setup
only:
- pushes
script:
- npm ci
artifacts:
paths:
- node_modules
expire_in: 1 week
audit:
image: python:3.9
stage: audit
needs: []
before_script:
- pip install poetry==1.1.4
- poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi
script:
- safety check
image: python:3.12.9
stage: audit
allow_failure: true
only:
- pushes
needs: []
before_script:
- pip install pip==25.3
- pip install poetry==2.1.1
- poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi
script:
- safety check
test:
image: python:3.9
stage: test
needs: []
services:
- postgres:12
variables:
POSTGRES_DB: ci
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
DB_HOST: postgres
before_script:
- pip install poetry==1.1.4
- poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi
script:
- python manage.py migrate --noinput
- python manage.py createdefaultadmin
- python manage.py test
image: python:3.12.9
stage: test
only:
- pushes
needs: []
services:
- postgres:12
variables:
POSTGRES_DB: ci
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
DB_HOST: postgres
before_script:
- pip install pip==25.3
- pip install poetry==2.1.1
- poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi
script:
- python manage.py migrate --noinput
- python manage.py createdefaultadmin
- python manage.py test
lint:py:
image: python:3.9
stage: lint
needs: []
script:
- pip install black==21.12b0
- black --check .
image: python:3.12.9
stage: lint
only:
- pushes
needs: []
script:
- pip install black==22.3.0
- black --check .
lint:js:
image: node:14
stage: lint
needs: ["install"]
script:
- npm run lint:js
image: node:22
stage: lint
only:
- pushes
needs: ["install"]
script:
- npm run lint:js
lint:md:
image: node:14
stage: lint
needs: ["install"]
script:
- npm run lint:md
image: node:22
stage: lint
only:
- pushes
needs: ["install"]
script:
- npm run lint:md
publish:
stage: publish
image: docker:stable
needs: ["test", "lint:py", "lint:js", "lint:md"]
services:
- docker:stable-dind
only:
- develop
- master
script:
- docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build . -t "$IMAGE_NAME"
- docker push "$IMAGE_NAME"
image: docker:25-cli
stage: publish
needs: ["test", "lint:py", "lint:js", "lint:md"]
services:
- docker:25-dind
only:
- main
- production
script:
- docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build . -t "$IMAGE_NAME"
- docker push "$IMAGE_NAME"
deploy:dev:
stage: deploy
image: docker:stable
only:
- develop
environment:
name: dev
url: http://api.dev.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
script:
- docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME"
after_script:
- docker logout "$CI_REGISTRY"
image: docker:25-cli
stage: deploy
only:
- main
environment:
name: dev
url: http://api.dev.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME"
after_script:
- docker logout "$CI_REGISTRY"
deploy:production:
stage: deploy
image: docker:stable
only:
- master
environment:
name: production
url: https://api.sahkoinsinoorikilta.fi
when: manual
variables:
DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
script:
- docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME"
after_script:
- docker logout "$CI_REGISTRY"
stage: deploy
image: docker:25-cli
only:
- production
environment:
name: production
url: https://api.sahkoinsinoorikilta.fi
when: manual
variables:
DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME"
after_script:
- docker logout "$CI_REGISTRY"
docker_prune:dev:
image: docker:stable
stage: cleanup
only:
- schedules
environment:
name: dev
url: http://api.dev.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- docker system prune
after_script:
- docker logout "$CI_REGISTRY"
docker_prune:prod:
image: docker:stable
stage: cleanup
only:
- schedules
environment:
name: production
url: https://api.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script:
- docker system prune
after_script:
- docker logout "$CI_REGISTRY"
-1
View File
@@ -1 +0,0 @@
_
+12
View File
@@ -0,0 +1,12 @@
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
. "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ]
then
printf "${PURPLE}Failed to find virtualenv. Skipping pre-commit hook.\n${NC}"
exit 0
fi
npm run lint
+1 -4
View File
@@ -1,10 +1,7 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
source "${VIRTUAL_ENV}/bin/activate"
. "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ]
then
+1
View File
@@ -0,0 +1 @@
22.13.1
+1 -1
View File
@@ -1 +1 @@
3.9
3.12.9
+2
View File
@@ -0,0 +1,2 @@
python 3.12.9
poetry 2.1.1
+8 -7
View File
@@ -1,13 +1,13 @@
FROM python:3.9-slim-buster as builder
FROM python:3.12.9-slim-bullseye AS builder
ENV PYTHONUNBUFFERED 1
COPY . ./
ENV POETRY_VERSION=1.1.4
ENV POETRY_VERSION=2.1.1
RUN pip install pip==25.3
RUN pip install "poetry==$POETRY_VERSION"
RUN poetry export > requirements.txt
RUN poetry self add poetry-plugin-export
RUN poetry export --without-hashes --format=requirements.txt --output requirements.txt
FROM python:3.9-slim-buster as server
FROM python:3.12.9-slim-bullseye AS server
WORKDIR /app
COPY . ./
@@ -22,7 +22,8 @@ ENV PYTHONUNBUFFERED=1 \
PIP_DEFAULT_TIMEOUT=100
RUN apt-get update && apt-get install --no-install-recommends -y build-essential
RUN pip install -r requirements.txt
RUN pip install pip==25.3
RUN pip install --no-deps -r requirements.txt
RUN python manage.py collectstatic --noinput
CMD ["sh", "-c", "./production_entrypoint.sh"]
+84 -54
View File
@@ -1,79 +1,107 @@
# SIKWEB 2.0
# Web 2.0 Backend
A modern web app using a Django backend and an Angular frontend.
[Django](https://www.djangoproject.com/) backend containing multiple small applications and api for Next.js frontend.
## Components
* **Web app:** Backend for the main website.
* **Member register:** Data table app for viewing and modifying the member register, member applications and membership payments.
* **Kaehmy:** Form for creating and listing kaehmys
* **Ohlhafv:** Form for creating and listing ohlhafv challenges.
* **Infoscreen:** Angular-based slideshow app for the guild room's screens.
### Infoscreen
Angular-based slideshow app for the guild room's screens.
### Member register
Data table app for viewing and modifying the member register, member applications and membership payments.
### Web app
Mostly static website with an event calendar and news feed.
## Accessing the source
### Clone this repository and enter it
## Installation
Set up your SSH key authentication in GitLab Profile Settings. Then clone the repository and checkout the development branch:
```bash
git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git
cd web2.0-backend
git checkout develop
```
## Development
Copy env file for local use:
```bash
cp .env.dev .env
```
### Poetry
For depedencies and virtual environment, we use [poetry](https://python-poetry.org/).
```bash
python3 -m pip install poetry
```
The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with CMD
First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry:
```bash
python3 -m poetry config virtualenvs.in-project true
python -m pip install poetry==2.1.1
```
Start developing by install dependencies first
#### CMDs
Activate virtual environment in shell
```bash
python3 -m poetry shell
```
Install dependencies
Install dependencies with
```bash
poetry install
```
### npm scripts
Poetry is configured to install dependencies in a virtual environment, so you should see `.venv` folder in repo root.
We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm).
Activate virtual environment in shell
TODO: List scripts
```bash
eval $(poetry env activate)
```
### Node
We use Node.js for few development tasks, like linting.
Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm).
After installing install dependencies:
```bash
npm install
```
See [Linting](#linting) for more info
### Database
To run a local development database **[docker](https://docs.docker.com/engine/install/)** is recommended. If you want to additianally use a db management tool **[pgAdmin](https://www.pgadmin.org/download/)** is nice.
After installing docker use the following to create a database:
```bash
docker run --name sik.web.db -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres:12
```
## Development
Install dependencies with
```bash
poetry install
```
and make sure you are using Python from your virutal environment.
Virtual environment can be activated with
```bash
eval $(poetry env activate)
```
and you verify correct Python executable with
```bash
which python
# should return path similar to {your-system path}/web2.0-backend/.venv/bin/python
```
### Initializing data
Run the following `manage.py` commands. Do not run these in production without thinking!
Run the following `manage.py` commands to initialize a new database. Do not run these in production without thinking!
```bash
python manage.py createdefaultadmin # creates an admin user
python manage.py initialize # creates user groups
python manage.py createdummydata # creates dummy members to the member register
python manage.py migrate # run migrations
python manage.py createdefaultadmin # creates an admin user
python manage.py initialize # creates user groups
python manage.py createdummydata # creates dummy members to the member register
```
### Running
@@ -82,8 +110,6 @@ python manage.py createdummydata # creates dummy members to the member regis
python manage.py runserver
```
#### Visit the page
Visit [https://localhost:8000](https://localhost:8000) in your browser!
Using address `0.0.0.0` will bind to all IP addresses. Using `localhost` will only bind to your machine.
@@ -99,27 +125,29 @@ When you start working on a feature, create a feature branch for your changes. T
Example of creating a feature branch:
```bash
git checkout -b feature-error-page
git checkout -b feature-branch-name
```
When your changes are ready and the code works without errors, submit a merge request to `develop` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge.
When your changes are ready and the code works without errors, submit a merge request to `main` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge.
Bugfixes do not need their own feature branches and can be pushed straight to `develop`, but if the fix needs a notable amount of work, it should be done in a `bugfix` branch instead.
Bugfixes do not need their own feature branches and can be pushed straight to `main`, but if the fix needs a notable amount of work, it should be done in a `bugfix` branch instead.
Merge requests to `master` should be reviewed by multiple developers. Only a moderator can accept merge requests to `master`.
Merge requests to `main` should be reviewed by multiple developers. Only a moderator can accept merge requests to `production`.
### Linting
Lint python files using `pycodestyle` with
Lint python files using `black` with
```bash
pycodestyle --config=pycodestyle.cfg --count .
npm run lint:py # check changes
npm run lint:py:fix # fix errors
```
Lint javascript and markdown using `eslint` and `remark` with
```bash
npm test
npm run lint:md # markdown
npm run lint:js # javascript
```
Use an editor with linting capabilities to write pretty code that passes linting. Examples include _VSCode_, _Atom_ and _Pycharm_.
@@ -140,6 +168,8 @@ Tests are located in `tests.py` under every subproject.
Project is run in production with Docker. See `Dockerfile` for details.
For more information about deployment check **[infra](https://gitlab.com/sahkoinsinoorikilta/vtmk/infra)** repository.
## GitLab CI
All pushed changes go through the GitLab Continuous Integration, which consists of automated unit testing and linting. Make sure your changes pass both before merging to `develop` or `master`.
All pushed changes go through the GitLab Continuous Integration, which consists of automated unit testing and linting. Make sure your changes pass both before merging to `main` or `production`.
+15 -7
View File
@@ -1,22 +1,30 @@
version: '3'
services:
db:
image: postgres:12
volumes:
- dbdata:/var/lib/postgresql/data
ports:
- "5432:5432"
- 5432:5432
environment:
- POSTGRES_PASSWORD=postgres
web:
build: .
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend
env_file:
- .env
environment:
- DEPLOY_ENV=local
- HOST=localhost
- DEBUG=True
- SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
- DB_NAME=postgres
- DB_USER=postgres
- DB_PASSWD=postgres
- DB_HOST=db
- DB_PORT=5432
- EMAIL_API_KEY=
- GROUP_KEY=
- GOOGLE_CREDS='{}'
ports:
- "8000:8000"
- 8000:8000
depends_on:
- db
+23
View File
@@ -0,0 +1,23 @@
import globals from "globals";
import js from "@eslint/js";
export default [
{
ignores: ["**/.venv/", "**/collected_static/", "**/static/js/lib/**"],
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.jquery,
angular: true,
moment: true,
_: true
},
},
},
{
...js.configs.recommended
}
];
@@ -0,0 +1,28 @@
# Generated by Django 3.2.14 on 2022-08-01 19:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("infoscreen", "0007_lunchitem"),
]
operations = [
migrations.AlterField(
model_name="infoinstance",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="infoitem",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="rotation",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
]
+6 -1
View File
@@ -7,7 +7,7 @@ from django import forms
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
class InfoItem(models.Model):
@@ -16,6 +16,7 @@ class InfoItem(models.Model):
class __meta__:
abstract = True
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
# expire_date = None means never expiring item
expire_date = models.DateTimeField(blank=True, null=True)
@@ -316,6 +317,7 @@ class ExternalImageInfoItem(InfoItem):
class InfoInstance(models.Model):
"""Class for Info instance in Infoscreen."""
id = models.AutoField(primary_key=True)
rotation = models.ForeignKey(
"Rotation", related_name="instances", on_delete=models.CASCADE
)
@@ -356,6 +358,7 @@ class InfoInstance(models.Model):
class Rotation(models.Model):
"""Class for rotation model."""
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
def get_dict(self):
@@ -388,6 +391,7 @@ class Rotation(models.Model):
class ImageUploadForm(forms.Form):
"""Form used to handle imageuploads to infoscreen app."""
id = models.AutoField(primary_key=True)
name = forms.CharField()
image = forms.ImageField()
@@ -395,5 +399,6 @@ class ImageUploadForm(forms.Form):
class UploadFileForm(forms.Form):
"""Form used for uploading file."""
id = models.AutoField(primary_key=True)
name = forms.CharField()
video = forms.FileField()
+1 -1
View File
@@ -3,7 +3,7 @@ body {
}
#header:after {
content: " ";
content: " ";
display: block;
clear: both;
}
+2 -2
View File
@@ -12,7 +12,7 @@ body {
.event {
font-size: 100px;
font-weight: bold;
margin-left: 20px;
margin-left: 20px;
}
.event-col{
padding-top:1vh;
@@ -21,7 +21,7 @@ body {
.header-row{
margin: 30px;
margin-left: 20px;
margin-left: 20px;
font-size: 130px;
padding-bottom:20px;
color:#24a05f;
@@ -1,8 +1,8 @@
#infocontent {
width: 100%;
height: 100%;
position: fixed;
left: 0px;
width: 100%;
height: 100%;
position: fixed;
left: 0px;
top: 0px;
z-index: -1; /* Ensure div tag stays behind content; -999 might work, too. */
}
+3 -3
View File
@@ -31,10 +31,10 @@
max-height 200px;
}
#sossoimage {
#sossoimage {
height:300px;
position: relative;
left: 0px;
position: relative;
left: 0px;
top: 0px;
}
@@ -1,5 +1,5 @@
<link rel="stylesheet" href="/static/infoscreen/css/events.css">
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
<div class="container" ng-app="myApp" ng-controller="EventController">
<div class="header-row row">
<div class="col-sm-6">Tapahtuma</div>
+23 -23
View File
@@ -1,6 +1,6 @@
"""File containing infoscreen urls."""
from django.conf.urls import url
from django.urls import re_path
from django.conf import settings
from infoscreen.views import index
@@ -27,28 +27,28 @@ from infoscreen.views import createApyItem
from infoscreen.views import get_apy_json
urlpatterns = [
url(r"^$", default),
url(r"^admin$", admin),
url(r"^(?P<idx>\d+)$", index),
url(r"^items$", info_items),
url(r"^rotation/(?P<idx>\d+)$", rotation),
url(r"^rotations$", rotations),
url(r"^instance$", createInstance),
url(r"^instance/(?P<idx>\d+)$", deleteInstance),
url(r"^types$", info_types),
url(r"^delete_item/(?P<type_id>\d+)/(?P<idx>\d+)$", delete_info_item),
url(r"^create_external_image$", createExternalImageInfoItem),
url(r"^create_image$", create_image_item),
url(r"^create_video$", create_video_item),
url(r"^create_abbitem$", createABBItem),
url(r"^create_sossoitem$", createSossoItem),
url(r"^create_lunchitem$", createLunchItem),
url(r"^create_eventitem$", createEventItem),
url(r"^create_apyitem$", createApyItem),
url(r"^create_websiteitem$", createExternalWebsiteItem),
url(r"^create_rotation$", create_rotation),
url(r"^delete_rotation/(?P<id>\d+)$", delete_rotation),
url(r"^apyjson", get_apy_json),
re_path(r"^$", default),
re_path(r"^admin$", admin),
re_path(r"^(?P<idx>\d+)$", index),
re_path(r"^items$", info_items),
re_path(r"^rotation/(?P<idx>\d+)$", rotation),
re_path(r"^rotations$", rotations),
re_path(r"^instance$", createInstance),
re_path(r"^instance/(?P<idx>\d+)$", deleteInstance),
re_path(r"^types$", info_types),
re_path(r"^delete_item/(?P<type_id>\d+)/(?P<idx>\d+)$", delete_info_item),
re_path(r"^create_external_image$", createExternalImageInfoItem),
re_path(r"^create_image$", create_image_item),
re_path(r"^create_video$", create_video_item),
re_path(r"^create_abbitem$", createABBItem),
re_path(r"^create_sossoitem$", createSossoItem),
re_path(r"^create_lunchitem$", createLunchItem),
re_path(r"^create_eventitem$", createEventItem),
re_path(r"^create_apyitem$", createApyItem),
re_path(r"^create_websiteitem$", createExternalWebsiteItem),
re_path(r"^create_rotation$", create_rotation),
re_path(r"^delete_rotation/(?P<id>\d+)$", delete_rotation),
re_path(r"^apyjson", get_apy_json),
]
if settings.DEBUG:
+1 -1
View File
@@ -36,7 +36,7 @@ from infoscreen.models import (
@permission_required("infoscreen.change_infoinstance", raise_exception=True)
def admin(request, *args, **kwargs):
"""Render infoscreen admin page."""
return render(request, "infoscreen:infoscreen_admin.html", {})
return render(request, "infoscreen/infoscreen_admin.html", {})
def create_item_generator(model):
+1 -1
View File
@@ -15,7 +15,7 @@ import requests
@require_http_methods(["GET"])
def index(request, idx, *args, **kwargs):
"""Render infoscreen index page."""
return render(request, "infoscreen_index.html", {"rotation": idx})
return render(request, "infoscreen/infoscreen_index.html", {"rotation": idx})
@require_http_methods(["GET"])
+6 -6
View File
@@ -1,20 +1,20 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from kaehmy.models import PresetRole, CustomRole, Application, Comment, KaehmyBaseRole
from kaehmy.models import PresetRole, CustomRole, Application, Comment, BaseRole
class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple):
option_template_name = "checkbox_option.html"
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
self, name, formIterator, label, selected, index, subindex=None, attrs=None
):
dic = super(CheckboxSelectMultiple, self).create_option(
name, value, label, selected, index, subindex, attrs
name, formIterator, label, selected, index, subindex, attrs
)
description = PresetRole.objects.get(id=value).description
description = PresetRole.objects.get(id=formIterator.value).description
dic["description"] = description
return dic
@@ -57,7 +57,7 @@ class ApplicationForm(forms.ModelForm):
self.fields["custom_roles"].label = _("Custom roles")
self.fields["custom_roles"].queryset = CustomRole.objects.all()
for cat_id, category in KaehmyBaseRole.CATEGORIES:
for cat_id, category in BaseRole.CATEGORIES:
key = "preset_roles_{}".format(cat_id)
qset = PresetRole.objects.filter(category=cat_id).order_by(
"category", "-is_board"
@@ -0,0 +1,18 @@
# Generated by Django 3.2.14 on 2022-08-01 19:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0006_delete_telegramchannel"),
]
operations = [
migrations.AlterField(
model_name="commentparent",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
]
+44
View File
@@ -0,0 +1,44 @@
# Generated by Django 3.2.14 on 2022-08-03 20:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0007_alter_commentparent_id"),
]
operations = [
migrations.CreateModel(
name="BaseRole",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("name", models.CharField(max_length=255, verbose_name="Name")),
("is_board", models.BooleanField(verbose_name="Board member")),
(
"category",
models.CharField(
choices=[
("corporate", "Corporate affairs"),
("freshman", "Freshmen"),
("international", "International"),
("external", "External affairs"),
("media", "Media"),
("tech", "Technology"),
("wellbeing", "Wellbeing"),
("elepaja", "Elepaja"),
("ceremonies", "Ceremonies"),
("studies", "Studies"),
("sosso", "Sössö magazine"),
("alumni", "Alumni relations"),
("others", "Others"),
],
default="others",
max_length=255,
verbose_name="Category",
),
),
],
),
]
@@ -0,0 +1,29 @@
# Generated by Django 2.2.28 on 2022-07-26 17:15
from unicodedata import category
from django.db import migrations
def copyBaseRolesToNewTable(apps, schema_editor):
Old = apps.get_model("kaehmy", "KaehmyBaseRole")
New = apps.get_model("kaehmy", "BaseRole")
for bases in Old.objects.all():
New.objects.create(
id=bases.id,
name=bases.name,
is_board=bases.is_board,
category=bases.category,
)
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0008_baserole"),
]
operations = [
migrations.RunPython(
copyBaseRolesToNewTable, reverse_code=migrations.RunPython.noop
),
]
@@ -0,0 +1,51 @@
# Generated by Django 2.2.28 on 2022-07-26 17:33
from django.db import migrations, models
import django.db.models.deletion
from sikweb.custom_operations import AlterModelBases
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0009_auto_20220726_2015"),
]
operations = [
AlterModelBases("customrole", (models.Model,)),
AlterModelBases("presetrole", (models.Model,)),
migrations.AlterField(
model_name="customrole",
name="kaehmybaserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="BaseRole",
),
),
migrations.AlterField(
model_name="presetrole",
name="kaehmybaserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="BaseRole",
),
),
migrations.RenameField(
model_name="customrole",
old_name="kaehmybaserole_ptr",
new_name="baserole_ptr",
),
migrations.RenameField(
model_name="presetrole",
old_name="kaehmybaserole_ptr",
new_name="baserole_ptr",
),
]
@@ -0,0 +1,16 @@
# Generated by Django 3.2.14 on 2022-08-03 20:19
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0010_auto_20220726_2033"),
]
operations = [
migrations.DeleteModel(
name="KaehmyBaseRole",
),
]
@@ -0,0 +1,65 @@
# Generated by Django 4.2.24 on 2025-10-13 14:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0011_delete_kaehmybaserole"),
]
operations = [
migrations.AlterField(
model_name="baserole",
name="category",
field=models.CharField(
choices=[
("board", "Board"),
("corporate", "Corporate affairs"),
("freshman", "Freshmen"),
("international", "International"),
("siwa", "SIK's free time"),
("media", "Media"),
("tech", "Technology"),
("wellbeing", "Wellbeing"),
("sikpaja", "Sik-paja"),
("ceremonies", "Ceremonies"),
("studies", "Studies"),
("sosso", "Sössö magazine"),
("pota", "PoTa"),
("alumni", "Alumni relations"),
("n", "N"),
("others", "Others"),
],
default="others",
max_length=255,
verbose_name="Category",
),
),
migrations.AlterField(
model_name="customrole",
name="baserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="kaehmy.baserole",
),
),
migrations.AlterField(
model_name="presetrole",
name="baserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="kaehmy.baserole",
),
),
]
+19 -15
View File
@@ -1,42 +1,45 @@
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from webapp.models import BaseRole
# TODO: Move BaseRole to Kaehmt App; will fuck up the DB since table is removed, if no data migration is done before-hand.
# Either reconstruct all kaehmy roles from scratch then, or do these migrations:
# 1. Create table here
# 2. Data migrate from webapp BaseRole to new kaehmy BaseRole
# 3. Delete webapp BaseRole table
from django.utils.translation import gettext_lazy as _
VERBOSE_NAME = _("Kaehmy")
class KaehmyBaseRole(BaseRole):
"""ABC"""
class BaseRole(models.Model):
"""Base model for occupations/roles."""
id = models.AutoField(primary_key=True)
name = models.CharField(_("Name"), max_length=255)
is_board = models.BooleanField(_("Board member"))
CATEGORIES = (
("board", _("Board")),
("corporate", _("Corporate affairs")),
("freshman", _("Freshmen")),
("international", _("International")),
("external", _("External affairs")),
("siwa", _("SIK's free time")),
("media", _("Media")),
("tech", _("Technology")),
("wellbeing", _("Wellbeing")),
("elepaja", _("Elepaja")),
("sikpaja", _("Sik-paja")),
("ceremonies", _("Ceremonies")),
("studies", _("Studies")),
("sosso", _("Sössö magazine")),
("pota", _("PoTa")),
("alumni", _("Alumni relations")),
("n", _("N")),
("others", _("Others")),
)
category = models.CharField(
_("Category"), choices=CATEGORIES, default="others", max_length=255
)
def __str__(self):
n = self.name.capitalize()
return "{} ({})".format(n, _("board member")) if self.is_board else n
class PresetRole(KaehmyBaseRole):
class PresetRole(BaseRole):
"""Model for kaehmy role."""
description = models.TextField(_("Description"))
@@ -46,7 +49,7 @@ class PresetRole(KaehmyBaseRole):
verbose_name_plural = _("Preset kaehmy roles")
class CustomRole(KaehmyBaseRole):
class CustomRole(BaseRole):
"""Model representing a user-specified custom occupation."""
class Meta:
@@ -56,6 +59,7 @@ class CustomRole(KaehmyBaseRole):
class CommentParent(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(_("Name"), max_length=255, default="")
email = models.EmailField(_("Email"), default="")
timestamp = models.DateTimeField(_("Timestamp"), default=timezone.now)
+4 -6
View File
@@ -5,12 +5,6 @@
margin-right: auto;
}
body {
max-width: 1000px;
margin-left: auto !important;
margin-right: auto !important;
}
div.tooltip-inner {
max-width: 25rem;
}
@@ -28,6 +22,10 @@ div.tooltip-inner {
.kaehmy-content {
padding-left: 0.5rem;
padding-right: 0.5rem;
max-width: 1000px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
p {
+2 -5
View File
@@ -3,13 +3,9 @@
}
footer {
/* position: absolute; */
bottom: 0;
width: 100%;
height: 60px; /* Set the fixed height of the footer here */
/* line-height: 60px; /* Vertically center the text there */
margin-top: 2rem;
margin-bottom: 1rem;
margin: 1rem;
}
footer .container .col .nav .nav-item {
@@ -26,6 +22,7 @@ footer .container .col .nav .nav-item {
.lang-select {
width: 10rem;
margin-bottom: 1rem;
display: inline-block;
}
+18 -27
View File
@@ -1,37 +1,28 @@
.header-content {
.kaehmy-header {
background-color: #0c2938;
}
.header-content .logo {
.kaehmy-header-content {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.header-content .logo img {
display: block;
height: auto;
margin: auto;
}
.kaehmy-banner {
max-width: 1000px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
@media screen and (min-width: 1000px) {
.kaehmy_header-content {
position: absolute;
left: 0;
top: 0;
background-color: #0c2938;
width: 100%;
}
.kaehmy_header {
margin-bottom: 331px;
}
}
.kaehmy-banner-image {
width: 100%;
max-height: 10rem;
max-width: 100%;
}
.heading {
display: flex;
place-content: center;
flex-direction: column;
text-align: center;
margin: 1rem;
}
+7 -3
View File
@@ -1,11 +1,15 @@
.kaehmy_navigation {
margin-bottom: 10px;
}
.navbar-border {
border-bottom: 2px solid #282b3b;
}
.navbar-light .navbar-nav .nav-link {
color: black;
}
.navbar {
max-width: 1000px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
+1 -1
View File
@@ -1,6 +1,6 @@
import django_tables2 as tables
from django.db.models import Count, Q
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from kaehmy.models import Application
-24
View File
@@ -1,24 +0,0 @@
{% load i18n %}
{% load static %}
{% load staticfiles %}
<footer style="text-align: center">
<div>
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
<span>
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</span>
</form>
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
</div>
</footer>
-7
View File
@@ -1,7 +0,0 @@
{% load i18n %}
<div class="kaehmy_header-content">
<div class="kaehmy-banner logo">
<a href="/kaehmy"><img class="kaehmy-banner-image" src="/static/kaehmy/img/kaehmy_banner.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a>
</div>
</div>
+8 -8
View File
@@ -1,8 +1,8 @@
"""Kaehmy urls."""
from django.conf.urls import url
from django.urls import re_path
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from kaehmy.views import view
from kaehmy.views import list_view
@@ -13,12 +13,12 @@ from kaehmy.views import export_view
urlpatterns = [
# kaehmy
url(r"^new", view),
url(r"^submit", submit),
url(r"^add_comment", comment),
url(r"^statistics", statistics_view),
url(r"^export", export_view),
url(r"^$", list_view),
re_path(r"^new", view),
re_path(r"^submit", submit),
re_path(r"^add_comment", comment),
re_path(r"^statistics", statistics_view),
re_path(r"^export", export_view),
re_path(r"^$", list_view),
]
if settings.DEBUG:
+19 -22
View File
@@ -1,15 +1,12 @@
from django.db.models import Count
from django.shortcuts import render, redirect
from django.contrib.auth import login, logout, authenticate
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import permission_required, login_required
from django.conf import settings
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.template.loader import render_to_string
import logging
import requests
from dealer.git import git
from sikweb.settings import URL
from members.views.utils import *
@@ -56,7 +53,7 @@ def list_view(request, *args, **kwargs):
"filter_options": filter_options,
}
return render(request, "kaehmy:list.html", context)
return render(request, "kaehmy/list.html", context)
@ensure_csrf_cookie
@@ -68,20 +65,21 @@ def comment(request, *args, **kwargs):
if form.is_valid():
comment = form.save()
name = comment.name
url = f"https://{URL}/kaehmy"
to_email = comment.parent.email
subject = "Kaehmyysi tai kommenttiisi on vastattu!"
email_body = (
f"{name.capitalize()} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n"
"Käy lukemassa viesti osoitteessa https://{URL}/kaehmy"
message = render_to_string(
"kaehmy/email_comment.html", {"name": name, "url": url}
)
send_email(to=to_email, subject=subject, body=email_body)
send_email(to=to_email, subject=subject, body=message, html=True)
logging.debug(f"Sent kaehmy comment email to recipient <{to_email}>")
return redirect("/kaehmy")
else:
context = {"error": form.errors}
return render(request, "kaehmy:error.html", context)
return render(request, "kaehmy/error.html", context)
@require_http_methods(["GET"])
@@ -106,14 +104,14 @@ def statistics_view(request, *args, **kwargs):
"application_count": len(applications),
"role_list": role_list,
}
return render(request, "kaehmy:statistics.html", context)
return render(request, "kaehmy/statistics.html", context)
@require_http_methods(["GET"])
def view(request, *args, **kwargs):
"""Render Kaehmy form page."""
form = ApplicationForm()
return render(request, "kaehmy:kaehmy.html", {"form": form})
return render(request, "kaehmy/kaehmy.html", {"form": form})
@ensure_csrf_cookie
@@ -125,6 +123,7 @@ def submit(request, *args, **kwargs):
application = form.save()
custom_name = form.cleaned_data.get("custom_role_name")
custom_is_board = form.cleaned_data.get("custom_role_is_board")
kaehmybot_allowed = form.cleaned_data.get("kaehmybot") == "1"
if len(custom_name) > 0:
custom_role = CustomRole(name=custom_name, is_board=custom_is_board)
@@ -133,22 +132,20 @@ def submit(request, *args, **kwargs):
url = f"https://{URL}/kaehmy"
name = form.cleaned_data.get("name", "Anonymous")
email_body = (
f"Moikka {name}!\r\n\r\nHienoa, että kilta kiinnostaa! Kaehmysi on vastaanotettu.\r\n"
"Mahdollisista kommenteista tulee ilmoitus sähköpostitse.\r\n\r\n"
"Käy katsomassa kaehmytilanne osoitteessa {url}"
)
to_email = form.cleaned_data.get("email", "")
subject = "Arwokas kirjattu kirje mahdolliselle tulewalle kiltahenkilölle"
message = render_to_string(
"kaehmy/email_kaehmy.html", {"name": name, "url": url}
)
send_email(to_email, subject, email_body)
send_email(to=to_email, subject=subject, body=message, html=True)
logging.debug(f"Sent kaehmy email to recipient <{to_email}>")
processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy")
else:
context = {"error": form.errors}
return render(request, "kaehmy:error.html", context)
return render(request, "kaehmy/error.html", context)
return HttpResponseRedirect("/kaehmy")
@@ -175,4 +172,4 @@ def export_view(request, *args, **kwargs):
"non_board_table": make_table(non_board),
"board_table": make_table(board),
}
return render(request, "kaehmy:export.html", context)
return render(request, "kaehmy/export.html", context)
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -3,8 +3,13 @@
from django.contrib import admin
from members.models import Member, Request, Payment
# Register your models here.
admin.site.register(Member)
class MemberAdmin(admin.ModelAdmin):
search_fields = ("first_name", "last_name", "email", "POR")
admin.site.register(Member, MemberAdmin)
admin.site.register(Request)
admin.site.register(Payment)
+1 -1
View File
@@ -2,7 +2,7 @@
from django import forms
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from members.models import Member, Payment, Request
@@ -0,0 +1,18 @@
# Generated by Django 3.2.14 on 2022-08-01 19:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("members", "0019_auto_20171029_1143"),
]
operations = [
migrations.AlterField(
model_name="payment",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 3.2.14 on 2022-08-01 19:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("members", "0020_alter_payment_id"),
]
operations = [
migrations.AlterField(
model_name="member",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
migrations.AlterField(
model_name="request",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
]
+3 -1
View File
@@ -2,13 +2,14 @@
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.db.models import Q, OuterRef, Subquery
class BaseMember(models.Model):
"""Abstract base model for member."""
id = models.AutoField(primary_key=True)
first_name = models.CharField(_("First name"), max_length=127)
last_name = models.CharField(_("Last name"), max_length=127)
email = models.EmailField(_("Email"), unique=True)
@@ -60,6 +61,7 @@ class Payment(models.Model):
class Meta:
permissions = (("read_payment", "Can see payment in list"),)
id = models.AutoField(primary_key=True)
date = models.DateTimeField(_("Date"), default=timezone.now)
source = models.CharField(
_("Source"),
+1 -1
View File
@@ -1,7 +1,7 @@
"""File containing member application django tables."""
import django_tables2 as tables
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import F, OuterRef, Subquery
from django.utils import timezone
@@ -1,11 +0,0 @@
{% load i18n %}
{% trans "Moi" %} {{ first_name }}!
{% trans "Onnittelut! Sinut on hyväksytty Sähköinsinöörikillan jäseneksi." %}
{% trans "Käy kurkkaamassa killan nettisivuilta" %} (https://sik.ayy.fi) {% trans "tulevia tapahtumia ja piipahda kiltahuoneella tutustumassa uusiin kiltatovereihisi!" %}
{% trans "Liity myös killan TG-kanaville" %}:
{% trans "SIK" %}: https://t.me/joinchat/A6EViD5FCWLxPcXCggY7hw
{% trans "SIK-fuksit 2019" %}: http://tinyurl.com/sikfuksit19-tg
{% trans "SIK-fuksit 2019 -tiedotuskanava" %}: http://tinyurl.com/sikfuksit19-tiedotus
+13 -5
View File
@@ -5,6 +5,7 @@ from unittest import skip
from django.contrib.auth.models import User
from members.models import Member, Payment, Request
from rest_framework.authtoken.models import Token
from datetime import timezone
import logging
@@ -108,8 +109,11 @@ class MemberRegisterTestCase(TestCase):
content = resp.content
arrays = pyexcel.get_array(file_content=content, file_type="xlsx")
created = Payment.objects.get(member__email="tidus@tester.fi").date.strftime(
"%Y-%m-%d %H:%M:%S"
created = (
Payment.objects.get(member__email="tidus@tester.fi")
.date.replace(tzinfo=timezone.utc)
.astimezone(tz=None)
.strftime("%Y-%m-%d %H:%M:%S")
)
tidus_array = ["Tidus Tester", created, "AYY"]
self.assertIn(tidus_array, arrays)
@@ -122,9 +126,13 @@ class MemberRegisterTestCase(TestCase):
content = resp.content
arrays = pyexcel.get_array(file_content=content, file_type="xlsx")
submitted = Request.objects.get(
email="liisa.mattila@pylly.com"
).submitted.strftime("%Y-%m-%d %H:%M:%S")
submitted = (
Request.objects.get(email="liisa.mattila@pylly.com")
.submitted.replace(tzinfo=timezone.utc)
.astimezone(tz=None)
.strftime("%Y-%m-%d %H:%M:%S")
)
liisa_array = [
"Liisa",
"Mattila",
+33 -33
View File
@@ -1,6 +1,6 @@
"""File containing Member application URLs."""
from django.conf.urls import url
from django.urls import re_path
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
@@ -42,61 +42,61 @@ from members.views import application_submit
urlpatterns = [
# landing page
url(r"^$", member_list),
url(r"^list$", member_list),
re_path(r"^$", member_list),
re_path(r"^list$", member_list),
# add member form view
url(r"^add$", member_add),
re_path(r"^add$", member_add),
# add many members view
url(r"^add_many$", member_add_many),
re_path(r"^add_many$", member_add_many),
# edit member information view
url(r"^edit/(?P<index>\d+)$", member_edit),
re_path(r"^edit/(?P<index>\d+)$", member_edit),
# delete confirmation view
url(r"^delete_member_confirm/(?P<index>\d+)$", member_delete_confirm),
re_path(r"^delete_member_confirm/(?P<index>\d+)$", member_delete_confirm),
# list all member applications
url(r"^applications$", application_list),
re_path(r"^applications$", application_list),
# edit member application
url(r"^edit_application/(?P<index>\d+)$", application_edit),
re_path(r"^edit_application/(?P<index>\d+)$", application_edit),
# post request targets
url(r"^submit_member$", member_submit),
url(r"^update_member$", member_update),
url(r"^delete_member$", member_delete),
url(r"^submit_payment$", payment_submit),
url(r"^update_payment$", payment_update),
url(r"^delete_payment$", payment_delete),
url(r"^submit_application$", application_submit),
url(r"^accept_application$", application_accept),
url(r"^delete_application$", application_delete),
re_path(r"^submit_member$", member_submit),
re_path(r"^update_member$", member_update),
re_path(r"^delete_member$", member_delete),
re_path(r"^submit_payment$", payment_submit),
re_path(r"^update_payment$", payment_update),
re_path(r"^delete_payment$", payment_delete),
re_path(r"^submit_application$", application_submit),
re_path(r"^accept_application$", application_accept),
re_path(r"^delete_application$", application_delete),
# the actual member application form
url(r"^application/$", application_form),
re_path(r"^application/$", application_form),
# delete confirmation view for applications
url(r"^delete_application_confirm/(?P<index>\d+)$", application_delete_confirm),
re_path(r"^delete_application_confirm/(?P<index>\d+)$", application_delete_confirm),
# list all payment events
url(r"^payments$", payment_list),
re_path(r"^payments$", payment_list),
# add payment event
url(r"^add_payment$", payment_add),
re_path(r"^add_payment$", payment_add),
# edit payment event
url(r"^edit_payment/(?P<index>\d+)$", payment_edit),
re_path(r"^edit_payment/(?P<index>\d+)$", payment_edit),
# delete confirmation view
url(r"^delete_payment_confirm/(?P<index>\d+)$", payment_delete_confirm),
re_path(r"^delete_payment_confirm/(?P<index>\d+)$", payment_delete_confirm),
# post endpoint for confirming multiple entries
url(r"^add_many_confirm$", add_many_confirm),
re_path(r"^add_many_confirm$", add_many_confirm),
# settings page
url(r"^settings$", settings_page),
re_path(r"^settings$", settings_page),
# send CSV member data by POST
url(r"^import_csv", import_csv),
re_path(r"^import_csv", import_csv),
# export members as excel file
url(r"export_members", export_members_excel),
url(r"export_payments", export_payments_excel),
url(r"export_applications", export_applications_excel),
re_path(r"export_members", export_members_excel),
re_path(r"export_payments", export_payments_excel),
re_path(r"export_applications", export_applications_excel),
# rest api url
url(r"^api/members/(?P<pk>\d+)$", MemberDetail.as_view()),
re_path(r"^api/members/(?P<pk>\d+)$", MemberDetail.as_view()),
# member select autocomplete view
url(
re_path(
r"^member-autocomplete/$",
MemberAutoComplete.as_view(),
name="member-autocomplete",
),
url(r"^check", CheckByEmail.as_view()),
re_path(r"^check", CheckByEmail.as_view()),
]
if settings.DEBUG:
+1 -1
View File
@@ -1,4 +1,4 @@
"""File containing Members application views."""
from django.conf import settings
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
+15 -9
View File
@@ -4,7 +4,7 @@ from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.forms.models import model_to_dict
from django.template.loader import render_to_string
@@ -12,6 +12,7 @@ import logging
import html
from webapp.utils import send_email
from webapp.utils import add_to_mailinglist
from members.views.utils import *
from members.tables import RequestTable
@@ -41,7 +42,7 @@ def application_list(request, *args, **kwargs):
"application_count": application_count,
"notification": request.GET.get("notification", None),
}
return render(request, "application_list.html", context)
return render(request, "members/application_list.html", context)
@ensure_csrf_cookie
@@ -57,7 +58,9 @@ def application_edit(request, *args, **kwargs):
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
return render(
request, "application_edit.html", {"application_id": i, "form": form}
request,
"members/application_edit.html",
{"application_id": i, "form": form},
)
@@ -86,6 +89,9 @@ def application_accept(request, *args, **kwargs):
).format(application.email),
)
if application.jas:
add_to_mailinglist(application.email)
member = application.to_member()
member.save()
application.delete()
@@ -101,10 +107,10 @@ def application_accept(request, *args, **kwargs):
subject = _("Jäsenhakemuksesi Sähköinsinöörikiltaan on hyväksytty!")
message = render_to_string(
"members:email_application_accept.html",
"members/email_application_accept.html",
{"first_name": application.first_name},
)
send_email(member.email, subject, message)
send_email(member.email, subject, message, True)
return HttpResponseRedirect(
"/members/list?notification={}".format(html.escape(notification))
@@ -158,7 +164,7 @@ def application_delete_confirm(request, *args, **kwargs):
form = ApplicationForm(instance=application)
return render(
request,
"application_delete_confirm.html",
"members/application_delete_confirm.html",
{"application_id": i, "form": form},
)
@@ -167,7 +173,7 @@ def application_delete_confirm(request, *args, **kwargs):
def application_form(request, *args, **kwargs):
"""Render member application form."""
form = ApplicationForm()
return render(request, "application_index.html", {"form": form})
return render(request, "members/application_index.html", {"form": form})
@ensure_csrf_cookie
@@ -186,7 +192,7 @@ def application_submit(request, *args, **kwargs):
)
message = render_to_string(
"members:email_application_submit.html",
"members/email_application_submit.html",
{
"application": application,
"ayy": _("Kyllä") if application.AYY else _("Ei"),
@@ -195,6 +201,6 @@ def application_submit(request, *args, **kwargs):
)
send_email(email, subject, message)
finally:
return render(request, "application_success.html", {})
return render(request, "members/application_success.html", {})
else:
return error_view(request, form.errors)
+11 -7
View File
@@ -10,7 +10,7 @@ from django.http import (
HttpResponseForbidden,
)
from django.conf import settings
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.forms.models import model_to_dict
from dal import autocomplete
from django.utils import timezone
@@ -70,7 +70,7 @@ def member_list(request, *args, **kwargs):
"paid_count": len(queryset.filter(last_paid__gte=filter_date)),
"notification": request.GET.get("notification", None),
}
return render(request, "member_list.html", context)
return render(request, "members/member_list.html", context)
@ensure_csrf_cookie
@@ -80,7 +80,7 @@ def member_list(request, *args, **kwargs):
def member_add(request, *args, **kwargs):
"""Render add member page."""
form = MemberForm()
return render(request, "member_add.html", {"form": form})
return render(request, "members/member_add.html", {"form": form})
@ensure_csrf_cookie
@@ -96,7 +96,9 @@ def member_delete_confirm(request, *args, **kwargs):
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
return render(
request, "member_delete_confirm.html", {"member_id": i, "form": form}
request,
"members/member_delete_confirm.html",
{"member_id": i, "form": form},
)
@@ -106,7 +108,7 @@ def member_delete_confirm(request, *args, **kwargs):
@permission_required("members.add_member", raise_exception=True)
def member_add_many(request, *args, **kwargs):
"""Render add multiple members page."""
return render(request, "member_add_many.html", {})
return render(request, "members/member_add_many.html", {})
@ensure_csrf_cookie
@@ -233,7 +235,9 @@ def member_edit(request, *args, **kwargs):
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
return render(request, "member_edit.html", {"member_id": i, "form": form})
return render(
request, "members/member_edit.html", {"member_id": i, "form": form}
)
@method_decorator(login_required(login_url="/admin/login"), name="dispatch")
@@ -245,7 +249,7 @@ class MemberAutoComplete(autocomplete.Select2QuerySetView):
if self.q:
qs = Member.find_members_by_name(self.q)
return qs
return qs.order_by("last_name")
class CheckByEmail(APIView):
+9 -5
View File
@@ -4,7 +4,7 @@ from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.forms.models import model_to_dict
import logging
@@ -43,7 +43,7 @@ def payment_list(request, *args, **kwargs):
"payment_count": len(payments),
"notification": request.GET.get("notification", None),
}
return render(request, "payment_list.html", context)
return render(request, "members/payment_list.html", context)
@ensure_csrf_cookie
@@ -53,7 +53,7 @@ def payment_list(request, *args, **kwargs):
def payment_add(request, *args, **kwargs):
"""Render add payment form."""
form = PaymentForm()
return render(request, "payment_add.html", {"form": form})
return render(request, "members/payment_add.html", {"form": form})
@ensure_csrf_cookie
@@ -92,7 +92,9 @@ def payment_edit(request, *args, **kwargs):
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
return render(request, "payment_edit.html", {"payment_id": i, "form": form})
return render(
request, "members/payment_edit.html", {"payment_id": i, "form": form}
)
@ensure_csrf_cookie
@@ -108,7 +110,9 @@ def payment_delete_confirm(request, *args, **kwargs):
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
return render(
request, "payment_delete_confirm.html", {"payment_id": i, "form": form}
request,
"members/payment_delete_confirm.html",
{"payment_id": i, "form": form},
)
+5 -5
View File
@@ -4,7 +4,7 @@ from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.conf import settings
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from django.forms.models import model_to_dict
from django_tables2.config import RequestConfig
@@ -46,7 +46,7 @@ class MemberDetail(generics.RetrieveAPIView):
def error_view(request, message, status=400):
return render(request, "error.html", {"error": message}, status=400)
return render(request, "members/error.html", {"error": message}, status=400)
def validate_recaptcha(response):
@@ -100,7 +100,7 @@ def convert_table_to_html(table, request):
@permission_required("members.change_member", raise_exception=True)
def settings_page(request, *args, **kwargs):
"""Render member app settings page."""
return render(request, "settings.html", {})
return render(request, "members/settings.html", {})
@ensure_csrf_cookie
@@ -135,7 +135,7 @@ def import_csv(request, *args, **kwargs):
member_table = MemberTable(
result.members,
request=request,
exclude=["id", "options"],
exclude=["id", "options", "last_paid"],
attrs={"class": "table table-bordered table-hover"},
)
@@ -155,7 +155,7 @@ def import_csv(request, *args, **kwargs):
request.session["models"] = result
request.session["payment_source"] = payment_source
context = {"members": member_table_html, "payments": payment_table_html}
return render(request, "member_add_many_confirm.html", context)
return render(request, "members/member_add_many_confirm.html", context)
def make_excel_response(Resource):
+1 -1
View File
@@ -1,7 +1,7 @@
"""File containing Ohlhafv forms."""
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
from ohlhafv.models import OhlhafvChallenge
@@ -0,0 +1,18 @@
# Generated by Django 3.2.14 on 2022-08-01 19:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("ohlhafv", "0002_remove_ohlhafvchallenge_challenger_email"),
]
operations = [
migrations.AlterField(
model_name="ohlhafvchallenge",
name="id",
field=models.AutoField(primary_key=True, serialize=False),
),
]
+2 -1
View File
@@ -5,7 +5,7 @@ from django.utils import timezone
from datetime import timedelta
from django.contrib.auth.models import User
from webapp.utils import month_from_now
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
from auditlog.registry import auditlog
from phonenumber_field.modelfields import PhoneNumberField
@@ -29,6 +29,7 @@ class OhlhafvChallenge(models.Model):
("Team", _("Team Challenge (1 x 0.33 L, 2 x 0.5 L, 1 x 1.0 L)")),
)
id = models.AutoField(primary_key=True)
challenger = models.CharField(_("Challenger"), max_length=255)
victim = models.CharField(_("Victim"), max_length=255)
victim_email = models.EmailField(_("Victim email"))
+1
View File
@@ -4,6 +4,7 @@
.navbar-border {
border-bottom: 2px solid #282b3b;
border-radius: 0px 0px 8px 8px;
}
.navbar-light .navbar-nav .nav-link {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

+1 -1
View File
@@ -1,6 +1,6 @@
import django_tables2 as tables
from django.db.models import Count, Q
from django.utils.translation import ugettext as _
from django.utils.translation import gettext as _
from ohlhafv.models import OhlhafvChallenge
-25
View File
@@ -1,25 +0,0 @@
{% load i18n %}
{% load static %}
{% load staticfiles %}
<link rel="stylesheet" href="{% static "ohlhafv/css/footer.css" %}">
<footer style="text-align: center">
<div>
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
<span>
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</span>
</form>
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
</div>
</footer>
+5 -5
View File
@@ -1,16 +1,16 @@
"""Ohlhafv urls."""
from django.conf.urls import url
from django.urls import re_path
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from ohlhafv.views import *
urlpatterns = [
# ohlhafv
url(r"^submit", ohlhafv_submit),
url(r"^list", ohlhafv_list),
url(r"^$", ohlhafv_view),
re_path(r"^submit", ohlhafv_submit),
re_path(r"^list", ohlhafv_list),
re_path(r"^$", ohlhafv_view),
]
if settings.DEBUG:
+5 -5
View File
@@ -3,7 +3,7 @@ from django.shortcuts import render
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from sikweb.settings import URL
@@ -18,7 +18,7 @@ from webapp.models import processHooks
def ohlhafv_view(request, *args, **kwargs):
"""Render Ohlhafv form page."""
form = OhlhafvForm()
return render(request, "ohlhafv:new.html", {"form": form})
return render(request, "ohlhafv/new.html", {"form": form})
@ensure_csrf_cookie
@@ -32,7 +32,7 @@ def ohlhafv_submit(request, *args, **kwargs):
url = f"https://{URL}/ohlhafv/list"
email_body = render_to_string(
"ohlhafv:email.html",
"ohlhafv/email.html",
{
"challenge": challenge,
"url": url,
@@ -46,7 +46,7 @@ def ohlhafv_submit(request, *args, **kwargs):
try:
webhook_message = render_to_string(
"ohlhafv:tgmsg.tpl", {"challenge": challenge, "url": url}
"ohlhafv/tgmsg.tpl", {"challenge": challenge, "url": url}
)
processHooks(message=webhook_message, eventType="ohlhafv")
except Exception:
@@ -67,4 +67,4 @@ def ohlhafv_list(request, *args, **kwargs):
"challenges": challenges,
"challenge_count": len(challenges),
}
return render(request, "ohlhafv:list.html", context)
return render(request, "ohlhafv/list.html", context)
+4756 -3742
View File
File diff suppressed because it is too large Load Diff
+10 -8
View File
@@ -6,10 +6,10 @@
"lint": "run-p lint:js lint:md lint:py",
"lint:js": "eslint .",
"lint:md": "remark .",
"lint:py": "black --diff .",
"lint:py": "black --diff --check .",
"lint:py:fix": "black .",
"lint:py-type": "pyright",
"prepare": "husky install"
"prepare": "husky"
},
"repository": {
"type": "git",
@@ -17,13 +17,15 @@
},
"author": "SIK ry",
"license": "ISC",
"dependencies": {
"eslint": "^7.28.0",
"husky": "^6.0.0",
"devDependencies": {
"@eslint/js": "^9.20.0",
"eslint": "^9.20.0",
"globals": "^15.14.0",
"husky": "^9.1.7",
"npm-run-all": "^4.1.5",
"pyright": "^1.1.149",
"remark-cli": "^9.0.0",
"remark-preset-lint-recommended": "^5.0.0"
"pyright": "^1.1.393",
"remark-cli": "^12.0.1",
"remark-preset-lint-recommended": "^7.0.1"
},
"remarkConfig": {
"plugins": [
Generated
-1297
View File
File diff suppressed because it is too large Load Diff
+19 -1
View File
@@ -10,6 +10,24 @@ fi
if test -f "$DB_PASSWD_FILE"; then
export DB_PASSWD=$(cat $DB_PASSWD_FILE)
fi
if test -f "$G_PRIVATE_KEY_ID_FILE"; then
export G_PRIVATE_KEY_ID=$(cat $G_PRIVATE_KEY_ID_FILE)
fi
if test -f "$G_PRIVATE_KEY_FILE"; then
export G_PRIVATE_KEY="$(cat $G_PRIVATE_KEY_FILE)"
fi
if test -f "$G_CLIENT_EMAIL_FILE"; then
export G_CLIENT_EMAIL=$(cat $G_CLIENT_EMAIL_FILE)
fi
if test -f "$G_CLIENT_ID_FILE"; then
export G_CLIENT_ID=$(cat $G_CLIENT_ID_FILE)
fi
if test -f "$G_CLIENT_URL_FILE"; then
export G_CLIENT_URL=$(cat $G_CLIENT_URL_FILE)
fi
if test -f "$GROUP_KEY_FILE"; then
export GROUP_KEY=$(cat $GROUP_KEY_FILE)
fi
# Collect static files
echo "Collect static files"
@@ -21,4 +39,4 @@ python manage.py migrate
# Start server
echo "Django running on http://localhost:8000 in production mode"
gunicorn -w 4 -b 0.0.0.0:8000 sikweb.wsgi
gunicorn --log-level debug -w 4 -b 0.0.0.0:8000 sikweb.wsgi
+56 -41
View File
@@ -1,50 +1,65 @@
[tool.poetry]
name = "web2.0-backend"
version = "0.1.0"
[project]
authors = [
{name = "Aarni Halinen", email = "aarni.halinen@sahkoinsinoorikilta.fi"},
]
description = "Backend for sahkoinsinoorikilta.fi"
authors = ["Aarni Halinen aarni.halinen@sahkoinsinoorikilta.fi"]
name = "web2.0-backend"
readme = "README.md"
requires-python = "~3.12"
version = "0.1.0"
[virtualenvs]
create = true
in-project = true
[tool.poetry.dependencies]
python = "^3.9"
decorator = "^4.0.9"
Django = "^2.2.19"
requests = "^2.11.1"
django-cors-headers = "^3.7.0"
djangorestframework = "^3.12.4"
djangorestframework-jwt = "^1.11.0"
django-nose = "^1.4.5"
psycopg2-binary = "2.8.6"
django-bootstrap3 = "^11.1.0"
django-tables2 = "^1.6.1"
dealer = "^2.0.5"
django-modeltranslation = "^0.13b1"
django-auditlog = "^0.4.5"
django-phonenumber-field = {version = "^4.0.0", extras = ["phonenumbers"]}
django-autocomplete-light = "^3.4.1"
six = "^1.12.0"
django-suit = "^0.2.26"
pyexcel = "^0.5.14"
pyexcel-xlsx = "^0.5.8"
django-import-export = "^0.7.0"
openpyxl = "^2.6.4"
decorator = "^4.4.2"
Django = "^4.2.19"
django-app-namespace-template-loader = "^0.4.1"
django-filter = "^2.0.0"
whitenoise = "^4.1.4"
jsonschema = "^3.2.0"
Markdown = "^3.2.2"
uWSGI = "^2.0.18"
gunicorn = "^20.1.0"
Pillow = "^8.4.0"
sendgrid = "^6.7.0"
sentry-sdk = "^1.4.3"
django-auditlog = "^2.1.1"
django-autocomplete-light = "^3.4.1"
django-bootstrap3 = "^21.2.0"
django-cors-headers = "^3.13.0"
django-filter = "^22.1.0"
django-import-export = "^2.8.0"
django-modeltranslation = "^0.18.4"
django-phonenumber-field = {version = "^6.4.0", extras = ["phonenumbers"]}
django-polymorphic = "^3.1.0"
django-tables2 = "^2.4.1"
djangorestframework = "^3.12.4"
djangorestframework-simplejwt = "^5.5.0"
google-auth = "^2.9.1"
google-api-python-client = "^2.54.0"
gunicorn = "^23.0.0"
jsonschema = "^4.9.0"
Markdown = "^3.2.2"
openpyxl = "^2.6.4"
Pillow = "^10.0.0"
psycopg2-binary = "^2.9.3"
pyexcel = "^0.7.0"
pyexcel-io = "^0.6.0"
pyexcel-xlsx = "^0.6.0"
python-dotenv = "^0.20.0"
requests = "^2.28.1"
sendgrid = "^6.7.0"
sentry-sdk = "^2.24.1"
six = "^1.12.0"
uWSGI = "^2.0.28"
whitenoise = "^6.2.0"
pyjwt = "^2.9.0"
setuptools = "^80.9.0"
[tool.poetry.dev-dependencies]
coverage = "^5.5"
nose-exclude = "^0.5.0"
safety = "^1.10.3"
black = "^21.12b0"
[tool.poetry.group.dev.dependencies]
black = "^25.1.0"
coverage = "^6.4.2"
safety = "^2.3.4"
[tool.poetry]
package-mode = false
[tool.poetry.requires-plugins]
poetry-plugin-export = "^1.9"
[build-system]
requires = ["poetry-core>=1.0.0"]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
+1 -1
View File
@@ -12,5 +12,5 @@
"reportMissingImports": true,
"reportMissingTypeStubs": false,
"pythonVersion": "3.9"
"pythonVersion": "3.12.9"
}
+2 -2
View File
@@ -3,8 +3,8 @@ CREATE USER sik WITH PASSWORD 'password123';
ALTER ROLE sik SET client_encoding TO 'utf8';
ALTER ROLE sik SET default_transaction_isolation TO 'read committed';
ALTER ROLE sik SET timezone TO 'UTC';
CREATE DATABASE sik
ENCODING 'UTF8'
CREATE DATABASE sik
ENCODING 'UTF8'
OWNER sik;
GRANT ALL PRIVILEGES ON DATABASE sik TO sik;
ALTER USER sik CREATEDB;
+24 -93
View File
@@ -2,19 +2,31 @@ import os
import logging
import datetime
from os.path import expanduser
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = "static/"
STATIC_ROOT = os.path.join(BASE_DIR, "collected_static")
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
# Login paths
LOGIN_URL = "/login/"
LOGIN_REDIRECT_URL = "/admin"
# Might need to be changed to JSON serializer
# https://docs.djangoproject.com/en/4.0/topics/http/sessions/#django.contrib.sessions.serializers.JSONSerializer
SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer"
# Logger level
# Logging
LOGGERLEVEL = logging.DEBUG
LOGPATH = os.path.join(BASE_DIR, "logs", "debug.log")
@@ -67,10 +79,15 @@ LOGGING = {
# Application definition
IMPORT_EXPORT_USE_TRANSACTIONS = True
# Could be replaced with CORS_ALLOWED_ORIGINS list.
# See (check correct package version in the link) https://github.com/adamchainz/django-cors-headers/tree/3.7.0#configuration
CORS_ALLOW_ALL_ORIGINS = True
ROOT_URLCONF = "sikweb.urls"
WSGI_APPLICATION = "sikweb.wsgi.application"
INSTALLED_APPS = [
"modeltranslation", # has to be before admin for translation admin to work
"suit",
"dal",
"dal_select2",
"django.contrib.admin",
@@ -87,8 +104,7 @@ INSTALLED_APPS = [
"kaehmy",
"ohlhafv",
"rest_framework",
"rest_framework_jwt",
"django_nose",
"rest_framework_simplejwt",
"bootstrap3",
"django_tables2",
"auditlog",
@@ -97,18 +113,6 @@ INSTALLED_APPS = [
"django_filters",
]
IMPORT_EXPORT_USE_TRANSACTIONS = True
TEST_RUNNER = "django_nose.NoseTestSuiteRunner"
NOSE_ARGS = [
"--with-coverage",
"--cover-package=webapp,members,infoscreen",
"--exclude-dir={}".format(os.path.join(BASE_DIR, "members", "migrations")),
"--exclude-dir={}".format(os.path.join(BASE_DIR, "infoscreen", "migrations")),
"--exclude-dir={}".format(os.path.join(BASE_DIR, "webapp", "migrations")),
]
MIDDLEWARE = [
"sikweb.middleware.ForceDefaultLanguageMiddleware",
"django.middleware.security.SecurityMiddleware",
@@ -124,17 +128,12 @@ MIDDLEWARE = [
"auditlog.middleware.AuditlogMiddleware",
]
CORS_ORIGIN_ALLOW_ALL = True
ROOT_URLCONF = "sikweb.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": ["templates"],
"OPTIONS": {
"loaders": [
"app_namespace.Loader",
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
],
@@ -145,17 +144,14 @@ TEMPLATES = [
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"django.template.context_processors.static",
"dealer.contrib.django.context_processor",
],
},
},
]
WSGI_APPLICATION = "sikweb.wsgi.application"
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation."
@@ -179,88 +175,23 @@ REST_FRAMEWORK = {
"rest_framework.permissions.IsAdminUser",
),
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_jwt.authentication.JSONWebTokenAuthentication",
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
# 'DEFAULT_THROTTLE_CLASSES': (
# 'members.throttles.BurstRateThrottle',
# 'members.throttles.SustainedRateThrottle'
# ),
# 'DEFAULT_THROTTLE_RATES': {
# 'burst': '60/min',
# 'sustained': '1000/day'
# },
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
"PAGE_SIZE": 1000,
"DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
}
# Email settings (tested working with gmail)
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# EMAIL_USE_TLS = True
# EMAIL_HOST = 'smtp.gmail.com'
# EMAIL_PORT = 587
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGES = (
("fi", _("Finnish")),
("en", _("English")),
)
LANGUAGE_CODE = "fi"
LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),)
TIME_ZONE = "Europe/Helsinki"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
"django.contrib.staticfiles.finders.FileSystemFinder",
)
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "collected_static")
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
LOGIN_URL = "/login/"
LOGIN_REDIRECT_URL = "/admin"
SUIT_CONFIG = {
# header
"ADMIN_NAME": "SIK Admin",
# 'HEADER_DATE_FORMAT': 'l, j. F Y',
# 'HEADER_TIME_FORMAT': 'H:i',
# forms
# 'SHOW_REQUIRED_ASTERISK': True, # Default True
# 'CONFIRM_UNSAVED_CHANGES': True, # Default True
# menu
# 'SEARCH_URL': '/admin/auth/user/',
# 'MENU_ICONS': {
# 'sites': 'icon-leaf',
# 'auth': 'icon-lock',
# },
# 'MENU_OPEN_FIRST_CHILD': True, # Default True
# 'MENU_EXCLUDE': ('auth.group',),
# 'MENU': (
# 'sites',
# {'app': 'auth', 'icon':'icon-lock', 'models': ('user', 'group')},
# {'label': 'Settings', 'icon':'icon-cog', 'models': ('auth.user', 'auth.group')},
# {'label': 'Support', 'icon':'icon-question-sign', 'url': '/support/'},
# ),
# misc
# 'LIST_PER_PAGE': 15
}
JWT_AUTH = {"JWT_EXPIRATION_DELTA": datetime.timedelta(days=7)}
+30
View File
@@ -0,0 +1,30 @@
# contents of yourapp/migrations/custom_operations.py
from django.db.migrations.operations.models import ModelOperation
class AlterModelBases(ModelOperation):
reduce_to_sql = False
reversible = True
def __init__(self, name, bases):
self.bases = bases
super().__init__(name)
def state_forwards(self, app_label, state):
"""
Overwrite a models base classes with a custom list of
bases instead, then force Django to reload the model
with this (probably completely) different class hierarchy.
"""
state.models[app_label, self.name_lower].bases = self.bases
state.reload_model(app_label, self.name_lower)
def database_forwards(self, app_label, schema_editor, from_state, to_state):
pass
def database_backwards(self, app_label, schema_editor, from_state, to_state):
pass
def describe(self):
return "Update %s bases to %s" % (self.name, self.bases)
+66 -17
View File
@@ -10,23 +10,17 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
from dotenv import load_dotenv
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
from sikweb.base import *
from datetime import timedelta
import json
load_dotenv() # loads the configs from .env
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
DEPLOY_ENV = os.getenv("DEPLOY_ENV", "production")
# Setup sentry
sentry_sdk.init(
dsn=SENTRY_DSN,
environment=DEPLOY_ENV,
integrations=[DjangoIntegration()],
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True,
)
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv("DEBUG", False) == "True"
@@ -44,6 +38,19 @@ SECRET_KEY = os.getenv(
"SECRET_KEY", "7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp("
)
# Sentry
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
sentry_sdk.init(
dsn=SENTRY_DSN,
environment=DEPLOY_ENV,
integrations=[DjangoIntegration()],
# If you wish to associate users to errors (assuming you are using
# django.contrib.auth) you may enable sending PII data.
send_default_pii=True,
)
# ReCaptcha
# http://www.yaconiello.com/blog/integrating-google-recaptcha-to-django/
GOOGLE_RECAPTCHA_SITE_KEY = os.getenv("GOOGLE_RECAPTCHA_SITE_KEY", "YOUR-PUBLIC-KEY")
@@ -51,21 +58,19 @@ GOOGLE_RECAPTCHA_SECRET_KEY = os.getenv(
"GOOGLE_RECAPTCHA_SECRET_KEY", "YOUR-PRIVATE-KEY"
)
# Email settings (more settings in base.py)
# Email settings (Sendgrid)
EMAIL_API_KEY = os.getenv("EMAIL_API_KEY", "")
# EMAIL_API_SECRET = os.getenv('EMAIL_API_SECRET', '')
DEFAULT_EMAIL_FROM = "SIK"
DEFAULT_EMAIL_FROM_ADDR = "noreply@sahkoinsinoorikilta.fi"
ENABLE_AUTOMATIC_EMAILS = True
# Database settings
# Only uncomment if default settings in base.py are not ok
## Database connection
DB_OPTIONS = {"sslmode": "require"} if os.getenv("DB_SSL", False) == "True" else {}
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("DB_NAME", "postgres"),
"USER": os.getenv("DB_USER", "postgres"),
"PASSWORD": os.getenv("DB_PASSWD", "postgres"),
@@ -74,3 +79,47 @@ DATABASES = {
"OPTIONS": DB_OPTIONS,
}
}
# Google api settings
GROUP_KEY = os.getenv("GROUP_KEY", "")
GOOGLE_CREDS = {
"type": "service_account",
"project_id": "web2-backend",
"private_key_id": os.getenv("G_PRIVATE_KEY_ID", ""),
"private_key": os.getenv("G_PRIVATE_KEY", ""),
"client_email": os.getenv("G_CLIENT_EMAIL", ""),
"client_id": os.getenv("G_CLIENT_ID", ""),
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": os.getenv("G_CLIENT_URL", ""),
}
# JWT authentication
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"ROTATE_REFRESH_TOKENS": False,
"BLACKLIST_AFTER_ROTATION": False,
"UPDATE_LAST_LOGIN": False,
"ALGORITHM": "HS256",
"SIGNING_KEY": SECRET_KEY,
"VERIFYING_KEY": None,
"AUDIENCE": None,
"ISSUER": None,
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
}
+13 -28
View File
@@ -1,23 +1,6 @@
"""sikweb URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Add an import: from blog import urls as blog_urls
2. Import the include() function: from django.conf.urls import url, include
3. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
"""
from django.conf.urls import url
from django.urls import re_path, include
from django.contrib import admin
from django.views.static import serve as static_serve
from django.conf.urls import include
from django.conf.urls.static import static
from django.conf import settings
from django.contrib.staticfiles import views as static_views
@@ -27,18 +10,20 @@ favicon_view = RedirectView.as_view(url="static/img/favicon.png", permanent=True
urlpatterns = [
url(r"", include("webapp.urls")),
url(r"^members/", include("members.urls")),
url(r"^infoscreen/", include("infoscreen.urls")),
url(r"^kaehmy/", include("kaehmy.urls")),
url(r"^ohlhafv/", include("ohlhafv.urls")),
re_path(r"", include("webapp.urls")),
re_path(r"^members/", include("members.urls")),
re_path(r"^infoscreen/", include("infoscreen.urls")),
re_path(r"^kaehmy/", include("kaehmy.urls")),
re_path(r"^ohlhafv/", include("ohlhafv.urls")),
# favourite icon
url(r"^favicon\.ico$", favicon_view),
re_path(r"^favicon\.ico$", favicon_view),
# admin
url(r"^admin/", admin.site.urls),
re_path(r"^admin/", admin.site.urls),
# i18n default view for changing the active language
url(r"^i18n/", include("django.conf.urls.i18n")),
re_path(r"^i18n/", include("django.conf.urls.i18n")),
# staticfiles default view for static files in development
url(r"^static/(?P<path>.*)$", static_views.serve),
url(r"^media/(?P<path>.*)$", static_serve, {"document_root": settings.MEDIA_ROOT}),
re_path(r"^static/(?P<path>.*)$", static_views.serve),
re_path(
r"^media/(?P<path>.*)$", static_serve, {"document_root": settings.MEDIA_ROOT}
),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+24
View File
@@ -29,15 +29,39 @@ services:
- FRONTEND_URL=dev.sahkoinsinoorikilta.fi
- DEBUG=True
- EMAIL_API_KEY_FILE=/run/secrets/DJANGO_EMAIL_API_KEY
- G_PRIVATE_KEY_ID_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY_ID
- G_PRIVATE_KEY_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY
- G_CLIENT_EMAIL_FILE=/run/secrets/BACKEND_G_CLIENT_EMAIL
- G_CLIENT_ID_FILE=/run/secrets/BACKEND_G_CLIENT_ID
- G_CLIENT_URL_FILE=/run/secrets/BACKEND_G_CLIENT_URL
- GROUP_KEY_FILE=/run/secrets/BACKEND_GROUP_KEY
- DB_HOST=db
- DB_PORT=5432
secrets:
- DJANGO_EMAIL_API_KEY
- BACKEND_G_PRIVATE_KEY_ID
- BACKEND_G_PRIVATE_KEY
- BACKEND_G_CLIENT_EMAIL
- BACKEND_G_CLIENT_ID
- BACKEND_G_CLIENT_URL
- BACKEND_GROUP_KEY
secrets:
DJANGO_EMAIL_API_KEY:
external: true
BACKEND_G_PRIVATE_KEY_ID:
external: true
BACKEND_G_PRIVATE_KEY:
external: true
BACKEND_G_CLIENT_EMAIL:
external: true
BACKEND_G_CLIENT_ID:
external: true
BACKEND_G_CLIENT_URL:
external: true
BACKEND_GROUP_KEY:
external: true
volumes:
dbdata:
+25
View File
@@ -34,11 +34,24 @@ services:
- SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY
- DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD
- EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY
- G_PRIVATE_KEY_ID_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY_ID
- G_PRIVATE_KEY_FILE=/run/secrets/BACKEND_G_PRIVATE_KEY
- G_CLIENT_EMAIL_FILE=/run/secrets/BACKEND_G_CLIENT_EMAIL
- G_CLIENT_ID_FILE=/run/secrets/BACKEND_G_CLIENT_ID
- G_CLIENT_URL_FILE=/run/secrets/BACKEND_G_CLIENT_URL
- GROUP_KEY_FILE=/run/secrets/BACKEND_GROUP_KEY
secrets:
- BACKEND_SECRET_KEY
- BACKEND_DB_PASSWD
- BACKEND_EMAIL_API_KEY
- BACKEND_G_PRIVATE_KEY_ID
- BACKEND_G_PRIVATE_KEY
- BACKEND_G_CLIENT_EMAIL
- BACKEND_G_CLIENT_ID
- BACKEND_G_CLIENT_URL
- BACKEND_GROUP_KEY
secrets:
BACKEND_SECRET_KEY:
external: true
@@ -46,3 +59,15 @@ secrets:
external: true
BACKEND_EMAIL_API_KEY:
external: true
BACKEND_G_PRIVATE_KEY_ID:
external: true
BACKEND_G_PRIVATE_KEY:
external: true
BACKEND_G_CLIENT_EMAIL:
external: true
BACKEND_G_CLIENT_ID:
external: true
BACKEND_G_CLIENT_URL:
external: true
BACKEND_GROUP_KEY:
external: true
@@ -3475,19 +3475,19 @@
}
while( (node = node.parentNode) && (node !== document.body) );
},
disableMouseover: false,
mouseover: function (evt){
if (!flash.disableMouseover) {
var target = api.event.fix(evt).target;
if( /input/i.test(target.nodeName) && target.type == 'file' && !target.disabled ){
var
state = target.getAttribute(_attr)
, wrapper = flash.getWrapper(target)
;
if( api.multiFlash ){
// check state:
// i — published
@@ -3500,14 +3500,14 @@
else if( state != 'p' ){
// set "init" state
target.setAttribute(_attr, 'i');
var dummy = document.createElement('div');
if( !wrapper ){
api.log('[err] FlashAPI.mouseover: js-fileapi-wrapper not found');
return;
}
_css(dummy, {
top: 0
, left: 0
@@ -3516,21 +3516,21 @@
, zIndex: 1e6+'' // set max zIndex
, position: 'absolute'
});
wrapper.appendChild(dummy);
flash.publish(dummy, api.uid());
// set "publish" state
target.setAttribute(_attr, 'p');
}
return true;
}
else if( wrapper ){
// Use one flash element
var box = _getDimensions(wrapper);
_css(flash.getEl(), box);
// Set current input
flash.curInp = target;
}
@@ -3543,7 +3543,7 @@
onEvent: function (evt){
var type = evt.type;
if( type == 'ready' ){
try {
// set "ready" state
@@ -3632,9 +3632,9 @@
_each(files, function (file){
api.checkFileObj(file);
});
_files[uid] = files;
if( document.createEvent ){
event = document.createEvent('Event');
event.files = files;
@@ -3664,7 +3664,7 @@
this.cmdFn(id, name, data, last);
}
},
cmdFn: function(id, name, data, last) {
try {
api.log('(js -> flash).'+name+':', data);
@@ -3727,7 +3727,7 @@
callback(evt);
});
},
getFiles: function (input, filter, callback){
if( callback ){
api.filterFiles(api.getFiles(input), filter, callback);
@@ -4039,7 +4039,7 @@
}
try { el.style[key] = val; } catch (e) {}
}
}
}
@@ -4161,7 +4161,7 @@
, body = document.body
, docEl = (el && el.ownerDocument).documentElement
;
function getOffset(obj) {
var left, top;
left = top = 0;
@@ -4176,7 +4176,7 @@
top : top
};
};
return {
top: getOffset(el).top
, left: getOffset(el).left
-15
View File
@@ -1,15 +0,0 @@
<!DOCTYPE html>
{% load static %}
<html>
<head>
<link rel="shortcut icon" type="image/x-icon" href="/static/img/favicon.png"/>
<link rel="stylesheet" href="{% static "css/about.css" %}">
</head>
<body>
<h1>SIKWEB 2.0</h1>
<p>{{ commit }}</p>
<p>{{ date }}</p>
<p>{{ tag }}</p>
</body>
</html>
-75
View File
@@ -1,75 +0,0 @@
{% extends "admin/base.html" %}
{% load admin_static %}
{% load i18n %}
{# Additional <head> content here, some extra meta tags or favicon #}
{% block extrahead %}
<link rel="shortcut icon" type="image/x-icon" href="/static/img/favicon.png"/>
{% endblock %}
{# Additional CSS includes #}
{% block extrastyle %}
<link rel="stylesheet" type="text/css" href="{% static 'css/sikadmin.css' %}" media="all">
{% endblock %}
{# Additional JS files in footer, right before </body> #}
{#{% block extrajs %}#}
{# <script type="text/javascript" src="{% static 'js/my_project.js' %}"></script>#}
{#{% endblock %}#}
{# Footer links (left side) #}
{#{% block footer_links %}#}
{# <a href="/docs/" class="icon"><i class="icon-question-sign"></i>Documentation</a>#}
{#{% endblock %}#}
{# Additional header content like notifications or language switcher #}
{% block header_content %}
{{ block.super }}
<div class="header-content">
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select name="language" style="width: auto;">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input class="btn btn-high" type="submit" value="{% trans 'Go' %}" style="vertical-align: top;"/>
</form>
<!-- First icon column -->
<!--
<div class="header-column icon">
<i class="icon-home"></i><br>
<i class="icon-cog"></i>
</div>
<div class="header-column" style="margin-right: 20px">
<a href="/" class="grey">Front-end</a><br>
<a href="" class="grey">One more link</a>
</div>
Second icon column
<div class="header-column icon">
<i class="icon-comment"></i>
</div>
<div class="header-column">
<a href="" class="grey">5 new messages</a>
</div>
-->
</div>
{% endblock %}
{# Footer branding name (center) #}
{#{% block footer_branding %}#}
{#{% endblock %}#}
{# Footer copyright (right side) #}
{#{% block copyright %}#}
{# Copyright &copy; 2013 Client<br>Developed by <a href="http://yoursite.com" target="_blank">YourName</a> #}
{#{% endblock %}#}
+19 -38
View File
@@ -1,43 +1,24 @@
{% load i18n %}
{% load static %}
{% load staticfiles %}
<link rel="stylesheet" href="{% static "css/footer.css" %}">
<footer class="footer">
<div class="container">
<div class="d-flex align-items-center justify-content-end">
<div class="p-2">
<span><i class="fa fa-copyright"></i>{% trans "Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
</div>
<div class="p-2">
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
<span class="form-group">
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</span>
</form>
</div>
<div class="ml-auto p-2">
<span class="nav-item">
<a href="/members"><i class="fa fa-group fa-2x"></i></a>
</span>
<span class="nav-item">
<a href="/infoscreen"><i class="fa fa-info fa-2x"></i></a>
</span>
<span class="nav-item">
<a href="/admin"><i class="fa fa-gears fa-2x"></i></a>
</span>
</div>
</div>
<div class="footer-padder"></div>
<footer style="text-align: center">
<div>
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
<span>
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
</span>
</form>
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
</div>
</footer>
@@ -1,6 +1,5 @@
{% load i18n %}
{% load static %}
{% load staticfiles %}
<!DOCTYPE html>
@@ -1,8 +1,7 @@
{% extends "infoscreen:base.html" %}
{% extends "infoscreen/base.html" %}
{% load i18n %}
{% load static %}
{% load staticfiles %}
{% block appname %}infoAdmin{% endblock appname %}
@@ -33,13 +32,13 @@
<h1>{% trans "Infoscreen Admin Pane" %}</h1>
</div>
</div>
{% include "infoscreen:nav.html" %}
{% include "infoscreen/nav.html" %}
<div class="tab-content" id="tabContent">
{% include "infoscreen:tabs/slides.html" %}
{% include "infoscreen:tabs/rotations.html" %}
{% include "infoscreen:tabs/add_remove.html" %}
{% include "infoscreen/tabs/slides.html" %}
{% include "infoscreen/tabs/rotations.html" %}
{% include "infoscreen/tabs/add_remove.html" %}
</div>
</div>
{% include "webapp:footer.html" %}
{% include "footer.html" %}
{% endblock body %}
@@ -1,4 +1,4 @@
{% extends "infoscreen:base.html" %}
{% extends "infoscreen/base.html" %}
{% load static %}
{% load i18n %}
@@ -26,7 +26,7 @@
<div class="col">
<div class="rotation-title-row">
<h2>{% trans "Rotation" %}: {$ selected_rot.name $}</h2>
<a class="btn btn-primary" href="/infoscreen/{$ selected_rot.id $}">{% trans "Preview" %}</a>
<a class="btn btn-primary" href="/infoscreen/{$ selected_rot.id $}">{% trans "Preview" %}</a>
</div>
<div>{% trans "Instances in currently selected rotation" %}:</div>
<table class="table table-striped">
@@ -13,13 +13,13 @@
{% block body %}
{% block header %}
<div class="kaehmy_header">
{% include "kaehmy:header.html" %}
<div class="kaehmy-header">
{% include "kaehmy/header.html" %}
</div>
{% endblock header %}
{% block navigation %}
{% include "kaehmy:navigation.html" %}
{% include "kaehmy/navigation.html" %}
{% endblock %}
<div class="kaehmy-content">
@@ -28,7 +28,7 @@
</div>
<div class="footer">
{% block footer %}
{% include "kaehmy:footer.html" %}
{% include "footer.html" %}
{% endblock footer %}
</div>
+10
View File
@@ -0,0 +1,10 @@
{% load i18n %}
<p>
Hei!
</p>
<p>
{{ name }} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.
Käy lukemassa viesti
<a href={{ url }}>täältä.</a>
</p>

Some files were not shown because too many files have changed in this diff Show More