Refactor the way the settings are handled.

Instead of a "create-settings" command, which didn't contain all the options
and was a bit awkward, we now have specific settings used for different things.
This commit is contained in:
Roland Geider
2025-12-23 22:23:03 +01:00
parent f580865f0d
commit a8ba6fe2d6
18 changed files with 575 additions and 276 deletions

View File

@@ -16,6 +16,11 @@ on:
jobs:
ci-job:
runs-on: ubuntu-latest
env:
DJANGO_SETTINGS_MODULE: settings.ci
DJANGO_MEDIA_ROOT: /tmp/wger-test
strategy:
matrix:
# Note: removed 3.14 because lingua-language-detector has no wheels yet
@@ -41,22 +46,19 @@ jobs:
- name: Install dependencies
run: |
uv sync --group dev
mkdir /tmp/wger-test
# Only run the tests with coverage for one version of python
- name: Test the application with coverage
if: matrix.python-version == 3.13
run: |
source .venv/bin/activate
wger create-settings
coverage run --source='.' ./manage.py test
coverage lcov
uv run coverage run --source='.' ./manage.py test
uv run coverage lcov
- name: Test the application
if: matrix.python-version != 3.13
run: |
source .venv/bin/activate
wger create-settings
python manage.py test
uv run ./manage.py test
- name: Coveralls
if: matrix.python-version == 3.13

2
.gitignore vendored
View File

