Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8393875963 | |||
| d5f67f4cc1 | |||
| ebd0bd9fa2 | |||
| c0a9321341 | |||
| 6693279348 | |||
| d0557ffb79 | |||
| 91ee3bea6d | |||
| b1d6bf359f | |||
| 61ac177ce3 | |||
| 9a2168e47f | |||
| a5732669da | |||
| 75800ee9ee | |||
| 5782c20b4b | |||
| 167b0bfabf | |||
| 43c9a6d328 | |||
| b3a159b3d8 | |||
| a7ed188dc8 | |||
| 162759dcb2 | |||
| 05e6ba01d9 | |||
| 8a061381f4 | |||
| d2fa7084da | |||
| e7278c8893 | |||
| 52bf21c8ba | |||
| 0cbc794c75 | |||
| 90a0550775 | |||
| 0d458cf2ea | |||
| 5155f52f29 | |||
| a2ccc43a36 | |||
| a045c6ac89 | |||
| dcb2115cb5 | |||
| 5c7528ca6a | |||
| a3ab12619a | |||
| c637ffb3f6 | |||
| 41fd3043d0 | |||
| 1331eeb1d7 | |||
| 1fd329f0c1 | |||
| 52eb9e370c | |||
| 00e5eff8db | |||
| 7554c1e7e8 | |||
| 50fd4ff9f7 | |||
| ff8230d9c0 | |||
| 3f73fbec62 | |||
| 916e0bdaf0 | |||
| 9e4a7c8569 | |||
| b5839da135 | |||
| f562912492 | |||
| f0a5b6e8e7 | |||
| 8fc3ee534d | |||
| 9b4fa56add | |||
| 90ca91970d | |||
| 955072370f | |||
| 709275c4d3 | |||
| 6f61c9dc32 | |||
| f5432a1ff9 | |||
| f23ce6b39e | |||
| 3a8c455031 | |||
| a6a973f008 | |||
| 01f7911352 | |||
| 55507f89d1 | |||
| 4e0a93631d | |||
| be9e308587 | |||
| 8550a9a02b | |||
| 703bb91bfd | |||
| 6b4a00ebd8 | |||
| 677c1400fa | |||
| a2e08d0ea6 | |||
| 901f2bed96 | |||
| 6004156b6f | |||
| e00323bffe | |||
| 6ef0dbf91b | |||
| 63a4781574 | |||
| e735ebe64a | |||
| f9db8476a1 | |||
| 2a24544056 | |||
| fb7bee5480 | |||
| 8355d10635 | |||
| 21892e277e | |||
| 81e1f994eb | |||
| fc6e02b71b | |||
| 8815ccf667 | |||
| b694370572 | |||
| fda9acfb2f | |||
| 30c6e4809b | |||
| 39754a5e63 | |||
| 0be3ee69be | |||
| c6f0f4615b | |||
| 3ac5400b79 | |||
| 429d3a0602 | |||
| 1b086843dc | |||
| b9280ea026 | |||
| b36022e546 | |||
| 9bb57840a7 | |||
| 36bd74c6cc | |||
| a2615ae27d | |||
| f7de9e32d3 | |||
| 57d8c4321f | |||
| 632eedea9c | |||
| 1405d89d9a | |||
| da1ae8d721 | |||
| 52a83b9336 | |||
| f67ce55d60 | |||
| 5e1390ab6b | |||
| 99348dc297 | |||
| 715b309c89 | |||
| 70676d5203 | |||
| 037e4ae6e8 | |||
| 8d6f13b61d | |||
| 03982ee620 | |||
| a8923b63d6 | |||
| 19975877cb | |||
| 2e0fad4bb2 | |||
| f0179c1840 | |||
| 37a9750d4d | |||
| 5575186570 | |||
| 9e179d5e06 | |||
| ea9a732803 | |||
| 0026b788b2 | |||
| da3a484f6c | |||
| a310d51f5e | |||
| 6732e30213 | |||
| 4e59eee200 | |||
| bb0b2a2628 | |||
| 32d636d3ee | |||
| c91b99cdb1 | |||
| c1a1f6e534 | |||
| f79d1467f7 | |||
| 40cf9121b6 |
@@ -1,9 +1,5 @@
|
|||||||
[report]
|
[report]
|
||||||
show_missing = True
|
show_missing = True
|
||||||
omit =
|
|
||||||
*/migrations/*
|
|
||||||
*/admin.py
|
|
||||||
*/translation.py
|
|
||||||
[run]
|
[run]
|
||||||
omit =
|
omit =
|
||||||
*/migrations/*
|
*/migrations/*
|
||||||
|
|||||||
@@ -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
@@ -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='{}'
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
members/static/js/lib
|
|
||||||
infoscreen/static/js/lib
|
|
||||||
webapp/static/js/lib
|
|
||||||
static/js/lib
|
|
||||||
collected_static
|
|
||||||
venv
|
|
||||||
-251
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-1
@@ -11,4 +11,5 @@ node_modules/
|
|||||||
.idea/
|
.idea/
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
venv/
|
venv/
|
||||||
.venv/
|
.venv/
|
||||||
|
poetry.lock
|
||||||
+172
-112
@@ -1,131 +1,191 @@
|
|||||||
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 pip==25.3
|
||||||
- safety check
|
- pip install poetry==2.1.1
|
||||||
|
- 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 pip==25.3
|
||||||
script:
|
- pip install poetry==2.1.1
|
||||||
- python manage.py migrate --noinput
|
- poetry config virtualenvs.create false
|
||||||
- python manage.py createdefaultadmin
|
- poetry install --no-interaction --no-ansi
|
||||||
- python manage.py test
|
script:
|
||||||
|
- 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 +0,0 @@
|
|||||||
_
|
|
||||||
Executable → Regular
+1
-4
@@ -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
@@ -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
-1
@@ -1 +1 @@
|
|||||||
3.9
|
3.12.9
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
python 3.12.9
|
||||||
|
poetry 2.1.1
|
||||||
+7
-6
@@ -1,13 +1,13 @@
|
|||||||
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=2.1.1
|
||||||
ENV POETRY_VERSION=1.1.13
|
RUN pip install pip==25.3
|
||||||
|
|
||||||
RUN pip install "poetry==$POETRY_VERSION"
|
RUN pip install "poetry==$POETRY_VERSION"
|
||||||
RUN poetry export --without-hashes > 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
|
WORKDIR /app
|
||||||
COPY . ./
|
COPY . ./
|
||||||
@@ -22,6 +22,7 @@ ENV PYTHONUNBUFFERED=1 \
|
|||||||
PIP_DEFAULT_TIMEOUT=100
|
PIP_DEFAULT_TIMEOUT=100
|
||||||
|
|
||||||
RUN apt-get update && apt-get install --no-install-recommends -y build-essential
|
RUN apt-get update && apt-get install --no-install-recommends -y build-essential
|
||||||
|
RUN pip install pip==25.3
|
||||||
RUN pip install --no-deps -r requirements.txt
|
RUN pip install --no-deps -r requirements.txt
|
||||||
|
|
||||||
RUN python manage.py collectstatic --noinput
|
RUN python manage.py collectstatic --noinput
|
||||||
|
|||||||
@@ -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
|
## 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/).
|
||||||
|
|
||||||
```bash
|
First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry:
|
||||||
python3 -m pip install poetry
|
|
||||||
```
|
|
||||||
|
|
||||||
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 pip install poetry==2.1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Start developing by install dependencies first
|
Install dependencies with
|
||||||
|
|
||||||
#### CMDs
|
|
||||||
|
|
||||||
Activate virtual environment in shell
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -m poetry shell
|
|
||||||
```
|
|
||||||
|
|
||||||
Install dependencies
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poetry install
|
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
|
### 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 +110,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 +125,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 +168,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`.
|
||||||
|
|||||||
+15
-7
@@ -1,22 +1,30 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:12
|
image: postgres:12
|
||||||
volumes:
|
volumes:
|
||||||
- dbdata:/var/lib/postgresql/data
|
- dbdata:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- 5432:5432
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=postgres
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend
|
environment:
|
||||||
env_file:
|
- DEPLOY_ENV=local
|
||||||
- .env
|
- 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:
|
ports:
|
||||||
- "8000:8000"
|
- 8000:8000
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
+6
-3
@@ -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(
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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.
+821
-808
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+627
-634
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -3,8 +3,13 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from members.models import Member, Request, Payment
|
from members.models import Member, Request, Payment
|
||||||
|
|
||||||
|
|
||||||
# Register your models here.
|
# 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(Request)
|
||||||
admin.site.register(Payment)
|
admin.site.register(Payment)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 _
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Generated
+4756
-3742
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -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
-1293
File diff suppressed because it is too large
Load Diff
@@ -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
-37
@@ -1,49 +1,65 @@
|
|||||||
[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"
|
||||||
|
|
||||||
|
[virtualenvs]
|
||||||
|
create = true
|
||||||
|
in-project = true
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
decorator = "^4.4.2"
|
||||||
decorator = "^4.0.9"
|
Django = "^4.2.19"
|
||||||
Django = "^3.2.14"
|
|
||||||
requests = "^2.28.1"
|
|
||||||
django-cors-headers = "^3.13.0"
|
|
||||||
djangorestframework = "^3.12.4"
|
|
||||||
psycopg2-binary = "^2.9.3"
|
|
||||||
django-bootstrap3 = "^21.2"
|
|
||||||
django-tables2 = "^2.4.1"
|
|
||||||
django-modeltranslation = "^0.18.4"
|
|
||||||
django-auditlog = "^2.1.1"
|
|
||||||
django-phonenumber-field = {version = "^6.3.0", extras = ["phonenumbers"]}
|
|
||||||
django-autocomplete-light = "^3.4.1"
|
|
||||||
six = "^1.12.0"
|
|
||||||
pyexcel = "^0.5.14"
|
|
||||||
pyexcel-xlsx = "^0.5.8"
|
|
||||||
django-import-export = "^2.8.0"
|
|
||||||
openpyxl = "^2.6.4"
|
|
||||||
django-app-namespace-template-loader = "^0.4.1"
|
django-app-namespace-template-loader = "^0.4.1"
|
||||||
django-filter = "^22.1"
|
django-auditlog = "^2.1.1"
|
||||||
whitenoise = "^6.2.0"
|
django-autocomplete-light = "^3.4.1"
|
||||||
jsonschema = "^4.9.0"
|
django-bootstrap3 = "^21.2.0"
|
||||||
Markdown = "^3.2.2"
|
django-cors-headers = "^3.13.0"
|
||||||
uWSGI = "^2.0.18"
|
django-filter = "^22.1.0"
|
||||||
gunicorn = "^20.1.0"
|
django-import-export = "^2.8.0"
|
||||||
Pillow = "^9.1.1"
|
django-modeltranslation = "^0.18.4"
|
||||||
sendgrid = "^6.7.0"
|
django-phonenumber-field = {version = "^6.4.0", extras = ["phonenumbers"]}
|
||||||
sentry-sdk = "^1.4.3"
|
|
||||||
django-polymorphic = "^3.1.0"
|
django-polymorphic = "^3.1.0"
|
||||||
python-dotenv = "^0.20.0"
|
django-tables2 = "^2.4.1"
|
||||||
djangorestframework-simplejwt = "^5.2.0"
|
djangorestframework = "^3.12.4"
|
||||||
|
djangorestframework-simplejwt = "^5.5.0"
|
||||||
google-auth = "^2.9.1"
|
google-auth = "^2.9.1"
|
||||||
google-api-python-client = "^2.54.0"
|
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]
|
[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
@@ -12,5 +12,5 @@
|
|||||||
"reportMissingImports": true,
|
"reportMissingImports": true,
|
||||||
"reportMissingTypeStubs": false,
|
"reportMissingTypeStubs": false,
|
||||||
|
|
||||||
"pythonVersion": "3.9"
|
"pythonVersion": "3.12.9"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -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
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "kaehmy/base.html" %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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/Suomeksi/Tietosuojaseloste%20–%20Toimihenkilöksi%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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "members/base.html" %}
|
||||||
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|||||||
@@ -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 12.2" %}.
|
||||||
|
|
||||||
{% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }}
|
{% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,35 +0,0 @@
|
|||||||
# Generated by Django 3.2.15 on 2022-08-06 14:33
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.utils.timezone
|
|
||||||
import webapp.utils
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0082_delete_baserole"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="basefeed",
|
|
||||||
name="base_autohide",
|
|
||||||
field=models.DateTimeField(default=webapp.utils.month_from_now),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="basefeed",
|
|
||||||
name="base_autohide_enabled",
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="basefeed",
|
|
||||||
name="base_deleted",
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="basefeed",
|
|
||||||
name="base_publish_time",
|
|
||||||
field=models.DateTimeField(default=django.utils.timezone.now),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 4.2.24 on 2025-10-13 14:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("contenttypes", "0002_remove_content_type_name"),
|
||||||
|
("webapp", "0082_delete_baserole"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="signup",
|
||||||
|
name="submit_id",
|
||||||
|
field=models.UUIDField(default=uuid.uuid4, editable=False, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="basewebhook",
|
||||||
|
name="polymorphic_ctype",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
editable=False,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="polymorphic_%(app_label)s.%(class)s_set+",
|
||||||
|
to="contenttypes.contenttype",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.24 on 2025-10-13 15:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("webapp", "0083_signup_submit_id_alter_basewebhook_polymorphic_ctype"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="signup",
|
||||||
|
name="submit_id",
|
||||||
|
field=models.UUIDField(null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 18:12
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def copyOldDataToNewFields(apps, schema_editor):
|
|
||||||
Event = apps.get_model("webapp", "Event")
|
|
||||||
Feed = apps.get_model("webapp", "Feed")
|
|
||||||
for event in Event.objects.all():
|
|
||||||
event.base_deleted = event.deleted
|
|
||||||
event.save()
|
|
||||||
|
|
||||||
for post in Feed.objects.all():
|
|
||||||
post.base_deleted = post.deleted
|
|
||||||
post.base_publish_time = post.publish_time
|
|
||||||
post.base_autohide = post.autohide
|
|
||||||
post.base_autohide_enabled = post.autohide_enabled
|
|
||||||
post.save()
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0083_auto_20220806_1733"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(
|
|
||||||
copyOldDataToNewFields, reverse_code=migrations.RunPython.noop
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.24 on 2025-10-13 15:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("webapp", "0084_alter_signup_submit_id"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="signup",
|
||||||
|
name="submit_id",
|
||||||
|
field=models.CharField(null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 18:28
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0084_auto_20220726_2112"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="event",
|
|
||||||
old_name="deleted",
|
|
||||||
new_name="old_deleted",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="feed",
|
|
||||||
old_name="autohide",
|
|
||||||
new_name="old_autohide",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="feed",
|
|
||||||
old_name="autohide_enabled",
|
|
||||||
new_name="old_autohide_enabled",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="feed",
|
|
||||||
old_name="deleted",
|
|
||||||
new_name="old_deleted",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="feed",
|
|
||||||
old_name="publish_time",
|
|
||||||
new_name="old_publish_time",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="base_autohide",
|
|
||||||
new_name="autohide",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="base_autohide_enabled",
|
|
||||||
new_name="autohide_enabled",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="base_deleted",
|
|
||||||
new_name="deleted",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="base_publish_time",
|
|
||||||
new_name="publish_time",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.24 on 2025-10-13 15:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("webapp", "0085_alter_signup_submit_id"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="signup",
|
||||||
|
name="submit_id",
|
||||||
|
field=models.UUIDField(editable=False, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 18:29
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0085_auto_20220726_2128"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="event",
|
|
||||||
name="old_deleted",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="feed",
|
|
||||||
name="old_autohide",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="feed",
|
|
||||||
name="old_autohide_enabled",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="feed",
|
|
||||||
name="old_deleted",
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="feed",
|
|
||||||
name="old_publish_time",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.24 on 2025-10-13 15:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("webapp", "0086_alter_signup_submit_id"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="signup",
|
||||||
|
name="submit_id",
|
||||||
|
field=models.UUIDField(editable=False, null=True, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 19:26
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0086_auto_20220726_2129"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameModel(
|
|
||||||
old_name="JobAd",
|
|
||||||
new_name="RemoveJobAd",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 19:49
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import django.utils.timezone
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0087_auto_20220726_2226"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="JobAd",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"basefeed_ptr",
|
|
||||||
models.OneToOneField(
|
|
||||||
auto_created=True,
|
|
||||||
on_delete=django.db.models.deletion.CASCADE,
|
|
||||||
parent_link=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
to="webapp.BaseFeed",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("created_at", models.DateTimeField(default=django.utils.timezone.now)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
"verbose_name": "JobAd",
|
|
||||||
"verbose_name_plural": "JobAds",
|
|
||||||
},
|
|
||||||
bases=("webapp.basefeed",),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 19:29
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
def copyOldDataToNewFields(apps, schema_editor):
|
|
||||||
Old = apps.get_model("webapp", "RemoveJobAd")
|
|
||||||
New = apps.get_model("webapp", "JobAd")
|
|
||||||
for jobAd in Old.objects.all():
|
|
||||||
New.objects.create(
|
|
||||||
id=jobAd.id,
|
|
||||||
title=jobAd.title,
|
|
||||||
tags=jobAd.tags,
|
|
||||||
visible=jobAd.visible,
|
|
||||||
deleted=jobAd.deleted,
|
|
||||||
publish_time=jobAd.publish_time,
|
|
||||||
autohide=jobAd.autohide_at,
|
|
||||||
autohide_enabled=jobAd.autohide_enabled,
|
|
||||||
description=jobAd.description,
|
|
||||||
content=jobAd.content,
|
|
||||||
created_at=jobAd.created_at,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0088_jobad"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RunPython(
|
|
||||||
copyOldDataToNewFields, reverse_code=migrations.RunPython.noop
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 19:52
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0089_auto_20220726_2229"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name="RemoveJobAd",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 20:11
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0090_delete_removejobad"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name="jobad",
|
|
||||||
name="created_at",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 21:16
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0091_remove_jobad_created_at"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="autohide_enabled",
|
|
||||||
new_name="autoUnpublish",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="visible",
|
|
||||||
new_name="isPublished",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="publish_time",
|
|
||||||
new_name="publishAt",
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name="basefeed",
|
|
||||||
old_name="autohide",
|
|
||||||
new_name="unpublishAt",
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2022-07-26 21:17
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("webapp", "0092_auto_20220727_0016"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="basefeed",
|
|
||||||
name="isPublished",
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
+125
-78
@@ -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
|
||||||
@@ -24,15 +24,15 @@ EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
|
|||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
"""Model for tag."""
|
"""Model for tag."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Tag")
|
||||||
|
verbose_name_plural = _("Tags")
|
||||||
|
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
slug = models.SlugField(unique=True)
|
slug = models.SlugField(unique=True)
|
||||||
name = models.CharField(max_length=127)
|
name = models.CharField(max_length=127)
|
||||||
icon = models.ImageField()
|
icon = models.ImageField()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Tag")
|
|
||||||
verbose_name_plural = _("Tags")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _("Tag: {}").format(self.slug)
|
return _("Tag: {}").format(self.slug)
|
||||||
|
|
||||||
@@ -41,85 +41,89 @@ class BaseFeed(models.Model):
|
|||||||
"""Model containing something showing on some info feed."""
|
"""Model containing something showing on some info feed."""
|
||||||
|
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
deleted = models.BooleanField(default=False)
|
tags = models.ManyToManyField(Tag, related_name="feeds", blank=True)
|
||||||
|
visible = models.BooleanField(default=True)
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
description = models.CharField(max_length=255)
|
description = models.CharField(max_length=255)
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
image = models.ImageField(blank=True, null=True)
|
image = models.ImageField(blank=True, null=True)
|
||||||
tags = models.ManyToManyField(Tag, related_name="feeds", blank=True)
|
|
||||||
|
|
||||||
# Require explicit publishing from creator
|
|
||||||
isPublished = models.BooleanField(default=False)
|
|
||||||
# Automatically publish after this time, unless still in draft (!isPublished)
|
|
||||||
publishAt = models.DateTimeField(default=timezone.now)
|
|
||||||
autoUnpublish = models.BooleanField(default=False)
|
|
||||||
# Automatically unpublish after this if auto_unpublish==True
|
|
||||||
unpublishAt = models.DateTimeField(default=month_from_now)
|
|
||||||
|
|
||||||
webhookUrl = ""
|
|
||||||
hookType = ""
|
|
||||||
wasPublishedBefore = False
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(BaseFeed, self).__init__(*args, **kwargs)
|
|
||||||
self.wasPublishedBefore = self.isPublished
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
delete_str = _("Deleted: ") if self.deleted else ""
|
|
||||||
return _("{}{}: {}").format(delete_str, self._meta.verbose_name, self.title)
|
|
||||||
|
|
||||||
def save(self, force_insert=False, force_update=False, *args, **kwargs):
|
|
||||||
created = self.pk is None
|
|
||||||
super(BaseFeed, self).save(force_insert, force_update, *args, **kwargs)
|
|
||||||
|
|
||||||
if self.isPublished and (created or not self.wasPublishedBefore):
|
|
||||||
self.refresh_from_db() # Fetch so we can use primary key
|
|
||||||
url = f"{self.webhookUrl}/{self.pk}"
|
|
||||||
processHooks(
|
|
||||||
message=generateMessage(
|
|
||||||
f"Uusi {self._meta.verbose_name}", self.title, self.description, url
|
|
||||||
),
|
|
||||||
eventType=self.hookType,
|
|
||||||
)
|
|
||||||
self.wasPublishedBefore = self.isPublished
|
|
||||||
|
|
||||||
|
|
||||||
class Feed(BaseFeed):
|
class Feed(BaseFeed):
|
||||||
"""Model representing feed."""
|
"""Model representing feed."""
|
||||||
|
|
||||||
webhookUrl = f"https://{FRONTEND_URL}/feed"
|
|
||||||
hookType = "feed"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Feed")
|
verbose_name = _("Feed")
|
||||||
verbose_name_plural = _("Feeds")
|
verbose_name_plural = _("Feeds")
|
||||||
|
|
||||||
|
publish_time = models.DateTimeField(default=timezone.now)
|
||||||
|
autohide = models.DateTimeField(default=month_from_now)
|
||||||
|
autohide_enabled = models.BooleanField(default=False)
|
||||||
|
deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
delete_str = _("Deleted: ") if self.deleted else ""
|
||||||
|
return _("{}Feed: {}").format(delete_str, self.title)
|
||||||
|
|
||||||
|
__previousVisible = False
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(Feed, self).__init__(*args, **kwargs)
|
||||||
|
self.__previousVisible = self.visible
|
||||||
|
|
||||||
|
def save(self, force_insert=False, force_update=False, *args, **kwargs):
|
||||||
|
created = self.pk is None
|
||||||
|
super(Feed, self).save(force_insert, force_update, *args, **kwargs)
|
||||||
|
|
||||||
|
if self.visible and (created or not self.__previousVisible):
|
||||||
|
self.refresh_from_db() # Fetch so we can use primary key
|
||||||
|
url = f"https://{FRONTEND_URL}/feed/{self.pk}"
|
||||||
|
processHooks(
|
||||||
|
message=generateMessage(
|
||||||
|
"Uusi uutinen", self.title, self.description, url
|
||||||
|
),
|
||||||
|
eventType="feed",
|
||||||
|
)
|
||||||
|
self.__previousVisible = self.visible
|
||||||
|
|
||||||
|
|
||||||
class Event(BaseFeed):
|
class Event(BaseFeed):
|
||||||
"""Model for event in guild calendar"""
|
"""Model for event in guild calendar"""
|
||||||
|
|
||||||
start_time = models.DateTimeField(default=timezone.now)
|
|
||||||
end_time = models.DateTimeField(default=timezone.now)
|
|
||||||
location = models.CharField(max_length=255, blank=True)
|
|
||||||
signupForm = models.ManyToManyField("SignupForm", blank=True, related_name="event")
|
|
||||||
|
|
||||||
webhookUrl = f"https://{FRONTEND_URL}/events"
|
|
||||||
hookType = "event"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Event")
|
verbose_name = _("Event")
|
||||||
verbose_name_plural = _("Events")
|
verbose_name_plural = _("Events")
|
||||||
|
|
||||||
|
start_time = models.DateTimeField(default=timezone.now)
|
||||||
|
end_time = models.DateTimeField(default=timezone.now)
|
||||||
|
signupForm = models.ManyToManyField("SignupForm", blank=True, related_name="event")
|
||||||
|
location = models.CharField(max_length=255, blank=True)
|
||||||
|
deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
class JobAd(BaseFeed):
|
def __str__(self):
|
||||||
"""Job advertisements shown on Corporate relations page"""
|
delete_str = _("Deleted: ") if self.deleted else ""
|
||||||
|
return _("{}Event: {}").format(delete_str, self.title)
|
||||||
|
|
||||||
webhookUrl = f"https://{FRONTEND_URL}/jobads"
|
__previousVisible = False
|
||||||
hookType = "jobad"
|
|
||||||
|
|
||||||
class Meta:
|
def __init__(self, *args, **kwargs):
|
||||||
verbose_name = _("JobAd")
|
super(Event, self).__init__(*args, **kwargs)
|
||||||
verbose_name_plural = _("JobAds")
|
self.__previousVisible = self.visible
|
||||||
|
|
||||||
|
def save(self, force_insert=False, force_update=False, *args, **kwargs):
|
||||||
|
created = self.pk is None
|
||||||
|
super(Event, self).save(force_insert, force_update, *args, **kwargs)
|
||||||
|
|
||||||
|
if self.visible and (created or not self.__previousVisible):
|
||||||
|
self.refresh_from_db() # Fetch so we can use primary key
|
||||||
|
url = f"https://{FRONTEND_URL}/events/{self.pk}"
|
||||||
|
processHooks(
|
||||||
|
message=generateMessage(
|
||||||
|
"Uusi tapahtuma", self.title, self.description, url
|
||||||
|
),
|
||||||
|
eventType="event",
|
||||||
|
)
|
||||||
|
self.__previousVisible = self.visible
|
||||||
|
|
||||||
|
|
||||||
class TemplateQuestion(models.Model):
|
class TemplateQuestion(models.Model):
|
||||||
@@ -127,15 +131,15 @@ class TemplateQuestion(models.Model):
|
|||||||
Stores template questions for signup forms as JSON format. Used in signup form creation.
|
Stores template questions for signup forms as JSON format. Used in signup form creation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Template question")
|
||||||
|
verbose_name_plural = _("Template questions")
|
||||||
|
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
questions = JSONField()
|
questions = JSONField()
|
||||||
deleted = models.BooleanField(default=False)
|
deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Template question")
|
|
||||||
verbose_name_plural = _("Template questions")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _("Template questions: {}").format(self.name)
|
return _("Template questions: {}").format(self.name)
|
||||||
|
|
||||||
@@ -143,20 +147,20 @@ class TemplateQuestion(models.Model):
|
|||||||
class SignupForm(models.Model):
|
class SignupForm(models.Model):
|
||||||
"""Model for event signup form. Stores questions in JSON format."""
|
"""Model for event signup form. Stores questions in JSON format."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Signup form")
|
||||||
|
verbose_name_plural = _("Signup forms")
|
||||||
|
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
deleted = models.BooleanField(default=False)
|
|
||||||
visible = models.BooleanField(default=True)
|
|
||||||
start_time = models.DateTimeField(default=timezone.now)
|
start_time = models.DateTimeField(default=timezone.now)
|
||||||
end_time = models.DateTimeField(default=timezone.now)
|
end_time = models.DateTimeField(default=timezone.now)
|
||||||
questions = JSONField()
|
questions = JSONField()
|
||||||
schema = JSONField()
|
schema = JSONField()
|
||||||
|
visible = models.BooleanField(default=True)
|
||||||
quota = models.PositiveIntegerField(blank=True, null=True)
|
quota = models.PositiveIntegerField(blank=True, null=True)
|
||||||
email_content = models.TextField(blank=True)
|
email_content = models.TextField(blank=True)
|
||||||
|
deleted = models.BooleanField(default=False)
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Signup form")
|
|
||||||
verbose_name_plural = _("Signup forms")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
delete_str = _("Deleted: ") if self.deleted else ""
|
delete_str = _("Deleted: ") if self.deleted else ""
|
||||||
@@ -177,9 +181,12 @@ class Signup(models.Model):
|
|||||||
Actual signup into any SignupForm. Deletes are soft.
|
Actual signup into any SignupForm. Deletes are soft.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Sign-up")
|
||||||
|
verbose_name_plural = _("Sign-ups")
|
||||||
|
|
||||||
id = models.AutoField(primary_key=True)
|
id = models.AutoField(primary_key=True)
|
||||||
signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
|
signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
|
||||||
deleted = models.BooleanField(default=False)
|
|
||||||
time = models.DateTimeField(default=timezone.now)
|
time = models.DateTimeField(default=timezone.now)
|
||||||
answer = JSONField()
|
answer = JSONField()
|
||||||
# Answer we use in signupForm signups field. Frontend uses first questions answer as this value.
|
# Answer we use in signupForm signups field. Frontend uses first questions answer as this value.
|
||||||
@@ -188,11 +195,9 @@ class Signup(models.Model):
|
|||||||
email = models.EmailField(blank=True, null=True)
|
email = models.EmailField(blank=True, null=True)
|
||||||
# Random unique identifier. Used for signup editing by the user.
|
# Random unique identifier. Used for signup editing by the user.
|
||||||
uuid = models.UUIDField(default=uuid4, editable=False)
|
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||||
signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
|
# Random unique identifier generated by browser upon opening signup form. Used to prevent duplicate signups.
|
||||||
|
submit_id = models.UUIDField(null=True, editable=False, unique=True)
|
||||||
class Meta:
|
deleted = models.BooleanField(default=False)
|
||||||
verbose_name = _("Sign-up")
|
|
||||||
verbose_name_plural = _("Sign-ups")
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
delete_str = _("Deleted: ") if self.deleted else ""
|
delete_str = _("Deleted: ") if self.deleted else ""
|
||||||
@@ -219,6 +224,49 @@ def email_on_signup(sender, instance, created, **kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class JobAd(models.Model):
|
||||||
|
"""Job advertisements shown on Corporate relations page"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("JobAd")
|
||||||
|
verbose_name_plural = _("JobAds")
|
||||||
|
|
||||||
|
id = models.AutoField(primary_key=True)
|
||||||
|
title = models.CharField(max_length=255)
|
||||||
|
description = models.CharField(max_length=255)
|
||||||
|
content = models.TextField()
|
||||||
|
visible = models.BooleanField(default=True)
|
||||||
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
|
autohide_at = models.DateTimeField(default=month_from_now)
|
||||||
|
autohide_enabled = models.BooleanField(default=False)
|
||||||
|
deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
delete_str = _("Deleted: ") if self.deleted else ""
|
||||||
|
return f"{delete_str}{self.title}"
|
||||||
|
|
||||||
|
__previousVisible = False
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(JobAd, self).__init__(*args, **kwargs)
|
||||||
|
self.__previousVisible = self.visible
|
||||||
|
|
||||||
|
def save(self, force_insert=False, force_update=False, *args, **kwargs):
|
||||||
|
created = self.pk is None
|
||||||
|
super(JobAd, self).save(force_insert, force_update, *args, **kwargs)
|
||||||
|
|
||||||
|
if self.visible and (created or not self.__previousVisible):
|
||||||
|
self.refresh_from_db() # Fetch so we can use primary key
|
||||||
|
url = f"https://{FRONTEND_URL}/jobads/{self.pk}"
|
||||||
|
processHooks(
|
||||||
|
message=generateMessage(
|
||||||
|
"Uusi työpaikkailmoitus", self.title, self.description, url
|
||||||
|
),
|
||||||
|
eventType="jobad",
|
||||||
|
)
|
||||||
|
self.__previousVisible = self.visible
|
||||||
|
|
||||||
|
|
||||||
def generateMessage(heading: str, title: str, description: str, url: str):
|
def generateMessage(heading: str, title: str, description: str, url: str):
|
||||||
return render_to_string(
|
return render_to_string(
|
||||||
"webapp/tg_message.tpl",
|
"webapp/tg_message.tpl",
|
||||||
@@ -301,9 +349,8 @@ class TelegramHook(BaseWebhook):
|
|||||||
auditlog.register(Tag)
|
auditlog.register(Tag)
|
||||||
auditlog.register(Feed)
|
auditlog.register(Feed)
|
||||||
auditlog.register(Event)
|
auditlog.register(Event)
|
||||||
auditlog.register(JobAd)
|
|
||||||
auditlog.register(TemplateQuestion)
|
|
||||||
auditlog.register(SignupForm)
|
auditlog.register(SignupForm)
|
||||||
auditlog.register(Signup)
|
auditlog.register(Signup)
|
||||||
|
auditlog.register(JobAd)
|
||||||
auditlog.register(GenericWebhook)
|
auditlog.register(GenericWebhook)
|
||||||
auditlog.register(TelegramHook)
|
auditlog.register(TelegramHook)
|
||||||
|
|||||||
+65
-72
@@ -2,19 +2,12 @@ from rest_framework import serializers
|
|||||||
from webapp.models import *
|
from webapp.models import *
|
||||||
|
|
||||||
|
|
||||||
class SavedQuestionsSerializer(serializers.ModelSerializer):
|
|
||||||
questions = serializers.JSONField()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = TemplateQuestion
|
|
||||||
fields = ("id", "name", "questions")
|
|
||||||
|
|
||||||
|
|
||||||
class SignupSerializer(serializers.ModelSerializer):
|
class SignupSerializer(serializers.ModelSerializer):
|
||||||
signupForm_id = serializers.PrimaryKeyRelatedField(
|
signupForm_id = serializers.PrimaryKeyRelatedField(
|
||||||
source="signupForm", queryset=SignupForm.objects.all()
|
source="signupForm", queryset=SignupForm.objects.all()
|
||||||
)
|
)
|
||||||
list_name = serializers.CharField(read_only=True)
|
list_name = serializers.CharField(read_only=True)
|
||||||
|
submit_id = serializers.UUIDField(required=False)
|
||||||
|
|
||||||
def add_extra_fields(self, validated_data):
|
def add_extra_fields(self, validated_data):
|
||||||
questions = validated_data["signupForm"].questions
|
questions = validated_data["signupForm"].questions
|
||||||
@@ -42,7 +35,7 @@ class SignupSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Signup
|
model = Signup
|
||||||
fields = ("id", "signupForm_id", "answer", "list_name")
|
fields = ("id", "submit_id", "signupForm_id", "answer", "list_name")
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"url": {
|
"url": {
|
||||||
"view_name": "signup-detail",
|
"view_name": "signup-detail",
|
||||||
@@ -76,54 +69,11 @@ class SignupFormSerializer(serializers.ModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TagSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Tag
|
|
||||||
fields = ("id", "slug", "name_fi", "name_en", "icon")
|
|
||||||
|
|
||||||
|
|
||||||
class FeedSerializer(serializers.ModelSerializer):
|
|
||||||
tagId = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=Tag.objects.all(),
|
|
||||||
many=True,
|
|
||||||
write_only=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Feed
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"title_fi",
|
|
||||||
"title_en",
|
|
||||||
"description_fi",
|
|
||||||
"description_en",
|
|
||||||
"content_fi",
|
|
||||||
"content_en",
|
|
||||||
"image",
|
|
||||||
"tags",
|
|
||||||
"tagId",
|
|
||||||
"isPublished",
|
|
||||||
"publishAt",
|
|
||||||
"autoUnpublish",
|
|
||||||
"unpublishAt",
|
|
||||||
)
|
|
||||||
read_only_fields = ["tags"]
|
|
||||||
depth = 1
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
tags_data = validated_data.pop("tagId")
|
|
||||||
feed = Feed.objects.create(**validated_data)
|
|
||||||
for tag in tags_data:
|
|
||||||
feed.tags.add(tag)
|
|
||||||
feed.save()
|
|
||||||
return feed
|
|
||||||
|
|
||||||
|
|
||||||
class EventSerializer(serializers.ModelSerializer):
|
class EventSerializer(serializers.ModelSerializer):
|
||||||
tagId = serializers.PrimaryKeyRelatedField(
|
signupForm = SignupFormSerializer(
|
||||||
queryset=Tag.objects.all(),
|
source="filtered_signup_forms",
|
||||||
many=True,
|
many=True,
|
||||||
write_only=True,
|
read_only=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
signup_id = serializers.PrimaryKeyRelatedField(
|
signup_id = serializers.PrimaryKeyRelatedField(
|
||||||
@@ -131,30 +81,26 @@ class EventSerializer(serializers.ModelSerializer):
|
|||||||
many=True,
|
many=True,
|
||||||
write_only=True,
|
write_only=True,
|
||||||
)
|
)
|
||||||
|
tag_id = serializers.PrimaryKeyRelatedField(
|
||||||
signupForm = SignupFormSerializer(
|
queryset=Tag.objects.all(),
|
||||||
source="filtered_signup_forms",
|
|
||||||
many=True,
|
many=True,
|
||||||
read_only=True,
|
write_only=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Event
|
model = Event
|
||||||
fields = (
|
fields = (
|
||||||
"id",
|
"id",
|
||||||
|
"tag_id",
|
||||||
|
"tags",
|
||||||
|
"visible",
|
||||||
|
"image",
|
||||||
"title_fi",
|
"title_fi",
|
||||||
"title_en",
|
"title_en",
|
||||||
"description_fi",
|
"description_fi",
|
||||||
"description_en",
|
"description_en",
|
||||||
"content_fi",
|
"content_fi",
|
||||||
"content_en",
|
"content_en",
|
||||||
"image",
|
|
||||||
"tags",
|
|
||||||
"tagId",
|
|
||||||
"isPublished",
|
|
||||||
"publishAt",
|
|
||||||
"autoUnpublish",
|
|
||||||
"unpublishAt",
|
|
||||||
"start_time",
|
"start_time",
|
||||||
"end_time",
|
"end_time",
|
||||||
"location_fi",
|
"location_fi",
|
||||||
@@ -167,7 +113,7 @@ class EventSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
signupForms = validated_data.pop("signup_id", [])
|
signupForms = validated_data.pop("signup_id", [])
|
||||||
tags = validated_data.pop("tagId")
|
tags = validated_data.pop("tag_id")
|
||||||
event = Event.objects.create(**validated_data)
|
event = Event.objects.create(**validated_data)
|
||||||
for form in signupForms:
|
for form in signupForms:
|
||||||
event.signupForm.add(form)
|
event.signupForm.add(form)
|
||||||
@@ -178,7 +124,7 @@ class EventSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
signupForms = validated_data.pop("signup_id", [])
|
signupForms = validated_data.pop("signup_id", [])
|
||||||
tags = validated_data.pop("tagId")
|
tags = validated_data.pop("tag_id")
|
||||||
instance.signupForm.clear()
|
instance.signupForm.clear()
|
||||||
instance.tags.clear()
|
instance.tags.clear()
|
||||||
for form in signupForms:
|
for form in signupForms:
|
||||||
@@ -189,6 +135,54 @@ class EventSerializer(serializers.ModelSerializer):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class SavedQuestionsSerializer(serializers.ModelSerializer):
|
||||||
|
questions = serializers.JSONField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = TemplateQuestion
|
||||||
|
fields = ("id", "name", "questions")
|
||||||
|
|
||||||
|
|
||||||
|
class TagSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Tag
|
||||||
|
fields = ("id", "slug", "name_fi", "name_en", "icon")
|
||||||
|
|
||||||
|
|
||||||
|
class FeedSerializer(serializers.ModelSerializer):
|
||||||
|
tag_id = serializers.PrimaryKeyRelatedField(
|
||||||
|
many=True, source="tags", queryset=Tag.objects.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Feed
|
||||||
|
fields = (
|
||||||
|
"id",
|
||||||
|
"tags",
|
||||||
|
"tag_id",
|
||||||
|
"visible",
|
||||||
|
"image",
|
||||||
|
"title_fi",
|
||||||
|
"title_en",
|
||||||
|
"description_fi",
|
||||||
|
"description_en",
|
||||||
|
"content_fi",
|
||||||
|
"content_en",
|
||||||
|
"publish_time",
|
||||||
|
"autohide",
|
||||||
|
"autohide_enabled",
|
||||||
|
)
|
||||||
|
depth = 1
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
tags_data = validated_data.pop("tags")
|
||||||
|
feed = Feed.objects.create(**validated_data)
|
||||||
|
for tag in tags_data:
|
||||||
|
feed.tags.add(tag)
|
||||||
|
feed.save()
|
||||||
|
return feed
|
||||||
|
|
||||||
|
|
||||||
class JobAdSerializer(serializers.ModelSerializer):
|
class JobAdSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = JobAd
|
model = JobAd
|
||||||
@@ -200,8 +194,7 @@ class JobAdSerializer(serializers.ModelSerializer):
|
|||||||
"description_en",
|
"description_en",
|
||||||
"content_fi",
|
"content_fi",
|
||||||
"content_en",
|
"content_en",
|
||||||
"isPublished",
|
"visible",
|
||||||
"publishAt",
|
"autohide_at",
|
||||||
"autoUnpublish",
|
"autohide_enabled",
|
||||||
"unpublishAt",
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from webapp.utils import month_from_now
|
|||||||
|
|
||||||
def createEventObject(
|
def createEventObject(
|
||||||
name="Testitapahtuma1",
|
name="Testitapahtuma1",
|
||||||
isPublished=True,
|
visible=True,
|
||||||
start_time=timezone.now(),
|
start_time=timezone.now(),
|
||||||
end_time=month_from_now(),
|
end_time=month_from_now(),
|
||||||
tag_id=[],
|
tag_id=[],
|
||||||
@@ -14,7 +14,7 @@ def createEventObject(
|
|||||||
return Event.objects.create(
|
return Event.objects.create(
|
||||||
title_fi=name,
|
title_fi=name,
|
||||||
title_en=f"title_en {name}",
|
title_en=f"title_en {name}",
|
||||||
isPublished=isPublished,
|
visible=visible,
|
||||||
description_fi=f"desc_fi {name}",
|
description_fi=f"desc_fi {name}",
|
||||||
description_en=f"desc_en {name}",
|
description_en=f"desc_en {name}",
|
||||||
content_fi=f"content_fi {name}",
|
content_fi=f"content_fi {name}",
|
||||||
@@ -27,15 +27,15 @@ def createEventObject(
|
|||||||
|
|
||||||
def createEventJSON(
|
def createEventJSON(
|
||||||
name="POST1",
|
name="POST1",
|
||||||
isPublished=True,
|
visible=True,
|
||||||
start_time=timezone.now(),
|
start_time=timezone.now(),
|
||||||
end_time=month_from_now(),
|
end_time=month_from_now(),
|
||||||
tagId=[],
|
tag_id=[],
|
||||||
signup_id=[],
|
signup_id=[],
|
||||||
):
|
):
|
||||||
return {
|
return {
|
||||||
"tagId": tagId,
|
"tag_id": tag_id,
|
||||||
"visible": isPublished,
|
"visible": visible,
|
||||||
"title_fi": f"title_fi {name}",
|
"title_fi": f"title_fi {name}",
|
||||||
"title_en": f"title_en {name}",
|
"title_en": f"title_en {name}",
|
||||||
"description_fi": f"desc_fi {name}",
|
"description_fi": f"desc_fi {name}",
|
||||||
|
|||||||
+16
-11
@@ -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",
|
||||||
isPublished=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",
|
||||||
isPublished=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",
|
||||||
isPublished=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)
|
||||||
|
|
||||||
@@ -122,7 +127,7 @@ class EventTestCase(APITestCase):
|
|||||||
self.client.force_authenticate(user=self.authClient)
|
self.client.force_authenticate(user=self.authClient)
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
URL,
|
URL,
|
||||||
createEventJSON(tagId=[self.testTagId], signup_id=[self.signupFormId]),
|
createEventJSON(tag_id=[self.testTagId], signup_id=[self.signupFormId]),
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,7 +137,7 @@ class EventTestCase(APITestCase):
|
|||||||
def test_post_event_unauth(self):
|
def test_post_event_unauth(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
URL,
|
URL,
|
||||||
createEventJSON(tagId=[self.testTagId], signup_id=[self.signupFormId]),
|
createEventJSON(tag_id=[self.testTagId], signup_id=[self.signupFormId]),
|
||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class FeedTestCase(APITestCase):
|
|||||||
|
|
||||||
feed = Feed.objects.create(
|
feed = Feed.objects.create(
|
||||||
title="TestFeed",
|
title="TestFeed",
|
||||||
isPublished=True,
|
visible=True,
|
||||||
description="diidadaapa",
|
description="diidadaapa",
|
||||||
content="lorem ipsum",
|
content="lorem ipsum",
|
||||||
)
|
)
|
||||||
@@ -51,10 +51,10 @@ class FeedTestCase(APITestCase):
|
|||||||
tag2_id = tagBuilder("test2").id
|
tag2_id = tagBuilder("test2").id
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"tagId": [tag1_id, tag2_id],
|
"tag_id": [tag1_id, tag2_id],
|
||||||
"title_fi": "testtitle",
|
"title_fi": "testtitle",
|
||||||
"title_en": "testtitle",
|
"title_en": "testtitle",
|
||||||
"isPublished": "True",
|
"visible": "True",
|
||||||
"description_fi": "liirumlaarum",
|
"description_fi": "liirumlaarum",
|
||||||
"description_en": "liirumlaarum",
|
"description_en": "liirumlaarum",
|
||||||
"content_fi": "lorem ipsum",
|
"content_fi": "lorem ipsum",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class JobAdTestCase(APITestCase):
|
|||||||
self.prefilled_jobad = JobAd.objects.create(
|
self.prefilled_jobad = JobAd.objects.create(
|
||||||
title_fi="ABB Test",
|
title_fi="ABB Test",
|
||||||
title_en="ABB Test",
|
title_en="ABB Test",
|
||||||
isPublished=True,
|
visible=True,
|
||||||
description_fi="desc",
|
description_fi="desc",
|
||||||
description_en="desc",
|
description_en="desc",
|
||||||
content_fi="lorem",
|
content_fi="lorem",
|
||||||
@@ -35,12 +35,12 @@ class JobAdTestCase(APITestCase):
|
|||||||
data = {
|
data = {
|
||||||
"title_fi": "testtitle",
|
"title_fi": "testtitle",
|
||||||
"title_en": "testtitle",
|
"title_en": "testtitle",
|
||||||
"isPublished": "True",
|
"visible": "True",
|
||||||
"description_fi": "liirumlaarum",
|
"description_fi": "liirumlaarum",
|
||||||
"description_en": "liirumlaarum",
|
"description_en": "liirumlaarum",
|
||||||
"content_fi": "lorem ipsum",
|
"content_fi": "lorem ipsum",
|
||||||
"content_en": "lorem ipsum",
|
"content_en": "lorem ipsum",
|
||||||
"autoUnpublish": "True",
|
"autohide_enabled": "True",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Try post without authentication
|
# Try post without authentication
|
||||||
|
|||||||
+19
-19
@@ -4,18 +4,9 @@ from modeltranslation.translator import register, TranslationOptions
|
|||||||
from webapp.models import *
|
from webapp.models import *
|
||||||
|
|
||||||
|
|
||||||
@register(Tag)
|
|
||||||
class TagTranslationOptions(TranslationOptions):
|
|
||||||
fields = ("name",)
|
|
||||||
|
|
||||||
|
|
||||||
@register(BaseFeed)
|
@register(BaseFeed)
|
||||||
class BaseFeedTranslationOptions(TranslationOptions):
|
class BaseFeedTranslationOptions(TranslationOptions):
|
||||||
fields = (
|
fields = ("title", "description", "content")
|
||||||
"title",
|
|
||||||
"description",
|
|
||||||
"content",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@register(Feed)
|
@register(Feed)
|
||||||
@@ -23,18 +14,18 @@ class FeedTranslationOptions(TranslationOptions):
|
|||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
|
|
||||||
|
@register(Tag)
|
||||||
|
class TagTranslationOptions(TranslationOptions):
|
||||||
|
fields = ("name",)
|
||||||
|
|
||||||
|
|
||||||
@register(Event)
|
@register(Event)
|
||||||
class EventTranslationOptions(TranslationOptions):
|
class EventTranslationOptions(TranslationOptions):
|
||||||
fields = ("location",)
|
fields = ("location",)
|
||||||
|
|
||||||
|
|
||||||
@register(JobAd)
|
@register(Signup)
|
||||||
class JobAdTranslationOptions(TranslationOptions):
|
class SignupTranslationOptions(TranslationOptions):
|
||||||
fields = ()
|
|
||||||
|
|
||||||
|
|
||||||
@register(TemplateQuestion)
|
|
||||||
class TemplateQuestionTranslationOptions(TranslationOptions):
|
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
|
|
||||||
@@ -43,11 +34,20 @@ class SignupFormTranslationOptions(TranslationOptions):
|
|||||||
fields = ("title",)
|
fields = ("title",)
|
||||||
|
|
||||||
|
|
||||||
@register(Signup)
|
@register(TemplateQuestion)
|
||||||
class SignupTranslationOptions(TranslationOptions):
|
class TemplateQuestionTranslationOptions(TranslationOptions):
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
|
|
||||||
|
@register(JobAd)
|
||||||
|
class JobAdTranslationOptions(TranslationOptions):
|
||||||
|
fields = (
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"content",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@register(BaseWebhook)
|
@register(BaseWebhook)
|
||||||
class BaseWebhookOptions(TranslationOptions):
|
class BaseWebhookOptions(TranslationOptions):
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user