Merge branch 'develop' into feature-nobot

This commit is contained in:
Aarni Halinen
2021-04-14 17:00:08 +03:00
117 changed files with 11636 additions and 6246 deletions
+2
View File
@@ -3,3 +3,5 @@ show_missing = True
[run]
omit =
*/migrations/*
*/admin.py
*/translation.py
+10
View File
@@ -0,0 +1,10 @@
HOST=api.dev.sahkoinsinoorikilta.fi
DEBUG=True
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
TG_BOT_TOKEN=
EMAIL_HOST=
EMAIL_PASSWD=
DB_USER=postgres
DB_PASSWD=postgres
DB_HOST=db
DB_PORT=5432
+10
View File
@@ -0,0 +1,10 @@
HOST=localhost
DEBUG=True
SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
TG_BOT_TOKEN=
EMAIL_HOST=
EMAIL_PASSWD=
DB_USER=postgres
DB_PASSWD=postgres
DB_HOST=db
DB_PORT=5432
+4 -5
View File
@@ -4,11 +4,10 @@
"jquery": true
},
"globals": {
"angular": 1,
"noty": 1,
"app": 1,
"_": 1,
"moment": 1
"angular": true,
"noty": true,
"_": true,
"moment": true
},
"extends": "eslint:recommended",
"rules": {
+2 -2
View File
@@ -1,5 +1,5 @@
*.swp
sikweb/settings.py
.env
*~
*.pyc
*.sqlite3
@@ -10,7 +10,7 @@ members/logs/*
logs/
/media/
node_modules/
/.coverage
.coverage
db.sqlite3
requirements_henu.txt
/collected_static/
+66 -51
View File
@@ -1,102 +1,117 @@
stages:
- test
- setup
- lint
- test
- publish
- deploy
install:
image: node:14
stage: setup
script:
- npm ci
artifacts:
paths:
- node_modules
expire_in: 1 week
test:
image: python:3.5
image: python:3.7
stage: test
needs: []
services:
- postgres:latest
- postgres:12
variables:
POSTGRES_DB: ci
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
DB_HOST: postgres
script:
- python -V
- pip install -r requirements.txt
- cp sikweb/settings-sample.py sikweb/default_settings.py
- cp sikweb/.ci-settings.py sikweb/settings.py
- python manage.py migrate --noinput
- python manage.py createdefaultadmin
- python manage.py test
pycodestyle:
image: python:3.5
lint:py:
image: python:3.7
stage: lint
needs: []
script:
- pip install pycodestyle
- pycodestyle --config=setup.cfg --count .
- pycodestyle --config=pycodestyle.cfg --count .
eslint:
image: node:alpine
lint:js:
image: node:14
stage: lint
before_script:
- npm install
needs: ["install"]
script:
- npm run eslint
- npm run lint:js
remark:
image: node:alpine
lint:md:
image: node:14
stage: lint
before_script:
- npm install
needs: ["install"]
script:
- npm run remark
- npm run lint:md
publish:
stage: publish
image: docker:stable
needs: ["test", "lint:py", "lint:js", "lint:md"]
services:
- docker:stable-dind
only:
- develop
- master
script:
- docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build . -t "$IMAGE_NAME"
- docker push "$IMAGE_NAME"
deploy_dev:
deploy:dev:
stage: deploy
image: alpine:latest
environment:
name: dev
url: http://web.sik.party:8000
image: docker:stable
only:
- develop
before_script:
- pwd
- apk add --update openssh
- ssh -V
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
script:
- scp docker-compose.yml $DEV_SSH_USER@$DEV_SSH_HOST:~/deployment/docker-compose.yml
- scp .deploy_dev.sh $DEV_SSH_USER@$DEV_SSH_HOST:~/deployment/deploy_dev.sh
- ssh $DEV_SSH_USER@$DEV_SSH_HOST "docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY"
- ssh $DEV_SSH_USER@$DEV_SSH_HOST "bash ~/deployment/deploy_dev.sh \"$IMAGE_NAME\""
deploy_production:
stage: deploy
image: alpine:latest
environment:
name: production
url: https://sika.sahkoinsinoorikilta.fi
when: manual
name: dev
url: http://api.dev.sahkoinsinoorikilta.fi
variables:
DOCKER_HOST: $DEV_CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- mkdir -p ~/.docker
- echo "$DEV_TLSCACERT" > ~/.docker/ca.pem
- echo "$DEV_TLSCERT" > ~/.docker/cert.pem
- echo "$DEV_TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
script:
- docker stack deploy --with-registry-auth -c stack-compose-dev.yml "$SERVICE_NAME"
after_script:
- docker logout "$CI_REGISTRY"
deploy:production:
stage: deploy
image: docker:stable
only:
- master
environment:
name: production
url: api.sahkoinsinoorikilta.fi
when: manual
variables:
DOCKER_HOST: $CI_DOCKER_HOST
DOCKER_TLS_VERIFY: 1
before_script:
- pwd
- apk add --update openssh
- ssh -V
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
- mkdir -p ~/.docker
- echo "$TLSCACERT" > ~/.docker/ca.pem
- echo "$TLSCERT" > ~/.docker/cert.pem
- echo "$TLSKEY" > ~/.docker/key.pem
- docker login -u gitlab-ci-token -p "$CI_BUILD_TOKEN" "$CI_REGISTRY"
script:
- ssh $PROD_SSH_USER@$PROD_SSH_HOST "zsh ~/deploy.sh"
- docker stack deploy --with-registry-auth -c stack-compose.yml "$SERVICE_NAME"
after_script:
- docker logout "$CI_REGISTRY"
+1
View File
@@ -0,0 +1 @@
_
+5 -6
View File
@@ -1,9 +1,10 @@
#!/bin/bash
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
. "${VIRTUAL_ENV}/bin/activate"
source "${VIRTUAL_ENV}/bin/activate"
if [ $? -ne 0 ]
then
@@ -14,10 +15,8 @@ fi
set -e
printf "${PURPLE}Running pre-push tests.${NC}\n"
printf "${PURPLE}npm tests...${NC}\n"
npm test
printf "${PURPLE}pycodestyle tests...${NC}\n"
pycodestyle .
printf "${PURPLE}linters...${NC}\n"
npm run lint
printf "${PURPLE}unit tests...${NC}\n"
python -Wall manage.py test --noinput
set +e
+1 -1
View File
@@ -1 +1 @@
3.6.8
3.7.4
+19 -7
View File
@@ -1,8 +1,20 @@
FROM python:3.5
FROM python:3.7-alpine
ENV PYTHONUNBUFFERED 1
ENV IS_DOCKER 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN env
ADD . /code/
WORKDIR /app
COPY requirements.txt ./
COPY requirements.production.txt ./
COPY . ./
# uWSGI, gunicorn etc.
RUN apk add --no-cache python3-dev build-base linux-headers pcre-dev openssl bash \
# PSQL
&& apk add --no-cache postgresql-dev \
# Pillow
&& apk add --no-cache jpeg-dev zlib-dev \
&& pip install --upgrade pip \
&& pip install -r requirements.txt \
&& pip install -r requirements.production.txt
RUN python manage.py collectstatic --noinput
CMD ["sh", "-c", "./production_entrypoint.sh"]
-52
View File
@@ -1,52 +0,0 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
flake8 = "*"
autopep8 = "*"
[packages]
decorator = "==4.0.9"
ipython = "==4.2.0"
pexpect = "==4.1.0"
pickleshare = "==0.7.2"
ptyprocess = "==0.5.1"
pytz = "==2016.4"
simplegeneric = "==0.8.1"
traitlets = "==4.2.1"
requests = "==2.11.1"
django-nocaptcha-recaptcha = "==0.0.19"
django-cors-headers = "==2.0.1"
djangorestframework = "==3.8.2"
djangorestframework-jwt = "==1.11.0"
coverage = "==4.3.4"
django-nose = "==1.4.5"
nose-exclude = "==0.5.0"
psycopg2-binary = "==2.7.6.1"
django-bootstrap3 = "==8.2.3"
django-tables2 = "==1.6.1"
pycodestyle = "==2.3.1"
dealer = "==2.0.5"
django-modeltranslation = "==0.13b1"
django-auditlog = "==0.4.5"
django-phonenumber-field = "==1.3.0"
django-autocomplete-light = "==3.2.10"
six = "==1.10.0"
django-suit = "==0.2.26"
telepot = "==12.3"
pyexcel = "==0.5.10"
pyexcel-xlsx = "==0.5.5"
django-import-export = "==0.7.0"
openpyxl = "==2.4.11"
django-app-namespace-template-loader = "==0.4.1"
django-filter = "==2.0.0"
"backports.shutil_get_terminal_size" = "==1.0.0"
Django = "==2.1.5"
ipython_genutils = "==0.1.0"
Pillow = "==5.4.1"
PyJWT = "==1.6.4"
[requires]
python_version = "3.6"
Generated
-706
View File
@@ -1,706 +0,0 @@
{
"_meta": {
"hash": {
"sha256": "fd7aa96e8c4fc9e3fa88b6f3b28a1101d7efd8b58e964fbece854850acf8bcea"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"aiohttp": {
"hashes": [
"sha256:00d198585474299c9c3b4f1d5de1a576cc230d562abc5e4a0e81d71a20a6ca55",
"sha256:0155af66de8c21b8dba4992aaeeabf55503caefae00067a3b1139f86d0ec50ed",
"sha256:09654a9eca62d1bd6d64aa44db2498f60a5c1e0ac4750953fdd79d5c88955e10",
"sha256:199f1d106e2b44b6dacdf6f9245493c7d716b01d0b7fbe1959318ba4dc64d1f5",
"sha256:296f30dedc9f4b9e7a301e5cc963012264112d78a1d3094cd83ef148fdf33ca1",
"sha256:368ed312550bd663ce84dc4b032a962fcb3c7cae099dbbd48663afc305e3b939",
"sha256:40d7ea570b88db017c51392349cf99b7aefaaddd19d2c78368aeb0bddde9d390",
"sha256:629102a193162e37102c50713e2e31dc9a2fe7ac5e481da83e5bb3c0cee700aa",
"sha256:6d5ec9b8948c3d957e75ea14d41e9330e1ac3fed24ec53766c780f82805140dc",
"sha256:87331d1d6810214085a50749160196391a712a13336cd02ce1c3ea3d05bcf8d5",
"sha256:9a02a04bbe581c8605ac423ba3a74999ec9d8bce7ae37977a3d38680f5780b6d",
"sha256:9c4c83f4fa1938377da32bc2d59379025ceeee8e24b89f72fcbccd8ca22dc9bf",
"sha256:9cddaff94c0135ee627213ac6ca6d05724bfe6e7a356e5e09ec57bd3249510f6",
"sha256:a25237abf327530d9561ef751eef9511ab56fd9431023ca6f4803f1994104d72",
"sha256:a5cbd7157b0e383738b8e29d6e556fde8726823dae0e348952a61742b21aeb12",
"sha256:a97a516e02b726e089cffcde2eea0d3258450389bbac48cbe89e0f0b6e7b0366",
"sha256:acc89b29b5f4e2332d65cd1b7d10c609a75b88ef8925d487a611ca788432dfa4",
"sha256:b05bd85cc99b06740aad3629c2585bda7b83bd86e080b44ba47faf905fdf1300",
"sha256:c2bec436a2b5dafe5eaeb297c03711074d46b6eb236d002c13c42f25c4a8ce9d",
"sha256:cc619d974c8c11fe84527e4b5e1c07238799a8c29ea1c1285149170524ba9303",
"sha256:d4392defd4648badaa42b3e101080ae3313e8f4787cb517efd3f5b8157eaefd6",
"sha256:e1c3c582ee11af7f63a34a46f0448fca58e59889396ffdae1f482085061a2889"
],
"version": "==3.5.4"
},
"async-timeout": {
"hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
],
"version": "==3.0.1"
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
],
"version": "==19.1.0"
},
"babel": {
"hashes": [
"sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669",
"sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23"
],
"version": "==2.6.0"
},
"backports.csv": {
"hashes": [
"sha256:1277dfff73130b2e106bf3dd347adb3c5f6c4340882289d88f31240da92cbd6d",
"sha256:21f6e09bab589e6c1f877edbc40277b65e626262a86e69a70137db714eaac5ce"
],
"version": "==1.0.7"
},
"backports.shutil-get-terminal-size": {
"hashes": [
"sha256:0975ba55054c15e346944b38956a4c9cbee9009391e41b86c68990effb8c1f64",
"sha256:713e7a8228ae80341c70586d1cc0a8caa5207346927e23d09dcbcaf18eadec80"
],
"index": "pypi",
"version": "==1.0.0"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"coverage": {
"hashes": [
"sha256:01406019418aabb2d4741647cc79b0e0deb0b8c5a6f936936c303e2f82ec8e5b",
"sha256:01a07b2b9212d4da3a1294436b58ac53f1d7aa445bda666648a5357048dc7ef3",
"sha256:024682371464c6e3caa975aba12b4d5428f35613489340fce1334c74d590a057",
"sha256:07c15c4a2287116a41d5966f1f5a7be765640c2e5a1917f882850a24615db6d3",
"sha256:1d23dea598fb4d61a8577d0eb0cb2b7932db0c8d2e1394088ad5f64e3fe1febf",
"sha256:1eeb9de833c3b976ee118a8d838af437bfa596bf60a5bf0705f4370e6d181a52",
"sha256:229ab9c0d53c55d698b8784d53077bef7a5f1fb5d27e90dc7b6f91243b024513",
"sha256:2f5a8bf29bdc69976d0913745daab11f8265e46ec41153f5e1e1794088019dad",
"sha256:2f959bc1b40a3ef2c5f0c7bc282226d6d4bd585b239bcce321013afc18ff0a0f",
"sha256:36407249a0b6669c6ad4425b0f29685579df745480c03afa70f101f09f4eead3",
"sha256:3efa49e3da8f32071ee3d5d464cc6b6f8818524d4099b4a94b86a70b8c88d4f5",
"sha256:422bcc6270e1c0cd9043048ce244f49072e9bd78a2c028c2ad2cfd58c79f5936",
"sha256:4fa2b181c3bf94cfdf841148d5d9abcab1890188dd908a639bcf7a38c50092bc",
"sha256:57c0c217270e628380f4befbbf8c5312b88ba7d81fd3d1b2218a25a2608f603c",
"sha256:6ae76a6cd594107ad45525278e8addeae4628a59c8cde3999548d7fe1646465b",
"sha256:6d3c762c87062a29771015f942752caef42fcc7fe4be2b03186f96788242290c",
"sha256:8a82664931a071399d703d65af2521e2202b34f2d8db20fa22a922fec0339022",
"sha256:8b282292973a1dc4eccfcc0776e0fde75b5b3de2e35164c2d854f7dd80149e4b",
"sha256:9a7874ca91cee8714277cd6d1b52374809ab925bf6ae92607bf02509019caadb",
"sha256:9c3e6551597593c1afedcbccf1371995f94457aea82cac726d1f3a25f4507386",
"sha256:a791068e1bbe443fcd3179b1c180c27a7fc58c1554b0d10311b7659d2d2d76f5",
"sha256:adf04843188418b012dd1974e397a7ac3faa1855cbcd69083e3af4da6de9dd81",
"sha256:adfbbd4a1d22fd77b13ff992946b19873407e035504abe9ba537494fe013300f",
"sha256:b25aa3531220faaf1727fc29bc000798476b4a30f429dc07898d5da48caefa15",
"sha256:c12f34c0b50e9e8bf8c049b6c8ca59929c33cea4b1c48362c99c36838c1ee025",
"sha256:c736faa1688222a6c8a5d8be4b66ec373ad6dab27fced8ca0d2c80fed70ac6e3",
"sha256:ca36d83cd591d027952e5019149c4386e7058cd674bf8cb52dc622f768d689e9",
"sha256:e1fb21a807aa0b5cc79806d8ef09078acaa83f994e15f0f7277489ca8eda51b7",
"sha256:e53199ae110cb7e250dd5505fde34452514f4eb2f1fb7532270d2ea037454b09",
"sha256:ea9808001dcf34d368cbef430e7885fdc76a2cf8ea96a8ed8b653797dd9555bb",
"sha256:eaaefe0f6aa33de5a65f48dd0040d7fe08cac9ac6c35a56d0a7db109c3e733df",
"sha256:f27772c9ee88ed3f2a784181f3d1724561499e7e448ed1706153336baa706bd5",
"sha256:f99066d76274800145a2e658026b30962eb5079346249197e88b55c9a7855e6a",
"sha256:fd3373ccd561b79932d12a986674e642816cfc4db4507b6a22ab30c318a85429"
],
"index": "pypi",
"version": "==4.3.4"
},
"dealer": {
"hashes": [
"sha256:0a5a536fdecd9c7679534a19c59392cd21989037c14782328970a185b39e7560",
"sha256:baaac37a4c7928545cb8b0335f48abd0ea51a0274159a9a989afb8b71f8b11c3"
],
"index": "pypi",
"version": "==2.0.5"
},
"decorator": {
"hashes": [
"sha256:90022e83316363788a55352fe39cfbed357aa3a71d90e5f2803a35471de4bba8",
"sha256:f4718552326c99544a6ec602d96b7d03ef61180cf4a492c515ecb2438dd14ccc"
],
"index": "pypi",
"version": "==4.0.9"
},
"defusedxml": {
"hashes": [
"sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4",
"sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20"
],
"version": "==0.5.0"
},
"diff-match-patch": {
"hashes": [
"sha256:a809a996d0f09b9bbd59e9bbd0b71eed8c807922512910e05cbd3f9480712ddb"
],
"version": "==20181111"
},
"django": {
"hashes": [
"sha256:a32c22af23634e1d11425574dce756098e015a165be02e4690179889b207c7a8",
"sha256:d6393918da830530a9516bbbcbf7f1214c3d733738779f06b0f649f49cc698c3"
],
"index": "pypi",
"version": "==2.1.5"
},
"django-app-namespace-template-loader": {
"hashes": [
"sha256:356539413b5d1de0eff91aea7a03806b5ef6874ee5420ea8c273f72bbc601d74",
"sha256:7a450985479a2e07fe8a1e4e8208fc9e1d8b35503526dd28eba5f8ad4ba31d4e"
],
"index": "pypi",
"version": "==0.4.1"
},
"django-auditlog": {
"hashes": [
"sha256:70bfc673e7023d91ab8449d745425e7a4ce5eaaf2bdcbfb9b1a2209a7af60b03"
],
"index": "pypi",
"version": "==0.4.5"
},
"django-autocomplete-light": {
"hashes": [
"sha256:5ccb1c8c4b75cf72bc5dabd920190ea1ca3a340f56fb6b12d07a62202837fa75"
],
"index": "pypi",
"version": "==3.2.10"
},
"django-bootstrap3": {
"hashes": [
"sha256:6f7946d513d6340bb70f25d8ec047bf3bf37bab9e499d3baca99b1aa0ec92a52"
],
"index": "pypi",
"version": "==8.2.3"
},
"django-cors-headers": {
"hashes": [
"sha256:638aaba85f96af62557656ec559672f03d7c61769685acc405eacfaba9d4e93f",
"sha256:c766daf9eefcb9536af9817703ea29124fffee06870f9e523b75144b4d39a694"
],
"index": "pypi",
"version": "==2.0.1"
},
"django-filter": {
"hashes": [
"sha256:6f4e4bc1a11151178520567b50320e5c32f8edb552139d93ea3e30613b886f56",
"sha256:86c3925020c27d072cdae7b828aaa5d165c2032a629abbe3c3a1be1edae61c58"
],
"index": "pypi",
"version": "==2.0.0"
},
"django-import-export": {
"hashes": [
"sha256:6e748fcc647fe2a82a55136ebcbe806a45fd7fb5b1e32b33759181f1e67eeb1b",
"sha256:7e7ebeb40702eafeb2e770914c01b9961063f472b3b395eeffbea5f39efa7257"
],
"index": "pypi",
"version": "==0.7.0"
},
"django-jsonfield": {
"hashes": [
"sha256:25e53eae8bda165721773145ee0f8ae53b746bb3051b32f2821ba84d79aa77ef",
"sha256:813c52463fbbb548fe0d85bf935d0bf72e933fae2bb00ce3ba27bf69ff6fd2ad",
"sha256:cacf5a21e7c2490109a60f1122c05aa3858a8dc06952ad764831b4428164fc8e"
],
"version": "==1.1.0"
},
"django-modeltranslation": {
"hashes": [
"sha256:254ebda6caea5683407e1fb3e45ceaa2275778c6dc2db2b9d4fec3df373c2cdd"
],
"index": "pypi",
"version": "==0.13b1"
},
"django-nocaptcha-recaptcha": {
"hashes": [
"sha256:d2512d5035d5f62aba5009082db28b28995a7e6f4a46713292e0f4f350f337da"
],
"index": "pypi",
"version": "==0.0.19"
},
"django-nose": {
"hashes": [
"sha256:5df2df802c607daeeab8ac1e93abf54508ed6133eb93852310f512000124b4a5",
"sha256:87663f18cb25f01d56c84ac1ff8a0e6e6a6246264b2549b751cb239d0642e76a"
],
"index": "pypi",
"version": "==1.4.5"
},
"django-phonenumber-field": {
"hashes": [
"sha256:8db9d2dc833678b163adabd593cda7ad1dede81a1c18f67c895701fc44dc44f1"
],
"index": "pypi",
"version": "==1.3.0"
},
"django-suit": {
"hashes": [
"sha256:19ed865a478dfca81cb5f50a70317700dd70da92c465093251d0e14330a2b92b"
],
"index": "pypi",
"version": "==0.2.26"
},
"django-tables2": {
"hashes": [
"sha256:d5d3ad99580121f7ec46ea9e2420069bbd6d2f33b4fde73a376c6bf27d551146"
],
"index": "pypi",
"version": "==1.6.1"
},
"djangorestframework": {
"hashes": [
"sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9",
"sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4"
],
"index": "pypi",
"version": "==3.8.2"
},
"djangorestframework-jwt": {
"hashes": [
"sha256:5efe33032f3a4518a300dc51a51c92145ad95fb6f4b272e5aa24701db67936a7",
"sha256:ab15dfbbe535eede8e2e53adaf52ef0cf018ee27dbfad10cbc4cbec2ab63d38c"
],
"index": "pypi",
"version": "==1.11.0"
},
"et-xmlfile": {
"hashes": [
"sha256:614d9722d572f6246302c4491846d2c393c199cfa4edc9af593437691683335b"
],
"version": "==1.0.1"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
],
"version": "==2.8"
},
"ipython": {
"hashes": [
"sha256:0480354f25b2f443e4ef1456b48f28ad1caaa6d316fca5a5eaa9ca7745ae7923",
"sha256:98452af6450e28c9c742d567d75eb6e3a7b391ad4ce8abd5679c5f85ce7fad00",
"sha256:d852fed59da67c7e45cb2192027da8bfd920a7856d295c247a45105968d24d5a",
"sha256:dba42f182b5f6f26630d2202efd30383712d9f7d8d8d9896b37ae2145deca616"
],
"index": "pypi",
"version": "==4.2.0"
},
"ipython-genutils": {
"hashes": [
"sha256:0c43fa84e93ad0e4dbecaffc6656ac1caf1a48359b2bb0a5da3af84164e3f49b",
"sha256:3a0624a251a26463c9dfa0ffa635ec51c4265380980d9a50d65611c3c2bd82a6",
"sha256:6218e9abd612fb5acfb175ea7c7b026006de4df9691d9a73c9b390cfa1a41c2b"
],
"index": "pypi",
"version": "==0.1.0"
},
"jdcal": {
"hashes": [
"sha256:948fb8d079e63b4be7a69dd5f0cd618a0a57e80753de8248fd786a8a20658a07",
"sha256:ea0a5067c5f0f50ad4c7bdc80abad3d976604f6fb026b0b3a17a9d84bb9046c9"
],
"version": "==1.4"
},
"lml": {
"hashes": [
"sha256:b1bef669dc077a1075fa64b99229b6341085b3b3a98d29c66df1853cc14e6c1a",
"sha256:ea5ba817b4adc9e9f5c21725cd2475f912933b7e2dfdf0792aed80077154f63f"
],
"version": "==0.0.9"
},
"multidict": {
"hashes": [
"sha256:024b8129695a952ebd93373e45b5d341dbb87c17ce49637b34000093f243dd4f",
"sha256:041e9442b11409be5e4fc8b6a97e4bcead758ab1e11768d1e69160bdde18acc3",
"sha256:045b4dd0e5f6121e6f314d81759abd2c257db4634260abcfe0d3f7083c4908ef",
"sha256:047c0a04e382ef8bd74b0de01407e8d8632d7d1b4db6f2561106af812a68741b",
"sha256:068167c2d7bbeebd359665ac4fff756be5ffac9cda02375b5c5a7c4777038e73",
"sha256:148ff60e0fffa2f5fad2eb25aae7bef23d8f3b8bdaf947a65cdbe84a978092bc",
"sha256:1d1c77013a259971a72ddaa83b9f42c80a93ff12df6a4723be99d858fa30bee3",
"sha256:1d48bc124a6b7a55006d97917f695effa9725d05abe8ee78fd60d6588b8344cd",
"sha256:31dfa2fc323097f8ad7acd41aa38d7c614dd1960ac6681745b6da124093dc351",
"sha256:34f82db7f80c49f38b032c5abb605c458bac997a6c3142e0d6c130be6fb2b941",
"sha256:3d5dd8e5998fb4ace04789d1d008e2bb532de501218519d70bb672c4c5a2fc5d",
"sha256:4a6ae52bd3ee41ee0f3acf4c60ceb3f44e0e3bc52ab7da1c2b2aa6703363a3d1",
"sha256:4b02a3b2a2f01d0490dd39321c74273fed0568568ea0e7ea23e02bd1fb10a10b",
"sha256:4b843f8e1dd6a3195679d9838eb4670222e8b8d01bc36c9894d6c3538316fa0a",
"sha256:5de53a28f40ef3c4fd57aeab6b590c2c663de87a5af76136ced519923d3efbb3",
"sha256:61b2b33ede821b94fa99ce0b09c9ece049c7067a33b279f343adfe35108a4ea7",
"sha256:6a3a9b0f45fd75dc05d8e93dc21b18fc1670135ec9544d1ad4acbcf6b86781d0",
"sha256:76ad8e4c69dadbb31bad17c16baee61c0d1a4a73bed2590b741b2e1a46d3edd0",
"sha256:7ba19b777dc00194d1b473180d4ca89a054dd18de27d0ee2e42a103ec9b7d014",
"sha256:7c1b7eab7a49aa96f3db1f716f0113a8a2e93c7375dd3d5d21c4941f1405c9c5",
"sha256:7fc0eee3046041387cbace9314926aa48b681202f8897f8bff3809967a049036",
"sha256:8ccd1c5fff1aa1427100ce188557fc31f1e0a383ad8ec42c559aabd4ff08802d",
"sha256:8e08dd76de80539d613654915a2f5196dbccc67448df291e69a88712ea21e24a",
"sha256:c18498c50c59263841862ea0501da9f2b3659c00db54abfbf823a80787fde8ce",
"sha256:c49db89d602c24928e68c0d510f4fcf8989d77defd01c973d6cbe27e684833b1",
"sha256:ce20044d0317649ddbb4e54dab3c1bcc7483c78c27d3f58ab3d0c7e6bc60d26a",
"sha256:d1071414dd06ca2eafa90c85a079169bfeb0e5f57fd0b45d44c092546fcd6fd9",
"sha256:d3be11ac43ab1a3e979dac80843b42226d5d3cccd3986f2e03152720a4297cd7",
"sha256:db603a1c235d110c860d5f39988ebc8218ee028f07a7cbc056ba6424372ca31b"
],
"version": "==4.5.2"
},
"nose": {
"hashes": [
"sha256:9ff7c6cc443f8c51994b34a667bbcf45afd6d945be7477b52e97516fd17c53ac",
"sha256:dadcddc0aefbf99eea214e0f1232b94f2fa9bd98fa8353711dacb112bfcbbb2a",
"sha256:f1bffef9cbc82628f6e7d7b40d7e255aefaa1adb6a1b1d26c69a8b79e6208a98"
],
"version": "==1.3.7"
},
"nose-exclude": {
"hashes": [
"sha256:f78fa8b41eeb815f0486414f710f1eea0949e346cfb11d59ba6295ed69e84304"
],
"index": "pypi",
"version": "==0.5.0"
},
"odfpy": {
"hashes": [
"sha256:596021f0519623ca8717331951c95e3b8d7b21e86edc7efe8cb650a0d0f59a2b"
],
"version": "==1.4.0"
},
"openpyxl": {
"hashes": [
"sha256:626d38647c063d55803ef4971c4d43226538d4e95cb6260c094e363ee33e10c7"
],
"index": "pypi",
"version": "==2.4.11"
},
"pexpect": {
"hashes": [
"sha256:09b0a7727ce012e0fa668ef848591102d6667521655f4e72d51197c872cb9fb9",
"sha256:c381c60f1987355b65df8f08a27f428831914c8a81091bd1778ac336fa2f27e7"
],
"index": "pypi",
"version": "==4.1.0"
},
"phonenumberslite": {
"hashes": [
"sha256:2cb034b158314ca3dc034b417a64777c14b74f27f47b451270686e22eefd57d3",
"sha256:fb9212bb8f27ec4bd5ff9a109d4309deed31b45ae5a7216bacc6644759fac82d"
],
"version": "==8.10.8"
},
"pickleshare": {
"hashes": [
"sha256:92ee3b0e21632542ecc9a0a245e69a126f62e5114081bdb0d32e0edd10410033",
"sha256:b58cf7d70658a091621c0d8cc35143c8569f3827496b27ed896918c237d05d96"
],
"index": "pypi",
"version": "==0.7.2"
},
"pillow": {
"hashes": [
"sha256:051de330a06c99d6f84bcf582960487835bcae3fc99365185dc2d4f65a390c0e",
"sha256:0ae5289948c5e0a16574750021bd8be921c27d4e3527800dc9c2c1d2abc81bf7",
"sha256:0b1efce03619cdbf8bcc61cfae81fcda59249a469f31c6735ea59badd4a6f58a",
"sha256:163136e09bd1d6c6c6026b0a662976e86c58b932b964f255ff384ecc8c3cefa3",
"sha256:18e912a6ccddf28defa196bd2021fe33600cbe5da1aa2f2e2c6df15f720b73d1",
"sha256:24ec3dea52339a610d34401d2d53d0fb3c7fd08e34b20c95d2ad3973193591f1",
"sha256:267f8e4c0a1d7e36e97c6a604f5b03ef58e2b81c1becb4fccecddcb37e063cc7",
"sha256:3273a28734175feebbe4d0a4cde04d4ed20f620b9b506d26f44379d3c72304e1",
"sha256:4c678e23006798fc8b6f4cef2eaad267d53ff4c1779bd1af8725cc11b72a63f3",
"sha256:4d4bc2e6bb6861103ea4655d6b6f67af8e5336e7216e20fff3e18ffa95d7a055",
"sha256:505738076350a337c1740a31646e1de09a164c62c07db3b996abdc0f9d2e50cf",
"sha256:5233664eadfa342c639b9b9977190d64ad7aca4edc51a966394d7e08e7f38a9f",
"sha256:5d95cb9f6cced2628f3e4de7e795e98b2659dfcc7176ab4a01a8b48c2c2f488f",
"sha256:7eda4c737637af74bac4b23aa82ea6fbb19002552be85f0b89bc27e3a762d239",
"sha256:801ddaa69659b36abf4694fed5aa9f61d1ecf2daaa6c92541bbbbb775d97b9fe",
"sha256:825aa6d222ce2c2b90d34a0ea31914e141a85edefc07e17342f1d2fdf121c07c",
"sha256:9c215442ff8249d41ff58700e91ef61d74f47dfd431a50253e1a1ca9436b0697",
"sha256:a3d90022f2202bbb14da991f26ca7a30b7e4c62bf0f8bf9825603b22d7e87494",
"sha256:a631fd36a9823638fe700d9225f9698fb59d049c942d322d4c09544dc2115356",
"sha256:a6523a23a205be0fe664b6b8747a5c86d55da960d9586db039eec9f5c269c0e6",
"sha256:a756ecf9f4b9b3ed49a680a649af45a8767ad038de39e6c030919c2f443eb000",
"sha256:b117287a5bdc81f1bac891187275ec7e829e961b8032c9e5ff38b70fd036c78f",
"sha256:ba04f57d1715ca5ff74bb7f8a818bf929a204b3b3c2c2826d1e1cc3b1c13398c",
"sha256:cd878195166723f30865e05d87cbaf9421614501a4bd48792c5ed28f90fd36ca",
"sha256:cee815cc62d136e96cf76771b9d3eb58e0777ec18ea50de5cfcede8a7c429aa8",
"sha256:d1722b7aa4b40cf93ac3c80d3edd48bf93b9208241d166a14ad8e7a20ee1d4f3",
"sha256:d7c1c06246b05529f9984435fc4fa5a545ea26606e7f450bdbe00c153f5aeaad",
"sha256:e9c8066249c040efdda84793a2a669076f92a301ceabe69202446abb4c5c5ef9",
"sha256:f227d7e574d050ff3996049e086e1f18c7bd2d067ef24131e50a1d3fe5831fbc",
"sha256:fc9a12aad714af36cf3ad0275a96a733526571e52710319855628f476dcb144e"
],
"index": "pypi",
"version": "==5.4.1"
},
"psycopg2-binary": {
"hashes": [
"sha256:036bcb198a7cc4ce0fe43344f8c2c9a8155aefa411633f426c8c6ed58a6c0426",
"sha256:1d770fcc02cdf628aebac7404d56b28a7e9ebec8cfc0e63260bd54d6edfa16d4",
"sha256:1fdc6f369dcf229de6c873522d54336af598b9470ccd5300e2f58ee506f5ca13",
"sha256:21f9ddc0ff6e07f7d7b6b484eb9da2c03bc9931dd13e36796b111d631f7135a3",
"sha256:247873cda726f7956f745a3e03158b00de79c4abea8776dc2f611d5ba368d72d",
"sha256:3aa31c42f29f1da6f4fd41433ad15052d5ff045f2214002e027a321f79d64e2c",
"sha256:475f694f87dbc619010b26de7d0fc575a4accf503f2200885cc21f526bffe2ad",
"sha256:4b5e332a24bf6e2fda1f51ca2a57ae1083352293a08eeea1fa1112dc7dd542d1",
"sha256:570d521660574aca40be7b4d532dfb6f156aad7b16b5ed62d1534f64f1ef72d8",
"sha256:59072de7def0690dd13112d2bdb453e20570a97297070f876fbbb7cbc1c26b05",
"sha256:5f0b658989e918ef187f8a08db0420528126f2c7da182a7b9f8bf7f85144d4e4",
"sha256:649199c84a966917d86cdc2046e03d536763576c0b2a756059ae0b3a9656bc20",
"sha256:6645fc9b4705ae8fbf1ef7674f416f89ae1559deec810f6dd15197dfa52893da",
"sha256:6872dd54d4e398d781efe8fe2e2d7eafe4450d61b5c4898aced7610109a6df75",
"sha256:6ce34fbc251fc0d691c8d131250ba6f42fd2b28ef28558d528ba8c558cb28804",
"sha256:73920d167a0a4d1006f5f3b9a3efce6f0e5e883a99599d38206d43f27697df00",
"sha256:8a671732b87ae423e34b51139628123bc0306c2cb85c226e71b28d3d57d7e42a",
"sha256:8d517e8fda2efebca27c2018e14c90ed7dc3f04d7098b3da2912e62a1a5585fe",
"sha256:9475a008eb7279e20d400c76471843c321b46acacc7ee3de0b47233a1e3fa2cf",
"sha256:96947b8cd7b3148fb0e6549fcb31258a736595d6f2a599f8cd450e9a80a14781",
"sha256:abf229f24daa93f67ac53e2e17c8798a71a01711eb9fcdd029abba8637164338",
"sha256:b1ab012f276df584beb74f81acb63905762c25803ece647016613c3d6ad4e432",
"sha256:b22b33f6f0071fe57cb4e9158f353c88d41e739a3ec0d76f7b704539e7076427",
"sha256:b3b2d53274858e50ad2ffdd6d97ce1d014e1e530f82ec8b307edd5d4c921badf",
"sha256:bab26a729befc7b9fab9ded1bba9c51b785188b79f8a2796ba03e7e734269e2e",
"sha256:daa1a593629aa49f506eddc9d23dc7f89b35693b90e1fbcd4480182d1203ea90",
"sha256:dd111280ce40e89fd17b19c1269fd1b74a30fce9d44a550840e86edb33924eb8",
"sha256:e0b86084f1e2e78c451994410de756deba206884d6bed68d5a3d7f39ff5fea1d",
"sha256:eb86520753560a7e89639500e2a254bb6f683342af598088cb72c73edcad21e6",
"sha256:ff18c5c40a38d41811c23e2480615425c97ea81fd7e9118b8b899c512d97c737"
],
"index": "pypi",
"version": "==2.7.6.1"
},
"ptyprocess": {
"hashes": [
"sha256:0530ce63a9295bfae7bd06edc02b6aa935619f486f0f1dc0972f516265ee81a6",
"sha256:464cb76f7a7122743dd25507650db89cd447c51f38e4671602b3eaa2e38e05ae"
],
"index": "pypi",
"version": "==0.5.1"
},
"pycodestyle": {
"hashes": [
"sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
"sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9"
],
"index": "pypi",
"version": "==2.3.1"
},
"pyexcel": {
"hashes": [
"sha256:2a32accc28aea3994922606ecf7bef00ef058b56b1bea6af119ae3bb56468333",
"sha256:f1ffe613f09285edf42132b9afc14f81adbd0f56797e5fe05e98307d00ce175f"
],
"index": "pypi",
"version": "==0.5.10"
},
"pyexcel-io": {
"hashes": [
"sha256:55b2aa4ef81ba6e3285edfb1a3f3c3c69f9f4d52b6867318ae2381f88741143f",
"sha256:de9de0d6bf9a8906c94e3b5dbe0b3a3e0a9bc893201d6a5c5b3cf84e5119d60d"
],
"version": "==0.5.16"
},
"pyexcel-xlsx": {
"hashes": [
"sha256:488783c3f5195bed8638f6064b11d97f706641b0f065a5416297a01db6cec5ea",
"sha256:b3566162f7232336ebe0d40dd298145c18715009b020dddc210890cf6436ffb2"
],
"index": "pypi",
"version": "==0.5.5"
},
"pyjwt": {
"hashes": [
"sha256:30b1380ff43b55441283cc2b2676b755cca45693ae3097325dea01f3d110628c",
"sha256:4ee413b357d53fd3fb44704577afac88e72e878716116270d722723d65b42176"
],
"index": "pypi",
"version": "==1.6.4"
},
"python-dateutil": {
"hashes": [
"sha256:3acbef017340600e9ff8f2994d8f7afd6eacb295383f286466a6df3961e486f0",
"sha256:537bf2a8f8ce6f6862ad705cd68f9e405c0b5db014aa40fa29eab4335d4b1716",
"sha256:62a2f8df3d66f878373fd0072eacf4ee52194ba302e00082828e0d263b0418d2"
],
"version": "==2.6.0"
},
"pytz": {
"hashes": [
"sha256:8781cdd3ca70f5a536884e051797ca213b9ff479a5c1cc57240adf37cc1eff1b",
"sha256:be2ff04e94a2b5454ddcfbebb81ee8e46162734d4c2fcc90c422d16ab51f810b",
"sha256:c823de61ff40d1996fe087cec343e0503881ca641b897e0f9b86c7683a0bfee1",
"sha256:ee7c751544e35a7b7fb5e3fb25a49dade37d51e70a93e5107f10575d7102c311"
],
"index": "pypi",
"version": "==2016.4"
},
"pyyaml": {
"hashes": [
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
],
"version": "==5.1"
},
"requests": {
"hashes": [
"sha256:545c4855cd9d7c12671444326337013766f4eea6068c3f0307fb2dc2696d580e",
"sha256:5acf980358283faba0b897c73959cecf8b841205bb4b2ad3ef545f46eae1a133"
],
"index": "pypi",
"version": "==2.11.1"
},
"simplegeneric": {
"hashes": [
"sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173"
],
"index": "pypi",
"version": "==0.8.1"
},
"six": {
"hashes": [
"sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1",
"sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a"
],
"index": "pypi",
"version": "==1.10.0"
},
"tablib": {
"hashes": [
"sha256:0f88a9cebdaa1a2cc29ae57387082ee81015d1149ecd34e48a8c8d3b4dd21670",
"sha256:5f33c079b07eb10cf9c4b4696add2ecf32c89db7729240546ecdcd5c92f67e13"
],
"version": "==0.13.0"
},
"telepot": {
"hashes": [
"sha256:8910fd6fb708e2c3ded7ca82cc945a645b717699d9f82ddff5123bb2e05f780f"
],
"index": "pypi",
"version": "==12.3"
},
"texttable": {
"hashes": [
"sha256:2b60a5304ccfbeac80ffae7350d7c2f5d7a24e9aab5036d0f82489746419d9b2"
],
"version": "==1.6.1"
},
"traitlets": {
"hashes": [
"sha256:05a66843c96a320eec09df674c16ff330a43cb07f731cf2bd88aa3645a180541",
"sha256:76eba33c89723b8fc024f950cacaf5bf2ef37999642cc9a61f4e7c1ca5cf0ac0",
"sha256:d6db3201395f9b955786d25a1817c07291e2bcb96eb7f41683ae3836836179d7"
],
"index": "pypi",
"version": "==4.2.1"
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
],
"version": "==1.24.1"
},
"xlrd": {
"hashes": [
"sha256:546eb36cee8db40c3eaa46c351e67ffee6eeb5fa2650b71bc4c758a29a1b29b2",
"sha256:e551fb498759fa3a5384a94ccd4c3c02eb7c00ea424426e212ac0c57be9dfbde"
],
"version": "==1.2.0"
},
"xlwt": {
"hashes": [
"sha256:a082260524678ba48a297d922cc385f58278b8aa68741596a87de01a9c628b2e",
"sha256:c59912717a9b28f1a3c2a98fd60741014b06b043936dcecbc113eaaada156c88"
],
"version": "==1.3.0"
},
"yarl": {
"hashes": [
"sha256:024ecdc12bc02b321bc66b41327f930d1c2c543fa9a561b39861da9388ba7aa9",
"sha256:2f3010703295fbe1aec51023740871e64bb9664c789cba5a6bdf404e93f7568f",
"sha256:3890ab952d508523ef4881457c4099056546593fa05e93da84c7250516e632eb",
"sha256:3e2724eb9af5dc41648e5bb304fcf4891adc33258c6e14e2a7414ea32541e320",
"sha256:5badb97dd0abf26623a9982cd448ff12cb39b8e4c94032ccdedf22ce01a64842",
"sha256:73f447d11b530d860ca1e6b582f947688286ad16ca42256413083d13f260b7a0",
"sha256:7ab825726f2940c16d92aaec7d204cfc34ac26c0040da727cf8ba87255a33829",
"sha256:b25de84a8c20540531526dfbb0e2d2b648c13fd5dd126728c496d7c3fea33310",
"sha256:c6e341f5a6562af74ba55205dbd56d248daf1b5748ec48a0200ba227bb9e33f4",
"sha256:c9bb7c249c4432cd47e75af3864bc02d26c9594f49c82e2a28624417f0ae63b8",
"sha256:e060906c0c585565c718d1c3841747b61c5439af2211e185f6739a9412dfbde1"
],
"version": "==1.3.0"
}
},
"develop": {
"autopep8": {
"hashes": [
"sha256:33d2b5325b7e1afb4240814fe982eea3a92ebea712869bfd08b3c0393404248c"
],
"index": "pypi",
"version": "==1.4.3"
},
"entrypoints": {
"hashes": [
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
],
"version": "==0.3"
},
"flake8": {
"hashes": [
"sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661",
"sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"
],
"index": "pypi",
"version": "==3.7.7"
},
"mccabe": {
"hashes": [
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
],
"version": "==0.6.1"
},
"pycodestyle": {
"hashes": [
"sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766",
"sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9"
],
"index": "pypi",
"version": "==2.3.1"
},
"pyflakes": {
"hashes": [
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
],
"version": "==2.1.1"
}
}
}
View File
-3
View File
@@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.
View File
-3
View File
@@ -1,3 +0,0 @@
from django.db import models
# Create your models here.
@@ -1,124 +0,0 @@
body {
background-color: white;
font-family: monospace;
color: black;
}
#container{
position:relative;
width:95%;
margin-left:auto;
margin-right:auto;
height:100%;
overflow:hidden;
}
#upper{
background-size: contain;
background-repeat: no-repeat;
background-position: bottom center;
background-image: url("/static/coffee_scale/img/smokes.png");
transform-origin: bottom;
animation: smokes 8s ease-in-out 0s infinite;
opacity:0;
height:40%;
}
#lower{
position:relative;
background-size: contain;
background-repeat: no-repeat;
background-position: top center;
background-image: url("/static/coffee_scale/img/coffeecup3.png");
height:60%;
}
#scale{
position:absolute;
top:80%;
width:90%;
height:10%;
margin: 0% 5% 0% 5%;
background: lightgrey;
border-radius: 10px;
overflow:hidden;
}
#scale2{
width: 0%;
transition: width 2s;
height:100%;
background: green;
border-radius: 10px;
}
.brewtime{
text-align:right;
position:absolute;
right:0px;
z-index:5;
font-size:10vw;
}
#address{
text-align:left;
position:absolute;
left:0px;
z-index:5;
font-size:4vw;
color: #333;
}
.layertwo{
display: None;
}
noscript{
color:red;
}
.text{
color:green;
position:absolute;
top:50%;
left:50%;
}
.brewing{
animation: brewing 5s ease-in-out 0s infinite;
}
.hurry{
color:red !important;
}
.unknown{
color:orange !important;
animation: unknown 5s ease-in-out 0s infinite;
}
.friday{
animation: friday 20s ease-in-out 0s infinite;
}
.normal{
animation: normal 1000s ease-in-out 0s infinite;
}
.coffeeready{
animation: coffeeready 10s ease-in-out 0s;
}
@keyframes smokes {
0% {transform: skewX(-10deg);}
50% {transform: skewX(10deg);}
100% {transform: skewX(-10deg);}
}
@keyframes brewing {
0% {color:green;}
50% {color: transparent;}
100% {color:green;}
}
@keyframes coffeeready {
0% {background-color:white;}
25% {background-color:rgb(100, 255, 100);}
50% {background-color:white;}
75% {background-color:rgb(100, 255, 100);}
100% {background-color:white;}
}
@keyframes unknown {
0%,40% {transform: rotate(0deg);}
60%,100% {transform: rotate(360deg);}
}
@keyframes friday {
0% {transform: rotate(0deg);}
100% {transform: rotate(360deg);}
}
@keyframes normal {
0%,49% {transform: rotate(0deg);}
50%,100% {transform: rotate(360deg);}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

@@ -1,183 +0,0 @@
//Inner state
var lastBrew = new Date(0);
var brewing = false;
var backoff = 2000;
//MQTT client config
var username = "coffee-user-"+ Math.random();
// eslint-disable-next-line no-undef
var client = new Paho.MQTT.Client("sika.sahkoinsinoorikilta.fi", 9001, username);
client.onMessageArrived = function (message) {
// eslint-disable-next-line no-console
console.log("Topic: "+message.destinationName+" msg: "+message.payloadString);
var ev = new CustomEvent(message.destinationName, {'detail': message.payloadString});
window.dispatchEvent(ev);
}
function reconnect(responseObject){
if (responseObject.errorCode !== 0) {
console.log("connection lost! Reason: "+responseObject.errorMessage); // eslint-disable-line no-console
setTimeout(function(){
client.connect({onSuccess:onConnect, useSSL:true, onFailure: reconnect});
}, backoff);
}
}
function onConnect() {
console.log("MQTT connected"); // eslint-disable-line no-console
//set and reset reconnector
client.onConnectionLost = reconnect
// subscribe to topics
client.subscribe("sik/kiltahuone/kahvivaaka/cups");
client.subscribe("sik/kiltahuone/kahvivaaka/brewing");
client.subscribe("sik/kiltahuone/kahvivaaka/brewtime");
}
// data update and parse functions
function parseCups(ev){
var cups = parseFloat(ev.detail).toFixed(1)
function makeEvent(cups) {
return (String(cups) !== '-1.0')
? new CustomEvent("cupsChanged", {'detail': cups})
: new CustomEvent("cupsError", {'detail': 'Error: unable to fetch cups :('});
}
window.dispatchEvent(makeEvent(cups));
}
function updateCups(ev){
$("#text").text(ev.detail);
}
function showCupsError(ev) {
$('#text').text(ev.detail);
$('#text').css({
'font-size': '7vh',
'left': '0',
'top': '40%',
'width': '100%',
'text-align': 'center',
'color': 'red',
});
$('#lower').css({'background-image': 'none'});
}
function updateScale(ev){
$("#scale2").css({width: Math.min(ev.detail/9*100,100) + '%'});
}
function tick(){
var ev = new CustomEvent("tick", {'detail': new Date()});
window.dispatchEvent(ev);
}
function updateTime(ev){
var now = ev.detail;
$("#time").html(formatTime(now.getHours(),now.getMinutes(),now.getSeconds()));
}
function coffeeLowEffect(ev){
ev.detail <= 2 ? $("#text").addClass("hurry") : $("#text").removeClass("hurry");
}
function coffeeReadyEffect(){
$("body").addClass("coffeeready");
// autoclear animation class in 10s
setTimeout(function(){$("body").removeClass("coffeeready");}, 10000);
}
function hotEffect(ev){
var opa = Math.max(100 - ev.detail / 90000,0);
$("#upper").css({opacity: opa/100});
}
function brewAnimStart(){
$(".text").addClass("brewing");
$(".layerone").hide();
$(".layertwo").show();
}
function brewAnimEnd(){
$(".text").removeClass("brewing");
$(".layertwo").hide();
$(".layerone").show();
}
function brewNotifier(ev){
var new_brewing = parseInt(ev.detail);
if (new_brewing == 1 && brewing == 0){
window.dispatchEvent(new Event("brewStart"));
} else if (new_brewing == 0 && brewing == 1){
window.dispatchEvent(new Event("brewEnd"));
}
brewing = new_brewing;
}
function brewTimeParser(ev){
lastBrew = new Date(parseInt(ev.detail)*1000.0);
}
function updateBrewDiff(){
var now = new Date();
var timeDiff = Math.max(now.getTime() - lastBrew.getTime(), 0);
var eve = new CustomEvent("dtUpdate", {'detail': timeDiff});
window.dispatchEvent(eve);
}
function updateBrewTime(ev){
var timeDiff = ev.detail;
var timeStr;
if (timeDiff < 3600000){
timeStr = Math.round(timeDiff / 60000) + ' min'
} else if (timeDiff < 10000* 3600 * 1000){ // 1000h
timeStr = '~' + Math.round(timeDiff / 3600000 * 2) / 2 + ' h';
} else {
timeStr = "???"
}
$("#brewtime").html(timeStr);
}
// Helpers
function nToS(num){
return num < 10 ? "0" + num : "" + num;
}
function formatTime(hours, minutes, seconds){
return nToS(hours)+":"+nToS(minutes)+":"+nToS(seconds)
}
function resize(){
var w = $("#container").width();
var h = $("#container").height();
var s = w > h ? h : w;
var font = s * 0.8 * 0.38/Math.sqrt(3);
$(".text").css({ top: s*0.16-font/2 + 'px',
fontSize: font + 'px',
marginLeft: -font*3*3/10 + 'px'});
}
// Init everything
$(document).ready(function(){
client.connect({onSuccess:onConnect, useSSL:true, onFailure:reconnect});
//connect MQTT event listeners
window.addEventListener("sik/kiltahuone/kahvivaaka/cups", parseCups);
window.addEventListener("sik/kiltahuone/kahvivaaka/brewing", brewNotifier);
window.addEventListener("sik/kiltahuone/kahvivaaka/brewtime", brewTimeParser);
//connect other event listeners
window.addEventListener("cupsChanged", updateCups);
window.addEventListener("cupsChanged", coffeeLowEffect);
window.addEventListener("cupsChanged", updateScale);
window.addEventListener("cupsChanged", resize);
window.addEventListener("cupsError", showCupsError);
window.addEventListener("cupsError", coffeeLowEffect);
window.addEventListener("cupsError", updateScale);
window.addEventListener("brewStart", brewAnimStart);
window.addEventListener("brewEnd", brewAnimEnd);
window.addEventListener("brewEnd", coffeeReadyEffect);
window.addEventListener("tick", updateTime);
window.addEventListener("tick", updateBrewDiff);
window.addEventListener("dtUpdate", updateBrewTime);
window.addEventListener("dtUpdate", hotEffect);
//start time based events
setInterval(tick, 100);
tick();
});
$(window).resize(resize);
-41
View File
@@ -1,41 +0,0 @@
{% load i18n %}
{% load static %}
<html>
<head>
<title>Coffee Cups @Guild Room - AYY SIK ry</title>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="3600">
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.2/mqttws31.js"
type="text/javascript"></script>
<link rel="stylesheet" href="{% static "coffee_scale/css/coffee.css" %}">
<script src="{% static "coffee_scale/js/coffee.js" %}"></script>
</head>
<body>
<div id="container">
<span id="brewtime" class="brewtime layerone"></span>
<span class="brewtime layertwo">:)</span>
<span id="address">
ka.dy.fi
<noscript><br>This page uses JavaScript!</noscript>
<br><span id="time"></span></span>
</span>
<div id="upper">
</div>
<!--Kahvinkeitin on rikki. Varakeittimellä keitettyä kahvia saattaa olla.-->
<div id="lower" class="normal">
<div id="text" class="text layerone">???</div>
<div class="text layertwo">&nbsp;+</div>
<div id="scale"><div id="scale2"></div></div>
</div>
</div>
</body>
</html>
-2
View File
@@ -1,2 +0,0 @@
from django.test import TestCase, Client
from django.conf import settings
-12
View File
@@ -1,12 +0,0 @@
from django.conf.urls import url
from django.conf import settings
from .views import coffee_view
urlpatterns = [
# landing page
url(r'^$', coffee_view),
]
if settings.DEBUG:
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns()
-11
View File
@@ -1,11 +0,0 @@
from django.shortcuts import render
from django.http import JsonResponse
from django.utils import timezone
import logging
from django.conf import settings
def coffee_view(request):
return render(request, 'coffee_scale:coffee.html')
+14 -2
View File
@@ -2,12 +2,24 @@ version: '3'
services:
db:
image: postgres
image: postgres:12
volumes:
- dbdata:/var/lib/postgresql/data
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=postgres
web:
build: .
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend
command: ["bash", "-c", "cd /code && ./wait-for-it.sh db:5432 -- bash setup.sh --no-input --no-npm && python manage.py runserver 0.0.0.0:8000"]
command: ["bash", "-c", "cd /app & bash setup.sh --no-input --no-npm && gunicorn -w 4 -b 0.0.0.0:8000 sikweb.wsgi"]
env_file:
- .env
ports:
- "8000:8000"
depends_on:
- db
volumes:
dbdata:
@@ -56,7 +56,8 @@ app.filter('unsafe', function($sce) {
app.controller('ABBController', function($scope, $http){
$scope.jobs = [];
var min_date = moment().subtract(30,'days').format("YYYY-MM-DD%20HH:mm:ss");
var url = "https://sahkoinsinoorikilta.fi/api/news.php";
// TODO: FIX, we try to get rid of php, not depend on it!
var url = "https://old.sahkoinsinoorikilta.fi/api/news.php";
var params = "?type=11&lang=fi&title_search=ABB&min_date="+min_date
$http.get(url+params).then(function(response){
$scope.jobs = _.filter(response.data, function(job){
+9 -3
View File
@@ -5,15 +5,21 @@ from webapp.utils import month_from_now
from django.utils.translation import ugettext_lazy as _
from auditlog.registry import auditlog
from phonenumber_field.modelfields import PhoneNumberField
from webapp.models import BaseRole
import logging
import webapp.models
# TODO: Move BaseRole to Kaehmt App; will fuck up the DB since table is removed, if no data migration is done before-hand.
# Either reconstruct all kaehmy roles from scratch then, or do these migrations:
# 1. Create table here
# 2. Data migrate from webapp BaseRole to new kaehmy BaseRole
# 3. Delete webapp BaseRole table
VERBOSE_NAME = _('Kaehmy')
class KaehmyBaseRole(webapp.models.BaseRole):
class KaehmyBaseRole(BaseRole):
"""ABC"""
CATEGORIES = (
('corporate', _('Corporate affairs')),
+1 -1
View File
@@ -23,7 +23,7 @@
position: absolute;
left: 0;
top: 0;
background-color: #052f5f;
background-color: #0c2938;
width: 100%;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 70 KiB

+2 -3
View File
@@ -1,5 +1,4 @@
{% if wrap_label %}
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>
{% include "django/forms/widgets/input.html" %}
{% if wrap_label %} {{ widget.label }}</label>{% endif %}
{{ widget.label }}</label>
<span class="fa fa-info-circle" data-toggle="tooltip" data-placement="right" title="{{ widget.description }}"></span>
+1 -1
View File
@@ -2,6 +2,6 @@
<div class="kaehmy_header-content">
<div class="kaehmy-banner logo">
<a href="/kaehmy"><img class="kaehmy-banner-image" src="/static/img/kaehmy_banner.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a>
<a href="/kaehmy"><img class="kaehmy-banner-image" src="/static/kaehmy/img/kaehmy_banner.png" alt="Aalto-yliopiston Sähköinsinöörikilta ry"></a>
</div>
</div>
+14 -12
View File
@@ -16,21 +16,23 @@
{% blocktrans %}Kaehmykoneella voit ilmaista kiinnostuksesi toimia killassa ensi vuonna.
Listassa on vastuualueittain sekä hallitus- että toimihenkilövirkoja.
Koska lista ei ole koskaan täydellinen, voit myös ehdottaa ihan uutta toimenkuvaa.
Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa konsultoida <a href="/static/other/kahmyopas.pdf">kaehmyopasta</a>
Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa konsultoida <a href="https://static.sahkoinsinoorikilta.fi/uus_webi/kahmyopas.pdf">kaehmyopasta</a>
tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan henkilöön.{% endblocktrans %}
</p>
<p>
{% blocktrans %}Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus
{% blocktrans %}(HUOM! Kaehmytekstin maksimipituus on 300 merkkiä. Tarvittaessa voit kirjoittaa lisätietoja kommenteihin.){% endblocktrans %}
</p>
<p>
{% blocktrans %}Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus
eikä siis missään nimessä sitova ilmoittautumien mihinkään tehtävään!{% endblocktrans %}
</p>
<h5>{% trans "Päivämääriä & deadlineja" %}</h5>
<ul>
<li><strong>29.10.</strong> {% blocktrans %}Hallitustyrkkypaneeli (haku hallitukseen olisi hyvä tehdä ennen tätä!){% endblocktrans %}</li>
<li><strong>5.11.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta){% endblocktrans %}</li>
<li><strong>12.11.</strong> {% blocktrans %}Kiltailta{% endblocktrans %}</li>
<li><strong>15.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen valinta){% endblocktrans %}</li>
<li><strong>20.11.</strong> {% blocktrans %}Haku toimariksi olisi hyvä tehdä ennen tätä!{% endblocktrans %}</li>
<li><strong>26.11.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li>
<li><strong>05.11.</strong> {% blocktrans %}Hallitustyrkkypaneeli{% endblocktrans %}</li>
<li><strong>12.11.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta){% endblocktrans %}</li>
<li><strong>19.11.</strong> {% blocktrans %}Toimikunta-appro{% endblocktrans %}</li>
<li><strong>23.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen valinta){% endblocktrans %}</li>
<li><strong>07.12.</strong> {% blocktrans %}Vaalikokous, osa 3 (toimarien valinta){% endblocktrans %}</li>
</ul>
<form name="kaehmyForm" action="/kaehmy/submit/" method="post" class="form">{% csrf_token %}
{% bootstrap_field form.name %}
@@ -46,7 +48,7 @@
{{ preset_field.label }}
</div>
<div class="card-block">
{% bootstrap_field preset_field show_label=False %}
{% bootstrap_field preset_field show_label=False %}
</div>
</div>
{% endif %}
@@ -59,11 +61,11 @@
{{ custom_field.label }}
</div>
<div class="card-block">
{% bootstrap_field custom_field show_label=False %}
{% bootstrap_field custom_field show_label=False %}
</div>
</div>
{% endif %}
{% endfor %}
{% endfor %}
<div class="card">
<div class="card-block">
{% bootstrap_field form.custom_role_name %}
@@ -71,7 +73,7 @@
</div>
</div>
{% bootstrap_field form.text %}
<input type="checkbox" required name="gdpr" value="1">
<span>{% blocktrans %}
Hyväksyn <a href="https://sik.ayy.fi/files/official/Tietosuojaseloste%20%E2%80%93%20Toimihenkil%C3%B6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen.
+5 -5
View File
@@ -6,11 +6,11 @@ from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import permission_required, login_required
from django.conf import settings
from django.core.mail import send_mail
import logging
import requests
from dealer.git import git
from sikweb.settings import URL
from members.views.utils import *
from kaehmy.models import Application, CustomRole, PresetRole, TelegramChannel
@@ -60,12 +60,12 @@ def comment(request, *args, **kwargs):
name = comment.name
subject = 'Kaehmyysi tai kommenttiisi on vastattu!'
body = ('{} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n'
'Käy lukemassa viesti osoitteessa http://sika.sahkoinsinoorikilta.fi/kaehmy').format(name.capitalize())
body = (f'{name.capitalize()} on vastannut kaehmyhakemukseesi tai kommenttiisi kaehmypalvelussa.\r\n\r\n'
'Käy lukemassa viesti osoitteessa https://{URL}/kaehmy')
send_email(email, subject, body)
logging.debug(
'Sent kaehmy comment email to recipient <{}>'.format(email))
f'Sent kaehmy comment email to recipient <{email}>')
return redirect('/kaehmy')
else:
@@ -123,7 +123,7 @@ def submit(request, *args, **kwargs):
custom_role.save()
application.custom_roles.add(custom_role)
url = 'https://sika.sahkoinsinoorikilta.fi/kaehmy'
url = f'https://{URL}/kaehmy'
email = form.cleaned_data.get('email', '')
name = form.cleaned_data.get('name', 'Anonymous')
Binary file not shown.
+255 -240
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-26 17:32+0200\n"
"POT-Creation-Date: 2021-01-18 21:36+0200\n"
"PO-Revision-Date: 2017-11-02 23:09+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -37,7 +37,7 @@ msgstr "Sössö articles"
msgid "Today's lunch"
msgstr ""
#: infoscreen/models.py:212 webapp/models.py:74
#: infoscreen/models.py:212 webapp/models.py:70
msgid "Events"
msgstr "Events"
@@ -112,8 +112,8 @@ msgstr "Preview"
msgid "Delete"
msgstr "Delete"
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:56
#: kaehmy/templates/list.html:36 webapp/models.py:125 webapp/models.py:154
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:62
#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173
msgid "Name"
msgstr "Name"
@@ -189,7 +189,7 @@ msgstr "Phone number (not public)"
msgid "Custom roles"
msgstr "Custom roles"
#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:41
#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:43
msgid "Preset roles"
msgstr "Preset roles"
@@ -201,157 +201,157 @@ msgstr "Invalid phone number"
msgid "Custom role with the same name already exists."
msgstr "Custom role with the same name already exists."
#: kaehmy/models.py:13
#: kaehmy/models.py:18
msgid "Kaehmy"
msgstr "Kaehmy"
#: kaehmy/models.py:19
#: kaehmy/models.py:25
msgid "Corporate affairs"
msgstr "Corporate affairs"
#: kaehmy/models.py:20 webapp/templates/freshmen.html:10
#: kaehmy/models.py:26 webapp/templates/freshmen.html:10
#: webapp/templates/navigation.html:8
msgid "Freshmen"
msgstr "Freshmen"
#: kaehmy/models.py:21 webapp/templates/international.html:10
#: kaehmy/models.py:27 webapp/templates/international.html:10
#: webapp/templates/navigation.html:14
msgid "International"
msgstr "International"
#: kaehmy/models.py:22
#: kaehmy/models.py:28
msgid "External affairs"
msgstr "External affairs"
#: kaehmy/models.py:23
#: kaehmy/models.py:29
msgid "Media"
msgstr ""
#: kaehmy/models.py:24
#: kaehmy/models.py:30
msgid "Technology"
msgstr ""
#: kaehmy/models.py:25
#: kaehmy/models.py:31
msgid "Wellbeing"
msgstr ""
#: kaehmy/models.py:26
#: kaehmy/models.py:32
msgid "Elepaja"
msgstr ""
#: kaehmy/models.py:27
#: kaehmy/models.py:33
msgid "Ceremonies"
msgstr ""
#: kaehmy/models.py:28
#: kaehmy/models.py:34
msgid "Studies"
msgstr ""
#: kaehmy/models.py:29
#: kaehmy/models.py:35
msgid "Sössö magazine"
msgstr "Sössö magazine"
#: kaehmy/models.py:30
#: kaehmy/models.py:36
msgid "Alumni relations"
msgstr "Alumni relations"
#: kaehmy/models.py:31
#: kaehmy/models.py:37
msgid "Others"
msgstr ""
#: kaehmy/models.py:33
#: kaehmy/models.py:39
msgid "Category"
msgstr ""
#: kaehmy/models.py:39 webapp/models.py:136
#: kaehmy/models.py:45
msgid "Description"
msgstr "Description"
#: kaehmy/models.py:42
#: kaehmy/models.py:48
msgid "Preset kaehmy role"
msgstr "Preset kaehmy role"
#: kaehmy/models.py:43
#: kaehmy/models.py:49
msgid "Preset kaehmy roles"
msgstr "Preset kaehmy roles"
#: kaehmy/models.py:50
#: kaehmy/models.py:56
msgid "Custom kaehmy role"
msgstr "Custom kaehmy role"
#: kaehmy/models.py:51
#: kaehmy/models.py:57
msgid "Custom kaehmy roles"
msgstr "Custom kaehmy roles"
#: kaehmy/models.py:57 kaehmy/templates/list.html:40 members/models.py:15
#: kaehmy/models.py:63 kaehmy/templates/list.html:40 members/models.py:14
msgid "Email"
msgstr "Email"
#: kaehmy/models.py:58
#: kaehmy/models.py:64
msgid "Timestamp"
msgstr ""
#: kaehmy/models.py:72
#: kaehmy/models.py:78
msgid "Kaehmykommentti"
msgstr "Kaehmy comment"
#: kaehmy/models.py:73
#: kaehmy/models.py:79
msgid "Kaehmykommentit"
msgstr "Kaehmy comments"
#: kaehmy/models.py:75 ohlhafv/models.py:36
#: kaehmy/models.py:81 ohlhafv/models.py:36
msgid "Message"
msgstr ""
#: kaehmy/models.py:94 kaehmy/templates/kaehmy.html:12
#: kaehmy/models.py:100 kaehmy/templates/kaehmy.html:12
msgid "Kaehmylomake"
msgstr "Kaehmy application"
#: kaehmy/models.py:95
#: kaehmy/models.py:101
msgid "Kaehmylomakkeet"
msgstr "Kaehmy applications"
#: kaehmy/models.py:98 webapp/models.py:189
#: kaehmy/models.py:104
msgid "Phone number"
msgstr ""
#: kaehmy/models.py:99
#: kaehmy/models.py:105
msgid "Year"
msgstr ""
#: kaehmy/models.py:100
#: kaehmy/models.py:106
msgid "Text"
msgstr ""
#: kaehmy/models.py:102
#: kaehmy/models.py:108
msgid "Custom role name"
msgstr ""
#: kaehmy/models.py:104 webapp/models.py:126
#: kaehmy/models.py:110 webapp/models.py:174
msgid "Board member"
msgstr "Board member"
#: kaehmy/models.py:112
#: kaehmy/models.py:118
msgid "Kaehmy application: {}"
msgstr "Kaehmy application: {}"
#: kaehmy/models.py:134
#: kaehmy/models.py:140
msgid "Board: {}"
msgstr ""
#: kaehmy/models.py:140
#: kaehmy/models.py:146
msgid "Official: {}"
msgstr ""
#: kaehmy/models.py:157
#: kaehmy/models.py:163
msgid "Telegram channel"
msgstr ""
#: kaehmy/models.py:158
#: kaehmy/models.py:164
msgid "Telegram channels"
msgstr ""
#: kaehmy/tables.py:13 webapp/models.py:173
#: kaehmy/tables.py:13
msgid "Roles"
msgstr ""
@@ -392,7 +392,8 @@ msgid ""
" Koska lista ei ole koskaan täydellinen, voit myös ehdottaa ihan "
"uutta toimenkuvaa.\n"
" Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa "
"konsultoida <a href=\"/static/other/kahmyopas.pdf\">kaehmyopasta</a> \n"
"konsultoida <a href=\"https://static.sahkoinsinoorikilta.fi/uus_webi/"
"kahmyopas.pdf\">kaehmyopasta</a>\n"
" tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan "
"henkilöön."
msgstr ""
@@ -406,42 +407,46 @@ msgstr ""
#: kaehmy/templates/kaehmy.html:23
msgid ""
"Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus \n"
"(HUOM! Kaehmytekstin maksimipituus on 300 merkkiä. Tarvittaessa voit "
"kirjoittaa lisätietoja kommenteihin.)"
msgstr ""
"(NOTE! The application text is limited to 300 characters. You can use the "
"comment section for additional info.)"
#: kaehmy/templates/kaehmy.html:26
msgid ""
"Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus\n"
" eikä siis missään nimessä sitova ilmoittautumien mihinkään "
"tehtävään!"
msgstr ""
"Note, that sending an application is not binding and should be considered as "
"a gesture of interest."
#: kaehmy/templates/kaehmy.html:26
#: kaehmy/templates/kaehmy.html:29
msgid "Päivämääriä & deadlineja"
msgstr "Dates and deadlines"
#: kaehmy/templates/kaehmy.html:28
msgid "Hallitustyrkkypaneeli (haku hallitukseen olisi hyvä tehdä ennen tätä!)"
msgstr "Board panel discussion (applications to the board before this date!)"
#: kaehmy/templates/kaehmy.html:31
msgid "Hallitustyrkkypaneeli"
msgstr "Panel for board applicants"
#: kaehmy/templates/kaehmy.html:29
#: kaehmy/templates/kaehmy.html:32
msgid "Vaalikokous, osa 1 (puheenjohtajan valinta)"
msgstr "Election meeting, part 1 (chairman election)"
#: kaehmy/templates/kaehmy.html:30
msgid "Kiltailta"
msgstr "Guild night"
#: kaehmy/templates/kaehmy.html:33
msgid "Toimikunta-appro"
msgstr "Guild committee crawl"
#: kaehmy/templates/kaehmy.html:31
#: kaehmy/templates/kaehmy.html:34
msgid "Vaalikokous, osa 2 (hallituksen valinta)"
msgstr "Election meeting, part 2 (board election)"
#: kaehmy/templates/kaehmy.html:32
msgid "Haku toimariksi olisi hyvä tehdä ennen tätä!"
msgstr "Deadline to apply as a non-board official!"
#: kaehmy/templates/kaehmy.html:33
#: kaehmy/templates/kaehmy.html:35
msgid "Vaalikokous, osa 3 (toimarien valinta)"
msgstr "Election meeting, part 3 (non-board election)"
#: kaehmy/templates/kaehmy.html:76
#: kaehmy/templates/kaehmy.html:78
#, python-format
msgid ""
"\n"
@@ -458,7 +463,7 @@ msgstr ""
"of personal data.\n"
" "
#: kaehmy/templates/kaehmy.html:82 members/templates/settings.html:23
#: kaehmy/templates/kaehmy.html:84 members/templates/settings.html:23
msgid "Submit"
msgstr "Submit"
@@ -512,7 +517,7 @@ msgstr "New application"
msgid "Statistics"
msgstr ""
#: members/forms.py:107 members/models.py:102 members/tables.py:41
#: members/forms.py:107 members/models.py:101 members/tables.py:41
msgid "Member"
msgstr "Member"
@@ -524,52 +529,52 @@ msgstr "I'm a member of AYY"
msgid "I want to receive a weekly newsletter"
msgstr "I want to receive a weekly newsletter"
#: members/models.py:13
#: members/models.py:12
msgid "First name"
msgstr "First name"
#: members/models.py:14
#: members/models.py:13
msgid "Last name"
msgstr "Last name"
#: members/models.py:16
#: members/models.py:15
msgid "Place of residence"
msgstr "Place of residence"
#: members/models.py:18 members/models.py:69
#: members/models.py:17 members/models.py:68
#: members/templates/member_add_many.html:39
msgid "AYY"
msgstr "AYY"
#: members/models.py:19
#: members/models.py:18
msgid "JAS"
msgstr "JAS"
#: members/models.py:50
#: members/models.py:49
msgid "Submitted"
msgstr "Submitted"
#: members/models.py:67
#: members/models.py:66
msgid "Date"
msgstr "Date"
#: members/models.py:68
#: members/models.py:67
msgid "Source"
msgstr "Source"
#: members/models.py:70
#: members/models.py:69
msgid "Cash"
msgstr "Cash"
#: members/models.py:71 members/templates/member_add_many.html:40
#: members/models.py:70 members/templates/member_add_many.html:40
msgid "Bank transfer"
msgstr "Bank transfer"
#: members/models.py:95
#: members/models.py:94
msgid "Created"
msgstr "Created"
#: members/models.py:103 members/templates/base.html:53
#: members/models.py:102 members/templates/base.html:53
#: members/templates/member_add_many_confirm.html:12
msgid "Members"
msgstr "Members"
@@ -634,6 +639,14 @@ msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
msgstr "Amazing! Your membership application has been sent."
#: members/templates/application_success.html:9
msgid ""
"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä sik-vtmk@list.ayy."
"fi jos viestiä ei näy."
msgstr ""
"Confirmation email is sent to given email address. Contact sik-vtmk@list.ayy."
"fi if you didn't receive it."
#: members/templates/application_success.html:10
msgid "Takaisin Sähköinsinöörikillan web-sivuille"
msgstr "Back to the front page"
@@ -687,6 +700,103 @@ msgstr "Application form"
msgid "Settings"
msgstr "Settings"
#: members/templates/email_application_accept.html:2
msgid "Moi"
msgstr "Hi"
#: members/templates/email_application_accept.html:4
msgid "Onnittelut! Sinut on hyväksytty Sähköinsinöörikillan jäseneksi."
msgstr ""
#: members/templates/email_application_accept.html:6
msgid "Käy kurkkaamassa killan nettisivuilta"
msgstr "Also go and check other challenges at"
#: members/templates/email_application_accept.html:6
msgid ""
"tulevia tapahtumia ja piipahda kiltahuoneella tutustumassa uusiin "
"kiltatovereihisi!"
msgstr ""
#: members/templates/email_application_accept.html:8
msgid "Liity myös killan TG-kanaville"
msgstr "Join Guild's Telegram channels"
#: members/templates/email_application_accept.html:9
msgid "SIK"
msgstr "SIK"
#: members/templates/email_application_accept.html:10
msgid "SIK-fuksit 2019"
msgstr "SIK Freshmen 2019"
#: members/templates/email_application_accept.html:11
msgid "SIK-fuksit 2019 -tiedotuskanava"
msgstr "SIK Freshmen 2019 Notification channel"
#: members/templates/email_application_submit.html:2
#: ohlhafv/templates/email.html:2
msgid "Moikka"
msgstr "Hi"
#: members/templates/email_application_submit.html:4
msgid ""
"Sait tämän viestin, sillä olet lähettänyt hakemuksen Aalto-yliopiston "
"Sähköinsinöörikillan jäseneksi alla olevin tiedoin. Siistiä!"
msgstr ""
"You received this email, since you sent a membership application to the "
"Guild of Electrical Engineering. That's so cool!"
#: members/templates/email_application_submit.html:6
msgid "Etunimi"
msgstr "First name"
#: members/templates/email_application_submit.html:7
msgid "Sukunimi"
msgstr "Last name"
#: members/templates/email_application_submit.html:8
msgid "Sähköposti"
msgstr "Email"
#: members/templates/email_application_submit.html:9
msgid "Kotipaikkakunta"
msgstr "Place of origin"
#: members/templates/email_application_submit.html:10
msgid "AYY:n jäsen"
msgstr "Member of AYY"
#: members/templates/email_application_submit.html:11
msgid "Haluan jäsenmailin"
msgstr "I want to receive weekly member email"
#: members/templates/email_application_submit.html:13
msgid ""
"Saat sähköpostiisi tiedon, kun sinut on hallituksen kokouksessa hyväksytty "
"jäseneksi"
msgstr "You will be notified via email once your application has been accepted"
#: members/templates/email_application_submit.html:15
msgid "Muistathan maksaa jäsenmaksun! Alla maksutiedot"
msgstr "Don't forget to pay your membership fee!"
#: members/templates/email_application_submit.html:17
msgid "Saaja"
msgstr "Recepient"
#: members/templates/email_application_submit.html:18
msgid "Tilinumero"
msgstr "Account number"
#: members/templates/email_application_submit.html:20
msgid "Viite"
msgstr "Reference Number"
#: members/templates/email_application_submit.html:21
msgid "Summa"
msgstr "Amount"
#: members/templates/member_add.html:15 members/templates/member_edit.html:18
#: members/templates/payment_add.html:20 members/templates/payment_edit.html:18
msgid "Save"
@@ -804,95 +914,111 @@ msgstr "Payments in register:"
msgid "Language"
msgstr "Language"
#: members/templates/settings.html:20 sikweb/base.py:255
#: members/templates/settings.html:20 sikweb/base.py:217
msgid "Finnish"
msgstr "Finnish"
#: members/templates/settings.html:21 sikweb/base.py:256
#: members/templates/settings.html:21 sikweb/base.py:218
msgid "English"
msgstr "English"
#: members/views/applications.py:51 members/views/applications.py:112
#: members/views/applications.py:137
#: members/views/applications.py:53 members/views/applications.py:124
#: members/views/applications.py:149
msgid "No application id specified"
msgstr "No application id specified"
#: members/views/applications.py:71
#: members/views/applications.py:73
msgid "Application missing 'id' field."
msgstr "Application missing 'id' field."
#: members/views/applications.py:80
#: members/views/applications.py:82
msgid "Email {} is already in use by a member. Application cannot be accepted."
msgstr ""
"Email {} is already in use by a member. Application cannot be accepted."
#: members/views/applications.py:91
#: members/views/applications.py:93
msgid "Successfully accepted application"
msgstr "Successfully accepted application"
#: members/views/applications.py:116
#: members/views/applications.py:96
msgid "Jäsenhakemuksesi Sähköinsinöörikiltaan on hyväksytty!"
msgstr "Your membership application has been approved!"
#: members/views/applications.py:128
msgid "Successfully deleted application"
msgstr "Successfully deleted application"
#: members/views/applications.py:126
#: members/views/applications.py:138
msgid "Could not delete application object"
msgstr "Could not delete application object"
#: members/views/members.py:86 members/views/members.py:188
#: members/views/members.py:212
#: members/views/applications.py:176
msgid "Jäsenhakemuksesi Sähköinsinöörikiltaan on lähetetty onnistuneesti!"
msgstr "Your membership application was sent successfully!"
#: members/views/applications.py:181 members/views/applications.py:182
msgid "Kyllä"
msgstr "Yes"
#: members/views/applications.py:181 members/views/applications.py:182
msgid "Ei"
msgstr "No"
#: members/views/members.py:85 members/views/members.py:187
#: members/views/members.py:211
msgid "No member id specified"
msgstr "No member id specified"
#: members/views/members.py:127
#: members/views/members.py:126
msgid "Failed to import members"
msgstr "Failed to import members"
#: members/views/members.py:141
#: members/views/members.py:140
msgid "Successfully added member"
msgstr "Successfully added member"
#: members/views/members.py:162
#: members/views/members.py:161
msgid "Member missing 'id' field."
msgstr "Member missing 'id' field."
#: members/views/members.py:171
#: members/views/members.py:170
msgid "Successfully updated member"
msgstr "Successfully updated member"
#: members/views/members.py:192
#: members/views/members.py:191
msgid "Successfully deleted member"
msgstr "Successfully deleted member"
#: members/views/members.py:201
#: members/views/members.py:200
msgid "Could not delete member object"
msgstr "Could not delete member object"
#: members/views/payments.py:71
#: members/views/payments.py:70
msgid "Successfully added payment for member"
msgstr "Successfully added payment for member"
#: members/views/payments.py:88 members/views/payments.py:105
#: members/views/payments.py:123
#: members/views/payments.py:87 members/views/payments.py:104
#: members/views/payments.py:122
msgid "No payment id specified"
msgstr "No payment id specified"
#: members/views/payments.py:128
#: members/views/payments.py:127
msgid "Successfully deleted payment"
msgstr "Successfully deleted payment"
#: members/views/payments.py:136
#: members/views/payments.py:135
msgid "Could not delete payment object"
msgstr "Could not delete payment object"
#: members/views/payments.py:156
#: members/views/payments.py:155
msgid "Successfully updated payment"
msgstr "Successfully updated payment"
#: members/views/payments.py:161
#: members/views/payments.py:160
msgid "Could not update payment object"
msgstr "Could not update payment object"
#: members/views/utils.py:112
#: members/views/utils.py:111
msgid "Missing CSV file"
msgstr "Missing CSV file"
@@ -932,10 +1058,6 @@ msgstr ""
msgid "Ohlhafv challenge: {} vs. {}"
msgstr ""
#: ohlhafv/templates/email.html:2
msgid "Moikka"
msgstr "Hi"
#: ohlhafv/templates/email.html:4
msgid "on haastanut sinut oluenjuontimittelöön"
msgstr "has challenged you to a beer drinking contest"
@@ -945,12 +1067,9 @@ msgid "-sarjassa"
msgstr "series"
#: ohlhafv/templates/email.html:8
#, fuzzy
#| msgid ""
#| "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 15.2"
msgid "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 14.2"
msgstr ""
"Remeber to confirm the challenge at Smökki on Thursday 15.2. at the event"
"Remeber to confirm the challenge at Smökki on Thursday 14.2. at the event"
#: ohlhafv/templates/email.html:10
msgid "Käy kurkkaamassa muutkin haasteet osoitteessa"
@@ -980,7 +1099,7 @@ msgstr ""
msgid "Challenge"
msgstr "Challenge"
#: ohlhafv/views.py:43
#: ohlhafv/views.py:44
msgid "Sinut on haastettu Øhlhäfviin!"
msgstr "You have been challenged at Ohlhafv!"
@@ -993,56 +1112,57 @@ msgstr "Go"
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: webapp/models.py:17
#: webapp/models.py:18
msgid "Webapp"
msgstr "Webapp"
#: webapp/models.py:28
#: webapp/models.py:26
msgid "Tag"
msgstr "Tag"
#: webapp/models.py:29
#: webapp/models.py:27
msgid "Tags"
msgstr "Tags"
#: webapp/models.py:32
#: webapp/models.py:34
msgid "Tag: {}"
msgstr "Tag: {}"
#: webapp/models.py:53
msgid "Feed: {}"
msgstr "Feed: {}"
#: webapp/models.py:56
#: webapp/models.py:52
msgid "Feed"
msgstr ""
#: webapp/models.py:57
#: webapp/models.py:53
msgid "Feeds"
msgstr ""
#: webapp/models.py:70
msgid "Event: {}"
#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119
#: webapp/models.py:152 webapp/models.py:198
msgid "Deleted: "
msgstr "Deleted: "
#: webapp/models.py:62
msgid "{}Feed: {}"
msgstr ""
#: webapp/models.py:73
#: webapp/models.py:69
msgid "Event"
msgstr ""
#: webapp/models.py:84
msgid "Template questions: {}"
#: webapp/models.py:81
msgid "{}Event: {}"
msgstr ""
#: webapp/models.py:87
#: webapp/models.py:91
msgid "Template question"
msgstr ""
#: webapp/models.py:88
#: webapp/models.py:92
msgid "Template questions"
msgstr ""
#: webapp/models.py:102
msgid "#{} {}"
#: webapp/models.py:98
msgid "Template questions: {}"
msgstr ""
#: webapp/models.py:105
@@ -1053,52 +1173,28 @@ msgstr ""
msgid "Signup forms"
msgstr ""
#: webapp/models.py:115
msgid "Sign-ups: {}"
#: webapp/models.py:120
msgid "#{} {}{}"
msgstr ""
#: webapp/models.py:118
#: webapp/models.py:138
msgid "Sign-up"
msgstr ""
#: webapp/models.py:119
#: webapp/models.py:139
msgid "Sign-ups"
msgstr ""
#: webapp/models.py:130
#: webapp/models.py:178
msgid "board member"
msgstr "board member"
#: webapp/models.py:148
msgid "Committee"
msgstr ""
#: webapp/models.py:149
msgid "Committees"
msgstr ""
#: webapp/models.py:152
msgid "Committee: {}"
msgstr ""
#: webapp/models.py:172
msgid "Role"
msgstr ""
#: webapp/models.py:175
msgid "Start date"
msgstr ""
#: webapp/models.py:176
msgid "End date"
#: webapp/models.py:185
msgid "JobAd"
msgstr ""
#: webapp/models.py:186
msgid "Official"
msgstr ""
#: webapp/models.py:187
msgid "Officials"
msgid "JobAds"
msgstr ""
#: webapp/templates/contact.html:9 webapp/templates/navigation.html:20
@@ -1144,84 +1240,3 @@ msgstr "Sössö"
#: webapp/templates/navigation.html:24
msgid "Corporate"
msgstr "Corporate"
#~ msgid "HSL timetables"
#~ msgstr "HSL timetables"
#~ msgid "Username"
#~ msgstr "Username"
#~ msgid "Password"
#~ msgstr "Password"
#~ msgid "Forgot password?"
#~ msgstr "Forgot password?"
#~ msgid "Log in"
#~ msgstr "Log in"
#~ msgid "New password set"
#~ msgstr "New password set"
#~ msgid ""
#~ "Your password has successfully been reset. You can use it right now on "
#~ "the login page."
#~ msgstr ""
#~ "Your password has successfully been reset. You can use it right now on "
#~ "the login page."
#~ msgid "Password recovery"
#~ msgstr "Password recovery"
#~ msgid ""
#~ "Sorry, this password reset link is invalid. You can still <a href="
#~ "\"%(recovery_url)s\">request a new one</a>."
#~ msgstr ""
#~ "Sorry, this password reset link is invalid. You can still <a href="
#~ "\"%(recovery_url)s\">request a new one</a>."
#~ msgid "Hi, <strong>%(username)s</strong>. Please choose your new password."
#~ msgstr "Hi, <strong>%(username)s</strong>. Please choose your new password."
#~ msgid "Set new password"
#~ msgstr "Set new password"
#~ msgid "Password recovery sent"
#~ msgstr "Password recovery sent"
#~ msgid ""
#~ "An email was sent to <strong>%(email)s</strong> %(ago)s ago. Use the link "
#~ "in it to set a new password."
#~ msgstr ""
#~ "An email was sent to <strong>%(email)s</strong> %(ago)s ago. Use the link "
#~ "in it to set a new password."
#~ msgid "SIK Admin"
#~ msgstr "SIK Admin"
#~ msgid "Admin tools"
#~ msgstr "Admin tools"
#~ msgid ""
#~ "\n"
#~ " first_name, last_name, email_address and place_of_origin "
#~ "should be given string values.\n"
#~ " ayy_member and jas_recipient should be given the value 0 "
#~ "(off) or 1 (on).\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " first_name, last_name, email_address and place_of_origin "
#~ "should be given string values.\n"
#~ " ayy_member and jas_recipient should be given the value 0 "
#~ "(off) or 1 (on).\n"
#~ " "
#~ msgid "Syntax"
#~ msgstr "Syntax"
#~ msgid "Missing \"textfield\" POST request field"
#~ msgstr "Missing \"textfield\" POST request field"
#~ msgid "Options"
#~ msgstr "Options"
Binary file not shown.
+256 -263
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-26 17:32+0200\n"
"POT-Creation-Date: 2021-01-18 21:36+0200\n"
"PO-Revision-Date: 2017-11-02 23:04+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -38,7 +38,7 @@ msgstr "Sössön artikkelit"
msgid "Today's lunch"
msgstr "Päivän lounas"
#: infoscreen/models.py:212 webapp/models.py:74
#: infoscreen/models.py:212 webapp/models.py:70
msgid "Events"
msgstr "Tapahtumat"
@@ -113,8 +113,8 @@ msgstr "Esikatsele"
msgid "Delete"
msgstr "Poista"
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:56
#: kaehmy/templates/list.html:36 webapp/models.py:125 webapp/models.py:154
#: infoscreen/templates/tabs/add_remove.html:23 kaehmy/models.py:62
#: kaehmy/templates/list.html:36 webapp/models.py:144 webapp/models.py:173
msgid "Name"
msgstr "Nimi"
@@ -190,7 +190,7 @@ msgstr "Puhelinnumero (ei julkinen)"
msgid "Custom roles"
msgstr "Uudet virat"
#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:41
#: kaehmy/forms.py:49 kaehmy/templates/kaehmy.html:43
msgid "Preset roles"
msgstr "Kaehmyvirat"
@@ -202,157 +202,157 @@ msgstr "Virheellinen puhelinnumero"
msgid "Custom role with the same name already exists."
msgstr "Samanniminen virka on jo olemassa."
#: kaehmy/models.py:13
#: kaehmy/models.py:18
msgid "Kaehmy"
msgstr "Kaehmy"
#: kaehmy/models.py:19
#: kaehmy/models.py:25
msgid "Corporate affairs"
msgstr "Yrityssuhteet"
#: kaehmy/models.py:20 webapp/templates/freshmen.html:10
#: kaehmy/models.py:26 webapp/templates/freshmen.html:10
#: webapp/templates/navigation.html:8
msgid "Freshmen"
msgstr "Fuksit"
#: kaehmy/models.py:21 webapp/templates/international.html:10
#: kaehmy/models.py:27 webapp/templates/international.html:10
#: webapp/templates/navigation.html:14
msgid "International"
msgstr "International"
#: kaehmy/models.py:22
#: kaehmy/models.py:28
msgid "External affairs"
msgstr "Ulkosuhteet"
#: kaehmy/models.py:23
#: kaehmy/models.py:29
msgid "Media"
msgstr "Media"
#: kaehmy/models.py:24
#: kaehmy/models.py:30
msgid "Technology"
msgstr "Teknologia"
#: kaehmy/models.py:25
#: kaehmy/models.py:31
msgid "Wellbeing"
msgstr "Hyvinvointi"
#: kaehmy/models.py:26
#: kaehmy/models.py:32
msgid "Elepaja"
msgstr "Elepaja"
#: kaehmy/models.py:27
#: kaehmy/models.py:33
msgid "Ceremonies"
msgstr "Hupitapahtumat"
#: kaehmy/models.py:28
#: kaehmy/models.py:34
msgid "Studies"
msgstr "Opinnot"
#: kaehmy/models.py:29
#: kaehmy/models.py:35
msgid "Sössö magazine"
msgstr "Kiltalehti Sössö"
#: kaehmy/models.py:30
#: kaehmy/models.py:36
msgid "Alumni relations"
msgstr "Alumnisuhteet"
#: kaehmy/models.py:31
#: kaehmy/models.py:37
msgid "Others"
msgstr "Muut"
#: kaehmy/models.py:33
#: kaehmy/models.py:39
msgid "Category"
msgstr "Kategoria"
#: kaehmy/models.py:39 webapp/models.py:136
#: kaehmy/models.py:45
msgid "Description"
msgstr "Kuvaus"
#: kaehmy/models.py:42
#: kaehmy/models.py:48
msgid "Preset kaehmy role"
msgstr "Kaehmyvirka"
#: kaehmy/models.py:43
#: kaehmy/models.py:49
msgid "Preset kaehmy roles"
msgstr "Kaehmyvirat"
#: kaehmy/models.py:50
#: kaehmy/models.py:56
msgid "Custom kaehmy role"
msgstr "Uusi virka"
#: kaehmy/models.py:51
#: kaehmy/models.py:57
msgid "Custom kaehmy roles"
msgstr "Uudet kaehmyvirat"
#: kaehmy/models.py:57 kaehmy/templates/list.html:40 members/models.py:15
#: kaehmy/models.py:63 kaehmy/templates/list.html:40 members/models.py:14
msgid "Email"
msgstr "Sähköposti"
#: kaehmy/models.py:58
#: kaehmy/models.py:64
msgid "Timestamp"
msgstr "Aikaleima"
#: kaehmy/models.py:72
#: kaehmy/models.py:78
msgid "Kaehmykommentti"
msgstr "Kaehmykommentti"
#: kaehmy/models.py:73
#: kaehmy/models.py:79
msgid "Kaehmykommentit"
msgstr "Kaehmykommentit"
#: kaehmy/models.py:75 ohlhafv/models.py:36
#: kaehmy/models.py:81 ohlhafv/models.py:36
msgid "Message"
msgstr "Viesti"
#: kaehmy/models.py:94 kaehmy/templates/kaehmy.html:12
#: kaehmy/models.py:100 kaehmy/templates/kaehmy.html:12
msgid "Kaehmylomake"
msgstr "Kaehmylomake"
#: kaehmy/models.py:95
#: kaehmy/models.py:101
msgid "Kaehmylomakkeet"
msgstr "Kaehmylomakkeet"
#: kaehmy/models.py:98 webapp/models.py:189
#: kaehmy/models.py:104
msgid "Phone number"
msgstr "Puhelinnumero"
#: kaehmy/models.py:99
#: kaehmy/models.py:105
msgid "Year"
msgstr "Vuosi"
#: kaehmy/models.py:100
#: kaehmy/models.py:106
msgid "Text"
msgstr "Teksti"
#: kaehmy/models.py:102
#: kaehmy/models.py:108
msgid "Custom role name"
msgstr "Uusi virka"
#: kaehmy/models.py:104 webapp/models.py:126
#: kaehmy/models.py:110 webapp/models.py:174
msgid "Board member"
msgstr "Hallituksen jäsen"
#: kaehmy/models.py:112
#: kaehmy/models.py:118
msgid "Kaehmy application: {}"
msgstr "Kaehmy: {}"
#: kaehmy/models.py:134
#: kaehmy/models.py:140
msgid "Board: {}"
msgstr "Hallitus: {}"
#: kaehmy/models.py:140
#: kaehmy/models.py:146
msgid "Official: {}"
msgstr "Toimari: {}"
#: kaehmy/models.py:157
#: kaehmy/models.py:163
msgid "Telegram channel"
msgstr "Telegram-kanava"
#: kaehmy/models.py:158
#: kaehmy/models.py:164
msgid "Telegram channels"
msgstr "Telegram-kanavat"
#: kaehmy/tables.py:13 webapp/models.py:173
#: kaehmy/tables.py:13
msgid "Roles"
msgstr "Roolit"
@@ -393,7 +393,8 @@ msgid ""
" Koska lista ei ole koskaan täydellinen, voit myös ehdottaa ihan "
"uutta toimenkuvaa.\n"
" Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa "
"konsultoida <a href=\"/static/other/kahmyopas.pdf\">kaehmyopasta</a> \n"
"konsultoida <a href=\"https://static.sahkoinsinoorikilta.fi/uus_webi/"
"kahmyopas.pdf\">kaehmyopasta</a>\n"
" tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan "
"henkilöön."
msgstr ""
@@ -403,13 +404,20 @@ msgstr ""
" Koska lista ei ole koskaan täydellinen, voit myös ehdottaa ihan "
"uutta toimenkuvaa.\n"
" Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa "
"konsultoida <a href=\"/static/other/kahmyopas.pdf\">kaehmyopasta</a> \n"
"konsultoida <a href=\"https://static.sahkoinsinoorikilta.fi/uus_webi/kahmyopas.pdf"
"\">kaehmyopasta</a> \n"
" tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan "
"henkilöön."
#: kaehmy/templates/kaehmy.html:23
msgid ""
"Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus \n"
"(HUOM! Kaehmytekstin maksimipituus on 300 merkkiä. Tarvittaessa voit "
"kirjoittaa lisätietoja kommenteihin.)"
msgstr ""
#: kaehmy/templates/kaehmy.html:26
msgid ""
"Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus\n"
" eikä siis missään nimessä sitova ilmoittautumien mihinkään "
"tehtävään!"
msgstr ""
@@ -417,35 +425,31 @@ msgstr ""
" eikä siis missään nimessä sitova ilmoittautumien mihinkään "
"tehtävään!"
#: kaehmy/templates/kaehmy.html:26
#: kaehmy/templates/kaehmy.html:29
msgid "Päivämääriä & deadlineja"
msgstr "Päivämääriä & deadlineja"
#: kaehmy/templates/kaehmy.html:28
msgid "Hallitustyrkkypaneeli (haku hallitukseen olisi hyvä tehdä ennen tätä!)"
msgstr "Hallitustyrkkypaneeli (haku hallitukseen olisi hyvä tehdä ennen tätä!)"
#: kaehmy/templates/kaehmy.html:31
msgid "Hallitustyrkkypaneeli"
msgstr ""
#: kaehmy/templates/kaehmy.html:29
#: kaehmy/templates/kaehmy.html:32
msgid "Vaalikokous, osa 1 (puheenjohtajan valinta)"
msgstr "Vaalikokous, osa 1 (puheenjohtajan valinta)"
#: kaehmy/templates/kaehmy.html:30
msgid "Kiltailta"
msgstr "Kiltailta"
#: kaehmy/templates/kaehmy.html:33
msgid "Toimikunta-appro"
msgstr ""
#: kaehmy/templates/kaehmy.html:31
#: kaehmy/templates/kaehmy.html:34
msgid "Vaalikokous, osa 2 (hallituksen valinta)"
msgstr "Vaalikokous, osa 2 (hallituksen valinta)"
#: kaehmy/templates/kaehmy.html:32
msgid "Haku toimariksi olisi hyvä tehdä ennen tätä!"
msgstr "Haku toimariksi olisi hyvä tehdä ennen tätä!"
#: kaehmy/templates/kaehmy.html:33
#: kaehmy/templates/kaehmy.html:35
msgid "Vaalikokous, osa 3 (toimarien valinta)"
msgstr "Vaalikokous, osa 3 (toimarien valinta)"
#: kaehmy/templates/kaehmy.html:76
#: kaehmy/templates/kaehmy.html:78
#, python-format
msgid ""
"\n"
@@ -456,7 +460,7 @@ msgid ""
" "
msgstr ""
#: kaehmy/templates/kaehmy.html:82 members/templates/settings.html:23
#: kaehmy/templates/kaehmy.html:84 members/templates/settings.html:23
msgid "Submit"
msgstr "Lisää"
@@ -510,7 +514,7 @@ msgstr "Uusi kaehmy"
msgid "Statistics"
msgstr "Kaehmytilastot"
#: members/forms.py:107 members/models.py:102 members/tables.py:41
#: members/forms.py:107 members/models.py:101 members/tables.py:41
msgid "Member"
msgstr "Jäsen"
@@ -522,52 +526,52 @@ msgstr "Olen AYY:n jäsen"
msgid "I want to receive a weekly newsletter"
msgstr "Haluan saada viikottaisen jäsentiedotteen"
#: members/models.py:13
#: members/models.py:12
msgid "First name"
msgstr "Etunimi"
#: members/models.py:14
#: members/models.py:13
msgid "Last name"
msgstr "Sukunimi"
#: members/models.py:16
#: members/models.py:15
msgid "Place of residence"
msgstr "Asuinpaikka"
#: members/models.py:18 members/models.py:69
#: members/models.py:17 members/models.py:68
#: members/templates/member_add_many.html:39
msgid "AYY"
msgstr "AYY"
#: members/models.py:19
#: members/models.py:18
msgid "JAS"
msgstr "JAS"
#: members/models.py:50
#: members/models.py:49
msgid "Submitted"
msgstr "Lisätty"
#: members/models.py:67
#: members/models.py:66
msgid "Date"
msgstr "Päivämäärä"
#: members/models.py:68
#: members/models.py:67
msgid "Source"
msgstr "Lähde"
#: members/models.py:70
#: members/models.py:69
msgid "Cash"
msgstr "Käteinen"
#: members/models.py:71 members/templates/member_add_many.html:40
#: members/models.py:70 members/templates/member_add_many.html:40
msgid "Bank transfer"
msgstr "Tilisiirto"
#: members/models.py:95
#: members/models.py:94
msgid "Created"
msgstr "Lisätty"
#: members/models.py:103 members/templates/base.html:53
#: members/models.py:102 members/templates/base.html:53
#: members/templates/member_add_many_confirm.html:12
msgid "Members"
msgstr "Jäsenet"
@@ -632,6 +636,12 @@ msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
msgstr "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
#: members/templates/application_success.html:9
msgid ""
"Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä sik-vtmk@list.ayy."
"fi jos viestiä ei näy."
msgstr ""
#: members/templates/application_success.html:10
msgid "Takaisin Sähköinsinöörikillan web-sivuille"
msgstr "Takaisin Sähköinsinöörikillan web-sivuille"
@@ -685,6 +695,101 @@ msgstr "Jäsenhakemuslomake"
msgid "Settings"
msgstr "Asetukset"
#: members/templates/email_application_accept.html:2
msgid "Moi"
msgstr ""
#: members/templates/email_application_accept.html:4
msgid "Onnittelut! Sinut on hyväksytty Sähköinsinöörikillan jäseneksi."
msgstr ""
#: members/templates/email_application_accept.html:6
msgid "Käy kurkkaamassa killan nettisivuilta"
msgstr ""
#: members/templates/email_application_accept.html:6
msgid ""
"tulevia tapahtumia ja piipahda kiltahuoneella tutustumassa uusiin "
"kiltatovereihisi!"
msgstr ""
#: members/templates/email_application_accept.html:8
msgid "Liity myös killan TG-kanaville"
msgstr ""
#: members/templates/email_application_accept.html:9
msgid "SIK"
msgstr ""
#: members/templates/email_application_accept.html:10
msgid "SIK-fuksit 2019"
msgstr ""
#: members/templates/email_application_accept.html:11
msgid "SIK-fuksit 2019 -tiedotuskanava"
msgstr ""
#: members/templates/email_application_submit.html:2
#: ohlhafv/templates/email.html:2
msgid "Moikka"
msgstr ""
#: members/templates/email_application_submit.html:4
msgid ""
"Sait tämän viestin, sillä olet lähettänyt hakemuksen Aalto-yliopiston "
"Sähköinsinöörikillan jäseneksi alla olevin tiedoin. Siistiä!"
msgstr ""
#: members/templates/email_application_submit.html:6
msgid "Etunimi"
msgstr ""
#: members/templates/email_application_submit.html:7
msgid "Sukunimi"
msgstr ""
#: members/templates/email_application_submit.html:8
msgid "Sähköposti"
msgstr ""
#: members/templates/email_application_submit.html:9
msgid "Kotipaikkakunta"
msgstr ""
#: members/templates/email_application_submit.html:10
msgid "AYY:n jäsen"
msgstr ""
#: members/templates/email_application_submit.html:11
msgid "Haluan jäsenmailin"
msgstr ""
#: members/templates/email_application_submit.html:13
msgid ""
"Saat sähköpostiisi tiedon, kun sinut on hallituksen kokouksessa hyväksytty "
"jäseneksi"
msgstr ""
#: members/templates/email_application_submit.html:15
msgid "Muistathan maksaa jäsenmaksun! Alla maksutiedot"
msgstr "Muistathan maksaa jäsenmaksun! Alla maksutiedot"
#: members/templates/email_application_submit.html:17
msgid "Saaja"
msgstr ""
#: members/templates/email_application_submit.html:18
msgid "Tilinumero"
msgstr ""
#: members/templates/email_application_submit.html:20
msgid "Viite"
msgstr ""
#: members/templates/email_application_submit.html:21
msgid "Summa"
msgstr ""
#: members/templates/member_add.html:15 members/templates/member_edit.html:18
#: members/templates/payment_add.html:20 members/templates/payment_edit.html:18
msgid "Save"
@@ -800,94 +905,110 @@ msgstr "Maksutapahtumia:"
msgid "Language"
msgstr "Kieli"
#: members/templates/settings.html:20 sikweb/base.py:255
#: members/templates/settings.html:20 sikweb/base.py:217
msgid "Finnish"
msgstr "suomi"
#: members/templates/settings.html:21 sikweb/base.py:256
#: members/templates/settings.html:21 sikweb/base.py:218
msgid "English"
msgstr "englanti"
#: members/views/applications.py:51 members/views/applications.py:112
#: members/views/applications.py:137
#: members/views/applications.py:53 members/views/applications.py:124
#: members/views/applications.py:149
msgid "No application id specified"
msgstr "Hakemuksen ID ei määritelty"
#: members/views/applications.py:71
#: members/views/applications.py:73
msgid "Application missing 'id' field."
msgstr "Hakemuksen ID ei määritelty."
#: members/views/applications.py:80
#: members/views/applications.py:82
msgid "Email {} is already in use by a member. Application cannot be accepted."
msgstr "Sähköpostiosoite {} on jo käytössä. Hakemusta ei hyväksytty."
#: members/views/applications.py:91
#: members/views/applications.py:93
msgid "Successfully accepted application"
msgstr "Onnistuneesti hyväksyttiin hakemus"
#: members/views/applications.py:116
#: members/views/applications.py:96
msgid "Jäsenhakemuksesi Sähköinsinöörikiltaan on hyväksytty!"
msgstr ""
#: members/views/applications.py:128
msgid "Successfully deleted application"
msgstr "Onnistuneesti poistettiin hakemus"
#: members/views/applications.py:126
#: members/views/applications.py:138
msgid "Could not delete application object"
msgstr "Hakemusobjektia ei voitu poistaa"
#: members/views/members.py:86 members/views/members.py:188
#: members/views/members.py:212
#: members/views/applications.py:176
msgid "Jäsenhakemuksesi Sähköinsinöörikiltaan on lähetetty onnistuneesti!"
msgstr ""
#: members/views/applications.py:181 members/views/applications.py:182
msgid "Kyllä"
msgstr ""
#: members/views/applications.py:181 members/views/applications.py:182
msgid "Ei"
msgstr ""
#: members/views/members.py:85 members/views/members.py:187
#: members/views/members.py:211
msgid "No member id specified"
msgstr "Jäsenen ID ei määritelty"
#: members/views/members.py:127
#: members/views/members.py:126
msgid "Failed to import members"
msgstr "Jäsenten tuonti epäonnistui"
#: members/views/members.py:141
#: members/views/members.py:140
msgid "Successfully added member"
msgstr "Onnistuneesti lisättiin jäsen"
#: members/views/members.py:162
#: members/views/members.py:161
msgid "Member missing 'id' field."
msgstr "Jäsenen ID ei määritelty."
#: members/views/members.py:171
#: members/views/members.py:170
msgid "Successfully updated member"
msgstr "Onnistuneesti päivitettiin jäsen"
#: members/views/members.py:192
#: members/views/members.py:191
msgid "Successfully deleted member"
msgstr "Onnistuneesti poistettiin jäsen"
#: members/views/members.py:201
#: members/views/members.py:200
msgid "Could not delete member object"
msgstr "Jäsenobjektia ei voitu poistaa"
#: members/views/payments.py:71
#: members/views/payments.py:70
msgid "Successfully added payment for member"
msgstr "Onnistuneesti lisättiin maksutapahtuma jäsenelle"
#: members/views/payments.py:88 members/views/payments.py:105
#: members/views/payments.py:123
#: members/views/payments.py:87 members/views/payments.py:104
#: members/views/payments.py:122
msgid "No payment id specified"
msgstr "Maksutapahtuman ID ei määritelty"
#: members/views/payments.py:128
#: members/views/payments.py:127
msgid "Successfully deleted payment"
msgstr "Onnistuneesti poistettiin maksutapahtuma"
#: members/views/payments.py:136
#: members/views/payments.py:135
msgid "Could not delete payment object"
msgstr "Maksutapahtumaobjektia ei voitu poistaa"
#: members/views/payments.py:156
#: members/views/payments.py:155
msgid "Successfully updated payment"
msgstr "Onnistuneesti päivitettiin maksutapahtuma"
#: members/views/payments.py:161
#: members/views/payments.py:160
msgid "Could not update payment object"
msgstr "Maksutapahtumaobjektia ei voitu päivittää"
#: members/views/utils.py:112
#: members/views/utils.py:111
msgid "Missing CSV file"
msgstr "Puuttuva CSV-tiedosto"
@@ -927,10 +1048,6 @@ msgstr "Sarja"
msgid "Ohlhafv challenge: {} vs. {}"
msgstr "Ohlhafv-haaste: {} vs. {}"
#: ohlhafv/templates/email.html:2
msgid "Moikka"
msgstr ""
#: ohlhafv/templates/email.html:4
msgid "on haastanut sinut oluenjuontimittelöön"
msgstr ""
@@ -971,7 +1088,7 @@ msgstr "Haasta kaverisi mittelöön!"
msgid "Challenge"
msgstr "Haasta"
#: ohlhafv/views.py:43
#: ohlhafv/views.py:44
msgid "Sinut on haastettu Øhlhäfviin!"
msgstr ""
@@ -984,57 +1101,58 @@ msgstr "Vaihda"
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: webapp/models.py:17
#: webapp/models.py:18
msgid "Webapp"
msgstr "Nettisivut"
#: webapp/models.py:28
#: webapp/models.py:26
msgid "Tag"
msgstr "Tunniste"
#: webapp/models.py:29
#: webapp/models.py:27
msgid "Tags"
msgstr "Tunnisteet"
#: webapp/models.py:32
#: webapp/models.py:34
msgid "Tag: {}"
msgstr "Tunniste: {}"
#: webapp/models.py:53
msgid "Feed: {}"
msgstr "Uutinen: {}"
#: webapp/models.py:56
#: webapp/models.py:52
msgid "Feed"
msgstr "Uutinen"
#: webapp/models.py:57
#: webapp/models.py:53
msgid "Feeds"
msgstr "Uutiset"
#: webapp/models.py:70
msgid "Event: {}"
msgstr "Tapahtuma: {}"
#: webapp/models.py:61 webapp/models.py:80 webapp/models.py:119
#: webapp/models.py:152 webapp/models.py:198
msgid "Deleted: "
msgstr "Poistettu: "
#: webapp/models.py:73
#: webapp/models.py:62
msgid "{}Feed: {}"
msgstr "{}Uutinen: {}"
#: webapp/models.py:69
msgid "Event"
msgstr "Tapahtuma"
#: webapp/models.py:84
msgid "Template questions: {}"
msgstr "Vakiokysymykset: {}"
#: webapp/models.py:81
msgid "{}Event: {}"
msgstr "{}Tapahtuma: {}"
#: webapp/models.py:87
#: webapp/models.py:91
msgid "Template question"
msgstr "Vakiokysymys"
#: webapp/models.py:88
#: webapp/models.py:92
msgid "Template questions"
msgstr "Vakiokysymykset"
#: webapp/models.py:102
msgid "#{} {}"
msgstr ""
#: webapp/models.py:98
msgid "Template questions: {}"
msgstr "Vakiokysymykset: {}"
#: webapp/models.py:105
msgid "Signup form"
@@ -1044,53 +1162,29 @@ msgstr "Ilmoittautumislomake"
msgid "Signup forms"
msgstr "Ilmoittautumislomakkeet"
#: webapp/models.py:115
msgid "Sign-ups: {}"
msgstr "Ilmoittautumiset: {}"
#: webapp/models.py:120
msgid "#{} {}{}"
msgstr ""
#: webapp/models.py:118
#: webapp/models.py:138
msgid "Sign-up"
msgstr "Ilmoittautuminen"
#: webapp/models.py:119
#: webapp/models.py:139
msgid "Sign-ups"
msgstr "Ilmoittautumiset"
#: webapp/models.py:130
#: webapp/models.py:178
msgid "board member"
msgstr "hallituksen jäsen"
#: webapp/models.py:148
msgid "Committee"
msgstr "Toimikunta"
#: webapp/models.py:149
msgid "Committees"
msgstr "Toimikunnat"
#: webapp/models.py:152
msgid "Committee: {}"
msgstr "Toimikunta: {}"
#: webapp/models.py:172
msgid "Role"
msgstr "Rooli"
#: webapp/models.py:175
msgid "Start date"
msgstr "Alkupäivämäärä"
#: webapp/models.py:176
msgid "End date"
msgstr "Loppupäivämäärä"
#: webapp/models.py:185
msgid "JobAd"
msgstr "Työpaikkailmoitus"
#: webapp/models.py:186
msgid "Official"
msgstr "Toimihenkilö"
#: webapp/models.py:187
msgid "Officials"
msgstr "Toimihenkilöt"
msgid "JobAds"
msgstr "Työpaikkailmoitukset"
#: webapp/templates/contact.html:9 webapp/templates/navigation.html:20
msgid "Contact"
@@ -1135,104 +1229,3 @@ msgstr "Sössö"
#: webapp/templates/navigation.html:24
msgid "Corporate"
msgstr "Yritys"
#~ msgid "HSL timetables"
#~ msgstr "HSL-aikataulut"
#~ msgid "Culture"
#~ msgstr "Kulttuuri"
#~ msgid "Username"
#~ msgstr "Käyttäjänimi"
#~ msgid "Password"
#~ msgstr "Salasana"
#~ msgid "Forgot password?"
#~ msgstr "Unohditko salasanasi?"
#~ msgid "Log in"
#~ msgstr "Kirjaudu sisään"
#~ msgid "New password set"
#~ msgstr "Uusi salasana asetettu"
#~ msgid ""
#~ "Your password has successfully been reset. You can use it right now on "
#~ "the login page."
#~ msgstr ""
#~ "Salasanasi on asetettu onnistuneesti. Voit käyttää sitä nyt "
#~ "kirjautuessasi."
#~ msgid "Password recovery"
#~ msgstr "Salasanan palautus"
#~ msgid ""
#~ "Sorry, this password reset link is invalid. You can still <a href="
#~ "\"%(recovery_url)s\">request a new one</a>."
#~ msgstr ""
#~ "Pahoittelut, tämä salasanan palautuslinkki on epäkelpo. Voit kuitenkin <a "
#~ "href=\"%(recovery_url)s\">hankkia uuden</a>."
#~ msgid "Hi, <strong>%(username)s</strong>. Please choose your new password."
#~ msgstr "Hei, <strong>%(username)s</strong>. Valitse uusi salasanasi."
#~ msgid "Set new password"
#~ msgstr "Aseta uusi salasana"
#~ msgid "Password recovery sent"
#~ msgstr "Salasanan palautusviesti lähetetty"
#~ msgid ""
#~ "An email was sent to <strong>%(email)s</strong> %(ago)s ago. Use the link "
#~ "in it to set a new password."
#~ msgstr ""
#~ "Sähköposti on lähetetty osoitteeseen <strong>%(email)s</strong> %(ago)s:a "
#~ "sitten. Käytä linkkiä asettaaksesi uuden salasanan."
#~ msgid "Registration: {}"
#~ msgstr "Registration: {}"
#~ msgid "Registration"
#~ msgstr "Ilmoittautuminen"
#~ msgid "Registrations"
#~ msgstr "Ilmoittautumiset"
#~ msgid "Challenger email"
#~ msgstr "Haastajan sähköpostiosoite"
#~ msgid "SIK Admin"
#~ msgstr "SIK Hallintapaneeli"
#~ msgid "Team"
#~ msgstr "Joukkue"
#~ msgid "Admin tools"
#~ msgstr "Hallintatyökalut"
#~ msgid ""
#~ "\n"
#~ " first_name, last_name, email_address and place_of_origin "
#~ "should be given string values.\n"
#~ " ayy_member and jas_recipient should be given the value 0 "
#~ "(off) or 1 (on).\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " first_name, last_name, email_address ja place_of_origin ovat "
#~ "merkkijonoja.\n"
#~ " ayy_member ja jas_recipient ovat joko 0 (off) tai 1 (on).\n"
#~ " "
#~ msgid "Syntax"
#~ msgstr "Syntaksi"
#~ msgid "Data"
#~ msgstr "Data"
#~ msgid "Missing \"textfield\" POST request field"
#~ msgstr "Puuttuva \"textfield\" POST-kenttä"
#~ msgid "Applied for board"
#~ msgstr "Hakenut hallitukseen"
@@ -1,30 +0,0 @@
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User, Permission
from rest_framework.authtoken.models import Token
class Command(BaseCommand):
user_name = "sahkopiikki"
password = User.objects.make_random_password()
def handle(self, *args, **options):
if User.objects.filter(username=self.user_name).exists():
self.stdout.write("Sahkopiikki user already exists. Skipping.")
user = User.objects.get(username=self.user_name)
token = Token.objects.get(user=user)
self.stdout.write("Token: {}".format(token))
return
u = User(username=self.user_name)
u.set_password(self.password)
u.save()
permission = Permission.objects.get(codename='check_by_email')
u.user_permissions.add(permission)
token = Token.objects.create(user=u)
self.stdout.write("Created sahkopiikki user '{}' with password '{}' and token '{}'.".format(
self.user_name, self.password, token
))
+2 -6
View File
@@ -3,8 +3,7 @@
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.db.models import Q, F, OuterRef, Subquery
import csv
from django.db.models import Q, OuterRef, Subquery
class BaseMember(models.Model):
@@ -124,11 +123,8 @@ class Member(BaseMember):
qs = qs.filter(Q(first_name__icontains=term) | Q(last_name__icontains=term))
return qs
@staticmethod
def get_members_with_latest_payment(members_query):
"""Return QuerySet of given members QS with last_paid attribute."""
latest = Payment.objects.filter(member=OuterRef('pk')).order_by('-date')
return members_query.annotate(last_paid=Subquery(latest.values('date')[:1]))
# To avoid problems with a cyclical import, this is at the bottom of the file
from members.forms import MemberForm # nopep8
@@ -35,5 +35,6 @@
<body>
{% block content %}
{% endblock content %}
{% include "webapp:footer.html" %}
</body>
</html>
@@ -6,5 +6,6 @@
{% block content %}
<link rel="stylesheet" href="{% static "css/application.css" %}">
<h3>{% trans "Hienoa! Jäsenhakemuksesi on nyt lähetetty." %}</h3>
<p>{% trans "Vahvistusviesti on lähetetty sähköpostiisi. Ota yhteyttä sik-vtmk@list.ayy.fi jos viestiä ei näy." %}</p>
<a href="/"><h4>{% trans "Takaisin Sähköinsinöörikillan web-sivuille" %}</h4></a>
{% endblock content %}
@@ -0,0 +1,11 @@
{% load i18n %}
{% trans "Moi" %} {{ first_name }}!
{% trans "Onnittelut! Sinut on hyväksytty Sähköinsinöörikillan jäseneksi." %}
{% trans "Käy kurkkaamassa killan nettisivuilta" %} (https://sik.ayy.fi) {% trans "tulevia tapahtumia ja piipahda kiltahuoneella tutustumassa uusiin kiltatovereihisi!" %}
{% trans "Liity myös killan TG-kanaville" %}:
{% trans "SIK" %}: https://t.me/joinchat/A6EViD5FCWLxPcXCggY7hw
{% trans "SIK-fuksit 2019" %}: http://tinyurl.com/sikfuksit19-tg
{% trans "SIK-fuksit 2019 -tiedotuskanava" %}: http://tinyurl.com/sikfuksit19-tiedotus
@@ -0,0 +1,21 @@
{% load i18n %}
{% trans "Moikka" %} {{ application.first_name }},
{% trans "Sait tämän viestin, sillä olet lähettänyt hakemuksen Aalto-yliopiston Sähköinsinöörikillan jäseneksi alla olevin tiedoin. Siistiä!" %}
{% trans "Etunimi" %}: {{ application.first_name }}
{% trans "Sukunimi" %}: {{ application.last_name }}
{% trans "Sähköposti" %}: {{ application.email }}
{% trans "Kotipaikkakunta" %}: {{ application.POR }}
{% trans "AYY:n jäsen" %}: {{ ayy }}
{% trans "Haluan jäsenmailin" %}: {{ jas }}
{% trans "Saat sähköpostiisi tiedon, kun sinut on hallituksen kokouksessa hyväksytty jäseneksi" %}.
{% trans "Muistathan maksaa jäsenmaksun! Alla maksutiedot" %}:
{% trans "Saaja" %}: Aalto-yliopiston Sähköinsinöörikilta ry
{% trans "Tilinumero" %}: FI97 1309 3000 1118 23
BIC: NDEAFIHH
{% trans "Viite" %}: 1313
{% trans "Summa" %}: 8 €
-35
View File
@@ -3,7 +3,6 @@
from django.test import TestCase, Client
from unittest import skip
from django.contrib.auth.models import User
from members.management.commands.createsahkopiikkiuser import Command as SahkopiikkiCommand
from members.models import Member, Payment, Request
from rest_framework.authtoken.models import Token
@@ -31,9 +30,6 @@ class MemberRegisterTestCase(TestCase):
self.c = Client()
self.c.login(username=username, password=password)
sc = SahkopiikkiCommand()
sc.handle()
def test_member_created(self):
"""Test member creation."""
exists = Member.objects.filter(first_name="Tidus").exists()
@@ -78,37 +74,6 @@ class MemberRegisterTestCase(TestCase):
results = response.json()['results']
self.assertEqual(len(results), 0)
def test_sahkopiikki_check_by_email_not_found(self):
"""Test if sähköpiikki auth and search work"""
email = 'teppo@tulppu.fi'
wrong_email = 'asd@asd.fi'
Member.objects.create(email=email, first_name='Teppo', last_name='Tulppu')
token = Token.objects.get(user__username='sahkopiikki').key
self.c.defaults['HTTP_AUTHORIZATION'] = 'Token ' + token
response = self.c.get('/members/check?email={}'.format(wrong_email), follow=True)
self.assertEqual(response.json()['exists'], False)
def test_sahkopiikki_check_by_email_found(self):
"""Test if sähköpiikki auth and search work"""
email = 'teppo@tulppu.fi'
Member.objects.create(email=email, first_name='Teppo', last_name='Tulppu')
token = Token.objects.get(user__username='sahkopiikki').key
self.c.defaults['HTTP_AUTHORIZATION'] = 'Token ' + token
response = self.c.get('/members/check?email={}'.format(email), follow=True)
self.assertEqual(response.json()['exists'], True)
def test_sahkopiikki_check_by_email_forbidden(self):
"""Test if sähköpiikki auth and search work"""
email = 'teppo@tulppu.fi'
Member.objects.create(email=email, first_name='Teppo', last_name='Tulppu')
token = Token.objects.get(user__username='sahkopiikki').key
self.c.defaults['HTTP_AUTHORIZATION'] = 'Token ' + token + 'DERP'
response = self.c.get('/members/check?email={}'.format(email), follow=True)
self.assertEqual(response.status_code, 401)
def test_export_members_excel(self):
"""Test if the user can download an excel file of the member register"""
resp = self.c.get('/members/export_members')
-55
View File
@@ -1,59 +1,4 @@
"""File containing Members application views."""
from django.shortcuts import render
from django.contrib.auth.decorators import permission_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
# Email validation
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
import json
import requests
import logging
import html
import csv
import pickle
from smtplib import SMTPAuthenticationError
from members.models import Member, Request, Payment
from members.forms import MemberForm, PaymentForm, ApplicationForm, CSVValidationError
from members.views.utils import send_mail_wrapper
@receiver(post_save, sender=Request)
def email_on_request(sender, instance, created, **kwargs):
"""Send email validation."""
if not settings.ENABLE_AUTOMATIC_EMAILS:
return
try:
if created:
subject = 'Test1'
message = 'Please validate your email address\r\n'
send_mail_wrapper(subject, message, instance.email)
except SMTPAuthenticationError:
logging.error('Failed to send email to accepted request!')
@receiver(post_save, sender=Member)
def email_on_accept(sender, instance, created, **kwargs):
"""Send email to accepted member."""
if not settings.ENABLE_AUTOMATIC_EMAILS:
return
try:
if created:
subject = 'Test2'
message = 'Jäsenhakemuksesi on hyväksytty!!!\r\n'
send_mail_wrapper(subject, message, instance.email)
except SMTPAuthenticationError:
logging.error('Failed to send email to accepted member!')
+29 -4
View File
@@ -3,14 +3,16 @@ from django.contrib.auth.decorators import permission_required, login_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
from django.template.loader import render_to_string
import logging
import html
from webapp.utils import send_email
from members.views.utils import *
from members.tables import RequestTable
from members.forms import ApplicationForm
@@ -90,6 +92,16 @@ def application_accept(request, *args, **kwargs):
.format(form))
notification = "{} {}.".format(_("Successfully accepted application"),
str(application))
subject = _('Jäsenhakemuksesi Sähköinsinöörikiltaan on hyväksytty!')
message = render_to_string(
'members:email_application_accept.html', {
'first_name': application.first_name
}
)
send_email(member.email, subject, message)
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
except Exception as ex:
@@ -152,13 +164,26 @@ def application_form(request, *args, **kwargs):
@ensure_csrf_cookie
@require_http_methods(["POST"])
@login_required(login_url='/admin/login')
@permission_required('members.delete_request', raise_exception=True)
def application_submit(request, *args, **kwargs):
"""Submit member application"""
form = ApplicationForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'application_success.html', {})
try:
application = form.instance
email = form.cleaned_data.get('email', '')
subject = _('Jäsenhakemuksesi Sähköinsinöörikiltaan on lähetetty onnistuneesti!')
message = render_to_string(
'members:email_application_submit.html', {
'application': application,
'ayy': _('Kyllä') if application.AYY else _('Ei'),
'jas': _('Kyllä') if application.jas else _('Ei')
}
)
send_email(email, subject, message)
finally:
return render(request, 'application_success.html', {})
else:
return error_view(request, form.errors)
-1
View File
@@ -4,7 +4,6 @@ from django.utils.decorators import method_decorator
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse, HttpResponseForbidden
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
-1
View File
@@ -3,7 +3,6 @@ from django.contrib.auth.decorators import permission_required, login_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
-10
View File
@@ -3,7 +3,6 @@ from django.contrib.auth.decorators import permission_required, login_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
@@ -145,15 +144,6 @@ def import_csv(request, *args, **kwargs):
return render(request, 'member_add_many_confirm.html', context)
def send_mail_wrapper(subject, message, email_to):
"""Send mail to default email."""
send_mail(subject,
message,
settings.DEFAULT_EMAIL_FROM,
[email_to],
fail_silently=False)
def make_excel_response(Resource):
res = Resource()
dataset = res.export()
+2 -2
View File
@@ -1,6 +1,6 @@
html, body {
background: #fdd504;
background: linear-gradient(#fdaa02, #fdd504) no-repeat center center fixed;
background: linear-gradient(#fcf4de, #fcf4de) no-repeat center center fixed;
}
body {
@@ -49,7 +49,7 @@ h3 {
.navbar {
border-radius: 8px;
background-color: #c1272d;
background-color: #a87538;
box-shadow: 0 0;
}
+2 -2
View File
@@ -11,9 +11,9 @@
border-radius: 8px;
padding: 10px 15px 10px 15px;
margin: 5px;
background-color: #fdaa02;
background-color: #fcf4de;
}
a.nav-item.nav-link:hover {
background-color: #fc8606;
background-color: #ecc155;
}
File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 227 KiB

+2 -1
View File
@@ -14,6 +14,7 @@ from django.template.loader import render_to_string
import logging
import requests
from dealer.git import git
from sikweb.settings import URL
from ohlhafv.models import OhlhafvChallenge
from ohlhafv.forms import OhlhafvForm
@@ -39,7 +40,7 @@ def ohlhafv_submit(request, *args, **kwargs):
challenge = form.instance
email = form.cleaned_data.get('victim_email', '')
url = 'https://sika.sahkoinsinoorikilta.fi/ohlhafv/list'
url = f'https://{URL}/ohlhafv/list'
subject = _('Sinut on haastettu Øhlhäfviin!')
message = render_to_string(
+4422 -3037
View File
File diff suppressed because it is too large Load Diff
+13 -7
View File
@@ -3,20 +3,26 @@
"version": "1.0.0",
"description": "A modern web app using a Django backend and an Angular frontend.",
"scripts": {
"test": "eslint . && remark .",
"eslint": "eslint .",
"remark": "remark ."
"lint": "run-p lint:js lint:md lint:py",
"lint:js": "eslint .",
"lint:md": "remark .",
"lint:py": "pycodestyle --config=pycodestyle.cfg --count .",
"lint:py-type": "pyright",
"prepare": "husky install"
},
"repository": {
"type": "git",
"url": "git@sika.sahkoinsinoorikilta.fi:vtmk/web2.0.git"
"url": "git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git"
},
"author": "SIK ry",
"license": "ISC",
"dependencies": {
"eslint": "3.19.0",
"remark-cli": "^4.0.0",
"remark-preset-lint-recommended": "^3.0.1"
"eslint": "^7.23.0",
"husky": "^5.2.0",
"npm-run-all": "^4.1.5",
"pyright": "^1.1.126",
"remark-cli": "^9.0.0",
"remark-preset-lint-recommended": "^5.0.0"
},
"remarkConfig": {
"plugins": [
Generated
+1242
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
#!/bin/sh
# Get ENVs from secrets
if test -f "$SECRET_KEY_FILE"; then
export SECRET_KEY=$(cat $SECRET_KEY_FILE)
fi
if test -f "$TG_BOT_TOKEN_FILE"; then
export TG_BOT_TOKEN=$(cat $TG_BOT_TOKEN_FILE)
fi
if test -f "$EMAIL_API_KEY_FILE"; then
export EMAIL_API_KEY=$(cat $EMAIL_API_KEY_FILE)
fi
if test -f "$EMAIL_API_SECRET_FILE"; then
export EMAIL_API_SECRET=$(cat $EMAIL_API_SECRET_FILE)
fi
if test -f "$DB_USER_FILE"; then
export DB_USER=$(cat $DB_USER_FILE)
fi
if test -f "$DB_PASSWD_FILE"; then
export DB_PASSWD=$(cat $DB_PASSWD_FILE)
fi
# Collect static files
echo "Collect static files"
python manage.py collectstatic --noinput
# Apply database migrations
echo "Apply database migrations"
python manage.py migrate
# Start server
echo "Django running on http://localhost:8000 in production mode"
gunicorn -w 4 -b 0.0.0.0:8000 sikweb.wsgi
+1 -1
View File
@@ -1,4 +1,4 @@
[pycodestyle]
max-line-length = 120
ignore = E501,E722
exclude = '*/migrations/*', venv/*
exclude = '*/migrations/*', venv/*, .venv/*
+60
View File
@@ -0,0 +1,60 @@
[tool.poetry]
name = "web2.0-backend"
version = "0.1.0"
description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.9"
Pillow = "^8.1.2"
"backports.shutil_get_terminal_size" = "1.0.0"
decorator = "4.0.9"
Django = "2.1.5"
ipython = "4.2.0"
ipython_genutils = "0.1.0"
pexpect = "4.1.0"
pickleshare = "0.7.2"
ptyprocess = "0.5.1"
pytz = "2016.4"
simplegeneric = "0.8.1"
traitlets = "4.2.1"
requests = "2.11.1"
django-nocaptcha-recaptcha = "0.0.19"
django-cors-headers = "2.0.1"
djangorestframework = "3.8.2"
PyJWT = "1.6.4"
djangorestframework-jwt = "1.11.0"
coverage = "4.3.4"
django-nose = "1.4.5"
nose-exclude = "0.5.0"
psycopg2-binary = "2.8.4"
django-bootstrap3 = "11.1.0"
django-tables2 = "1.6.1"
pycodestyle = "2.6.0"
dealer = "2.0.5"
django-modeltranslation = "0.13b1"
django-auditlog = "0.4.5"
phonenumbers = "8.11.4"
django-phonenumber-field = {version = "4.0.0", extras = ["phonenumbers"]}
django-autocomplete-light = "3.4.1"
six = "1.12.0"
django-suit = "0.2.26"
telepot = "12.3"
pyexcel = "0.5.14"
pyexcel-xlsx = "0.5.8"
django-import-export = "0.7.0"
openpyxl = "2.6.4"
django-app-namespace-template-loader = "0.4.1"
django-filter = "2.0.0"
whitenoise = "4.1.4"
jsonschema = "3.2.0"
mailjet-rest = "1.3.3"
Markdown = "3.2.2"
uWSGI = "2.0.18"
gunicorn = "19.9.0"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
+16
View File
@@ -0,0 +1,16 @@
{
"include": [
"."
],
"exclude": [
"**/node_modules",
"**/__pycache__"
],
"venvPath": "./venv",
"reportMissingImports": true,
"reportMissingTypeStubs": false,
"pythonVersion": "3.7.4"
}
+1 -5
View File
@@ -16,10 +16,6 @@ Data table app for viewing and modifying the member register, member application
Mostly static website with an event calendar and news feed.
### Coffee scale
Shows the current coffee scale status.
## Accessing the source
### Clone this repository and enter it
@@ -98,7 +94,7 @@ Merge requests to `master` should be reviewed by multiple developers. Only a mod
Lint python files using `pycodestyle` with
```bash
pycodestyle --config=setup.cfg --count .
pycodestyle --config=pycodestyle.cfg --count .
```
Lint javascript and markdown using `eslint` and `remark` with
+1 -1
View File
@@ -1,2 +1,2 @@
uWSGI==2.0.14
uWSGI==2.0.18
gunicorn==19.9.0
+15 -10
View File
@@ -9,7 +9,7 @@ ptyprocess==0.5.1
pytz==2016.4
simplegeneric==0.8.1
traitlets==4.2.1
Pillow==5.4.1
Pillow==7.2.0
requests==2.11.1
django-nocaptcha-recaptcha==0.0.19
django-cors-headers==2.0.1
@@ -19,21 +19,26 @@ djangorestframework-jwt==1.11.0
coverage==4.3.4
django-nose==1.4.5
nose-exclude==0.5.0
psycopg2-binary==2.7.6.1
django-bootstrap3==8.2.3
psycopg2-binary==2.8.4
django-bootstrap3==11.1.0
django-tables2==1.6.1
pycodestyle==2.3.1
pycodestyle==2.6.0
dealer==2.0.5
django-modeltranslation==0.13b1
django-auditlog==0.4.5
django-phonenumber-field==1.3.0
django-autocomplete-light==3.2.10
six==1.10.0
phonenumbers==8.11.4
django-phonenumber-field[phonenumbers]==4.0.0
django-autocomplete-light==3.4.1
six==1.12.0
django-suit==0.2.26
telepot==12.3
pyexcel==0.5.10
pyexcel-xlsx==0.5.5
pyexcel==0.5.14
pyexcel-xlsx==0.5.8
django-import-export==0.7.0
openpyxl==2.4.11
openpyxl==2.6.4
django-app-namespace-template-loader==0.4.1
django-filter==2.0.0
whitenoise==4.1.4
jsonschema==3.2.0
mailjet-rest==1.3.3
markdown==3.2.2
+2 -9
View File
@@ -3,7 +3,7 @@
echo "SIK WEB 2.0"
echo "This script will set up the environment for this project."
echo "========================================================="
echo "Dependencies: python>3.5"
echo "Dependencies: python3.7"
INTERACTIVE="true"
USE_NPM="true"
@@ -36,14 +36,6 @@ then
exit 0
fi
$INTERACTIVE && read -p "Copy settings from template? (recommended) [y/n]" -n 1 -r || REPLY="y"
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]
then
cp "$PWD/sikweb/settings-sample.py" "$PWD/sikweb/settings.py"
fi
$INTERACTIVE && read -p "Copy pre-push hook to .git/hooks? (recommended) [y/n]" -n 1 -r || REPLY="y"
echo ""
@@ -65,6 +57,7 @@ fi
set -e
set -x
pip install -r requirements.txt
pip install -r requirements.production.txt
$USE_NPM && npm install
python manage.py migrate
python manage.py createdefaultadmin
-14
View File
@@ -1,14 +0,0 @@
"""File containing CI settings."""
from sikweb.default_settings import *
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'ci',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'postgres',
'PORT': '5432',
},
}
+11 -50
View File
@@ -7,19 +7,12 @@ from django.utils.translation import ugettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
IS_DOCKER = bool(os.getenv('IS_DOCKER', None))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
if not IS_DOCKER:
ALLOWED_HOSTS = []
else:
ALLOWED_HOSTS = ["*"]
# Logger level
LOGGERLEVEL = logging.DEBUG
@@ -93,7 +86,6 @@ INSTALLED_APPS = [
'webapp',
'members',
'infoscreen',
'coffee_scale',
'kaehmy',
'ohlhafv',
'rest_framework',
@@ -124,6 +116,7 @@ NOSE_ARGS = [
MIDDLEWARE = [
'sikweb.middleware.ForceDefaultLanguageMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'corsheaders.middleware.CorsMiddleware',
@@ -134,9 +127,7 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'auditlog.middleware.AuditlogMiddleware'
]
MIDDLEWARE_CLASSES = [
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True
ROOT_URLCONF = 'sikweb.urls'
@@ -166,36 +157,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'sikweb.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
if not IS_DOCKER:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'sik',
'USER': 'sik',
'PASSWORD': 'password123',
'HOST': 'localhost',
'PORT': '5432',
'TEST': {
'NAME': 'sik_test',
},
},
}
else:
logging.info('Using docker database configuration')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': 'db',
'PORT': '5432',
},
}
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
@@ -227,16 +188,16 @@ REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'DEFAULT_THROTTLE_CLASSES': (
'members.throttles.BurstRateThrottle',
'members.throttles.SustainedRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'burst': '60/min',
'sustained': '1000/day'
},
# 'DEFAULT_THROTTLE_CLASSES': (
# 'members.throttles.BurstRateThrottle',
# 'members.throttles.SustainedRateThrottle'
# ),
# 'DEFAULT_THROTTLE_RATES': {
# 'burst': '60/min',
# 'sustained': '1000/day'
# },
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 10,
'PAGE_SIZE': 1000,
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
-74
View File
@@ -1,74 +0,0 @@
"""
Django settings for sikweb project.
Generated by 'django-admin startproject' using Django 1.9.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
from sikweb.base import *
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# ALLOWED_HOSTS = ["*"]
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp('
# MQTT settings
MQTT_SETTINGS = {
'HOST': 'mqtt.sik.party',
'PORT': 1883,
'TOPICS': {
'BREW_TIME': 'sik/kiltahuone/kahvivaaka/brewtime',
'WEIGHT': 'sik/kiltahuone/kahvivaaka/weight',
'BREWING': 'sik/kiltahuone/kahvivaaka/brewing',
'CUPS': 'sik/kiltahuone/kahvivaaka/cups',
}
}
# ReCaptcha
# http://www.yaconiello.com/blog/integrating-google-recaptcha-to-django/
GOOGLE_RECAPTCHA_SITE_KEY = "YOUR-PUBLIC-KEY"
GOOGLE_RECAPTCHA_SECRET_KEY = "YOUR-PRIVATE-KEY"
# Email settings (more settings in base.py)
EMAIL_HOST_USER = '<gmailtunnarisi>@gmail.com'
EMAIL_HOST_PASSWORD = '<gmail_passu>'
DEFAULT_EMAIL_FROM = 'SIK Viestintä <sikviestinta@gmail.com>'
ENABLE_AUTOMATIC_EMAILS = False
# Token for Telegram bot
TELEGRAM_BOT_TOKEN = "<BOT_TOKEN>"
# Database settings
# Only uncomment if default settings in base.py are not ok
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
# 'NAME': 'sik',
# 'USER': 'sik',
# 'PASSWORD': 'password123',
# 'HOST': 'localhost',
# 'PORT': '5432',
# 'TEST': {
# 'NAME': 'sik_test',
# },
# },
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase',
'TEST': {
'NAME': 'sik_test',
},
}
}
+58
View File
@@ -0,0 +1,58 @@
"""
Django settings for sikweb project.
Generated by 'django-admin startproject' using Django 1.9.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
from sikweb.base import *
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG', False) == 'True'
URL = os.getenv("HOST", "api.sahkoinsinoorikilta.fi")
FRONTEND_URL = os.getenv("FRONTEND_URL", "sahkoinsinoorikilta.fi")
ALLOWED_HOSTS = ["localhost", "127.0.0.1", FRONTEND_URL, URL]
if DEBUG:
ALLOWED_HOSTS = ["*"]
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv('SECRET_KEY', '7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(')
# ReCaptcha
# http://www.yaconiello.com/blog/integrating-google-recaptcha-to-django/
GOOGLE_RECAPTCHA_SITE_KEY = os.getenv("GOOGLE_RECAPTCHA_SITE_KEY", "YOUR-PUBLIC-KEY")
GOOGLE_RECAPTCHA_SECRET_KEY = os.getenv("GOOGLE_RECAPTCHA_SECRET_KEY", "YOUR-PRIVATE-KEY")
# Email settings (more settings in base.py)
EMAIL_API_KEY = os.getenv('EMAIL_API_KEY', '')
EMAIL_API_SECRET = os.getenv('EMAIL_API_SECRET', '')
DEFAULT_EMAIL_FROM = 'SIK'
DEFAULT_EMAIL_FROM_ADDR = 'noreply@sahkoinsinoorikilta.fi'
ENABLE_AUTOMATIC_EMAILS = True
# Token for Telegram bot
TELEGRAM_BOT_TOKEN = os.getenv('TG_BOT_TOKEN', '<tg token>')
# Database settings
# Only uncomment if default settings in base.py are not ok
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': os.getenv('DB_USER', 'postgres'),
'USER': os.getenv('DB_USER', 'postgres'),
'PASSWORD': os.getenv('DB_PASSWD', 'postgres'),
'HOST': os.getenv('DB_HOST', 'localhost'),
'PORT': os.getenv('DB_PORT', 5432),
}
}
-2
View File
@@ -26,7 +26,6 @@ from django.views.generic.base import RedirectView
import webapp.urls
import infoscreen.urls
import members.urls
import coffee_scale.urls
favicon_view = RedirectView.as_view(
url='static/img/favicon.png', permanent=True)
@@ -36,7 +35,6 @@ urlpatterns = [
url(r'', include('webapp.urls')),
url(r'^members/', include('members.urls')),
url(r'^infoscreen/', include('infoscreen.urls')),
url(r'^coffee/', include('coffee_scale.urls')),
url(r'^kaehmy/', include('kaehmy.urls')),
url(r'^ohlhafv/', include('ohlhafv.urls')),
+45
View File
@@ -0,0 +1,45 @@
version: '3.4'
services:
db:
image: postgres:12
deploy:
replicas: 1
environment:
- POSTGRES_PASSWORD=postgres
ports:
- 5432:5432
volumes:
- dbdata:/var/lib/postgresql/data
backend:
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend:latest
deploy:
replicas: 1
update_config:
order: start-first
depends_on:
- db
ports:
- 8000:8000
environment:
- HOST=api.dev.sahkoinsinoorikilta.fi
- FRONTEND_URL=dev.sahkoinsinoorikilta.fi
- DEBUG=True
- EMAIL_API_KEY_FILE=/run/secrets/DJANGO_EMAIL_API_KEY
- EMAIL_API_SECRET_FILE=/run/secrets/DJANGO_EMAIL_API_SECRET
- DB_HOST=db
- DB_PORT=5432
secrets:
- DJANGO_EMAIL_API_KEY
- DJANGO_EMAIL_API_SECRET
secrets:
DJANGO_EMAIL_API_KEY:
external: true
DJANGO_EMAIL_API_SECRET:
external: true
volumes:
dbdata:
+76
View File
@@ -0,0 +1,76 @@
version: '3.4'
services:
db:
image: postgres:12
deploy:
replicas: 1
environment:
- POSTGRES_USER_FILE=/run/secrets/BACKEND_DB_USER
- POSTGRES_PASSWORD_FILE=/run/secrets/BACKEND_DB_PASSWD
ports:
- 5432:5432
volumes:
- dbdata:/var/lib/postgresql/data
secrets:
- BACKEND_DB_USER
- BACKEND_DB_PASSWD
backend:
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend:latest
deploy:
replicas: 1
update_config:
order: start-first
depends_on:
- db
ports:
- 8000:8000
volumes:
- type: bind
source: /home/sik/production/files/static/.hidden/django/media
target: /app/media
- type: bind
source: /home/sik/production/files/static/.hidden/django/static
target: /app/static
- type: bind
source: /home/sik/production/files/static/.hidden/django/collected_static
target: /app/collected_static
environment:
- SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY
- TG_BOT_TOKEN_FILE=/run/secrets/BACKEND_TG_BOT_TOKEN
- DB_USER_FILE=/run/secrets/BACKEND_DB_USER
- DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD
- HOST=api.sahkoinsinoorikilta.fi
- FRONTEND_URL=sahkoinsinoorikilta.fi
- DB_HOST=db
- DB_PORT=5432
- EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY
- EMAIL_API_SECRET_FILE=/run/secrets/BACKEND_EMAIL_API_SECRET
secrets:
- BACKEND_SECRET_KEY
- BACKEND_TG_BOT_TOKEN
- BACKEND_DB_USER
- BACKEND_DB_PASSWD
- BACKEND_EMAIL_API_KEY
- BACKEND_EMAIL_API_SECRET
secrets:
BACKEND_SECRET_KEY:
external: true
BACKEND_TG_BOT_TOKEN:
external: true
BACKEND_DB_NAME:
external: true
BACKEND_DB_USER:
external: true
BACKEND_DB_PASSWD:
external: true
BACKEND_EMAIL_API_KEY:
external: true
BACKEND_EMAIL_API_SECRET:
external: true
volumes:
dbdata:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.
-177
View File
@@ -1,177 +0,0 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
cmdname=$(basename $0)
echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $TIMEOUT -gt 0 ]]; then
echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
else
echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
fi
start_ts=$(date +%s)
while :
do
if [[ $ISBUSY -eq 1 ]]; then
nc -z $HOST $PORT
result=$?
else
(echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
result=$?
fi
if [[ $result -eq 0 ]]; then
end_ts=$(date +%s)
echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
break
fi
sleep 1
done
return $result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $QUIET -eq 1 ]]; then
timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
else
timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
fi
PID=$!
trap "kill -INT -$PID" INT
wait $PID
RESULT=$?
if [[ $RESULT -ne 0 ]]; then
echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
fi
return $RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
hostport=(${1//:/ })
HOST=${hostport[0]}
PORT=${hostport[1]}
shift 1
;;
--child)
CHILD=1
shift 1
;;
-q | --quiet)
QUIET=1
shift 1
;;
-s | --strict)
STRICT=1
shift 1
;;
-h)
HOST="$2"
if [[ $HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
HOST="${1#*=}"
shift 1
;;
-p)
PORT="$2"
if [[ $PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
PORT="${1#*=}"
shift 1
;;
-t)
TIMEOUT="$2"
if [[ $TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$HOST" == "" || "$PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
TIMEOUT=${TIMEOUT:-15}
STRICT=${STRICT:-0}
CHILD=${CHILD:-0}
QUIET=${QUIET:-0}
# check to see if timeout is from busybox?
# check to see if timeout is from busybox?
TIMEOUT_PATH=$(realpath $(which timeout))
if [[ $TIMEOUT_PATH =~ "busybox" ]]; then
ISBUSY=1
BUSYTIMEFLAG="-t"
else
ISBUSY=0
BUSYTIMEFLAG=""
fi
if [[ $CHILD -gt 0 ]]; then
wait_for
RESULT=$?
exit $RESULT
else
if [[ $TIMEOUT -gt 0 ]]; then
wait_for_wrapper
RESULT=$?
else
wait_for
RESULT=$?
fi
fi
if [[ $CLI != "" ]]; then
if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
echoerr "$cmdname: strict mode, refusing to execute subprocess"
exit $RESULT
fi
exec "${CLI[@]}"
else
exit $RESULT
fi
+2 -5
View File
@@ -1,8 +1,7 @@
"""File containing webapp app admin registers."""
from django.contrib import admin
from webapp.models import Official, Role, Committee
from webapp.models import Feed, Tag, BaseFeed, Event, Signup, SignupForm, TemplateQuestion
from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd
from modeltranslation.admin import TranslationAdmin
from django.contrib.auth.models import Permission
# this is needed so that the models get registered for translation
@@ -16,6 +15,4 @@ admin.site.register(Event, TranslationAdmin)
admin.site.register(SignupForm, TranslationAdmin)
admin.site.register(Signup, TranslationAdmin)
admin.site.register(TemplateQuestion, TranslationAdmin)
admin.site.register(Official)
admin.site.register(Role)
admin.site.register(Committee)
admin.site.register(JobAd, TranslationAdmin)
+1 -1
View File
@@ -10,4 +10,4 @@ class WebappConfig(AppConfig):
def ready(self):
"""Import translations."""
import webapp.translations
import webapp.translation
-5
View File
@@ -1,5 +0,0 @@
"""File containing webapp forms."""
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
@@ -0,0 +1,19 @@
# Generated by Django 2.1.5 on 2019-09-26 17:48
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('webapp', '0054_auto_20190313_1642'),
]
operations = [
migrations.DeleteModel(
name='official'
)
]
@@ -0,0 +1,58 @@
# Generated by Django 2.1.5 on 2019-09-26 17:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('webapp', '0055_auto_20190926_2048'),
]
operations = [
migrations.CreateModel(
name='Occupation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start_date', models.DateField(verbose_name='Start date')),
('end_date', models.DateField(verbose_name='End date')),
],
options={
'verbose_name': 'Occupation',
'verbose_name_plural': 'Occupations',
},
),
migrations.CreateModel(
name='Official',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=30, verbose_name='First name')),
('last_name', models.CharField(max_length=150, verbose_name='Last name')),
('email', models.EmailField(max_length=254, verbose_name='Email address')),
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, verbose_name='Phone number')),
('role_history', models.ManyToManyField(blank=True, to='webapp.Occupation')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Official',
'verbose_name_plural': 'Officials',
},
),
migrations.RemoveField(
model_name='role',
name='end_date',
),
migrations.RemoveField(
model_name='role',
name='start_date',
),
migrations.AddField(
model_name='occupation',
name='role',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='webapp.Role'),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 2.1.5 on 2019-09-26 18:02
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('webapp', '0056_auto_20190926_2051'),
]
operations = [
migrations.AlterField(
model_name='occupation',
name='role',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='webapp.Role'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2019-10-10 15:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0057_auto_20190926_2102'),
]
operations = [
migrations.AlterField(
model_name='official',
name='role_history',
field=models.ManyToManyField(blank=True, related_name='officials', to='webapp.Occupation'),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 2.1.5 on 2019-10-10 16:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0058_auto_20191010_1837'),
]
operations = [
migrations.AddField(
model_name='committee',
name='name_en',
field=models.CharField(max_length=255, null=True, verbose_name='Name'),
),
migrations.AddField(
model_name='committee',
name='name_fi',
field=models.CharField(max_length=255, null=True, verbose_name='Name'),
),
]
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2019-10-10 16:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0059_auto_20191010_1900'),
]
operations = [
migrations.AddField(
model_name='official',
name='image',
field=models.ImageField(null=True, upload_to=''),
),
]
@@ -0,0 +1,29 @@
# Generated by Django 2.1.5 on 2019-11-10 18:24
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0060_official_image'),
]
operations = [
migrations.AlterField(
model_name='official',
name='image',
field=models.ImageField(blank=True, null=True, upload_to=''),
),
migrations.AlterField(
model_name='signup',
name='answer',
field=django.contrib.postgres.fields.jsonb.JSONField(),
),
migrations.AlterField(
model_name='signupform',
name='questions',
field=django.contrib.postgres.fields.jsonb.JSONField(),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 2.1.5 on 2019-11-10 19:17
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('webapp', '0061_auto_20191110_2024'),
]
operations = [
migrations.AlterField(
model_name='templatequestion',
name='question',
field=django.contrib.postgres.fields.jsonb.JSONField(),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 2.1.5 on 2020-06-16 18:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0062_auto_20191110_2117'),
]
operations = [
migrations.AddField(
model_name='signup',
name='list_name',
field=models.CharField(default='', max_length=255, verbose_name='Name'),
preserve_default=False,
),
]
+19
View File
@@ -0,0 +1,19 @@
# Generated by Django 2.1.5 on 2020-06-22 15:42
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('webapp', '0063_signup_list_name'),
]
operations = [
migrations.AddField(
model_name='signup',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, editable=False),
),
]
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2020-06-22 18:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0064_signup_uuid'),
]
operations = [
migrations.AddField(
model_name='signup',
name='email',
field=models.EmailField(blank=True, max_length=254, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2020-06-22 20:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0065_signup_email'),
]
operations = [
migrations.AlterField(
model_name='event',
name='signupForm',
field=models.ManyToManyField(blank=True, related_name='event', to='webapp.SignupForm'),
),
]
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2020-07-22 14:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0066_auto_20200622_2302'),
]
operations = [
migrations.AddField(
model_name='basefeed',
name='image',
field=models.ImageField(blank=True, null=True, upload_to=''),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2020-07-22 17:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0067_basefeed_image'),
]
operations = [
migrations.AddField(
model_name='signupform',
name='quota',
field=models.PositiveIntegerField(blank=True, null=True),
),
]
@@ -0,0 +1,20 @@
# Generated by Django 2.1.5 on 2020-07-23 19:18
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('webapp', '0068_signupform_quota'),
]
operations = [
migrations.AddField(
model_name='signupform',
name='schema',
field=django.contrib.postgres.fields.jsonb.JSONField(default=[]),
preserve_default=False,
),
]
@@ -0,0 +1,39 @@
# Generated by Django 2.1.5 on 2020-10-04 15:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0069_signupform_schema'),
]
operations = [
migrations.AddField(
model_name='event',
name='location_en',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='event',
name='location_fi',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='signupform',
name='email_content',
field=models.TextField(default=''),
preserve_default=False,
),
migrations.AddField(
model_name='signupform',
name='title_en',
field=models.CharField(max_length=255, null=True),
),
migrations.AddField(
model_name='signupform',
name='title_fi',
field=models.CharField(max_length=255, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2020-10-06 14:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0070_auto_20201004_1820'),
]
operations = [
migrations.AlterField(
model_name='signupform',
name='email_content',
field=models.TextField(blank=True),
),
]
+38
View File
@@ -0,0 +1,38 @@
# Generated by Django 2.1.5 on 2020-11-03 15:38
from django.db import migrations, models
import django.utils.timezone
import webapp.utils
class Migration(migrations.Migration):
dependencies = [
('webapp', '0071_auto_20201006_1749'),
]
operations = [
migrations.CreateModel(
name='JobAd',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('title_fi', models.CharField(max_length=255, null=True)),
('title_en', models.CharField(max_length=255, null=True)),
('description', models.CharField(max_length=255)),
('description_fi', models.CharField(max_length=255, null=True)),
('description_en', models.CharField(max_length=255, null=True)),
('content', models.TextField()),
('content_fi', models.TextField(null=True)),
('content_en', models.TextField(null=True)),
('visible', models.BooleanField(default=True)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('autohide_at', models.DateTimeField(default=webapp.utils.month_from_now)),
('autohide_enabled', models.BooleanField(default=False)),
],
options={
'verbose_name': 'JobAd',
'verbose_name_plural': 'JobAds',
},
),
]
@@ -0,0 +1,60 @@
# Generated by Django 2.1.5 on 2020-11-07 17:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('webapp', '0072_jobad'),
]
operations = [
migrations.RemoveField(
model_name='occupation',
name='role',
),
migrations.RemoveField(
model_name='official',
name='role_history',
),
migrations.RemoveField(
model_name='official',
name='user',
),
migrations.RemoveField(
model_name='presetrole',
name='baserole_ptr',
),
migrations.RemoveField(
model_name='role',
name='committee',
),
migrations.RemoveField(
model_name='role',
name='presetrole_ptr',
),
migrations.RemoveField(
model_name='baserole',
name='name_en',
),
migrations.RemoveField(
model_name='baserole',
name='name_fi',
),
migrations.DeleteModel(
name='Committee',
),
migrations.DeleteModel(
name='Occupation',
),
migrations.DeleteModel(
name='Official',
),
migrations.DeleteModel(
name='PresetRole',
),
migrations.DeleteModel(
name='Role',
),
]
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2020-11-07 18:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0073_auto_20201107_1916'),
]
operations = [
migrations.AddField(
model_name='signup',
name='deleted',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,33 @@
# Generated by Django 2.1.5 on 2021-01-14 19:55
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('webapp', '0074_signup_deleted'),
]
operations = [
migrations.AddField(
model_name='event',
name='deleted',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='feed',
name='deleted',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='jobad',
name='deleted',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='signupform',
name='deleted',
field=models.BooleanField(default=False),
),
]
+115 -110
View File
@@ -1,33 +1,35 @@
"""Webapp app models."""
from django.conf import settings
from django.db import models
from django.utils import timezone
# from datetime import timedelta
from django.contrib.auth.models import User
from webapp.utils import month_from_now
from django.db.models.signals import post_save
from django.dispatch import receiver
from webapp.utils import month_from_now, send_signup_email
from django.utils.translation import ugettext_lazy as _
# from django.contrib.auth.models import User
from auditlog.registry import auditlog
from phonenumber_field.modelfields import PhoneNumberField
# from django.contrib.postgres.fields import JSONField
# import logging
from django.contrib.postgres.fields import JSONField
from uuid import uuid4
import logging
VERBOSE_NAME = _('Webapp')
EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
class Tag(models.Model):
"""Model for tag."""
slug = models.SlugField(unique=True)
name = models.CharField(max_length=127)
icon = models.ImageField()
class Meta:
verbose_name = _('Tag')
verbose_name_plural = _('Tags')
slug = models.SlugField(unique=True)
name = models.CharField(max_length=127)
icon = models.ImageField()
def __str__(self):
return _('Tag: {}').format(self.slug)
@@ -40,83 +42,129 @@ class BaseFeed(models.Model):
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
content = models.TextField()
image = models.ImageField(blank=True, null=True)
class Feed(BaseFeed):
"""Model representing feed."""
publish_time = models.DateTimeField(default=timezone.now)
autohide = models.DateTimeField(default=month_from_now)
autohide_enabled = models.BooleanField(default=False)
def __str__(self):
return _('Feed: {}').format(self.title)
class Meta:
verbose_name = _('Feed')
verbose_name_plural = _('Feeds')
class Event(BaseFeed):
"""Model for event."""
start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(default=timezone.now)
signupForm = models.ManyToManyField(
'SignupForm', blank=True)
location = models.CharField(max_length=255, blank=True)
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):
return _('Event: {}').format(self.title)
delete_str = _("Deleted: ") if self.deleted else ""
return _('{}Feed: {}').format(delete_str, self.title)
class Event(BaseFeed):
"""Model for event in guild calendar"""
class Meta:
verbose_name = _('Event')
verbose_name_plural = _('Events')
class TemplateQuestion(models.Model):
"""Stores template questions for signup forms as JSONB"""
# question = JSONField()
name = models.CharField(max_length=255)
question = models.CharField(max_length=255)
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)
def __str__(self):
return _('Template questions: {}').format(self.name)
delete_str = _("Deleted: ") if self.deleted else ""
return _('{}Event: {}').format(delete_str, self.title)
class TemplateQuestion(models.Model):
"""
NOT IMPLEMENTED!!!
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')
class SignupForm(models.Model):
"""Model for event signup form. Stores questions in JSONB."""
title = models.CharField(max_length=255)
start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(default=timezone.now)
# question = JSONField()
questions = models.TextField(default="[]")
visible = models.BooleanField(default=True)
name = models.CharField(max_length=255)
question = JSONField()
def __str__(self):
return _('#{} {}').format(self.id, self.title)
return _('Template questions: {}').format(self.name)
class SignupForm(models.Model):
"""Model for event signup form. Stores questions in JSON format."""
class Meta:
verbose_name = _('Signup form')
verbose_name_plural = _('Signup forms')
class Signup(models.Model):
signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE)
time = models.DateTimeField(default=timezone.now)
answer = models.CharField(max_length=255)
title = models.CharField(max_length=255)
start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(default=timezone.now)
questions = JSONField()
schema = JSONField()
visible = models.BooleanField(default=True)
quota = models.PositiveIntegerField(blank=True, null=True)
email_content = models.TextField(blank=True)
deleted = models.BooleanField(default=False)
def __str__(self):
return _('Sign-ups: {}').format(self.signupForm)
delete_str = _("Deleted: ") if self.deleted else ""
return _('#{} {}{}').format(self.id, delete_str, self.title)
@property
def signups(self):
return Signup.objects.filter(signupForm=self, deleted=False).order_by('pk')
@property
def isOpen(self):
now = timezone.now()
return self.start_time <= now and now < self.end_time
class Signup(models.Model):
"""
Actual signup into any SignupForm. Deletes are soft.
"""
class Meta:
verbose_name = _('Sign-up')
verbose_name_plural = _('Sign-ups')
signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE)
time = models.DateTimeField(default=timezone.now)
answer = JSONField()
# Answer we use in signupForm signups field. Frontend uses first questions answer as this value.
list_name = models.CharField(_('Name'), max_length=255)
# If there is email in questions, we save it as own field
email = models.EmailField(blank=True, null=True)
# Random unique identifier. Used for signup editing by the user.
uuid = models.UUIDField(default=uuid4, editable=False)
deleted = models.BooleanField(default=False)
def __str__(self):
delete_str = _("Deleted: ") if self.deleted else ""
return f"{self.signupForm}: {delete_str}{self.list_name} ({self.pk})"
@receiver(post_save, sender=Signup)
def email_on_signup(sender, instance, created, **kwargs):
if created and instance.email:
# TODO: Possible bug due to many-to-many relationship with events and forms.
# TODO: Subject field crashes with lazy loaded translations.
try:
# subject = _(f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}")
subject = f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}"
except AttributeError:
# subject = _(f"Olet ilmoittautunut ilmoon {instance.signupForm.title}")
subject = f"Olet ilmoittautunut ilmoon {instance.signupForm.title}"
send_signup_email(instance.email, subject, instance.id, instance.uuid, instance.signupForm.email_content)
class BaseRole(models.Model):
@@ -130,73 +178,30 @@ class BaseRole(models.Model):
return '{} ({})'.format(n, _('board member')) if self.is_board else n
class PresetRole(BaseRole):
"""Model representing a preset occupation in the guild."""
description = models.TextField(_('Description'))
class Committee(models.Model):
"""
Committee model
Has many Roles found under variable roles
"""
class JobAd(models.Model):
"""Job advertisements shown on Corporate relations page"""
class Meta:
"""Meta class for Committee class."""
verbose_name = _('JobAd')
verbose_name_plural = _('JobAds')
verbose_name = _('Committee')
verbose_name_plural = _('Committees')
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):
return _('Committee: {}').format(self.name)
name = models.CharField(_("Name"), max_length=255)
@property
def current_roles(self):
return self.roles.all().filter(end_date__gte=timezone.now()).filter(start_date__lte=timezone.now())
class Role(PresetRole):
"""
Model for Role.
Model representing an active or historical occupation
in an official's history.
"""
class Meta:
"""Meta class for Role model."""
verbose_name = _('Role')
verbose_name_plural = _('Roles')
start_date = models.DateField(_('Start date'))
end_date = models.DateField(_('End date'))
committee = models.ForeignKey('Committee', related_name='roles', on_delete=models.SET_NULL, null=True)
class Official(User):
"""Model representing a guild official."""
class Meta:
"""Meta class for Official class."""
verbose_name = _('Official')
verbose_name_plural = _('Officials')
phone_number = PhoneNumberField(_('Phone number'))
role = models.ManyToManyField('Role', related_name='official')
def __str__(self):
return '{} {}'.format(self.first_name, self.last_name)
delete_str = _("Deleted: ") if self.deleted else ""
return f'{delete_str}{self.title}'
auditlog.register(Tag)
auditlog.register(Feed)
auditlog.register(Event)
auditlog.register(SignupForm)
auditlog.register(Signup)
auditlog.register(PresetRole)
auditlog.register(Role)
auditlog.register(Official)
auditlog.register(JobAd)
+87 -40
View File
@@ -2,34 +2,88 @@ from rest_framework import serializers
from webapp.models import *
class SignupFormSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = SignupForm
fields = ('id', 'title', 'start_time', 'end_time', 'questions')
class EventSerializer(serializers.HyperlinkedModelSerializer):
signupForm = SignupFormSerializer(many=True, read_only=True, required=False)
signup_id = serializers.PrimaryKeyRelatedField(
many=True,
class SignupSerializer(serializers.ModelSerializer):
signupForm_id = serializers.PrimaryKeyRelatedField(
source="signupForm",
queryset=SignupForm.objects.all()
)
tag_id = serializers.PrimaryKeyRelatedField(
list_name = serializers.CharField(read_only=True)
def add_extra_fields(self, validated_data):
questions = validated_data["signupForm"].questions
name_ids = list(filter(lambda x: x["type"] == "name", questions))
email_ids = list(filter(lambda x: x["type"] == "email", questions))
# Send email to first email field in the form
if (len(email_ids) > 0):
id = email_ids[0]["id"]
email_value = validated_data["answer"].get(id)
validated_data["email"] = email_value
# Combine all name fields to list_name
if (len(name_ids) > 0):
# name_value = validated_data["answer"].get(name_fields[0]["id"], None)
all_names = map(lambda x: validated_data["answer"].get(x["id"]), name_ids)
validated_data["list_name"] = " ".join(all_names)
def create(self, validated_data):
self.add_extra_fields(validated_data)
return super().create(validated_data)
def update(self, instance, validated_data):
self.add_extra_fields(validated_data)
return super().update(instance, validated_data)
class Meta:
model = Signup
fields = ('id', 'signupForm_id', 'answer', 'list_name')
extra_kwargs = {
'url': {
'view_name': 'signup-detail',
}
}
class SignupFormSerializer(serializers.ModelSerializer):
signups = serializers.SlugRelatedField(
slug_field="list_name",
many=True,
source="tags",
queryset=Tag.objects.all()
read_only=True,
required=False,
)
class Meta:
model = SignupForm
fields = ('id', 'title_fi', 'title_en', 'visible', 'isOpen', 'start_time', 'end_time', 'email_content', 'questions', 'schema', 'signups', 'quota')
class EventSerializer(serializers.ModelSerializer):
signupForm = SignupFormSerializer(
source='filtered_signup_forms',
many=True,
read_only=True,
)
signup_id = serializers.PrimaryKeyRelatedField(
queryset=SignupForm.objects.all(),
many=True,
write_only=True,
)
tag_id = serializers.PrimaryKeyRelatedField(
queryset=Tag.objects.all(),
many=True,
write_only=True,
)
class Meta:
model = Event
fields = ('id', 'tag_id', 'tags', 'visible', 'title', 'description',
'content', 'start_time', 'end_time', 'location', 'signup_id', 'signupForm')
fields = ('id', 'tag_id', 'tags', 'visible', 'image', 'title_fi', 'title_en', 'description_fi', 'description_en',
'content_fi', 'content_en', 'start_time', 'end_time', 'location_fi', 'location_en', 'signup_id', 'signupForm')
read_only_fields = ['tags', 'signupForm']
depth = 1
def create(self, validated_data):
signupForms = validated_data.pop('signupForm')
tags = validated_data.pop('tags')
signupForms = validated_data.pop('signup_id', [])
tags = validated_data.pop('tag_id')
event = Event.objects.create(**validated_data)
for form in signupForms:
event.signupForm.add(form)
@@ -39,27 +93,21 @@ class EventSerializer(serializers.HyperlinkedModelSerializer):
return event
def update(self, instance, validated_data):
signupForms = validated_data.pop('signupForm')
instance = super(EventSerializer, self).update(instance, validated_data)
signupForms = validated_data.pop('signup_id', [])
tags = validated_data.pop('tag_id')
instance.signupForm.clear()
for form_data in signupForms:
# form_qs = SignupForms.objects.filter(id=form['id'])
instance.signupForm.add(form_data)
instance.tags.clear()
for form in signupForms:
instance.signupForm.add(form)
for tag in tags:
instance.tags.add(tag)
instance = super(EventSerializer, self).update(instance, validated_data)
return instance
class SignupSerializer(serializers.ModelSerializer):
class Meta:
model = Signup
fields = ('id', 'signupForm', 'answer')
extra_kwargs = {
'url': {
'view_name': 'signup-detail',
}
}
class SavedQuestionsSerializer(serializers.ModelSerializer):
question = serializers.JSONField()
class Meta:
model = TemplateQuestion
fields = ('id', 'name', 'question')
@@ -68,7 +116,7 @@ class SavedQuestionsSerializer(serializers.ModelSerializer):
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('id', 'slug', 'name', 'icon')
fields = ('id', 'slug', 'name_fi', 'name_en', 'icon')
class FeedSerializer(serializers.ModelSerializer):
@@ -80,8 +128,8 @@ class FeedSerializer(serializers.ModelSerializer):
class Meta:
model = Feed
fields = ('id', 'tags', 'tag_id', 'visible', 'title', 'description',
'content', 'publish_time', 'autohide', 'autohide_enabled')
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):
@@ -93,8 +141,7 @@ class FeedSerializer(serializers.ModelSerializer):
return feed
class ContactsSerializer(serializers.ModelSerializer):
class JobAdSerializer(serializers.ModelSerializer):
class Meta:
model = Official
fields = ('id', 'first_name', 'last_name', 'phone_number', 'role')
depth = 2
model = JobAd
fields = ('id', 'title_fi', 'title_en', 'description_fi', 'description_en', 'content_fi', 'content_en', 'visible', 'autohide_at', 'autohide_enabled')

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