@@ -46,7 +46,7 @@ coverage.xml
# Django stuff:
*.log
settings*.py
settings/*_extra.py
*.sqlite
/CACHE

View File

@@ -41,15 +41,18 @@ RUN wget -O- https://deb.nodesource.com/setup_22.x | bash - \
WORKDIR /home/wger/src
COPY README.md pyproject.toml package.json package-lock.json package-lock.json /home/wger/src/
COPY wger/version.py wger/__init__.py /home/wger/src/wger/
COPY wger/core/static /root/src/wger/core/static
COPY wger/core/static /home/wger/src/wger/core/static
RUN npm ci --production \
&& npm run build:css:sass \
&& PACKAGE_NAME="@wger-project/react-components" \
&& PACKAGE_VERSION=$(node -p "require('./package.json').devDependencies['$PACKAGE_NAME']") \
&& TARBALL=$(npm pack ${PACKAGE_NAME}@${PACKAGE_VERSION}) \
&& mkdir -p node_modules/${PACKAGE_NAME} \
&& tar -xzf ${TARBALL} -C node_modules/${PACKAGE_NAME} --strip-components=1
&& tar -xzf ${TARBALL} -C node_modules/${PACKAGE_NAME} --strip-components=1 \
&& pip3 wheel \
--no-cache-dir \
--wheel-dir /wheels \
--group docker .
########
# Final
@@ -111,9 +114,8 @@ RUN --mount=type=bind,from=builder,source=/wheels,target=/wheels . /home/wger/v
&& pip install --upgrade pip \
&& pip install --no-cache /wheels/* \
&& pip install -e . \
&& wger create-settings --database-path /home/wger/db/database.sqlite \
&& sed -i "/^MEDIA_ROOT/c\MEDIA_ROOT='\/home\/wger\/media'" settings.py \
&& echo STATIC_ROOT=\'/home/wger/static\' >> settings.py \
&& sed -i "/^MEDIA_ROOT/c\MEDIA_ROOT='\/home\/wger\/media'" settings/production.py \
&& echo STATIC_ROOT=\'/home/wger/static\' >> settings/production.py \
&& wger bootstrap --no-process-static \
&& python3 manage.py sync-exercises \
&& wger load-online-fixtures \

View File

@@ -79,6 +79,7 @@ ARG BUILD_DATE
ENV PATH="/home/wger/.local/bin:$PATH"
ENV APP_BUILD_COMMIT=$BUILD_COMMIT
ENV APP_BUILD_DATE=$BUILD_DATE
ENV DJANGO_SETTINGS_MODULE='settings.main'
WORKDIR /home/wger/src
EXPOSE 8000
@@ -88,8 +89,6 @@ COPY --chown=wger:wger . /home/wger/src
COPY --chown=wger:wger --from=builder /root/src/node_modules /home/wger/src/node_modules
COPY --chown=wger:wger --from=builder /root/src/wger/core/static/bootstrap-compiled.css /home/wger/src/wger/core/static/bootstrap-compiled.css
COPY --chown=wger:wger --from=builder /root/src/wger/core/static/bootstrap-compiled.css.map /home/wger/src/wger/core/static/bootstrap-compiled.css.map
COPY ${DOCKER_DIR}/settings.py /home/wger/src
COPY ${DOCKER_DIR}/settings.py /tmp/
COPY ${DOCKER_DIR}/entrypoint.sh /home/wger/entrypoint.sh
COPY ${DOCKER_DIR}/celery/start-beat /start-beat
COPY ${DOCKER_DIR}/celery/start-worker /start-worker

View File

@@ -1,24 +1,12 @@
#!/usr/bin/env python3
# Standard Library
import os
import sys
# Django
from django.core.management import execute_from_command_line
# wger
from wger.tasks import (
get_path,
setup_django_environment,
)
if __name__ == '__main__':
# If user passed the settings flag ignore the default wger settings
if not any('--settings' in s for s in sys.argv):
setup_django_environment(get_path('settings.py'))
# Alternative to above
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.main")
execute_from_command_line(sys.argv)

127
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "wger",
"version": "2.4.alpha1",
"version": "2.4.alpha3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wger",
"version": "2.4.alpha1",
"version": "2.4.alpha3",
"license": "AGPL-3.0",
"dependencies": {
"@popperjs/core": "^2.11.8",
@@ -52,7 +52,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -385,7 +384,6 @@
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -432,7 +430,6 @@
"integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -495,6 +492,7 @@
"os": [
"aix"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -512,6 +510,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -529,6 +528,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -546,6 +546,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -563,6 +564,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -580,6 +582,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -597,6 +600,7 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -614,6 +618,7 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -631,6 +636,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -648,6 +654,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -665,6 +672,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -682,6 +690,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -699,6 +708,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -716,6 +726,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -733,6 +744,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -750,6 +762,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -767,6 +780,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -784,6 +798,7 @@
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -801,6 +816,7 @@
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -818,6 +834,7 @@
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -835,6 +852,7 @@
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -852,6 +870,7 @@
"os": [
"openharmony"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -869,6 +888,7 @@
"os": [
"sunos"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -886,6 +906,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -903,6 +924,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -920,6 +942,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -1081,7 +1104,6 @@
"integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/core-downloads-tracker": "^7.3.6",
@@ -1195,7 +1217,6 @@
"integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/private-theming": "^7.3.6",
@@ -1437,7 +1458,6 @@
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@@ -1500,7 +1520,8 @@
"optional": true,
"os": [
"android"
]
],
"peer": true
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.53.3",
@@ -1514,7 +1535,8 @@
"optional": true,
"os": [
"android"
]
],
"peer": true
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.53.3",
@@ -1528,7 +1550,8 @@
"optional": true,
"os": [
"darwin"
]
],
"peer": true
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.53.3",
@@ -1542,7 +1565,8 @@
"optional": true,
"os": [
"darwin"
]
],
"peer": true
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.53.3",
@@ -1556,7 +1580,8 @@
"optional": true,
"os": [
"freebsd"
]
],
"peer": true
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.53.3",
@@ -1570,7 +1595,8 @@
"optional": true,
"os": [
"freebsd"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.53.3",
@@ -1584,7 +1610,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.53.3",
@@ -1598,7 +1625,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.53.3",
@@ -1612,7 +1640,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.53.3",
@@ -1626,7 +1655,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.53.3",
@@ -1640,7 +1670,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.53.3",
@@ -1654,7 +1685,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.53.3",
@@ -1668,7 +1700,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.53.3",
@@ -1682,7 +1715,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.53.3",
@@ -1696,7 +1730,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.53.3",
@@ -1710,7 +1745,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.53.3",
@@ -1724,7 +1760,8 @@
"optional": true,
"os": [
"linux"
]
],
"peer": true
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.53.3",
@@ -1738,7 +1775,8 @@
"optional": true,
"os": [
"openharmony"
]
],
"peer": true
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.53.3",
@@ -1752,7 +1790,8 @@
"optional": true,
"os": [
"win32"
]
],
"peer": true
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.53.3",
@@ -1766,7 +1805,8 @@
"optional": true,
"os": [
"win32"
]
],
"peer": true
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.53.3",
@@ -1780,7 +1820,8 @@
"optional": true,
"os": [
"win32"
]
],
"peer": true
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.53.3",
@@ -1794,7 +1835,8 @@
"optional": true,
"os": [
"win32"
]
],
"peer": true
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
@@ -1960,7 +2002,8 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.7",
@@ -2163,7 +2206,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2649,6 +2691,7 @@
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"peer": true,
"bin": {
"esbuild": "bin/esbuild"
},
@@ -2733,6 +2776,7 @@
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12.0.0"
},
@@ -2836,6 +2880,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
@@ -3037,7 +3082,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4"
},
@@ -3135,8 +3179,7 @@
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/js-tokens": {
"version": "4.0.0",
@@ -3228,7 +3271,6 @@
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
}
@@ -3305,6 +3347,7 @@
}
],
"license": "MIT",
"peer": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -3451,6 +3494,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -3516,7 +3560,6 @@
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -3598,8 +3641,7 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.1.tgz",
"integrity": "sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/react-redux": {
"version": "9.2.0",
@@ -3771,8 +3813,7 @@
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"dev": true,
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
@@ -3835,6 +3876,7 @@
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -3928,6 +3970,7 @@
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -3979,6 +4022,7 @@
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
@@ -4051,7 +4095,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"

View File

@@ -1,6 +1,6 @@
{
"name": "wger",
"version": "2.4.alpha1",
"version": "2.4.alpha3",
"description": "Self hosted FLOSS fitness/workout and weight tracker",
"repository": "github:wger-project/wger",
"author": "wger team <hello@wger.de>",

View File

@@ -165,7 +165,7 @@ line-ending = "auto"
src_paths = ["wger", "extras"]
sections = ["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
skip = ["extras", "build", "dist", "node_modules", "migrations", "docs", "settings.py", "apps.py"]
skip = ["extras", "build", "dist", "node_modules", "migrations", "docs", "settings", "apps.py"]
# If set to true - ensures that if a star import is present, nothing else is
# imported from that namespace.
combine_star = false

15
settings/README.md Normal file
View File

@@ -0,0 +1,15 @@
# Settings
This directory contains configuration files and settings for the project.
You can add your own configuration files here, e.g. for development. Set
the `DJANGO_SETTINGS_MODULE` environment variable to point to the new settings
file. E.g.:
```bash
export DJANGO_SETTINGS_MODULE=settings.local_dev
python manage.py runserver
```
If you want to add settings that are not tracked by git, you can create a
`local_dev_extra.py` file, this will be imported by `local_dev.py` if it exists.

0
settings/__init__.py Normal file
View File

View File

@@ -1,9 +1,26 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# wger Workout Manager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# ruff: noqa: F405
# Third Party
import environ
# wger
from wger.settings_global import *
from .settings_global import * # noqa: F403
env = environ.Env()
# Use 'DEBUG = True' to get more details for server errors
DEBUG = True
@@ -16,16 +33,16 @@ WGER_SETTINGS["ALLOW_UPLOAD_VIDEOS"] = False
WGER_SETTINGS["MIN_ACCOUNT_AGE_TO_TRUST"] = 21 # in days
WGER_SETTINGS["EXERCISE_CACHE_TTL"] = 3600 # in seconds
DATABASES = {{
'default': {{
'ENGINE': 'django.db.backends.{dbengine}',
'NAME': '{dbname}',
'USER': '{dbuser}',
'PASSWORD': '{dbpassword}',
'HOST': '{dbhost}',
'PORT': '{dbport}',
}}
}} # yapf: disable
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/Users/roland/Entwicklung/wger/server/database.sqlite',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
}
} # yapf: disable
# List of administrations
ADMINS = (('Your name', 'your_email@example.com'),)
@@ -39,7 +56,7 @@ MANAGERS = ADMINS
TIME_ZONE = 'Europe/Berlin'
# Make this unique, and don't share it with anybody.
SECRET_KEY = '{default_key}'
SECRET_KEY = '61fxc$k%9nj!be-_up9%xzm(z)9l7$h33b1!@bf9581=c-03%p'
# Your reCaptcha keys
RECAPTCHA_PUBLIC_KEY = ''
@@ -49,14 +66,14 @@ USE_RECAPTCHA = False
# The site's URL (e.g. http://www.my-local-gym.com or http://localhost:8000)
# This is needed for uploaded files and images (exercise images, etc.) to be
# properly served.
SITE_URL = '{siteurl}'
SITE_URL = 'http://localhost:8000'
# Path to uploaded files
# Absolute filesystem path to the directory that will hold user-uploaded files.
MEDIA_ROOT = '{media_folder_path}'
MEDIA_ROOT = env.str("DJANGO_MEDIA_ROOT", '/tmp/')
MEDIA_URL = '/media/'
# Allow all hosts to access the application. Change if used in production.
# Allow all hosts to access the application.
ALLOWED_HOSTS = [
'*',
]
@@ -78,23 +95,3 @@ EMAIL_PAGE_DOMAIN = SITE_URL
# https://django-axes.readthedocs.io/en/latest/
#
AXES_ENABLED = False
# AXES_FAILURE_LIMIT = 10
# AXES_COOLOFF_TIME = timedelta(minutes=30)
# AXES_HANDLER = 'axes.handlers.cache.AxesCacheHandler'
#
# Sometimes needed if deployed behind a proxy with HTTPS enabled:
# https://docs.djangoproject.com/en/4.1/ref/csrf/
#
# CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1', 'https://my.domain.example.com']
# Alternative to above, needs changes to the reverse proxy's config
# https://docs.djangoproject.com/en/4.1/ref/settings/#secure-proxy-ssl-header
#
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO, 'https')
#
# Celery
# Needed if you plan to use celery for background tasks
# CELERY_BROKER_URL = "redis://localhost:6379/2"
# CELERY_RESULT_BACKEND = "redis://localhost:6379/2"

124
settings/local_dev.py Normal file
View File

@@ -0,0 +1,124 @@
"""Local development settings for wger"""
# ruff: noqa: F405
# wger
from .settings_global import * # noqa: F403
DEBUG = True
# List of administrators
ADMINS = (('Your name', 'your_email@example.com'),)
MANAGERS = ADMINS
# Don't use this key in production!
SECRET_KEY = 'wger-local-development-supersecret-key-1234567890!'
# Allow all hosts to access the application.
ALLOWED_HOSTS = ['*', ]
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# WGER application
WGER_SETTINGS['ALLOW_UPLOAD_VIDEOS'] = True
WGER_SETTINGS['ALLOW_GUEST_USERS'] = True
WGER_SETTINGS['ALLOW_REGISTRATION'] = True
WGER_SETTINGS['DOWNLOAD_INGREDIENTS_FROM'] = 'WGER' # or 'None' to disable
WGER_SETTINGS['EMAIL_FROM'] = 'wger Workout Manager <wger@example.com>'
WGER_SETTINGS['EXERCISE_CACHE_TTL'] = 500
WGER_SETTINGS['INGREDIENT_CACHE_TTL'] = 500
WGER_SETTINGS['SYNC_EXERCISES_CELERY'] = False
WGER_SETTINGS['SYNC_EXERCISE_IMAGES_CELERY'] = True
WGER_SETTINGS['SYNC_EXERCISE_VIDEOS_CELERY'] = False
WGER_SETTINGS['SYNC_INGREDIENTS_CELERY'] = True
WGER_SETTINGS['USE_CELERY'] = False
WGER_SETTINGS['CACHE_API_EXERCISES_CELERY'] = True
WGER_SETTINGS['CACHE_API_EXERCISES_CELERY_FORCE_UPDATE'] = True
WGER_SETTINGS['ROUTINE_CACHE_TTL'] = 500
DEFAULT_FROM_EMAIL = WGER_SETTINGS['EMAIL_FROM']
# CELERY_BROKER_URL = "redis://localhost:6379/2"
# CELERY_RESULT_BACKEND = "redis://localhost:6379/2"
CSRF_TRUSTED_ORIGINS = ['http://localhost:8000', 'http://127.0.0.1:8000']
EXPOSE_PROMETHEUS_METRICS = True
COMPRESS_ENABLED = False
AXES_ENABLED = False
# Does not really cache anything
CACHES_DUMMY = {
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
'TIMEOUT': 100,
}
}
# In-memory cache, resets when the server restarts
CACHE_LOCMEM = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'wger-cache',
'TIMEOUT': 100,
}
}
# Redis cache
CACHE_REDIS = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://localhost:6379/1',
'TIMEOUT': 5000,
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient'
}
}
}
# CACHES = CACHE_REDIS
# CACHES = CACHE_LOCMEM
CACHES = CACHES_DUMMY
# Django Debug Toolbar
# INSTALLED_APPS += ['django_extensions', 'debug_toolbar']
# MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware", ]
# INTERNAL_IPS = ["127.0.0.1", ]
# AUTH_PROXY_HEADER = 'HTTP_X_REMOTE_USER'
# AUTH_PROXY_USER_EMAIL_HEADER = 'HTTP_X_REMOTE_USER_EMAIL'
# AUTH_PROXY_USER_NAME_HEADER = 'HTTP_X_REMOTE_USER_NAME'
# AUTH_PROXY_TRUSTED_IPS = ['127.0.0.1', ]
# AUTH_PROXY_CREATE_UNKNOWN_USER = True
DBCONFIG_PG = {
'ENGINE': 'django_prometheus.db.backends.postgresql',
'NAME': 'wger',
'USER': 'wger',
'PASSWORD': 'wger',
'HOST': 'localhost',
'PORT': '5432',
}
DBCONFIG_SQLITE = {
'ENGINE': 'django_prometheus.db.backends.sqlite3',
'NAME': BASE_DIR.parent / 'db' / 'database.sqlite',
}
DATABASES = {
# 'default': DBCONFIG_PG,
'default': DBCONFIG_SQLITE,
}
# Import other local settings that are not in version control
try:
from .local_dev_extra import * # noqa: F403
except ImportError:
pass

247
settings/main.py Normal file
View File

@@ -0,0 +1,247 @@
# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# wger Workout Manager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# ruff: noqa: F405
# Third Party
import environ
# wger
from .settings_global import * # noqa: F403
"""Main settings file for a production deployment of wger."""
env = environ.Env(
# set casting, default value
DJANGO_DEBUG=(bool, False)
)
# Use 'DEBUG = True' to get more details for server errors
DEBUG = env("DJANGO_DEBUG")
if os.environ.get('DJANGO_ADMINS'):
ADMINS = [env.tuple('DJANGO_ADMINS'), ]
MANAGERS = ADMINS
if os.environ.get("DJANGO_DB_ENGINE"):
DATABASES = {
'default': {
'ENGINE': env.str("DJANGO_DB_ENGINE"),
'NAME': env.str("DJANGO_DB_DATABASE"),
'USER': env.str("DJANGO_DB_USER"),
'PASSWORD': env.str("DJANGO_DB_PASSWORD"),
'HOST': env.str("DJANGO_DB_HOST"),
'PORT': env.int("DJANGO_DB_PORT"),
}
}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': env.str('DJANGO_DB_DATABASE', '/home/wger/db/database.sqlite'),
}
}
# Timezone for this installation. Consult settings_global.py for more information
TIME_ZONE = env.str("TIME_ZONE", 'Europe/Berlin')
# Make this unique, and don't share it with anybody.
# Generate e.g. with: python -c "import secrets; print(secrets.token_urlsafe(50))" or https://djecrety.ir/
SECRET_KEY = env.str("SECRET_KEY", 'wger-docker-supersecret-key-1234567890!@#$%^&*(-_)')
# Your reCaptcha keys
RECAPTCHA_PUBLIC_KEY = env.str('RECAPTCHA_PUBLIC_KEY', '')
RECAPTCHA_PRIVATE_KEY = env.str('RECAPTCHA_PRIVATE_KEY', '')
# The site's URL (e.g. http://www.my-local-gym.com or http://localhost:8000)
# This is needed for uploaded files and images (exercise images, etc.) to be
# properly served.
SITE_URL = env.str('SITE_URL', 'http://localhost:8000')
# Path to uploaded files
# Absolute filesystem path to the directory that will hold user-uploaded files.
MEDIA_ROOT = env.str("DJANGO_MEDIA_ROOT", '/home/wger/media')
STATIC_ROOT = env.str("DJANGO_STATIC_ROOT", '/home/wger/static')
# If you change these, adjust nginx alias definitions as well
MEDIA_URL = env.str('MEDIA_URL', '/media/')
STATIC_URL = env.str('STATIC_URL', '/static/')
LOGIN_REDIRECT_URL = env.str('LOGIN_REDIRECT_URL', '/')
# Allow all hosts to access the application. Change if used in production.
ALLOWED_HOSTS = ['*', ]
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# Configure a real backend in production
if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
if env.bool("ENABLE_EMAIL", False):
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = env.str("EMAIL_HOST")
EMAIL_PORT = env.int("EMAIL_PORT")
EMAIL_HOST_USER = env.str("EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD")
EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True)
EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False)
EMAIL_TIMEOUT = 60
# Sender address used for sent emails
DEFAULT_FROM_EMAIL = env.str("FROM_EMAIL", "wger Workout Manager <wger@example.com>")
WGER_SETTINGS['EMAIL_FROM'] = DEFAULT_FROM_EMAIL
SERVER_EMAIL = DEFAULT_FROM_EMAIL
EMAIL_FROM_ADDRESS = DEFAULT_FROM_EMAIL
# Management
WGER_SETTINGS["ALLOW_GUEST_USERS"] = env.bool("ALLOW_GUEST_USERS", True)
WGER_SETTINGS["ALLOW_REGISTRATION"] = env.bool("ALLOW_REGISTRATION", True)
WGER_SETTINGS["ALLOW_UPLOAD_VIDEOS"] = env.bool("ALLOW_UPLOAD_VIDEOS", True)
WGER_SETTINGS["DOWNLOAD_INGREDIENTS_FROM"] = env.str("DOWNLOAD_INGREDIENTS_FROM", "WGER")
WGER_SETTINGS["EXERCISE_CACHE_TTL"] = env.int("EXERCISE_CACHE_TTL", 3600)
WGER_SETTINGS["MIN_ACCOUNT_AGE_TO_TRUST"] = env.int("MIN_ACCOUNT_AGE_TO_TRUST", 21) # in days
WGER_SETTINGS["SYNC_EXERCISES_CELERY"] = env.bool("SYNC_EXERCISES_CELERY", False)
WGER_SETTINGS["SYNC_EXERCISE_IMAGES_CELERY"] = env.bool("SYNC_EXERCISE_IMAGES_CELERY", False)
WGER_SETTINGS["SYNC_EXERCISE_VIDEOS_CELERY"] = env.bool("SYNC_EXERCISE_VIDEOS_CELERY", False)
WGER_SETTINGS["SYNC_INGREDIENTS_CELERY"] = env.bool("SYNC_INGREDIENTS_CELERY", False)
WGER_SETTINGS["SYNC_OFF_DAILY_DELTA_CELERY"] = env.bool("SYNC_OFF_DAILY_DELTA_CELERY", False)
WGER_SETTINGS["USE_RECAPTCHA"] = env.bool("USE_RECAPTCHA", False)
WGER_SETTINGS["USE_CELERY"] = env.bool("USE_CELERY", False)
WGER_SETTINGS["CACHE_API_EXERCISES_CELERY"] = env.bool("CACHE_API_EXERCISES_CELERY", False)
WGER_SETTINGS["CACHE_API_EXERCISES_CELERY_FORCE_UPDATE"] = env.bool("CACHE_API_EXERCISES_CELERY_FORCE_UPDATE", False)
#
# Auth Proxy Authentication
# https://wger.readthedocs.io/en/latest/administration/auth_proxy.html
AUTH_PROXY_HEADER = env.str("AUTH_PROXY_HEADER", '')
AUTH_PROXY_TRUSTED_IPS = env.list("AUTH_PROXY_TRUSTED_IPS", default=[])
AUTH_PROXY_CREATE_UNKNOWN_USER = env.bool("AUTH_PROXY_CREATE_UNKNOWN_USER", False)
AUTH_PROXY_USER_EMAIL_HEADER = env.str("AUTH_PROXY_USER_EMAIL_HEADER", '')
AUTH_PROXY_USER_NAME_HEADER = env.str("AUTH_PROXY_USER_NAME_HEADER", '')
# Cache
if os.environ.get("DJANGO_CACHE_BACKEND"):
CACHES = {
'default': {
'BACKEND': env.str("DJANGO_CACHE_BACKEND"),
'LOCATION': env.str("DJANGO_CACHE_LOCATION"),
'TIMEOUT': env.int("DJANGO_CACHE_TIMEOUT"),
'OPTIONS': {
'CLIENT_CLASS': env.str("DJANGO_CACHE_CLIENT_CLASS")
}
}
}
if os.environ.get('DJANGO_CACHE_CLIENT_PASSWORD'):
CACHES['default']['OPTIONS']['PASSWORD'] = env.str('DJANGO_CACHE_CLIENT_PASSWORD')
CONNECTION_POOL_KWARGS = dict()
if "DJANGO_CACHE_CLIENT_SSL_KEYFILE" in os.environ:
CONNECTION_POOL_KWARGS['ssl_keyfile'] = env.str("DJANGO_CACHE_CLIENT_SSL_KEYFILE")
if "DJANGO_CACHE_CLIENT_SSL_CERTFILE" in os.environ:
CONNECTION_POOL_KWARGS['ssl_certfile'] = env.str("DJANGO_CACHE_CLIENT_SSL_CERTFILE")
if "DJANGO_CACHE_CLIENT_SSL_CERT_REQS" in os.environ:
CONNECTION_POOL_KWARGS['ssl_cert_reqs'] = env.str("DJANGO_CACHE_CLIENT_SSL_CERT_REQS")
if "DJANGO_CACHE_CLIENT_SSL_CHECK_HOSTNAME" in os.environ:
CONNECTION_POOL_KWARGS['ssl_check_hostname'] = env.bool(
"DJANGO_CACHE_CLIENT_SSL_CHECK_HOSTNAME")
if CONNECTION_POOL_KWARGS:
CACHES["default"]["OPTIONS"]["CONNECTION_POOL_KWARGS"] = CONNECTION_POOL_KWARGS
# Folder for compressed CSS and JS files
COMPRESS_ROOT = STATIC_ROOT
COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', not DEBUG)
# The site's domain as used by the email verification workflow
EMAIL_PAGE_DOMAIN = SITE_URL
#
# Django Axes
#
AXES_ENABLED = env.bool('AXES_ENABLED', True)
AXES_LOCKOUT_PARAMETERS = env.list('AXES_LOCKOUT_PARAMETERS', default=['ip_address'])
AXES_FAILURE_LIMIT = env.int('AXES_FAILURE_LIMIT', 10)
AXES_COOLOFF_TIME = timedelta(minutes=env.float('AXES_COOLOFF_TIME', 30))
AXES_HANDLER = env.str('AXES_HANDLER', 'axes.handlers.cache.AxesCacheHandler')
AXES_IPWARE_PROXY_COUNT = env.int('AXES_IPWARE_PROXY_COUNT', 0)
AXES_IPWARE_META_PRECEDENCE_ORDER = env.list('AXES_IPWARE_META_PRECEDENCE_ORDER',
default=['REMOTE_ADDR'])
#
# Django Rest Framework SimpleJWT
#
SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'] = timedelta(minutes=env.int("ACCESS_TOKEN_LIFETIME", 15))
SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'] = timedelta(hours=env.int("REFRESH_TOKEN_LIFETIME", 24))
SIMPLE_JWT['SIGNING_KEY'] = env.str("SIGNING_KEY", SECRET_KEY)
#
# https://docs.djangoproject.com/en/4.1/ref/csrf/
#
CSRF_TRUSTED_ORIGINS = env.list(
"CSRF_TRUSTED_ORIGINS",
default=['http://127.0.0.1', 'http://localhost', 'https://localhost'],
)
if env.bool('X_FORWARDED_PROTO_HEADER_SET', False):
SECURE_PROXY_SSL_HEADER = (
env.str('SECURE_PROXY_SSL_HEADER', 'HTTP_X_FORWARDED_PROTO'),
'https'
)
REST_FRAMEWORK['NUM_PROXIES'] = env.int('NUMBER_OF_PROXIES', 1)
#
# Celery message queue configuration
#
CELERY_BROKER_URL = env.str("CELERY_BROKER", "redis://cache:6379/2")
CELERY_RESULT_BACKEND = env.str("CELERY_BACKEND", "redis://cache:6379/2")
#
# Prometheus metrics
#
EXPOSE_PROMETHEUS_METRICS = env.bool('EXPOSE_PROMETHEUS_METRICS', False)
PROMETHEUS_URL_PATH = env.str('PROMETHEUS_URL_PATH', 'super-secret-path')
#
# Logging
#
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': 'level={levelname} ts={asctime} module={module} path={pathname} line={lineno} message={message}',
'style': '{',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
},
'loggers': {
'': {
'handlers': ['console'],
'level': env.str('LOG_LEVEL_PYTHON', 'INFO').upper(),
'propagate': True,
},
}
}

View File

@@ -18,20 +18,27 @@ import os
import re
import sys
from datetime import timedelta
from pathlib import Path
# wger
from wger.utils.constants import DOWNLOAD_INGREDIENT_WGER
from wger.version import get_version
"""
This file contains the global settings that don't usually need to be changed.
For a full list of options, visit:
https://docs.djangoproject.com/en/dev/ref/settings/
"""
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
BASE_DIR = Path(__file__).resolve().parent.parent / 'wger'
SITE_ROOT = Path(__file__).resolve().parent.parent / 'wger'
# Static and media files (only during development)
MEDIA_ROOT = BASE_DIR / 'media'
STATIC_ROOT = BASE_DIR / 'static'
MEDIA_URL = '/media/'
STATIC_URL = '/static/'
#
# Application definition
@@ -40,6 +47,7 @@ SITE_ID = 1
ROOT_URLCONF = 'wger.urls'
WSGI_APPLICATION = 'wger.wsgi.application'
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
@@ -437,7 +445,7 @@ else:
STATIC_URL = '/static/'
#
# Django compressor
# Django compressor for CSS and JS files
#
# The default is not DEBUG, override if needed
@@ -620,3 +628,8 @@ ACTSTREAM_SETTINGS = {
# Whether the application is being run regularly or during tests
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
# Your reCaptcha keys
RECAPTCHA_PUBLIC_KEY = ''
RECAPTCHA_PRIVATE_KEY = ''
NOCAPTCHA = True

View File

@@ -21,7 +21,7 @@ import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main')
app = Celery('wger')
# read config from Django settings, the CELERY namespace would make celery

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
@@ -17,7 +15,6 @@
# Standard Library
import logging
import os
import pathlib
import sys
import tempfile
@@ -25,9 +22,9 @@ import tempfile
import django
from django.core.management import (
call_command,
color_style,
execute_from_command_line,
)
from django.utils.crypto import get_random_string
# Third Party
import requests
@@ -42,6 +39,8 @@ from tqdm import tqdm
logger = logging.getLogger(__name__)
FIXTURE_URL = 'https://github.com/wger-project/data/raw/master/fixtures/'
style = color_style()
@task(
help={
@@ -56,8 +55,6 @@ def start(context, address='localhost', port=8000, settings_path=None, extra_arg
"""
Start the application using django's built in webserver
"""
# Find the path to the settings and setup the django environment
setup_django_environment(settings_path)
argv = ['', 'runserver', '--noreload']
@@ -70,22 +67,14 @@ def start(context, address='localhost', port=8000, settings_path=None, extra_arg
@task(
help={
'settings-path': 'Path to settings file (absolute path). Leave empty for default',
'database-path': 'Path to sqlite database (absolute path). Leave empty for default',
'settings-path': 'Path to settings file. Leave empty for default (settings.main)',
'process-static': 'Whether to process static files (install npm packages and process css). Default: True',
}
)
def bootstrap(context, settings_path=None, database_path=None, process_static=True):
def bootstrap(context, settings_path=None, process_static=True):
"""
Performs all steps necessary to bootstrap the application
"""
# Create settings if necessary
if settings_path is None:
settings_path = get_path('settings.py')
if not os.path.exists(settings_path):
create_settings(context, settings_path=settings_path, database_path=database_path)
# Find the path to the settings and setup the django environment
setup_django_environment(settings_path)
# Create Database if necessary
@@ -101,93 +90,11 @@ def bootstrap(context, settings_path=None, database_path=None, process_static=Tr
context.run('npm run build:css:sass')
@task(
help={
'settings-path': 'Path to settings file (absolute path). Leave empty for default',
'database-path': 'Path to sqlite database (absolute path). Leave empty for default',
'database-type': 'Database type to use. Supported: sqlite3, postgresql. Default: sqlite3',
'key-length': 'Length of the generated secret key. Default: 50',
}
)
def create_settings(
context,
settings_path=None,
database_path=None,
database_type='sqlite3',
key_length=50,
):
"""
Creates a local settings file
"""
if settings_path is None:
settings_path = get_path('settings.py')
settings_module = os.path.dirname(settings_path)
print(f'*** Creating settings file at {settings_module}')
if database_path is None:
database_path = get_path('database.sqlite').as_posix()
dbpath_value = database_path
media_folder_path = get_path('media').as_posix()
# Use localhost with default django port if no URL given
url = 'http://localhost:8000'
# Fill in the config file template
settings_template = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'settings.tpl')
with open(settings_template, 'r') as settings_file:
settings_content = settings_file.read()
if database_type == 'postgresql':
dbengine = 'postgresql'
dbname = 'wger'
dbuser = 'wger'
dbpassword = 'wger'
dbhost = 'localhost'
dbport = 5432
elif database_type == 'sqlite3':
dbengine = 'sqlite3'
dbname = dbpath_value
dbuser = ''
dbpassword = ''
dbhost = ''
dbport = ''
# Create a random SECRET_KEY to put it in the settings.
# from django.core.management.commands.startproject
secret_key = get_random_string(key_length, 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')
settings_content = settings_content.format(
dbname=dbname,
dbpath=dbpath_value,
dbengine=dbengine,
dbuser=dbuser,
dbpassword=dbpassword,
dbhost=dbhost,
dbport=dbport,
default_key=secret_key,
siteurl=url,
media_folder_path=media_folder_path,
)
if not os.path.exists(settings_module):
os.makedirs(settings_module)
if not os.path.exists(os.path.dirname(database_path)):
os.makedirs(os.path.dirname(database_path))
with open(settings_path, 'w') as settings_file:
settings_file.write(settings_content)
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
def create_or_reset_admin(context, settings_path=None):
def create_or_reset_admin(context, settings_path: str = None):
"""
Creates an admin user or resets the password for an existing one
"""
# Find the path to the settings and setup the django environment
setup_django_environment(settings_path)
# can't be imported in global scope as it already requires
@@ -207,25 +114,21 @@ def create_or_reset_admin(context, settings_path=None):
call_command('loaddata', path + 'users.json')
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
@task(help={'settings-path': 'Path to settings file. Leave empty for default (settings.main)'})
def migrate_db(context, settings_path=None):
"""
Run all database migrations
"""
# Find the path to the settings and setup the django environment
setup_django_environment(settings_path)
call_command('migrate')
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
def load_fixtures(context, settings_path=None):
@task(help={'settings-path': 'Path to settings file. Leave empty for default (settings.main)'})
def load_fixtures(context, settings_path: str = None):
"""
Loads all fixtures
"""
# Find the path to the settings and setup the django environment
setup_django_environment(settings_path)
# Gym
@@ -257,13 +160,11 @@ def load_fixtures(context, settings_path=None):
call_command('loaddata', 'gym-adminconfig.json')
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
def load_online_fixtures(context, settings_path=None):
@task(help={'settings-path': 'Path to settings file. Leave empty for default (settings.main)'})
def load_online_fixtures(context, settings_path: str = None):
"""
Downloads fixtures from server and installs them (at the moment only ingredients)
"""
# Find the path to the settings and set up the django environment
setup_django_environment(settings_path)
# Prepare the download
@@ -291,57 +192,28 @@ def load_online_fixtures(context, settings_path=None):
os.unlink(f.name)
@task
def config_location(context):
"""
Returns the default location for the settings file and the data folder
"""
print('Default locations:')
print(f'* settings: {get_path("settings.py")}')
print(f'* media folder: {get_path("media")}')
print(f'* database path: {get_path("database.sqlite")}')
#
#
# Helper functions
#
# Note: these functions were originally in wger/utils/main.py but were moved
# here because of different import problems (the packaged pip-installed
# packaged has a different sys path than the local one)
#
def get_path(file='settings.py') -> pathlib.Path:
"""
Return the path of the given file relatively to the wger source folder
Note: one parent is the step from e.g. some-checkout/wger/settings.py
to some-checkout/wger, the second one to get to the source folder
itself.
"""
return (pathlib.Path(__file__).parent.parent / file).resolve()
def setup_django_environment(settings_path):
def setup_django_environment(settings_path: str = None):
"""
Setup the django environment
"""
if settings_path is not None:
print(f'*** Using settings from argument: {settings_path}')
os.environ['DJANGO_SETTINGS_MODULE'] = settings_path
elif os.environ.get('DJANGO_SETTINGS_MODULE') is not None:
print(f'*** Using settings from env: {os.environ.get("DJANGO_SETTINGS_MODULE")}')
else:
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings.main'
print('*** No settings given, using settings.main')
# Use default settings if the user didn't specify something else
if settings_path is None:
settings_path = get_path('settings.py').as_posix()
print(f'*** No settings given, using {settings_path}')
# Find out file path and fine name of settings and setup django
settings_file = os.path.basename(settings_path)
settings_module_name = ''.join(settings_file.split('.')[:-1])
if '.' in settings_module_name:
print("'.' is not an allowed character in the settings-file")
# Check if we are in the wger source folder
if not os.path.isfile('manage.py'):
print(style.ERROR('Error: please run this script from the wger checkout folder'))
sys.exit(1)
settings_module_dir = os.path.dirname(settings_path)
sys.path.append(settings_module_dir)
os.environ[django.conf.ENVIRONMENT_VARIABLE] = '%s' % settings_module_name
django.setup()
@@ -356,12 +228,11 @@ def database_exists():
from django.db import DatabaseError
try:
# TODO: Use another model, the User could be deactivated
User.objects.count()
except DatabaseError:
return False
except ImproperlyConfigured:
print('Your settings file seems broken')
except ImproperlyConfigured as e:
print(style.ERROR('Your settings file seems broken: '), e)
sys.exit(0)
else:
return True
@@ -369,9 +240,7 @@ def database_exists():
def make_program():
ns = Collection(
start,
bootstrap,
create_settings,
create_or_reset_admin,
migrate_db,
load_fixtures,

View File

@@ -35,7 +35,7 @@ Always use versions in the x.y.z format, without any suffixes like "beta1" or su
MIN_SERVER_VERSION = Version('2.4.0-alpha2')
"""Minimum version of the server required to run sync commands on this server"""
VERSION = Version('2.4.0-alpha2')
VERSION = Version('2.4.0-alpha3')
"""Current version of the app"""

View File

@@ -21,7 +21,7 @@ import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main')
# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION