93 Commits

Author SHA1 Message Date
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
77 changed files with 8110 additions and 6517 deletions
-4
View File
@@ -1,9 +1,5 @@
[report] [report]
show_missing = True show_missing = True
omit =
*/migrations/*
*/admin.py
*/translation.py
[run] [run]
omit = omit =
*/migrations/* */migrations/*
+4 -2
View File
@@ -1,11 +1,13 @@
DEPLOY_ENV=local DEPLOY_ENV=local
SENTRY_DSN= SENTRY_DSN=
HOST=api.dev.sahkoinsinoorikilta.fi HOST=localhost
DEBUG=True DEBUG=True
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp( SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
DB_NAME=postgres DB_NAME=postgres
DB_USER=postgres DB_USER=postgres
DB_PASSWD=postgres DB_PASSWD=postgres
DB_HOST=db DB_HOST=localhost
DB_PORT=5432 DB_PORT=5432
EMAIL_API_KEY= EMAIL_API_KEY=
GROUP_KEY=
GOOGLE_CREDS='{}'
+1 -1
View File
@@ -10,4 +10,4 @@ DB_HOST=db
DB_PORT=5432 DB_PORT=5432
EMAIL_API_KEY= EMAIL_API_KEY=
GROUP_KEY= GROUP_KEY=
GOOGLE_CREDS_JSON='{}' 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"
}
}
+170 -112
View File
@@ -1,131 +1,189 @@
stages: stages:
- setup - setup
- audit - audit
- lint - lint
- test - test
- publish - publish
- deploy - deploy
- cleanup
install: install:
image: node:14 image: node:22
stage: setup stage: setup
script: only:
- npm ci - pushes
artifacts: script:
paths: - npm ci
- node_modules artifacts:
expire_in: 1 week paths:
- node_modules
expire_in: 1 week
audit: audit:
image: python:3.9 image: python:3.12.9
stage: audit stage: audit
needs: [] allow_failure: true
before_script: only:
- pip install poetry==1.1.13 - pushes
- poetry config virtualenvs.create false needs: []
- poetry install --no-interaction --no-ansi before_script:
script: - pip install poetry==2.0.1
- safety check - poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi
script:
- safety check
test: test:
image: python:3.9 image: python:3.12.9
stage: test stage: test
needs: [] only:
services: - pushes
- postgres:12 needs: []
variables: services:
POSTGRES_DB: ci - postgres:12
POSTGRES_USER: postgres variables:
POSTGRES_PASSWORD: postgres POSTGRES_DB: ci
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB" POSTGRES_USER: postgres
DB_HOST: postgres POSTGRES_PASSWORD: postgres
before_script: DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
- pip install poetry==1.1.13 DB_HOST: postgres
- poetry config virtualenvs.create false before_script:
- poetry install --no-interaction --no-ansi - pip install poetry==2.0.1
script: - poetry config virtualenvs.create false
- python manage.py migrate --noinput - poetry install --no-interaction --no-ansi
- python manage.py createdefaultadmin script:
- python manage.py test - python manage.py migrate --noinput
- python manage.py createdefaultadmin
- python manage.py test
lint:py: lint:py:
image: python:3.9 image: python:3.12.9
stage: lint stage: lint
needs: [] only:
script: - pushes
- pip install black==22.3.0 needs: []
- black --check . script:
- pip install black==22.3.0
- black --check .
lint:js: lint:js:
image: node:14 image: node:22
stage: lint stage: lint
needs: ["install"] only:
script: - pushes
- npm run lint:js needs: ["install"]
script:
- npm run lint:js
lint:md: lint:md:
image: node:14 image: node:22
stage: lint stage: lint
needs: ["install"] only:
script: - pushes
- npm run lint:md needs: ["install"]
script:
- npm run lint:md
publish: publish:
stage: publish image: docker:25-cli
image: docker:stable stage: publish
needs: ["test", "lint:py", "lint:js", "lint:md"] needs: ["test", "lint:py", "lint:js", "lint:md"]
services: services:
- docker:stable-dind - docker:25-dind
only: only:
- develop - main
- master - production
script: script:
- docker info - docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build . -t "$IMAGE_NAME" - docker build . -t "$IMAGE_NAME"
- docker push "$IMAGE_NAME" - docker push "$IMAGE_NAME"
deploy:dev: deploy:dev:
stage: deploy image: docker:25-cli
image: docker:stable stage: deploy
only: only:
- develop - main
environment: environment:
name: dev name: dev
url: http://api.dev.sahkoinsinoorikilta.fi url: http://api.dev.sahkoinsinoorikilta.fi
variables: variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
before_script: before_script:
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem - echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem - echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem - echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY" - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script: script:
- docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME" - docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME"
after_script: after_script:
- docker logout "$CI_REGISTRY" - docker logout "$CI_REGISTRY"
deploy:production: deploy:production:
stage: deploy stage: deploy
image: docker:stable image: docker:25-cli
only: only:
- master - production
environment: environment:
name: production name: production
url: https://api.sahkoinsinoorikilta.fi url: https://api.sahkoinsinoorikilta.fi
when: manual when: manual
variables: variables:
DOCKER_HOST: $CI_DOCKER_HOST DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1 DOCKER_TLS_VERIFY: 1
before_script: before_script:
- mkdir -p ~/.docker - mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem - echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem - echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem - echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY" - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
script: script:
- docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME" - docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME"
after_script: after_script:
- docker logout "$CI_REGISTRY" - 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 @@
_
Executable → Regular
+1 -4
View File
@@ -1,10 +1,7 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
PURPLE='\033[0;35m' PURPLE='\033[0;35m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
source "${VIRTUAL_ENV}/bin/activate" . "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
+1 -4
View File
@@ -1,10 +1,7 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
PURPLE='\033[0;35m' PURPLE='\033[0;35m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
source "${VIRTUAL_ENV}/bin/activate" . "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
+1
View File
@@ -0,0 +1 @@
22.13.1
+1 -1
View File
@@ -1 +1 @@
3.9 3.12.9
+4 -3
View File
@@ -1,13 +1,14 @@
FROM python:3.9-slim-buster as builder FROM python:3.12.9-slim-bullseye AS builder
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
COPY . ./ COPY . ./
ENV POETRY_VERSION=1.1.13 ENV POETRY_VERSION=2.0.1
RUN pip install "poetry==$POETRY_VERSION" RUN pip install "poetry==$POETRY_VERSION"
RUN poetry self add poetry-plugin-export
RUN poetry export --without-hashes > requirements.txt RUN poetry export --without-hashes > requirements.txt
FROM python:3.9-slim-buster as server FROM python:3.12.9-slim-bullseye AS server
WORKDIR /app WORKDIR /app
COPY . ./ COPY . ./
+55 -45
View File
@@ -1,57 +1,70 @@
# 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 ## Installation
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
Set up your SSH key authentication in GitLab Profile Settings. Then clone the repository and checkout the development branch: Set up your SSH key authentication in GitLab Profile Settings. Then clone the repository and checkout the development branch:
```bash ```bash
git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git
cd web2.0-backend cd web2.0-backend
git checkout develop
``` ```
## Development Copy env file for local use:
```bash
cp .env.dev .env
```
### Poetry ### Poetry
For depedencies and virtual environment, we use [poetry](https://python-poetry.org/). For depedencies and virtual environment, we use [poetry](https://python-poetry.org/).
First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry:
```bash ```bash
python3 -m pip install poetry python -m pip install poetry==2.0.1
``` ```
The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with CMD The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with CMD
```bash ```bash
python3 -m poetry config virtualenvs.in-project true python -m poetry config virtualenvs.in-project true
``` ```
Start developing by install dependencies first ### Node
#### CMDs 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
```
TODO: List scripts
### 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
Activate virtual environment in shell Activate virtual environment in shell
```bash ```bash
python3 -m poetry shell eval $(python -m poetry env activate)
``` ```
Install dependencies Install dependencies
@@ -60,20 +73,15 @@ Install dependencies
poetry install poetry install
``` ```
### npm scripts
We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm).
TODO: List scripts
### Initializing data ### 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 ```bash
python manage.py createdefaultadmin # creates an admin user python manage.py migrate # run migrations
python manage.py initialize # creates user groups python manage.py createdefaultadmin # creates an admin user
python manage.py createdummydata # creates dummy members to the member register python manage.py initialize # creates user groups
python manage.py createdummydata # creates dummy members to the member register
``` ```
### Running ### Running
@@ -82,8 +90,6 @@ python manage.py createdummydata # creates dummy members to the member regis
python manage.py runserver python manage.py runserver
``` ```
#### Visit the page
Visit [https://localhost:8000](https://localhost:8000) in your browser! 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. Using address `0.0.0.0` will bind to all IP addresses. Using `localhost` will only bind to your machine.
@@ -99,27 +105,29 @@ When you start working on a feature, create a feature branch for your changes. T
Example of creating a feature branch: Example of creating a feature branch:
```bash ```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 ### Linting
Lint python files using `pycodestyle` with Lint python files using `black` with
```bash ```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 Lint javascript and markdown using `eslint` and `remark` with
```bash ```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_. Use an editor with linting capabilities to write pretty code that passes linting. Examples include _VSCode_, _Atom_ and _Pycharm_.
@@ -140,6 +148,8 @@ Tests are located in `tests.py` under every subproject.
Project is run in production with Docker. See `Dockerfile` for details. 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 ## 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`.
-2
View File
@@ -1,5 +1,3 @@
version: '3'
services: services:
db: db:
image: postgres:12 image: postgres:12
+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
}
];
+1 -1
View File
@@ -7,7 +7,7 @@ from django import forms
from django.utils import timezone from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType 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): class InfoItem(models.Model):
+23 -23
View File
@@ -1,6 +1,6 @@
"""File containing infoscreen urls.""" """File containing infoscreen urls."""
from django.conf.urls import url from django.urls import re_path
from django.conf import settings from django.conf import settings
from infoscreen.views import index from infoscreen.views import index
@@ -27,28 +27,28 @@ from infoscreen.views import createApyItem
from infoscreen.views import get_apy_json from infoscreen.views import get_apy_json
urlpatterns = [ urlpatterns = [
url(r"^$", default), re_path(r"^$", default),
url(r"^admin$", admin), re_path(r"^admin$", admin),
url(r"^(?P<idx>\d+)$", index), re_path(r"^(?P<idx>\d+)$", index),
url(r"^items$", info_items), re_path(r"^items$", info_items),
url(r"^rotation/(?P<idx>\d+)$", rotation), re_path(r"^rotation/(?P<idx>\d+)$", rotation),
url(r"^rotations$", rotations), re_path(r"^rotations$", rotations),
url(r"^instance$", createInstance), re_path(r"^instance$", createInstance),
url(r"^instance/(?P<idx>\d+)$", deleteInstance), re_path(r"^instance/(?P<idx>\d+)$", deleteInstance),
url(r"^types$", info_types), re_path(r"^types$", info_types),
url(r"^delete_item/(?P<type_id>\d+)/(?P<idx>\d+)$", delete_info_item), re_path(r"^delete_item/(?P<type_id>\d+)/(?P<idx>\d+)$", delete_info_item),
url(r"^create_external_image$", createExternalImageInfoItem), re_path(r"^create_external_image$", createExternalImageInfoItem),
url(r"^create_image$", create_image_item), re_path(r"^create_image$", create_image_item),
url(r"^create_video$", create_video_item), re_path(r"^create_video$", create_video_item),
url(r"^create_abbitem$", createABBItem), re_path(r"^create_abbitem$", createABBItem),
url(r"^create_sossoitem$", createSossoItem), re_path(r"^create_sossoitem$", createSossoItem),
url(r"^create_lunchitem$", createLunchItem), re_path(r"^create_lunchitem$", createLunchItem),
url(r"^create_eventitem$", createEventItem), re_path(r"^create_eventitem$", createEventItem),
url(r"^create_apyitem$", createApyItem), re_path(r"^create_apyitem$", createApyItem),
url(r"^create_websiteitem$", createExternalWebsiteItem), re_path(r"^create_websiteitem$", createExternalWebsiteItem),
url(r"^create_rotation$", create_rotation), re_path(r"^create_rotation$", create_rotation),
url(r"^delete_rotation/(?P<id>\d+)$", delete_rotation), re_path(r"^delete_rotation/(?P<id>\d+)$", delete_rotation),
url(r"^apyjson", get_apy_json), re_path(r"^apyjson", get_apy_json),
] ]
if settings.DEBUG: if settings.DEBUG:
+5 -5
View File
@@ -1,20 +1,20 @@
from django import 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 django.core.exceptions import ValidationError
from kaehmy.models import PresetRole, CustomRole, Application, Comment, BaseRole from kaehmy.models import PresetRole, CustomRole, Application, Comment, BaseRole
class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple): class CheckboxSelectMultiple(forms.widgets.CheckboxSelectMultiple):
option_template_name = "kaehmy/checkbox_option.html" option_template_name = "checkbox_option.html"
def create_option( 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( 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 dic["description"] = description
return dic return dic
+6 -3
View File
@@ -1,6 +1,6 @@
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
VERBOSE_NAME = _("Kaehmy") VERBOSE_NAME = _("Kaehmy")
@@ -13,18 +13,21 @@ class BaseRole(models.Model):
is_board = models.BooleanField(_("Board member")) is_board = models.BooleanField(_("Board member"))
CATEGORIES = ( CATEGORIES = (
("board", _("Board")),
("corporate", _("Corporate affairs")), ("corporate", _("Corporate affairs")),
("freshman", _("Freshmen")), ("freshman", _("Freshmen")),
("international", _("International")), ("international", _("International")),
("external", _("External affairs")), ("siwa", _("SIK's free time")),
("media", _("Media")), ("media", _("Media")),
("tech", _("Technology")), ("tech", _("Technology")),
("wellbeing", _("Wellbeing")), ("wellbeing", _("Wellbeing")),
("elepaja", _("Elepaja")), ("sikpaja", _("Sik-paja")),
("ceremonies", _("Ceremonies")), ("ceremonies", _("Ceremonies")),
("studies", _("Studies")), ("studies", _("Studies")),
("sosso", _("Sössö magazine")), ("sosso", _("Sössö magazine")),
("pota", _("PoTa")),
("alumni", _("Alumni relations")), ("alumni", _("Alumni relations")),
("n", _("N")),
("others", _("Others")), ("others", _("Others")),
) )
category = models.CharField( category = models.CharField(
+4 -6
View File
@@ -5,12 +5,6 @@
margin-right: auto; margin-right: auto;
} }
body {
max-width: 1000px;
margin-left: auto !important;
margin-right: auto !important;
}
div.tooltip-inner { div.tooltip-inner {
max-width: 25rem; max-width: 25rem;
} }
@@ -28,6 +22,10 @@ div.tooltip-inner {
.kaehmy-content { .kaehmy-content {
padding-left: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem; padding-right: 0.5rem;
max-width: 1000px;
width: 100%;
margin-left: auto;
margin-right: auto;
} }
p { p {
+2 -5
View File
@@ -3,13 +3,9 @@
} }
footer { footer {
/* position: absolute; */
bottom: 0; bottom: 0;
width: 100%; width: 100%;
height: 60px; /* Set the fixed height of the footer here */ margin: 1rem;
/* line-height: 60px; /* Vertically center the text there */
margin-top: 2rem;
margin-bottom: 1rem;
} }
footer .container .col .nav .nav-item { footer .container .col .nav .nav-item {
@@ -26,6 +22,7 @@ footer .container .col .nav .nav-item {
.lang-select { .lang-select {
width: 10rem; width: 10rem;
margin-bottom: 1rem;
display: inline-block; 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; max-width: 1000px;
width: 100%;
margin-left: auto; margin-left: auto;
margin-right: 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 { .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 { .kaehmy_navigation {
margin-bottom: 10px; margin-bottom: 10px;
}
.navbar-border {
border-bottom: 2px solid #282b3b; border-bottom: 2px solid #282b3b;
} }
.navbar-light .navbar-nav .nav-link { .navbar-light .navbar-nav .nav-link {
color: black; 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 import django_tables2 as tables
from django.db.models import Count, Q 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 from kaehmy.models import Application
+8 -8
View File
@@ -1,8 +1,8 @@
"""Kaehmy urls.""" """Kaehmy urls."""
from django.conf.urls import url from django.urls import re_path
from django.conf import settings 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 view
from kaehmy.views import list_view from kaehmy.views import list_view
@@ -13,12 +13,12 @@ from kaehmy.views import export_view
urlpatterns = [ urlpatterns = [
# kaehmy # kaehmy
url(r"^new", view), re_path(r"^new", view),
url(r"^submit", submit), re_path(r"^submit", submit),
url(r"^add_comment", comment), re_path(r"^add_comment", comment),
url(r"^statistics", statistics_view), re_path(r"^statistics", statistics_view),
url(r"^export", export_view), re_path(r"^export", export_view),
url(r"^$", list_view), re_path(r"^$", list_view),
] ]
if settings.DEBUG: if settings.DEBUG:
+11 -10
View File
@@ -4,6 +4,7 @@ from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.template.loader import render_to_string
import logging import logging
from sikweb.settings import URL from sikweb.settings import URL
@@ -64,14 +65,15 @@ def comment(request, *args, **kwargs):
if form.is_valid(): if form.is_valid():
comment = form.save() comment = form.save()
name = comment.name name = comment.name
url = f"https://{URL}/kaehmy"
to_email = comment.parent.email to_email = comment.parent.email
subject = "Kaehmyysi tai kommenttiisi on vastattu!" subject = "Kaehmyysi tai kommenttiisi on vastattu!"
email_body = ( message = render_to_string(
f"{name.capitalize()} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n" "kaehmy/email_comment.html", {"name": name, "url": url}
"Käy lukemassa viesti osoitteessa https://{URL}/kaehmy"
) )
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}>") logging.debug(f"Sent kaehmy comment email to recipient <{to_email}>")
return redirect("/kaehmy") return redirect("/kaehmy")
@@ -121,6 +123,7 @@ def submit(request, *args, **kwargs):
application = form.save() application = form.save()
custom_name = form.cleaned_data.get("custom_role_name") custom_name = form.cleaned_data.get("custom_role_name")
custom_is_board = form.cleaned_data.get("custom_role_is_board") custom_is_board = form.cleaned_data.get("custom_role_is_board")
kaehmybot_allowed = form.cleaned_data.get("kaehmybot") == "1"
if len(custom_name) > 0: if len(custom_name) > 0:
custom_role = CustomRole(name=custom_name, is_board=custom_is_board) custom_role = CustomRole(name=custom_name, is_board=custom_is_board)
@@ -129,16 +132,14 @@ def submit(request, *args, **kwargs):
url = f"https://{URL}/kaehmy" url = f"https://{URL}/kaehmy"
name = form.cleaned_data.get("name", "Anonymous") 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", "") to_email = form.cleaned_data.get("email", "")
subject = "Arwokas kirjattu kirje mahdolliselle tulewalle kiltahenkilölle" 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}>") logging.debug(f"Sent kaehmy email to recipient <{to_email}>")
processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy") processHooks(message=f"Uusi New kaehmy! {name} -> {url}", eventType="kaehmy")
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
+1 -1
View File
@@ -2,7 +2,7 @@
from django import forms from django import forms
from django.utils import timezone 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 from members.models import Member, Payment, Request
+1 -1
View File
@@ -2,7 +2,7 @@
from django.db import models from django.db import models
from django.utils import timezone 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 from django.db.models import Q, OuterRef, Subquery
+1 -1
View File
@@ -1,7 +1,7 @@
"""File containing member application django tables.""" """File containing member application django tables."""
import django_tables2 as 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.core.exceptions import ObjectDoesNotExist
from django.db.models import F, OuterRef, Subquery from django.db.models import F, OuterRef, Subquery
from django.utils import timezone from django.utils import timezone
+33 -33
View File
@@ -1,6 +1,6 @@
"""File containing Member application URLs.""" """File containing Member application URLs."""
from django.conf.urls import url from django.urls import re_path
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
@@ -42,61 +42,61 @@ from members.views import application_submit
urlpatterns = [ urlpatterns = [
# landing page # landing page
url(r"^$", member_list), re_path(r"^$", member_list),
url(r"^list$", member_list), re_path(r"^list$", member_list),
# add member form view # add member form view
url(r"^add$", member_add), re_path(r"^add$", member_add),
# add many members view # add many members view
url(r"^add_many$", member_add_many), re_path(r"^add_many$", member_add_many),
# edit member information view # edit member information view
url(r"^edit/(?P<index>\d+)$", member_edit), re_path(r"^edit/(?P<index>\d+)$", member_edit),
# delete confirmation view # 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 # list all member applications
url(r"^applications$", application_list), re_path(r"^applications$", application_list),
# edit member application # 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 # post request targets
url(r"^submit_member$", member_submit), re_path(r"^submit_member$", member_submit),
url(r"^update_member$", member_update), re_path(r"^update_member$", member_update),
url(r"^delete_member$", member_delete), re_path(r"^delete_member$", member_delete),
url(r"^submit_payment$", payment_submit), re_path(r"^submit_payment$", payment_submit),
url(r"^update_payment$", payment_update), re_path(r"^update_payment$", payment_update),
url(r"^delete_payment$", payment_delete), re_path(r"^delete_payment$", payment_delete),
url(r"^submit_application$", application_submit), re_path(r"^submit_application$", application_submit),
url(r"^accept_application$", application_accept), re_path(r"^accept_application$", application_accept),
url(r"^delete_application$", application_delete), re_path(r"^delete_application$", application_delete),
# the actual member application form # the actual member application form
url(r"^application/$", application_form), re_path(r"^application/$", application_form),
# delete confirmation view for applications # 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 # list all payment events
url(r"^payments$", payment_list), re_path(r"^payments$", payment_list),
# add payment event # add payment event
url(r"^add_payment$", payment_add), re_path(r"^add_payment$", payment_add),
# edit payment event # 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 # 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 # 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 # settings page
url(r"^settings$", settings_page), re_path(r"^settings$", settings_page),
# send CSV member data by POST # send CSV member data by POST
url(r"^import_csv", import_csv), re_path(r"^import_csv", import_csv),
# export members as excel file # export members as excel file
url(r"export_members", export_members_excel), re_path(r"export_members", export_members_excel),
url(r"export_payments", export_payments_excel), re_path(r"export_payments", export_payments_excel),
url(r"export_applications", export_applications_excel), re_path(r"export_applications", export_applications_excel),
# rest api url # 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 # member select autocomplete view
url( re_path(
r"^member-autocomplete/$", r"^member-autocomplete/$",
MemberAutoComplete.as_view(), MemberAutoComplete.as_view(),
name="member-autocomplete", name="member-autocomplete",
), ),
url(r"^check", CheckByEmail.as_view()), re_path(r"^check", CheckByEmail.as_view()),
] ]
if settings.DEBUG: if settings.DEBUG:
+1 -1
View File
@@ -1,4 +1,4 @@
"""File containing Members application views.""" """File containing Members application views."""
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
+1 -1
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.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings 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.forms.models import model_to_dict
from django.template.loader import render_to_string from django.template.loader import render_to_string
+2 -2
View File
@@ -10,7 +10,7 @@ from django.http import (
HttpResponseForbidden, HttpResponseForbidden,
) )
from django.conf import settings 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.forms.models import model_to_dict
from dal import autocomplete from dal import autocomplete
from django.utils import timezone from django.utils import timezone
@@ -249,7 +249,7 @@ class MemberAutoComplete(autocomplete.Select2QuerySetView):
if self.q: if self.q:
qs = Member.find_members_by_name(self.q) qs = Member.find_members_by_name(self.q)
return qs return qs.order_by("last_name")
class CheckByEmail(APIView): class CheckByEmail(APIView):
+1 -1
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.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.conf import settings 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.forms.models import model_to_dict
import logging import logging
+2 -2
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.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.conf import settings 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.forms.models import model_to_dict
from django_tables2.config import RequestConfig from django_tables2.config import RequestConfig
@@ -135,7 +135,7 @@ def import_csv(request, *args, **kwargs):
member_table = MemberTable( member_table = MemberTable(
result.members, result.members,
request=request, request=request,
exclude=["id", "options"], exclude=["id", "options", "last_paid"],
attrs={"class": "table table-bordered table-hover"}, attrs={"class": "table table-bordered table-hover"},
) )
+1 -1
View File
@@ -1,7 +1,7 @@
"""File containing Ohlhafv forms.""" """File containing Ohlhafv forms."""
from django import 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 django.core.exceptions import ValidationError
from ohlhafv.models import OhlhafvChallenge from ohlhafv.models import OhlhafvChallenge
+1 -1
View File
@@ -5,7 +5,7 @@ from django.utils import timezone
from datetime import timedelta from datetime import timedelta
from django.contrib.auth.models import User from django.contrib.auth.models import User
from webapp.utils import month_from_now 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 django.contrib.auth.models import User
from auditlog.registry import auditlog from auditlog.registry import auditlog
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
+1
View File
@@ -4,6 +4,7 @@
.navbar-border { .navbar-border {
border-bottom: 2px solid #282b3b; border-bottom: 2px solid #282b3b;
border-radius: 0px 0px 8px 8px;
} }
.navbar-light .navbar-nav .nav-link { .navbar-light .navbar-nav .nav-link {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
import django_tables2 as tables import django_tables2 as tables
from django.db.models import Count, Q 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 from ohlhafv.models import OhlhafvChallenge
+5 -5
View File
@@ -1,16 +1,16 @@
"""Ohlhafv urls.""" """Ohlhafv urls."""
from django.conf.urls import url from django.urls import re_path
from django.conf import settings 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 * from ohlhafv.views import *
urlpatterns = [ urlpatterns = [
# ohlhafv # ohlhafv
url(r"^submit", ohlhafv_submit), re_path(r"^submit", ohlhafv_submit),
url(r"^list", ohlhafv_list), re_path(r"^list", ohlhafv_list),
url(r"^$", ohlhafv_view), re_path(r"^$", ohlhafv_view),
] ]
if settings.DEBUG: if settings.DEBUG:
+1 -1
View File
@@ -3,7 +3,7 @@ from django.shortcuts import render
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponseRedirect 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 django.template.loader import render_to_string
from sikweb.settings import URL from sikweb.settings import URL
+4756 -3742
View File
File diff suppressed because it is too large Load Diff
+9 -7
View File
@@ -9,7 +9,7 @@
"lint:py": "black --diff --check .", "lint:py": "black --diff --check .",
"lint:py:fix": "black .", "lint:py:fix": "black .",
"lint:py-type": "pyright", "lint:py-type": "pyright",
"prepare": "husky install" "prepare": "husky"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -17,13 +17,15 @@
}, },
"author": "SIK ry", "author": "SIK ry",
"license": "ISC", "license": "ISC",
"dependencies": { "devDependencies": {
"eslint": "^7.28.0", "@eslint/js": "^9.20.0",
"husky": "^6.0.0", "eslint": "^9.20.0",
"globals": "^15.14.0",
"husky": "^9.1.7",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"pyright": "^1.1.149", "pyright": "^1.1.393",
"remark-cli": "^9.0.0", "remark-cli": "^12.0.1",
"remark-preset-lint-recommended": "^5.0.0" "remark-preset-lint-recommended": "^7.0.1"
}, },
"remarkConfig": { "remarkConfig": {
"plugins": [ "plugins": [
Generated
+1264 -632
View File
File diff suppressed because it is too large Load Diff
+18 -3
View File
@@ -10,8 +10,23 @@ fi
if test -f "$DB_PASSWD_FILE"; then if test -f "$DB_PASSWD_FILE"; then
export DB_PASSWD=$(cat $DB_PASSWD_FILE) export DB_PASSWD=$(cat $DB_PASSWD_FILE)
fi fi
if test -f "$GOOGLE_CREDS_JSON"; then if test -f "$G_PRIVATE_KEY_ID_FILE"; then
export GOOGLE_CREDS_JSON=$(cat $GOOGLE_CRED_JSON_FILE) 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 fi
# Collect static files # Collect static files
@@ -24,4 +39,4 @@ python manage.py migrate
# Start server # Start server
echo "Django running on http://localhost:8000 in production mode" 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
+53 -42
View File
@@ -1,49 +1,60 @@
[tool.poetry] [project]
name = "web2.0-backend" authors = [
version = "0.1.0" {name = "Aarni Halinen", email = "aarni.halinen@sahkoinsinoorikilta.fi"},
]
description = "Backend for 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"
[tool.poetry.dependencies] dependencies = [
python = "^3.9" "decorator (>=4.4.2,<5.0.0)",
decorator = "^4.0.9" "Django (>=4.2.19,<5.0.0)",
Django = "^3.2.14" "django-app-namespace-template-loader (>=0.4.1,<1.0.0)",
requests = "^2.28.1" "django-auditlog (>=2.1.1,<3.0.0)",
django-cors-headers = "^3.13.0" "django-autocomplete-light (>=3.4.1,<4.0.0)",
djangorestframework = "^3.12.4" "django-bootstrap3 (>=21.2.0,<22.0.0)",
psycopg2-binary = "^2.9.3" "django-cors-headers (>=3.13.0,<4.0.0)",
django-bootstrap3 = "^21.2" "django-filter (>=22.1.0,<23.0.0)",
django-tables2 = "^2.4.1" "django-import-export (>=2.8.0,<3.0.0)",
django-modeltranslation = "^0.18.4" "django-modeltranslation (>=0.18.4,<1.0.0)",
django-auditlog = "^2.1.1" "django-phonenumber-field[phonenumbers] (>=6.4.0,<7.0.0)",
django-phonenumber-field = {version = "^6.3.0", extras = ["phonenumbers"]} "django-polymorphic (>=3.1.0,<4.0.0)",
django-autocomplete-light = "^3.4.1" "django-tables2 (>=2.4.1,<3.0.0)",
six = "^1.12.0" "djangorestframework (>=3.12.4,<4.0.0)",
pyexcel = "^0.5.14" "djangorestframework-simplejwt (>=5.2.0,<6.0.0)",
pyexcel-xlsx = "^0.5.8" "google-auth (>=2.9.1,<3.0.0)",
django-import-export = "^2.8.0" "google-api-python-client (>=2.54.0,<3.0.0)",
openpyxl = "^2.6.4" "gunicorn (>=20.1.0,<21.0.0)",
django-app-namespace-template-loader = "^0.4.1" "jsonschema (>=4.9.0,<5.0.0)",
django-filter = "^22.1" "Markdown (>=3.2.2,<4.0.0)",
whitenoise = "^6.2.0" "openpyxl (>=2.6.4,<3.0.0)",
jsonschema = "^4.9.0" "Pillow (>=10.0.0,<11.0.0)",
Markdown = "^3.2.2" "psycopg2-binary (>=2.9.3,<3.0.0)",
uWSGI = "^2.0.18" "pyexcel (>=0.7.0,<1.0.0)",
gunicorn = "^20.1.0" "pyexcel-io (>=0.6.0,<1.0.0)",
Pillow = "^9.1.1" "pyexcel-xlsx (>=0.6.0,<1.0.0)",
sendgrid = "^6.7.0" "python-dotenv (>=0.20.0,<1.0.0)",
sentry-sdk = "^1.4.3" "requests (>=2.28.1,<3.0.0)",
django-polymorphic = "^3.1.0" "sendgrid (>=6.7.0,<7.0.0)",
python-dotenv = "^0.20.0" "sentry-sdk (>=1.4.3,<2.0.0)",
djangorestframework-simplejwt = "^5.2.0" "six (>=1.12.0,<2.0.0)",
google-auth = "^2.9.1" "uWSGI (>=2.0.28,<3.0.0)",
google-api-python-client = "^2.54.0" "whitenoise (>=6.2.0,<7.0.0)",
]
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
black = "^25.1.0"
coverage = "^6.4.2" coverage = "^6.4.2"
safety = "^2.1.1" safety = "^2.3.4"
black = "^22.6.0"
[tool.poetry]
package-mode = false
[tool.poetry.requires-plugins]
poetry-plugin-export = "^1.9"
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
+1 -1
View File
@@ -12,5 +12,5 @@
"reportMissingImports": true, "reportMissingImports": true,
"reportMissingTypeStubs": false, "reportMissingTypeStubs": false,
"pythonVersion": "3.9" "pythonVersion": "3.12.9"
} }
+1 -1
View File
@@ -2,7 +2,7 @@ import os
import logging import logging
import datetime import datetime
from os.path import expanduser 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, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+13 -1
View File
@@ -82,7 +82,19 @@ DATABASES = {
# Google api settings # Google api settings
GROUP_KEY = os.getenv("GROUP_KEY", "") GROUP_KEY = os.getenv("GROUP_KEY", "")
GOOGLE_SERVICE_ACCOUNT = json.loads(os.getenv("GOOGLE_CREDS_JSON", "{}"))
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 # JWT authentication
SIMPLE_JWT = { SIMPLE_JWT = {
+13 -28
View File
@@ -1,23 +1,6 @@
"""sikweb URL Configuration from django.urls import re_path, include
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.contrib import admin from django.contrib import admin
from django.views.static import serve as static_serve 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.urls.static import static
from django.conf import settings from django.conf import settings
from django.contrib.staticfiles import views as static_views 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 = [ urlpatterns = [
url(r"", include("webapp.urls")), re_path(r"", include("webapp.urls")),
url(r"^members/", include("members.urls")), re_path(r"^members/", include("members.urls")),
url(r"^infoscreen/", include("infoscreen.urls")), re_path(r"^infoscreen/", include("infoscreen.urls")),
url(r"^kaehmy/", include("kaehmy.urls")), re_path(r"^kaehmy/", include("kaehmy.urls")),
url(r"^ohlhafv/", include("ohlhafv.urls")), re_path(r"^ohlhafv/", include("ohlhafv.urls")),
# favourite icon # favourite icon
url(r"^favicon\.ico$", favicon_view), re_path(r"^favicon\.ico$", favicon_view),
# admin # admin
url(r"^admin/", admin.site.urls), re_path(r"^admin/", admin.site.urls),
# i18n default view for changing the active language # 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 # staticfiles default view for static files in development
url(r"^static/(?P<path>.*)$", static_views.serve), re_path(r"^static/(?P<path>.*)$", static_views.serve),
url(r"^media/(?P<path>.*)$", static_serve, {"document_root": settings.MEDIA_ROOT}), re_path(
r"^media/(?P<path>.*)$", static_serve, {"document_root": settings.MEDIA_ROOT}
),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+24
View File
@@ -29,15 +29,39 @@ services:
- FRONTEND_URL=dev.sahkoinsinoorikilta.fi - FRONTEND_URL=dev.sahkoinsinoorikilta.fi
- DEBUG=True - DEBUG=True
- EMAIL_API_KEY_FILE=/run/secrets/DJANGO_EMAIL_API_KEY - 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_HOST=db
- DB_PORT=5432 - DB_PORT=5432
secrets: secrets:
- DJANGO_EMAIL_API_KEY - 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: secrets:
DJANGO_EMAIL_API_KEY: DJANGO_EMAIL_API_KEY:
external: true 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: volumes:
dbdata: dbdata:
+25 -4
View File
@@ -34,13 +34,24 @@ services:
- SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY - SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY
- DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD - DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD
- EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY - EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY
- GOOGLE_CREDS_JSON=/run/secrets/GOOGLE_CREDS_JSON - 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: secrets:
- BACKEND_SECRET_KEY - BACKEND_SECRET_KEY
- BACKEND_DB_PASSWD - BACKEND_DB_PASSWD
- BACKEND_EMAIL_API_KEY - BACKEND_EMAIL_API_KEY
- GOOGLE_CREDS_JSON - 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: secrets:
BACKEND_SECRET_KEY: BACKEND_SECRET_KEY:
external: true external: true
@@ -48,5 +59,15 @@ secrets:
external: true external: true
BACKEND_EMAIL_API_KEY: BACKEND_EMAIL_API_KEY:
external: true external: true
GOOGLE_CREDS_JSON: BACKEND_G_PRIVATE_KEY_ID:
EXTERNAL: true 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
+1 -1
View File
@@ -13,7 +13,7 @@
{% block body %} {% block body %}
{% block header %} {% block header %}
<div class="kaehmy_header"> <div class="kaehmy-header">
{% include "kaehmy/header.html" %} {% include "kaehmy/header.html" %}
</div> </div>
{% endblock header %} {% endblock header %}
+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>
+13
View File
@@ -0,0 +1,13 @@
{% load i18n %}
<p>
Moikka {{ name }}!
</p>
<p>
Hienoa, että kilta kiinnostaa! Kaehmysi on vastaanotettu.
Mahdollisista kommenteista tulee ilmoitus sähköpostitse.
</p>
<p>
Käy katsomassa kaehmytilanne
<a href={{ url }}>täältä.</a>
</p>
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "kaehmy/base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
+9 -2
View File
@@ -1,7 +1,14 @@
{% load i18n %} {% load i18n %}
<div class="kaehmy_header-content"> <div class="kaehmy-header-content center">
<div class="kaehmy-banner logo"> <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> <a href="/kaehmy">
<img class="kaehmy-banner-image" src="https://static.sahkoinsinoorikilta.fi/logot-ja-grafiikka/web/side/SIK_RGB_W_side.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry">
</a>
</div>
<div class="kaehmy-banner heading">
<p style="color:#D57A2D; font-size:2rem">{% blocktrans %}Kähmyt ovat auki!{% endblocktrans %}</p>
<p style="color:#BFDBD9; font-size:1rem">{% blocktrans %}Haku hallitukseen 23.10. mennessä ja toimihenkilöksi 15.11 mennessä.{% endblocktrans %}</p>
<p style="color:#BFDBD9; font-size:1.5rem">{% blocktrans %}Hae nyt!{% endblocktrans %}</p>
</div> </div>
</div> </div>
+13 -5
View File
@@ -28,10 +28,12 @@
</p> </p>
<h5>{% trans "Päivämääriä & deadlineja" %}</h5> <h5>{% trans "Päivämääriä & deadlineja" %}</h5>
<ul> <ul>
<li><strong>25.10.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli{% endblocktrans %}</li> <li><strong>11.10.</strong> {% blocktrans %}Toimikuntablää$t @Kiltis{% endblocktrans %}</li>
<li><strong>01.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen valinta){% endblocktrans %}</li> <li><strong>23.10.</strong> {% blocktrans %}Deadline hallitusvirkoihin hakemiselle.{% endblocktrans %}</li>
<li><strong>09.11.</strong> {% blocktrans %}Toimikunta-appro{% endblocktrans %}</li> <li><strong>24.10.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli{% endblocktrans %}</li>
<li><strong>17.11.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li> <li><strong>6.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen ja toimikuntien puheenjohtajien valinta){% endblocktrans %}</li>
<li><strong>15.11.</strong> {% blocktrans %}Deadline toimivirkoihin hakemiselle.{% endblocktrans %}</li>
<li><strong>21.11.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li>
</ul> </ul>
<form name="kaehmyForm" action="/kaehmy/submit/" method="post" class="form">{% csrf_token %} <form name="kaehmyForm" action="/kaehmy/submit/" method="post" class="form">{% csrf_token %}
{% bootstrap_field form.name %} {% bootstrap_field form.name %}
@@ -75,7 +77,13 @@
<input type="checkbox" required name="gdpr" value="1"> <input type="checkbox" required name="gdpr" value="1">
<span>{% blocktrans %} <span>{% blocktrans %}
Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Tietosuojaseloste%20%23U2013%20Toimihenkil%23U00f6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen. Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Tietosuojaseloste%20%E2%80%93%20Toimihenkil%C3%B6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen.
{% endblocktrans %}
</span>
<br>
<input type="checkbox" name="kaehmybot" value="1" checked>
<span>{% blocktrans %}
Kähmybot saa lähettää hakemuksestani ilmoituksen killan telegramiin (hallitusvirkoihin hakiessa valitse kyllä).
{% endblocktrans %} {% endblocktrans %}
</span> </span>
{% buttons %} {% buttons %}
+1 -1
View File
@@ -2,7 +2,7 @@
<div class="card" style="margin-top: 0.5rem; margin-bottom: 0"> <div class="card" style="margin-top: 0.5rem; margin-bottom: 0">
<div class="card-block"> <div class="card-block">
<h4>{{ message.name }}</h4> <h4>{{ message.name }}</h4>
<p>{{ message.message|linebreaks|urlize }}</p> <p>{{ message.message|linebreaks|urlize }}</p>
<h6 class="card-subtitle mb-2 text-muted">{{ message.timestamp }}</h6> <h6 class="card-subtitle mb-2 text-muted">{{ message.timestamp }}</h6>
+2 -2
View File
@@ -1,8 +1,8 @@
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
<div class="kaehmy_navigation"> <div class="kaehmy_navigation bg-faded">
<nav class="navbar-border navbar navbar-toggleable-md navbar-light bg-faded"> <nav class="navbar navbar-toggleable-md navbar-light">
<div class="navbar-nav"> <div class="navbar-nav">
<a class="nav-item nav-link" href="/kaehmy">{% trans "List kaehmys" %}</a> <a class="nav-item nav-link" href="/kaehmy">{% trans "List kaehmys" %}</a>
<a class="nav-item nav-link" href="/kaehmy/new">{% trans "New kaehmy" %} <span class="sr-only">(current)</span></a> <a class="nav-item nav-link" href="/kaehmy/new">{% trans "New kaehmy" %} <span class="sr-only">(current)</span></a>
+1 -1
View File
@@ -7,5 +7,5 @@
<link rel="stylesheet" href="{% static "css/application.css" %}"> <link rel="stylesheet" href="{% static "css/application.css" %}">
<h3>{% trans "Hienoa! Jäsenhakemuksesi on nyt lähetetty." %}</h3> <h3>{% trans "Hienoa! Jäsenhakemuksesi on nyt lähetetty." %}</h3>
<p>{% trans "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä admin@sahkoinsinoorikilta.fi jos viestiä ei näy." %}</p> <p>{% trans "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä admin@sahkoinsinoorikilta.fi jos viestiä ei näy." %}</p>
<a href="https://sahkoinsinoorikilta.fi/"><h4>{% trans "Takaisin Sähköinsinöörikillan web-sivuille" %}</h4></a> <a href="https://sahkoinsinoorikilta.fi/"><h4>{% trans "Takaisin Sähköinsinöörikillan etusivulle" %}</h4></a>
{% endblock content %} {% endblock content %}
@@ -9,7 +9,5 @@
tulevia tapahtumia ja piipahda kiltahuoneella tutustumassa uusiin kiltatovereihisi! tulevia tapahtumia ja piipahda kiltahuoneella tutustumassa uusiin kiltatovereihisi!
</p> </p>
<p>Liity myös killan TG-kanaville:</p> <p>Liity myös killan TG-kanavalle:</p>
<p><a href="https://t.me/+ubTeGSYKTvg3NmVk">Killan yleinen telegram</a></p> <p><a href="https://t.me/+AB-JMbAxM2c0MDc0">Killan yleinen telegram</a></p>
<p><a href="https://t.me/+1PqQHRVMjiAxMTU0">SIK-fuksit 2022</a></p>
<p><a href="https://t.me/+Ln8TvQ-_id9kZTU0">SIK-fuksit 2022 -tiedotuskanava</a></p>
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "members/base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
+1 -1
View File
@@ -5,6 +5,6 @@
{{ challenge.message }} {{ challenge.message }}
{% trans "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 26.5" %}. {% trans "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 13.2" %}.
{% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }} {% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }}
+1 -1
View File
@@ -3,6 +3,6 @@
<div class="ohlhafv-header-content"> <div class="ohlhafv-header-content">
<div class="ohlhafv-banner logo"> <div class="ohlhafv-banner logo">
<a href="/ohlhafv"><img class="ohlhafv-banner-image" src="{% static "ohlhafv/img/heevit.jpg" %}" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a> <a href="/ohlhafv"><img class="ohlhafv-banner-image" src="{% static "ohlhafv/img/heevi_banner.svg" %}" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a>
</div> </div>
</div> </div>
+1 -1
View File
@@ -6,4 +6,4 @@
<a href={{ url }}>{{url}}</a> <a href={{ url }}>{{url}}</a>
<p>Hädässä ota yhteyttä admin@sahkoinsinoorikilta.fi</p> <p>Hädässä ota yhteyttä tapahtuman järjestään.</p>
+1 -1
View File
@@ -10,7 +10,7 @@ from django.dispatch import receiver
import requests import requests
from uuid import uuid4 from uuid import uuid4
import logging import logging
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db.models import JSONField from django.db.models import JSONField
from auditlog.registry import auditlog from auditlog.registry import auditlog
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
View File
+11 -6
View File
@@ -2,6 +2,7 @@ from django.contrib.auth.models import User, AnonymousUser
from django.utils import timezone from django.utils import timezone
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase, APIRequestFactory from rest_framework.test import APITestCase, APIRequestFactory
import zoneinfo
from webapp.models import Event from webapp.models import Event
from webapp.serializers import EventSerializer from webapp.serializers import EventSerializer
@@ -14,28 +15,30 @@ URL = "/api/events/"
class EventTestCase(APITestCase): class EventTestCase(APITestCase):
def setUp(self): def setUp(self):
tz = zoneinfo.ZoneInfo(key="Europe/Helsinki")
# Visible and relevant # Visible and relevant
test1 = createEventObject( test1 = createEventObject(
"Testitapahtuma1", start_time=timezone.datetime(2019, 11, 9, 12, 0, 0) "Testitapahtuma1",
start_time=timezone.datetime(2019, 11, 9, 12, 0, 0, tzinfo=tz),
) )
# Invisible but relevant # Invisible but relevant
createEventObject( createEventObject(
"Testitapahtuma2", "Testitapahtuma2",
visible=False, visible=False,
start_time=timezone.datetime(2018, 11, 9, 12, 0, 0), start_time=timezone.datetime(2018, 11, 9, 12, 0, 0, tzinfo=tz),
) )
# Visible but unrelevant # Visible but unrelevant
test2 = createEventObject( test2 = createEventObject(
"Testitapahtuma3", "Testitapahtuma3",
visible=True, visible=True,
start_time=timezone.datetime(2018, 12, 9, 12, 0, 0), start_time=timezone.datetime(2018, 12, 9, 12, 0, 0, tzinfo=tz),
end_time=timezone.datetime(2018, 12, 9, 13, 0, 0), end_time=timezone.datetime(2018, 12, 9, 13, 0, 0, tzinfo=tz),
) )
# Visible and relevant # Visible and relevant
createEventObject( createEventObject(
"Testitapahtuma4", "Testitapahtuma4",
visible=True, visible=True,
start_time=timezone.datetime(2018, 12, 9, 12, 0, 0), start_time=timezone.datetime(2018, 12, 9, 12, 0, 0, tzinfo=tz),
) )
# Add some tags # Add some tags
tag1 = tagBuilder() tag1 = tagBuilder()
@@ -77,7 +80,9 @@ class EventTestCase(APITestCase):
self.assertEqual(response.data["results"], expected) self.assertEqual(response.data["results"], expected)
def test_get_events_since(self): def test_get_events_since(self):
response = self.client.get(f"{URL}?since=2018-01-01", format="json") response = self.client.get(
f"{URL}?since=2018-01-01%2000:00:00%2B0200", format="json"
)
self.assertTrue(response.status_code, status.HTTP_200_OK) self.assertTrue(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 3) self.assertEqual(len(response.data["results"]), 3)
+3 -4
View File
@@ -1,7 +1,6 @@
"""Webapp urls.""" """Webapp urls."""
from django.conf.urls import url, include from django.urls import path, re_path, include
from django.urls import path
from rest_framework import routers from rest_framework import routers
from rest_framework_simplejwt.views import ( from rest_framework_simplejwt.views import (
TokenObtainPairView, TokenObtainPairView,
@@ -25,9 +24,9 @@ router.register(r"tags", TagsViewSet)
router.register(r"jobads", JobAdViewSet) router.register(r"jobads", JobAdViewSet)
urlpatterns = [ urlpatterns = [
url(r"^api/", include(router.urls)), re_path(r"^api/", include(router.urls)),
path(r"api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), path(r"api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path(r"api/token/verify/", TokenVerifyView.as_view(), name="token_verify"), path(r"api/token/verify/", TokenVerifyView.as_view(), name="token_verify"),
path(r"api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), path(r"api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
url(r"^jwt_nginx", nginx_jwt_resp), re_path(r"^jwt_nginx", nginx_jwt_resp),
] ]
+13 -2
View File
@@ -25,7 +25,7 @@ from sikweb.settings import (
DEFAULT_EMAIL_FROM_ADDR, DEFAULT_EMAIL_FROM_ADDR,
ENABLE_AUTOMATIC_EMAILS, ENABLE_AUTOMATIC_EMAILS,
GROUP_KEY, GROUP_KEY,
GOOGLE_SERVICE_ACCOUNT, GOOGLE_CREDS,
) )
from datetime import timedelta from datetime import timedelta
@@ -136,7 +136,7 @@ def add_to_mailinglist(email: str):
# create credentials, with subject is used to impersonate admin account # create credentials, with subject is used to impersonate admin account
# jas_manager has groups editor rights in google admin # jas_manager has groups editor rights in google admin
credentials = service_account.Credentials.from_service_account_info( credentials = service_account.Credentials.from_service_account_info(
info=GOOGLE_SERVICE_ACCOUNT, scopes=SCOPES info=GOOGLE_CREDS, scopes=SCOPES
).with_subject("jas_manager@sahkoinsinoorikilta.fi") ).with_subject("jas_manager@sahkoinsinoorikilta.fi")
service = build("admin", "directory_v1", credentials=credentials) service = build("admin", "directory_v1", credentials=credentials)
@@ -157,3 +157,14 @@ def add_to_mailinglist(email: str):
) )
send_email(to, subject, body) send_email(to, subject, body)
except ValueError as err:
logging.exception("Formatting of google credentials is incorrect")
if DEPLOY_ENV == "production":
to = "ilari.ojakorpi@sahkoinsinoorikilta.fi"
subject = "Web error: Failed adding to google groups"
body = "Google credential formatted incorretly\nEmail that was not added: {}\n\nAdd user manually to jäsenet groups.".format(
email
)
send_email(to, subject, body)