diff --git a/.editorconfig b/.editorconfig index 8ac421b7..658dda71 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,7 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true -[*.{py,rst,ini}] +[*.{py,rst,ini,sh}] indent_style = space indent_size = 4 diff --git a/.gitignore b/.gitignore index 1f9e5551..3fa6450e 100644 --- a/.gitignore +++ b/.gitignore @@ -283,3 +283,5 @@ cypress/screenshots cypress/test-reports /server/vbv_lernwelt/static/css/tailwind.css +/server/vbv_lernwelt/static/vue/ +/server/vbv_lernwelt/templates/vue/index.html diff --git a/README.md b/README.md index 8a1be72e..9b1affa6 100644 --- a/README.md +++ b/README.md @@ -11,38 +11,38 @@ npm run tailwind # run vue vite dev server cd client && npm run dev -# run django dev server -cd server && python manage.py runserver +# reset db and run django dev server +./prepare_server.sh ``` ## Installation See `.tool-versions` file for used django and node version +You have to set up at least the following environment variables: + +```bash +export IT_APP_ENVIRONMENT=development +``` + +See `.env_secrets/local_daniel.env` for more possible environment variables. +Especially set correct values for `POSTGRES_*` and `DATABASE_URL` + + ### Server part -Run every sub command in the `server` directory - -Create a new PostgreSQL database and role +Install python dependencies: ```bash -createdb vbv_lernwelt -createuser vbv_lernwelt +pip install -r server/requirements/requirements-dev.txt ``` -Set the environment variable accordingly +The "prepare_server.sh" script will create the database according to `POSTGRES_*` environment variables. +It will also setup the tables for django and run the django development server. ```bash -export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt' -``` - -Set `VBV_DJANGO_READ_DOT_ENV_FILE=True` to make the config read the `example.env` file (with direnv!?). - -```bash -python manage.py migrate - -# sync server -python manage.py runserver +# will initial`migrate` and `runserver` etc... +./prepare_server.sh # or async server # uvicorn config.asgi:application --host 0.0.0.0 --reload @@ -50,9 +50,9 @@ python manage.py runserver ### Client part -Run every command in the `client` directory - ```bash +cd client + npm install # run dev server @@ -64,6 +64,7 @@ npm run dev Cypress and TailwindCSS ist installed for client and server, so there is this package.json on the project root directory ```bash +# in project root directory npm install ``` @@ -90,8 +91,6 @@ npm install #### Install the tailwind css Plugin from Jetbrains - - ## Wagtail API intro get all pages: diff --git a/caprover_create_app.py b/caprover_create_app.py index 44cde962..2306907f 100644 --- a/caprover_create_app.py +++ b/caprover_create_app.py @@ -28,7 +28,7 @@ cap.deploy_one_click_app( namespace='vbv-lernwelt', # check https://github.com/caprover/one-click-apps/blob/master/public/v4/apps/postgres.yml app_variables={ - '$$cap_postgres_version': '14.1', + '$$cap_postgres_version': '14.2', '$$cap_pg_user': db_user, '$$cap_pg_pass': db_pass, '$$cap_pg_db': db_name, @@ -44,11 +44,11 @@ cap.create_and_update_app( image_name='docker.io/iterativ/vbv-lernwelt-django', environment_variables={ # 'DJANGO_SETTINGS_MODULE': 'config.settings.base', - 'VBV_DJANGO_SECRET_KEY': env.str('VBV_DJANGO_SECRET_KEY'), - 'VBV_DJANGO_ADMIN_URL': env.str('VBV_DJANGO_ADMIN_URL'), - 'VBV_DJANGO_ALLOWED_HOSTS': env.str('VBV_DJANGO_ALLOWED_HOSTS'), - 'VBV_SENTRY_DSN': env.str('VBV_SENTRY_DSN'), - 'VBV_DJANGO_DEV_MODE': 'caprover', + 'IT_DJANGO_SECRET_KEY': env.str('IT_DJANGO_SECRET_KEY'), + 'IT_DJANGO_ADMIN_URL': env.str('IT_DJANGO_ADMIN_URL'), + 'IT_DJANGO_ALLOWED_HOSTS': env.str('IT_DJANGO_ALLOWED_HOSTS'), + 'IT_SENTRY_DSN': env.str('IT_SENTRY_DSN'), + 'IT_APP_ENVIRONMENT': 'caprover', 'POSTGRES_HOST': 'srv-captain--vbv-lernwelt-postgres-db', 'POSTGRES_PORT': 5432, 'POSTGRES_DB': db_name, diff --git a/caprover_deploy.sh b/caprover_deploy.sh index 292cf0d7..aa996df9 100755 --- a/caprover_deploy.sh +++ b/caprover_deploy.sh @@ -1,5 +1,8 @@ #!/bin/bash +# create client +npm run build + # create and push new docker container docker build -f compose/django/Dockerfile -t iterativ/vbv-lernwelt-django . docker push iterativ/vbv-lernwelt-django diff --git a/client/index.html b/client/index.html index 11603f87..595da947 100644 --- a/client/index.html +++ b/client/index.html @@ -5,6 +5,7 @@ Vite App +
diff --git a/client/package.json b/client/package.json index bbdae473..5b95bf13 100644 --- a/client/package.json +++ b/client/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "scripts": { "dev": "vite", - "build": "vue-tsc --noEmit && vite build", + "build": "vue-tsc --noEmit && vite build && cp ./dist/index.html ../server/vbv_lernwelt/templates/vue/index.html && cp -r ./dist/static/vue ../server/vbv_lernwelt/static/", "preview": "vite preview --port 5050", "test:unit": "vitest --environment jsdom", "test:e2e": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress open'", @@ -13,6 +13,7 @@ }, "dependencies": { "axios": "^0.26.1", + "loglevel": "^1.8.0", "pinia": "^2.0.13", "vue": "^3.2.31", "vue-i18n": "^9.1.9", diff --git a/client/src/assets/circle-analyse.svg b/client/src/assets/circle-analyse.svg new file mode 100644 index 00000000..be722b24 --- /dev/null +++ b/client/src/assets/circle-analyse.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/src/assets/styles/_fonts.scss b/client/src/assets/styles/_fonts.scss deleted file mode 100644 index f1812e34..00000000 --- a/client/src/assets/styles/_fonts.scss +++ /dev/null @@ -1,9 +0,0 @@ -@font-face { - font-family: 'BuenosAires'; - font-style: normal; - src: url("@/assets/styles/fonts/BuenosAires-Regular.woff2") format("woff2"), - url("@/assets/styles/fonts/BuenosAires-Regular.woff") format("woff"); - font-weight: 400; - font-style: normal; - font-display: swap; -} diff --git a/client/src/assets/styles/index.scss b/client/src/assets/styles/index.scss index 3544d7e7..a47067f8 100644 --- a/client/src/assets/styles/index.scss +++ b/client/src/assets/styles/index.scss @@ -1,2 +1 @@ -@import "fonts"; @import "../../../../server/vbv_lernwelt/static/css/tailwind.css"; diff --git a/client/src/components/CircleSidebar.vue b/client/src/components/CircleSidebar.vue deleted file mode 100644 index 0dc94b35..00000000 --- a/client/src/components/CircleSidebar.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - - - diff --git a/client/src/components/LearningPath.vue b/client/src/components/LearningPath.vue deleted file mode 100644 index f7ed978a..00000000 --- a/client/src/components/LearningPath.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/client/src/components/LearningSequence.vue b/client/src/components/LearningSequence.vue deleted file mode 100644 index e858121d..00000000 --- a/client/src/components/LearningSequence.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - - - diff --git a/client/src/components/MainNavigationBar.vue b/client/src/components/MainNavigationBar.vue index 760c41c4..3e06df82 100644 --- a/client/src/components/MainNavigationBar.vue +++ b/client/src/components/MainNavigationBar.vue @@ -1,18 +1,82 @@ - + - + +
+ +
+ + + + + + + diff --git a/client/src/components/VBVNavigationBar.vue b/client/src/components/VBVNavigationBar.vue deleted file mode 100644 index f23cdf16..00000000 --- a/client/src/components/VBVNavigationBar.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/client/src/components/circle/LearningSequence.vue b/client/src/components/circle/LearningSequence.vue new file mode 100644 index 00000000..7e2a3bbc --- /dev/null +++ b/client/src/components/circle/LearningSequence.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/client/src/components/icons/IconArrowDown.vue b/client/src/components/icons/IconArrowDown.vue new file mode 100644 index 00000000..96f7b2d0 --- /dev/null +++ b/client/src/components/icons/IconArrowDown.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconArrowLeft.vue b/client/src/components/icons/IconArrowLeft.vue new file mode 100644 index 00000000..eee84291 --- /dev/null +++ b/client/src/components/icons/IconArrowLeft.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconArrowRight.vue b/client/src/components/icons/IconArrowRight.vue new file mode 100644 index 00000000..907d78b1 --- /dev/null +++ b/client/src/components/icons/IconArrowRight.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconArrowUp.vue b/client/src/components/icons/IconArrowUp.vue new file mode 100644 index 00000000..1c9cc238 --- /dev/null +++ b/client/src/components/icons/IconArrowUp.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconCheck.vue b/client/src/components/icons/IconCheck.vue new file mode 100644 index 00000000..db492cd1 --- /dev/null +++ b/client/src/components/icons/IconCheck.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconCheckboxChecked.vue b/client/src/components/icons/IconCheckboxChecked.vue new file mode 100644 index 00000000..79718f51 --- /dev/null +++ b/client/src/components/icons/IconCheckboxChecked.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconCheckboxUnchecked.vue b/client/src/components/icons/IconCheckboxUnchecked.vue new file mode 100644 index 00000000..0d84821c --- /dev/null +++ b/client/src/components/icons/IconCheckboxUnchecked.vue @@ -0,0 +1,5 @@ + diff --git a/client/src/components/icons/IconClose.vue b/client/src/components/icons/IconClose.vue new file mode 100644 index 00000000..bd32921e --- /dev/null +++ b/client/src/components/icons/IconClose.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconCommunity.vue b/client/src/components/icons/IconCommunity.vue deleted file mode 100644 index 2dc8b055..00000000 --- a/client/src/components/icons/IconCommunity.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/client/src/components/icons/IconDocumentation.vue b/client/src/components/icons/IconDocumentation.vue deleted file mode 100644 index 6d4791cf..00000000 --- a/client/src/components/icons/IconDocumentation.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/client/src/components/icons/IconEcosystem.vue b/client/src/components/icons/IconEcosystem.vue deleted file mode 100644 index c3a4f078..00000000 --- a/client/src/components/icons/IconEcosystem.vue +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/client/src/components/icons/IconInfo.vue b/client/src/components/icons/IconInfo.vue new file mode 100644 index 00000000..d9424c3f --- /dev/null +++ b/client/src/components/icons/IconInfo.vue @@ -0,0 +1,8 @@ + diff --git a/client/src/components/icons/IconLsApply.vue b/client/src/components/icons/IconLsApply.vue new file mode 100644 index 00000000..3c2dae05 --- /dev/null +++ b/client/src/components/icons/IconLsApply.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconLsEnd.vue b/client/src/components/icons/IconLsEnd.vue new file mode 100644 index 00000000..00aa08ce --- /dev/null +++ b/client/src/components/icons/IconLsEnd.vue @@ -0,0 +1,10 @@ + diff --git a/client/src/components/icons/IconLsNetwork.vue b/client/src/components/icons/IconLsNetwork.vue new file mode 100644 index 00000000..bcfb5d35 --- /dev/null +++ b/client/src/components/icons/IconLsNetwork.vue @@ -0,0 +1,14 @@ + diff --git a/client/src/components/icons/IconLsPractice.vue b/client/src/components/icons/IconLsPractice.vue new file mode 100644 index 00000000..940f9e26 --- /dev/null +++ b/client/src/components/icons/IconLsPractice.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconLsStart.vue b/client/src/components/icons/IconLsStart.vue new file mode 100644 index 00000000..08aa8a88 --- /dev/null +++ b/client/src/components/icons/IconLsStart.vue @@ -0,0 +1,10 @@ + diff --git a/client/src/components/icons/IconLsTest.vue b/client/src/components/icons/IconLsTest.vue new file mode 100644 index 00000000..050448d0 --- /dev/null +++ b/client/src/components/icons/IconLsTest.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconLsWatch.vue b/client/src/components/icons/IconLsWatch.vue new file mode 100644 index 00000000..e2e3bb75 --- /dev/null +++ b/client/src/components/icons/IconLsWatch.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconMessage.vue b/client/src/components/icons/IconMessage.vue new file mode 100644 index 00000000..5bd94641 --- /dev/null +++ b/client/src/components/icons/IconMessage.vue @@ -0,0 +1,7 @@ + diff --git a/client/src/components/icons/IconSmileyHappy.vue b/client/src/components/icons/IconSmileyHappy.vue new file mode 100644 index 00000000..5d6829bb --- /dev/null +++ b/client/src/components/icons/IconSmileyHappy.vue @@ -0,0 +1,14 @@ + diff --git a/client/src/components/icons/IconSmileyNeutral.vue b/client/src/components/icons/IconSmileyNeutral.vue new file mode 100644 index 00000000..c48f93c2 --- /dev/null +++ b/client/src/components/icons/IconSmileyNeutral.vue @@ -0,0 +1,13 @@ + diff --git a/client/src/components/icons/IconSmileyThinking.vue b/client/src/components/icons/IconSmileyThinking.vue new file mode 100644 index 00000000..990bef88 --- /dev/null +++ b/client/src/components/icons/IconSmileyThinking.vue @@ -0,0 +1,21 @@ + diff --git a/client/src/main.ts b/client/src/main.ts index 52412ad1..3707db7e 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -1,20 +1,37 @@ -import { createApp } from 'vue' -import { createPinia } from 'pinia' +import {createApp} from 'vue' +import {createPinia} from 'pinia' -import {setupI18n, loadLocaleMessages} from './i18n' +import {setupI18n} from './i18n' import App from './App.vue' import router from './router' +import IconLsApply from '@/components/icons/IconLsApply.vue'; +import IconLsWatch from '@/components/icons/IconLsWatch.vue'; +import IconLsTest from '@/components/icons/IconLsTest.vue'; +import IconLsPractice from '@/components/icons/IconLsPractice.vue'; +import IconLsNetwork from '@/components/icons/IconLsNetwork.vue'; +import IconLsStart from '@/components/icons/IconLsStart.vue'; +import IconLsEnd from '@/components/icons/IconLsEnd.vue'; + import '@/assets/styles/index.scss' const i18n = setupI18n() const app = createApp(App) // todo: define lang setup -await loadLocaleMessages(i18n, 'de') +// await loadLocaleMessages(i18n, 'de') app.use(createPinia()) app.use(router) app.use(i18n) app.mount('#app') + +// register icons globally +app.component('IconLsApply', IconLsApply) +app.component('IconLsWatch', IconLsWatch) +app.component('IconLsTest', IconLsTest) +app.component('IconLsPractice', IconLsPractice) +app.component('IconLsNetwork', IconLsNetwork) +app.component('IconLsStart', IconLsStart) +app.component('IconLsEnd', IconLsEnd) diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 5d5356d7..f821c851 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -27,21 +27,22 @@ const router = createRouter({ } }, { - path: '/analyse', - component: () => import('../views/CircleAnalyseExampleView.vue'), + path: '/circle/:circleSlug', + component: () => import('../views/CircleView.vue'), + props: true }, { path: '/profile', component: () => import('../views/ProfileView.vue'), }, { - path: '/learningpath/:learningPathId', - component: () => import('../views/LearningPathOverview.vue'), + path: '/styleguide', + component: () => import('../views/StyelGuideView.vue'), }, { - path: '/learningpath/:learningPathId/circle/:circleId', - component: () => import('../views/CircleView.vue'), - } + path: '/:pathMatch(.*)*', + component: () => import('../views/404View.vue'), + }, ] }) diff --git a/client/src/views/404View.vue b/client/src/views/404View.vue new file mode 100644 index 00000000..4437a8d1 --- /dev/null +++ b/client/src/views/404View.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/client/src/views/CircleAnalyseExampleView.vue b/client/src/views/CircleAnalyseExampleView.vue deleted file mode 100644 index 795b631e..00000000 --- a/client/src/views/CircleAnalyseExampleView.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/client/src/views/CircleView.vue b/client/src/views/CircleView.vue index aa84cc4d..7912e36d 100644 --- a/client/src/views/CircleView.vue +++ b/client/src/views/CircleView.vue @@ -1,36 +1,119 @@ - - - diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue index 16c2d953..7fb5eee8 100644 --- a/client/src/views/HomeView.vue +++ b/client/src/views/HomeView.vue @@ -1,8 +1,19 @@ +import MainNavigationBar from '@/components/MainNavigationBar.vue'; + + diff --git a/client/src/views/LearningPathOverview.vue b/client/src/views/LearningPathOverview.vue deleted file mode 100644 index 197c1a8c..00000000 --- a/client/src/views/LearningPathOverview.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - - - diff --git a/client/src/views/StyelGuideView.vue b/client/src/views/StyelGuideView.vue new file mode 100644 index 00000000..731bf25f --- /dev/null +++ b/client/src/views/StyelGuideView.vue @@ -0,0 +1,201 @@ + + + + + diff --git a/client/tailwind.config.js b/client/tailwind.config.js deleted file mode 100644 index bfe2b279..00000000 --- a/client/tailwind.config.js +++ /dev/null @@ -1,18 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-var-requires -module.exports = { - content: [ - './index.html', - './src/**/*.{vue,js,ts,jsx,tsx}', - ], - theme: { - colors: { - 'white': '#FFFFFF', - 'blue-dark': '#00224D' - }, - fontFamily: { - sans: ['BuenosAires', 'sans-serif'], - }, - extend: {}, - }, - plugins: [], -} diff --git a/client/vite.config.ts b/client/vite.config.ts index 6fcf6971..0420dde0 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -1,9 +1,8 @@ -import path from 'path' import {fileURLToPath, URL} from 'url' import {defineConfig, loadEnv} from 'vite' import vue from '@vitejs/plugin-vue' -import vueI18n from '@intlify/vite-plugin-vue-i18n' +// import vueI18n from '@intlify/vite-plugin-vue-i18n' import alias from '@rollup/plugin-alias' // https://vitejs.dev/config/ @@ -12,17 +11,18 @@ export default ({mode}) => { return defineConfig({ plugins: [ vue(), - vueI18n({ - include: path.resolve(__dirname, './locales/**') - }), + // vueI18n({ + // include: path.resolve(__dirname, './locales/**') + // }), // won't work in vite's resolve.alias, so we'll make the alias here alias({ - entries: [ - { - find: 'vue-i18n', - replacement: path.resolve(__dirname, './node_modules/vue-i18n/dist/vue-i18n.runtime.esm-bundler.js') - } - ] + // TODO: why is that used? + // entries: [ + // { + // find: 'vue-i18n', + // replacement: path.resolve(__dirname, './node_modules/vue-i18n/dist/vue-i18n.runtime.esm-bundler.js') + // } + // ] }) ], resolve: { @@ -30,5 +30,8 @@ export default ({mode}) => { '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, + build: { + assetsDir: 'static/vue', + } }) } diff --git a/compose/django/Dockerfile b/compose/django/Dockerfile index dbd61353..c437b663 100644 --- a/compose/django/Dockerfile +++ b/compose/django/Dockerfile @@ -13,10 +13,7 @@ FROM node:16-bullseye-slim as client-builder ARG APP_HOME=/app WORKDIR ${APP_HOME} -COPY ./server/package.json ${APP_HOME} -RUN npm install && npm cache clean --force COPY ./server ${APP_HOME} -RUN npm run build # define an alias for the specfic python version used in this file. FROM python:${PYTHON_VERSION} as python diff --git a/compose/django/docker_entrypoint.sh b/compose/django/docker_entrypoint.sh index 5192d90e..524f6902 100644 --- a/compose/django/docker_entrypoint.sh +++ b/compose/django/docker_entrypoint.sh @@ -13,9 +13,9 @@ if [ -z "${POSTGRES_USER}" ]; then base_postgres_image_default_user='postgres' export POSTGRES_USER="${base_postgres_image_default_user}" fi -export VBV_DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" +export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" -echo $VBV_DATABASE_URL +echo $DATABASE_URL postgres_ready() { python << END diff --git a/compose/django/docker_start.sh b/compose/django/docker_start.sh index ddc94453..9c06873f 100644 --- a/compose/django/docker_start.sh +++ b/compose/django/docker_start.sh @@ -4,9 +4,8 @@ set -o errexit set -o pipefail set -o nounset - python /app/manage.py collectstatic --noinput -python /app/manage.py migrate python /app/manage.py createcachetable +python /app/manage.py migrate /usr/local/bin/gunicorn config.asgi --bind 0.0.0.0:80 --chdir=/app -k uvicorn.workers.UvicornWorker diff --git a/env/docker_local.env b/env/docker_local.env index 93a5c471..7919aebc 100644 --- a/env/docker_local.env +++ b/env/docker_local.env @@ -8,5 +8,5 @@ POSTGRES_PASSWORD=hNqfCdG6bwCLcnfboDtNM1L2Hiwp8GuKp1DJ6t2rcKl15Vls2QbByoIZ6IQlci # General # ------------------------------------------------------------------------------ -VBV_DJANGO_LOCAL_DOCKER=True +IT_DJANGO_LOCAL_DOCKER=True IPYTHONDIR=/app/.ipython diff --git a/env_secrets/caprover.env b/env_secrets/caprover.env index c1f40ca6..738c5c3f 100644 Binary files a/env_secrets/caprover.env and b/env_secrets/caprover.env differ diff --git a/env_secrets/local_daniel.env b/env_secrets/local_daniel.env index b0f76bee..2cbf413e 100644 Binary files a/env_secrets/local_daniel.env and b/env_secrets/local_daniel.env differ diff --git a/env_secrets/local_lorenz.env b/env_secrets/local_lorenz.env index 611e670c..4104dedb 100644 Binary files a/env_secrets/local_lorenz.env and b/env_secrets/local_lorenz.env differ diff --git a/env_secrets/production.env b/env_secrets/production.env index 36afa782..7169a777 100644 Binary files a/env_secrets/production.env and b/env_secrets/production.env differ diff --git a/example.env b/example.env new file mode 100644 index 00000000..06329196 --- /dev/null +++ b/example.env @@ -0,0 +1 @@ +export IT_APP_ENVIRONMENT='development' diff --git a/local-setup-for-tests.sh b/local-setup-for-tests.sh deleted file mode 100755 index f861cfe5..00000000 --- a/local-setup-for-tests.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -#export DATABASE_HOST=postgres -#export DATABASE_PORT=5432 -#export DATABASE_URL=postgres://$DATABASE_USER:$PG_PASSWORD@$DATABASE_HOST:$DATABASE_PORT/$DATABASE_NAME -# -#echo $DATABASE_URL -#DJANGO_SETTINGS_MODULE=config.settings.base -#DATABASE_NAME=vbv_lernwelt -SKIP_SETUP=false -## - -echo "Setting up VBV Project for Local usage" -if [ "$SKIP_SETUP" = false ]; then - if [ -z "$PG_PORT" ]; then # if the port is set in the env, use iterg - DB_PORT=""; - else - DB_PORT="-p $PG_PORT"; - fi - if [ -z "$PG_USER" ]; then # if the user is set in the env, use it - DB_USER=""; - else - DB_USER="-U $PG_USER"; - fi - echo "psql -h localhost --port=$DB_PORT --username=$DB_USER -c 'drop database if exists' $DATABASE_NAME;" - - echo "Drop all connections to the database" - psql -h localhost --port=$DB_PORT --username=$DB_USER -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$DATABASE_NAME' AND pid <> pg_backend_pid();" - - echo "Drop database: $DATABASE_NAME" - psql -h localhost --port=$DB_PORT --username=$DB_USER -c "drop database if exists $DATABASE_NAME;" - - echo "Create database: $DATABASE_NAME" - psql -h localhost --port=$DB_PORT --username=$DB_USER -c "create database $DATABASE_NAME;" - - # reset data - python3 server/manage.py createcachetable --settings="$DJANGO_SETTINGS_MODULE" - python3 server/manage.py migrate --settings="$DJANGO_SETTINGS_MODULE" - python3 server/manage.py create_default_users --settings="$DJANGO_SETTINGS_MODULE" - python3 server/manage.py create_default_learningpath --settings="$DJANGO_SETTINGS_MODULE" -# -# # make django translations - (cd server && python3 manage.py compilemessages --settings="$DJANGO_SETTINGS_MODULE") -fi - diff --git a/package.json b/package.json index 5e6a82aa..6f79d5ff 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,8 @@ "name": "vbv_lernwelt_cypress", "version": "1.0.0", "scripts": { + "build": "npm install --prefix client && npm run build --prefix client && npm run build:tailwind", + "build:tailwind": "tailwindcss -i ./tailwind/input.css -o ./server/vbv_lernwelt/static/css/tailwind.css --minify", "test": "echo \"Error: no test specified\" && exit 1", "cypress:open": "cypress open", "cypress:run": "cypress run", @@ -10,6 +12,8 @@ "tailwind": "tailwindcss -i ./tailwind/input.css -o ./server/vbv_lernwelt/static/css/tailwind.css --watch" }, "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "@tailwindcss/typography": "^0.5.2", "cypress": "^9.4.1", "tailwindcss": "^3.0.24" } diff --git a/prepare_server.sh b/prepare_server.sh new file mode 100755 index 00000000..81a13cf0 --- /dev/null +++ b/prepare_server.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +if [ -z "$CI" ]; +then + # kill all subprocess on exit so that Bitbucket Pipelines process will not hang + trap "exit" INT TERM ERR + trap "kill 0" EXIT +else echo "CI is set to $CI"; +fi + +# script should fail when any process returns non zero code +set -ev + +# handle arguments +SKIP_SETUP=false +START_BACKGROUND=false + +for i in "$@" +do +case $i in + --start-background) + START_BACKGROUND=true + shift # past argument + ;; + --skip-setup) + SKIP_SETUP=true + shift # past argument with no value + ;; + *) + # unknown option + ;; +esac +done + +echo "SKIP_SETUP = ${SKIP_SETUP}" + +POSTGRES_DB=${POSTGRES_DB:-vbv_lernwelt} +POSTGRES_HOST=${POSTGRES_HOST:-localhost} +POSTGRES_PORT=${POSTGRES_PORT:-5432} +POSTGRES_USER=${POSTGRES_USER:-postgres} +DJANGO_PORT=${DJANGO_PORT:-8000} + +mypsql() { + psql -h "${POSTGRES_HOST}" -p "${POSTGRES_PORT}" -U "${POSTGRES_USER}" "$@" +} + +if [ "$SKIP_SETUP" = false ]; then + # TODO: in heroku we must do a `pg:resets` to reset the db + echo "Drop all connections to $POSTGRES_DB" + mypsql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$POSTGRES_DB' AND pid <> pg_backend_pid();" > /dev/null 2>&1 + echo "Drop database: $POSTGRES_DB" + mypsql -c "drop database if exists $POSTGRES_DB;" + echo "Create database: $POSTGRES_DB" + mypsql -c "create database $POSTGRES_DB;" + + echo "initialize database for django" + python3 server/manage.py createcachetable --settings="$DJANGO_SETTINGS_MODULE" + python3 server/manage.py migrate --settings="$DJANGO_SETTINGS_MODULE" + python3 server/manage.py create_default_users --settings="$DJANGO_SETTINGS_MODULE" + python3 server/manage.py create_default_learning_path --settings="$DJANGO_SETTINGS_MODULE" + + # make django translations + (cd server && python3 manage.py compilemessages --settings="$DJANGO_SETTINGS_MODULE") +else + # TODO: can we reset important data without resetting the database? + echo "Skip database setup" + # python3 src/manage.py cypress_reset --settings="$DJANGO_SETTINGS_MODULE" +fi + +if [ "$START_BACKGROUND" = true ]; then + python3 server/manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE" > /dev/null & +else + python3 server/manage.py runserver "${DJANGO_PORT}" --settings="$DJANGO_SETTINGS_MODULE" +fi diff --git a/prepare_server_cypress.sh b/prepare_server_cypress.sh index 8ef42ba9..fa2e45ce 100755 --- a/prepare_server_cypress.sh +++ b/prepare_server_cypress.sh @@ -1,91 +1,6 @@ -#!/bin/bash +export IT_APP_ENVIRONMENT=development +export DJANGO_SETTINGS_MODULE=config.settings.test_cypress +export DJANGO_PORT=8001 +export POSTGRES_DB=vbv_lernwelt_cypress -if [ -z "$CI" ]; -then - # kill all subprocess on exit - trap "exit" INT TERM ERR - trap "kill 0" EXIT -else echo "CI is set to $CI"; -fi - -# script should fail when any process returns non zero code -set -ev - -# handle arguments -SKIP_SETUP=false -START_BACKGROUND=false -PROXY_VUE=false - -for i in "$@" -do -case $i in - --start-background) - START_BACKGROUND=true - shift # past argument - ;; - --skip-setup) - SKIP_SETUP=true - shift # past argument with no value - ;; - --proxy-vue) - PROXY_VUE=true - shift # past argument with no value - ;; - *) - # unknown option - ;; -esac -done - -echo "SKIP_SETUP = ${SKIP_SETUP}" - -DJANGO_SETTINGS_MODULE=config.settings.test_cypress -CYPRESS_DB=vbv_lernwelt_cypress - -if [ "$SKIP_SETUP" = false ]; then - if [ -z "$PG_PORT" ]; then # if the port is set in the env, use iterg - DB_PORT=""; - else - DB_PORT="-p $PG_PORT"; - fi - if [ -z "$PG_USER" ]; then # if the user is set in the env, use it - DB_USER=""; - else - DB_USER="-U $PG_USER"; - fi - echo "psql -h localhost $DB_PORT $DB_USER -c 'drop database if exists $CYPRESS_DB;'" - # create database - psql -h localhost $DB_PORT $DB_USER -c "drop database if exists $CYPRESS_DB;" - psql -h localhost $DB_PORT $DB_USER -c "create database $CYPRESS_DB;" - - # reset data - # python3 src/manage.py randomdata --settings="$DJANGO_SETTINGS_MODULE" - python3 server/manage.py createcachetable --settings="$DJANGO_SETTINGS_MODULE" - python3 server/manage.py migrate --settings="$DJANGO_SETTINGS_MODULE" - - # make django translations - (cd server && python3 manage.py compilemessages --settings="$DJANGO_SETTINGS_MODULE") - - -else - echo "else" -# python3 src/manage.py recreate_customer_data_for_integration_tests --settings="$DJANGO_SETTINGS_MODULE" -fi - -if [ "$PROXY_VUE" = true ]; then - export DJANGO_VUE_LANDINGPAGE_PROXY=http://localhost:8080/ -fi - -# install cypress here to avoid problems with `npm install` on the iesc servers -#CYPRESS_INSTALLED=0 -##npx --no-install cypress --version || CYPRESS_INSTALLED=$? -#if [ $CYPRESS_INSTALLED -ne 0 ]; then -# echo "install cypress" -## npm install cypress@5.6.0 @testing-library/cypress@7.0.2 --no-save -#fi - -if [ "$START_BACKGROUND" = true ]; then - python3 server/manage.py runserver 8001 --settings="$DJANGO_SETTINGS_MODULE" > /dev/null & -else - python3 server/manage.py runserver 8001 --settings="$DJANGO_SETTINGS_MODULE" -fi +./prepare_server.sh "$@" diff --git a/production.yml b/production.yml deleted file mode 100644 index f04b1f56..00000000 --- a/production.yml +++ /dev/null @@ -1,46 +0,0 @@ -version: '3' - -volumes: - production_postgres_data: {} - production_postgres_data_backups: {} - production_traefik: {} - -services: - django: - build: - context: . - dockerfile: ./compose/production/django/Dockerfile - image: vbv_lernwelt_production_django - depends_on: - - postgres - - redis - env_file: - - env_secrets/production.env - command: /start - - postgres: - build: - context: . - dockerfile: ./compose/production/postgres/Dockerfile - image: vbv_lernwelt_production_postgres - volumes: - - production_postgres_data:/var/lib/postgresql/data:Z - - production_postgres_data_backups:/backups:z - env_file: - - env_secrets/production.env - - traefik: - build: - context: . - dockerfile: ./compose/production/traefik/Dockerfile - image: vbv_lernwelt_production_traefik - depends_on: - - django - volumes: - - production_traefik:/etc/traefik/acme:z - ports: - - "0.0.0.0:80:80" - - "0.0.0.0:443:443" - - redis: - image: redis:6 diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 9894b2a7..34152070 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -10,21 +10,17 @@ from environs import Env from vbv_lernwelt.core.utils import structlog_add_app_info SERVER_ROOT_DIR = Path(__file__).resolve(strict=True).parent.parent.parent -# vbv_lernwelt/ APPS_DIR = SERVER_ROOT_DIR / "vbv_lernwelt" env = Env() -READ_DOT_ENV_FILE = env.bool("VBV_DJANGO_READ_DOT_ENV_FILE", default=False) -if READ_DOT_ENV_FILE: - # OS environment variables take precedence over variables from .env - env.read_env(str(SERVER_ROOT_DIR / "example.env")) - -DJANGO_DEV_MODE = env("VBV_DJANGO_DEV_MODE", default="development") +# set to "development" for local development +APP_ENVIRONMENT = env("IT_APP_ENVIRONMENT") # GENERAL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#debug -DEBUG = env.bool("VBV_DJANGO_DEBUG", False) +DEBUG = env.bool("IT_DJANGO_DEBUG", True if APP_ENVIRONMENT == "development" else False) + # Local time zone. Choices are # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # though not all of them may be available with every OS. @@ -48,8 +44,8 @@ LOCALE_PATHS = [str(SERVER_ROOT_DIR / "locale")] # https://docs.djangoproject.com/en/dev/ref/settings/#databases DATABASES = { "default": env.dj_db_url( - "VBV_DATABASE_URL", - default="postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt", + "DATABASE_URL", + default="postgres://postgres@localhost:5432/vbv_lernwelt", ) } DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405 @@ -128,12 +124,12 @@ AUTHENTICATION_BACKENDS = [ # https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model AUTH_USER_MODEL = "core.User" # https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url -# LOGIN_REDIRECT_URL = "users:redirect" # https://docs.djangoproject.com/en/dev/ref/settings/#login-url # FIXME make configurable!? # LOGIN_URL = "/sso/login/" LOGIN_URL = "/login/" +LOGIN_REDIRECT_URL = "/" ALLOW_LOCAL_LOGIN = env.bool("IT_ALLOW_LOCAL_LOGIN", default=False) @@ -308,11 +304,14 @@ MANAGERS = ADMINS # https://docs.djangoproject.com/en/dev/ref/settings/#logging # See https://docs.djangoproject.com/en/dev/topics/logging for # more details on how to customize your logging configuration. -VBV_DJANGO_LOGGING_CONF = env( - "VBV_DJANGO_LOGGING_CONF", default="VBV_DJANGO_LOGGING_CONF_JSON_FILE" -) -if VBV_DJANGO_LOGGING_CONF == "VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR": +logging_conf_default = "IT_DJANGO_LOGGING_CONF_JSON_FILE" +if DEBUG: + logging_conf_default = "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR" + +IT_DJANGO_LOGGING_CONF = env("IT_DJANGO_LOGGING_CONF", default=logging_conf_default) + +if IT_DJANGO_LOGGING_CONF == "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR": timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S") LOGGING = { "version": 1, @@ -474,14 +473,17 @@ SPECTACULAR_SETTINGS = { # Your stuff... # ------------------------------------------------------------------------------ +if DEBUG: + SECRET_KEY = env( + "IT_DJANGO_SECRET_KEY", + default="J9FiYN31FuY7lHrmx9Mpai3GGpTVCxakEclOfCLretDe7bTf2DtTsgazJ0aIMtbq", + ) +else: + SECRET_KEY = env("IT_DJANGO_SECRET_KEY") -SECRET_KEY = env( - "VBV_DJANGO_SECRET_KEY", - default="J9FiYN31FuY7lHrmx9Mpai3GGpTVCxakEclOfCLretDe7bTf2DtTsgazJ0aIMtbq", -) # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts ALLOWED_HOSTS = env.list( - "VBV_DJANGO_ALLOWED_HOSTS", default=["localhost", "0.0.0.0", "127.0.0.1"] + "IT_DJANGO_ALLOWED_HOSTS", default=["localhost", "0.0.0.0", "127.0.0.1"] ) @@ -489,21 +491,21 @@ ALLOWED_HOSTS = env.list( CACHES = { "default": { "BACKEND": env( - "VBV_DJANGO_CACHE_BACKEND", + "IT_DJANGO_CACHE_BACKEND", default="django.core.cache.backends.db.DatabaseCache", ), - "LOCATION": env("VBV_DJANGO_CACHE_LOCATION", default="django_cache_table"), + "LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"), } } -if "django_redis.cache.RedisCache" in env("VBV_DJANGO_CACHE_BACKEND", default=""): +if "django_redis.cache.RedisCache" in env("IT_DJANGO_CACHE_BACKEND", default=""): CACHES = { "default": { "BACKEND": env( - "VBV_DJANGO_CACHE_BACKEND", + "IT_DJANGO_CACHE_BACKEND", default="django.core.cache.backends.db.DatabaseCache", ), - "LOCATION": env("VBV_DJANGO_CACHE_LOCATION", default="django_cache_table"), + "LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"), "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", # Mimicing memcache behavior. @@ -530,7 +532,7 @@ OAUTH = { } } -if DJANGO_DEV_MODE == "development": +if APP_ENVIRONMENT == "development": # http://whitenoise.evans.io/en/latest/django.html#using-whitenoise-in-development INSTALLED_APPS = ["whitenoise.runserver_nostatic"] + INSTALLED_APPS # noqa F405 @@ -547,7 +549,7 @@ if DJANGO_DEV_MODE == "development": # } # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips INTERNAL_IPS = ["127.0.0.1", "10.0.2.2"] - if env.bool("VBV_DJANGO_LOCAL_DOCKER", False): + if env.bool("IT_DJANGO_LOCAL_DOCKER", False): import socket hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) @@ -564,13 +566,13 @@ if DJANGO_DEV_MODE == "development": # https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration INSTALLED_APPS += ["django_extensions"] # noqa F405 -if DJANGO_DEV_MODE in ["production", "caprover"]: +if APP_ENVIRONMENT in ["production", "caprover"]: # SECURITY # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect - SECURE_SSL_REDIRECT = env.bool("VBV_DJANGO_SECURE_SSL_REDIRECT", default=True) + SECURE_SSL_REDIRECT = env.bool("IT_DJANGO_SECURE_SSL_REDIRECT", default=True) # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure SESSION_COOKIE_SECURE = True # https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure @@ -604,7 +606,7 @@ if DJANGO_DEV_MODE in ["production", "caprover"]: default="VBV Lernwelt ", ) # https://docs.djangoproject.com/en/dev/ref/settings/#server-email - SERVER_EMAIL = env("VBV_DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL) + SERVER_EMAIL = env("IT_DJANGO_SERVER_EMAIL", default=DEFAULT_FROM_EMAIL) # https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix EMAIL_SUBJECT_PREFIX = env( "DJANGO_EMAIL_SUBJECT_PREFIX", @@ -614,7 +616,7 @@ if DJANGO_DEV_MODE in ["production", "caprover"]: # ADMIN # ------------------------------------------------------------------------------ # Django Admin URL regex. - ADMIN_URL = env("VBV_DJANGO_ADMIN_URL") + ADMIN_URL = env("IT_DJANGO_ADMIN_URL", 'admin/') # Anymail # ------------------------------------------------------------------------------ @@ -633,7 +635,7 @@ if DJANGO_DEV_MODE in ["production", "caprover"]: from sentry_sdk.integrations.logging import LoggingIntegration from sentry_sdk.integrations.redis import RedisIntegration - SENTRY_DSN = env("VBV_SENTRY_DSN") + SENTRY_DSN = env("IT_SENTRY_DSN") SENTRY_LOG_LEVEL = env.int("DJANGO_SENTRY_LOG_LEVEL", logging.INFO) sentry_logging = LoggingIntegration( diff --git a/server/config/settings/local.py b/server/config/settings/local.py deleted file mode 100644 index e0af5343..00000000 --- a/server/config/settings/local.py +++ /dev/null @@ -1,42 +0,0 @@ -# pylint: disable=unused-wildcard-import,wildcard-import,wrong-import-position -import getpass -import os - -from .base import * # noqa -from .base import env - -# GENERAL -# ------------------------------------------------------------------------------ -# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -SECRET_KEY = env( - "VBV_DJANGO_SECRET_KEY", - default="1NpUCSvAKLpDZL9e3tqDaUesdfsadfasdfasdfMD3UjB72ZS", -) -# https://docs.djangoproject.com/en/dev/ref/settings/#test-runner -TEST_RUNNER = "django.test.runner.DiscoverRunner" - -# PASSWORDS -# ------------------------------------------------------------------------------ -# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers -PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] - -# EMAIL -# ------------------------------------------------------------------------------ -# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend -EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" - - -class DisableMigrations(dict): - def __contains__(self, item): - return True - - def __getitem__(self, item): - return None - -#MIGRATION_MODULES = DisableMigrations() - - - - -# Your stuff... -# ------------------------------------------------------------------------------ diff --git a/server/config/settings/test.py b/server/config/settings/test.py index 90c4b66c..bb7e88fc 100644 --- a/server/config/settings/test.py +++ b/server/config/settings/test.py @@ -1,27 +1,17 @@ # pylint: disable=unused-wildcard-import,wildcard-import,wrong-import-position -import getpass import os +os.environ['IT_APP_ENVIRONMENT'] = 'development' + from .base import * # noqa from .base import env -# GENERAL -# ------------------------------------------------------------------------------ -# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -SECRET_KEY = env( - "VBV_DJANGO_SECRET_KEY", - default="1NpUCSvAKLpDZL9e3tqDaUe8Kk2xAuF1tXosFjBanc4lFCgNcfBp02MD3UjB72ZS", -) # https://docs.djangoproject.com/en/dev/ref/settings/#test-runner TEST_RUNNER = "django.test.runner.DiscoverRunner" -# PASSWORDS -# ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] -# EMAIL -# ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" @@ -35,18 +25,3 @@ class DisableMigrations(dict): MIGRATION_MODULES = DisableMigrations() - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.postgresql_psycopg2", - "NAME": "vbv_lernwelt_test", - "USER": os.environ.get("PG_USER", getpass.getuser()), - "PASSWORD": os.environ.get("PG_PASSWORD"), - "HOST": "localhost", - "PORT": os.environ.get("PG_PORT", ""), - } -} - - -# Your stuff... -# ------------------------------------------------------------------------------ diff --git a/server/config/settings/test_cypress.py b/server/config/settings/test_cypress.py index dd94cb1f..5f4c706a 100644 --- a/server/config/settings/test_cypress.py +++ b/server/config/settings/test_cypress.py @@ -1,17 +1,11 @@ # pylint: disable=unused-wildcard-import,wildcard-import,wrong-import-position -import getpass -import os from .base import * # noqa -from .base import env # GENERAL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#secret-key -SECRET_KEY = env( - "VBV_DJANGO_SECRET_KEY", - default="1LhwZ0DvP4cGBgbBdCfaBQV7eiaOc4jWKdzO9WEXLFT7AaqBN6jqd0uyaZeAZ19K", -) +DATABASES['default']['NAME'] = 'vbv_lernwelt_cypress' # EMAIL # ------------------------------------------------------------------------------ @@ -19,19 +13,6 @@ SECRET_KEY = env( EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" CYPRESS_TEST = True -DEBUG = True - - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.postgresql_psycopg2", - "NAME": "vbv_lernwelt_cypress", - "USER": os.environ.get("PG_USER", getpass.getuser()), - "PASSWORD": os.environ.get("PG_PASSWORD"), - "HOST": "localhost", - "PORT": os.environ.get("PG_PORT", ""), - } -} # Your stuff... # ------------------------------------------------------------------------------ diff --git a/server/config/urls.py b/server/config/urls.py index efb530e4..25023fe1 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -1,4 +1,3 @@ - from django.conf import settings from django.conf.urls.static import static from django.contrib import admin @@ -19,9 +18,9 @@ from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt from vbv_lernwelt.core.views import ( rate_limit_exceeded_view, permission_denied_view, - check_rate_limit, vue_home, + check_rate_limit, cypress_reset_view, vue_home, ) -from .wagtail_api import api_router +from .wagtail_api import wagtail_api_router def raise_example_error(request): @@ -43,6 +42,7 @@ urlpatterns = [ path('cms/', include(wagtailadmin_urls)), path('documents/', include(wagtaildocs_urls)), path('pages/', include(wagtail_urls)), + path('learnpath/', include("vbv_lernwelt.learnpath.urls")), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: # Static file serving when using Gunicorn + Uvicorn for local web socket development @@ -57,7 +57,7 @@ if settings.ALLOW_LOCAL_LOGIN: urlpatterns += [ # API base url path("api/", include("config.api_router")), - path('wagtailapi/v2/', api_router.urls), + path('wagtailapi/v2/', wagtail_api_router.urls), # DRF auth token path("auth-token/", obtain_auth_token), @@ -65,6 +65,11 @@ urlpatterns += [ path("api/docs/", SpectacularSwaggerView.as_view(url_name="api-schema"), name="api-docs",), path("", include(grapple_urls)), ] + +if settings.APP_ENVIRONMENT != 'production': + urlpatterns += [ + re_path(r'cypressreset/$', cypress_reset_view, name='cypress_reset_view'), + ] # fmt: on diff --git a/server/config/wagtail_api.py b/server/config/wagtail_api.py index 1647ee43..d12d1b50 100644 --- a/server/config/wagtail_api.py +++ b/server/config/wagtail_api.py @@ -1,16 +1,16 @@ -from wagtail.api.v2.views import PagesAPIViewSet from wagtail.api.v2.router import WagtailAPIRouter -from wagtail.images.api.v2.views import ImagesAPIViewSet +from wagtail.api.v2.views import PagesAPIViewSet from wagtail.documents.api.v2.views import DocumentsAPIViewSet +from wagtail.images.api.v2.views import ImagesAPIViewSet # Create the router. "wagtailapi" is the URL namespace -api_router = WagtailAPIRouter('wagtailapi') +wagtail_api_router = WagtailAPIRouter('wagtailapi') # Add the three endpoints using the "register_endpoint" method. # The first parameter is the name of the endpoint (eg. pages, images). This # is used in the URL of the endpoint # The second parameter is the endpoint class that handles the requests -api_router.register_endpoint('pages', PagesAPIViewSet) -api_router.register_endpoint('images', ImagesAPIViewSet) -api_router.register_endpoint('documents', DocumentsAPIViewSet) +wagtail_api_router.register_endpoint('pages', PagesAPIViewSet) +wagtail_api_router.register_endpoint('images', ImagesAPIViewSet) +wagtail_api_router.register_endpoint('documents', DocumentsAPIViewSet) diff --git a/server/example.env b/server/example.env deleted file mode 100644 index 7e4c6848..00000000 --- a/server/example.env +++ /dev/null @@ -1,11 +0,0 @@ -export VBV_DATABASE_URL='postgres://vbv_lernwelt@localhost:5432/vbv_lernwelt' -#export VBV_DJANGO_LOGGING_CONF=VBV_DJANGO_LOGGING_CONF_CONSOLE_COLOR -export VBV_DJANGO_DEBUG=True - -# oauth is for the moment not used -export OAUTH_CLIENT_ID=iterativ -export OAUTH_CLIENT_SECRET=abced-1234 -export OAUTH_ACCESS_TOKEN_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token -export OAUTH_AUTHORIZE_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth -export OAUTH_API_BASE_URL=https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/ -export OAUTH_LOCAL_REDIRECT_URI=http://localhost:8000/api/oauth/callback/ diff --git a/server/setup_test_data.sh b/server/setup_test_data.sh deleted file mode 100644 index 7a1dd173..00000000 --- a/server/setup_test_data.sh +++ /dev/null @@ -1,5 +0,0 @@ -python manage.py migrate -python manage.py createcachetable -python manage.py create_default_users -#python manage.py create_default_learingpath - diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py index 82960734..886e736f 100644 --- a/server/vbv_lernwelt/core/create_default_users.py +++ b/server/vbv_lernwelt/core/create_default_users.py @@ -4,19 +4,24 @@ from django.contrib.auth.models import Group from vbv_lernwelt.core.models import User -def create_default_users(user_model=User, group_model=Group): +def create_default_users(user_model=User, group_model=Group, default_password=None): admin_group, created = group_model.objects.get_or_create(name='admin_group') content_creator_grop, created = group_model.objects.get_or_create(name='content_creator_grop') student_group, created = group_model.objects.get_or_create(name='student_group') - admin_user, created = _get_or_create_user(user_model=user_model, - username='admin', - password='admin') - admin_user.is_superuser=True + admin_password = default_password + if not admin_password: + admin_password = 'admin' + admin_user, created = _get_or_create_user(user_model=user_model, username='admin', password=admin_password) + admin_user.is_superuser = True + admin_user.is_staff = True admin_user.groups.add(admin_group) - admin_user.save() - student_user, created = _get_or_create_user(user_model=user_model, username='student', password='student') + + student_user_password = default_password + if not student_user_password: + student_user_password = 'student' + student_user, created = _get_or_create_user(user_model=user_model, username='student', password=student_user_password) student_user.groups.add(student_group) student_user.save() diff --git a/server/vbv_lernwelt/core/management/commands/create_default_users.py b/server/vbv_lernwelt/core/management/commands/create_default_users.py index 2d7c365d..2053c336 100644 --- a/server/vbv_lernwelt/core/management/commands/create_default_users.py +++ b/server/vbv_lernwelt/core/management/commands/create_default_users.py @@ -1,8 +1,7 @@ -from vbv_lernwelt.core.create_default_users import create_default_users - - import djclick as click +from vbv_lernwelt.core.create_default_users import create_default_users + @click.command() def command(): diff --git a/server/vbv_lernwelt/core/management/commands/cypress_reset.py b/server/vbv_lernwelt/core/management/commands/cypress_reset.py index 76dbf7cb..934dc1f2 100644 --- a/server/vbv_lernwelt/core/management/commands/cypress_reset.py +++ b/server/vbv_lernwelt/core/management/commands/cypress_reset.py @@ -1,7 +1,7 @@ -# pylint: disable=import-outside-toplevel - import djclick as click -from django.contrib.auth import get_user_model + +from vbv_lernwelt.learnpath.tests.create_default_learning_path import create_default_learning_path, \ + delete_default_learning_path @click.command() @@ -9,17 +9,5 @@ from django.contrib.auth import get_user_model def command(customer_language): print("cypress reset data") - User = get_user_model() - - users = [ - "cypress@example.com", - ] - for user in users: - User.objects.filter(username=user).delete() - - user = User.objects.create( - username=user, - email=user, - ) - user.set_password("test") - user.save() + delete_default_learning_path() + create_default_learning_path() diff --git a/server/vbv_lernwelt/core/migrations/0002_create_users.py b/server/vbv_lernwelt/core/migrations/0002_create_users.py index 0d19f917..e5d92cfa 100644 --- a/server/vbv_lernwelt/core/migrations/0002_create_users.py +++ b/server/vbv_lernwelt/core/migrations/0002_create_users.py @@ -1,20 +1,16 @@ +from django.conf import settings +from django.contrib.auth.models import Group from django.db import migrations +from vbv_lernwelt.core.create_default_users import create_default_users from vbv_lernwelt.core.models import User -def create_iterativ_users(apps, schema_editor): - for username in [ - "info@iterativ.ch", - ]: - user = User.objects.create( - username=username, - email=username, - is_superuser=True, - is_staff=True, - ) - user.set_password("ACEEs0DCmNaPxdoNV8vhccuCTRl9b") - user.save() +def create_users(apps, schema_editor): + default_password = 'ACEEs0DCmNaPxdoNV8vhccuCTRl9b' + if settings.APP_ENVIRONMENT == 'development': + default_password = None + create_default_users(user_model=User, group_model=Group, default_password=default_password) class Migration(migrations.Migration): @@ -23,5 +19,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(create_iterativ_users), + migrations.RunPython(create_users), ] diff --git a/server/vbv_lernwelt/core/templates/core/partials/header.html b/server/vbv_lernwelt/core/templates/core/partials/header.html deleted file mode 100644 index d852ec5d..00000000 --- a/server/vbv_lernwelt/core/templates/core/partials/header.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
- - -
- - SimpleTodo App - -
-
Infos zu Berufen
-
Branchennews
-
-
diff --git a/server/vbv_lernwelt/core/utils.py b/server/vbv_lernwelt/core/utils.py index af7f5938..637707c7 100644 --- a/server/vbv_lernwelt/core/utils.py +++ b/server/vbv_lernwelt/core/utils.py @@ -10,8 +10,8 @@ def structlog_add_app_info( logger: logging.Logger, method_name: str, event_dict: EventDict ) -> EventDict: event_dict["django_app"] = "vbv_lernwelt" - event_dict["django_dev_mode"] = settings.DJANGO_DEV_MODE - event_dict["django_app_dev_mode"] = f"vbv_lernwelt_{settings.DJANGO_DEV_MODE}" + event_dict["APP_ENVIRONMENT"] = settings.APP_ENVIRONMENT + event_dict["django_app_dev_mode"] = f"vbv_lernwelt_{settings.APP_ENVIRONMENT}" return event_dict diff --git a/server/vbv_lernwelt/core/views.py b/server/vbv_lernwelt/core/views.py index 5fe68853..52d37736 100644 --- a/server/vbv_lernwelt/core/views.py +++ b/server/vbv_lernwelt/core/views.py @@ -1,10 +1,14 @@ # Create your views here. import requests from django.conf import settings -from django.http import JsonResponse, HttpResponse +from django.core.management import call_command +from django.http import JsonResponse, HttpResponse, HttpResponseRedirect from django.shortcuts import render from django.views.decorators.csrf import ensure_csrf_cookie from ratelimit.decorators import ratelimit +from rest_framework import authentication +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAdminUser from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt @@ -19,11 +23,12 @@ def vue_home(request): content_type = headers.get('content-type', 'text/html') return HttpResponse(res.text, content_type=content_type) except Exception as e: - print(f'Can not connect to vue dev server at {settings.IT_SERVE_VUE_URL}:', e) - return + return HttpResponse( + f'Can not connect to vue dev server at {settings.IT_SERVE_VUE_URL}: {e}' + ) # render index.html from `npm run build` - return render(request, 'index.html', {}) + return render(request, 'vue/index.html', {}) def permission_denied_view(request, exception): @@ -50,3 +55,13 @@ def server_json_error(request, *args, **kwargs): @django_view_authentication_exempt def check_rate_limit(request): return HttpResponse(content=b"Hello") + + +@api_view(['POST']) +@authentication_classes((authentication.SessionAuthentication,)) +@permission_classes((IsAdminUser,)) +def cypress_reset_view(request): + if settings.APP_ENVIRONMENT != 'production': + call_command('cypress_reset') + + return HttpResponseRedirect('/admin/') diff --git a/server/vbv_lernwelt/learnpath/management/commands/create_default_learningpath.py b/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py similarity index 68% rename from server/vbv_lernwelt/learnpath/management/commands/create_default_learningpath.py rename to server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py index 5faf3e59..7df9caaf 100644 --- a/server/vbv_lernwelt/learnpath/management/commands/create_default_learningpath.py +++ b/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py @@ -1,7 +1,4 @@ -# pylint: disable=import-outside-toplevel - import djclick as click -from django.contrib.auth import get_user_model from vbv_lernwelt.learnpath.tests.create_default_learning_path import create_default_learning_path diff --git a/server/vbv_lernwelt/learnpath/management/commands/delete_default_learning_path.py b/server/vbv_lernwelt/learnpath/management/commands/delete_default_learning_path.py new file mode 100644 index 00000000..c07106f5 --- /dev/null +++ b/server/vbv_lernwelt/learnpath/management/commands/delete_default_learning_path.py @@ -0,0 +1,8 @@ +import djclick as click + +from vbv_lernwelt.learnpath.tests.create_default_learning_path import delete_default_learning_path + + +@click.command() +def command(): + delete_default_learning_path() diff --git a/server/vbv_lernwelt/learnpath/management/commands/delete_default_learningpath.py b/server/vbv_lernwelt/learnpath/management/commands/delete_default_learningpath.py deleted file mode 100644 index 2da68a66..00000000 --- a/server/vbv_lernwelt/learnpath/management/commands/delete_default_learningpath.py +++ /dev/null @@ -1,14 +0,0 @@ -# pylint: disable=import-outside-toplevel - -import djclick as click -from django.contrib.auth import get_user_model - -from vbv_lernwelt.learnpath.tests.create_default_learning_path import create_default_learning_path, \ - delete_default_learning_path - -import djclick as click - - -@click.command() -def command(): - delete_default_learning_path() diff --git a/server/vbv_lernwelt/learnpath/migrations/0001_initial.py b/server/vbv_lernwelt/learnpath/migrations/0001_initial.py index 6b3a574a..e16881cf 100644 --- a/server/vbv_lernwelt/learnpath/migrations/0001_initial.py +++ b/server/vbv_lernwelt/learnpath/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.12 on 2022-05-04 15:52 +# Generated by Django 3.2.12 on 2022-06-03 13:11 from django.db import migrations, models import django.db.models.deletion @@ -16,19 +16,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name='Circle', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), - ('description', models.TextField(blank=True, default='')), - ('goals', models.TextField(blank=True, default='')), - ], - options={ - 'verbose_name': 'Circle', - }, - bases=('wagtailcore.page', models.Model), - ), migrations.CreateModel( name='Competence', fields=[ @@ -64,15 +51,26 @@ class Migration(migrations.Migration): migrations.CreateModel( name='LearningSequence', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), - ('title', models.CharField(default='', max_length=256)), - ('category', models.CharField(choices=[('INCIRCLE', 'In Circle'), ('START', 'Start'), ('END', 'End')], default='INCIRCLE', max_length=16)), - ('circle', modelcluster.fields.ParentalKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='learning_sequences', to='learnpath.circle')), + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('icon', models.CharField(default='IconLsStart', max_length=255)), ], options={ 'verbose_name': 'Learning Sequence', }, + bases=('wagtailcore.page',), + ), + migrations.CreateModel( + name='LearningUnit', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('minutes', models.PositiveIntegerField(default=15)), + ('package', models.CharField(blank=True, default='', max_length=255)), + ('contents', wagtail.core.fields.StreamField([('video', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('rise_training', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('podcast', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('competence', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock())])), ('exercise', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock())])), ('self_evaluation', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock())])), ('document', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock())])), ('knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.TextBlock())]))])), + ], + options={ + 'verbose_name': 'Learning Unit', + }, + bases=('wagtailcore.page',), ), migrations.CreateModel( name='Topic', @@ -87,23 +85,11 @@ class Migration(migrations.Migration): 'verbose_name': 'Topic', }, ), - migrations.CreateModel( - name='LearningUnit', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), - ('contents', wagtail.core.fields.StreamField([('web_based_training', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('video', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())]))], blank=True, null=True)), - ('learning_sequence', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='learning_units', to='learnpath.learningsequence')), - ], - options={ - 'verbose_name': 'Learning Unit', - }, - bases=('wagtailcore.page', models.Model), - ), migrations.CreateModel( name='FullfillmentCriteria', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), ('name', models.CharField(max_length=2048)), ('competence', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='learnpath.competence')), ], @@ -116,9 +102,17 @@ class Migration(migrations.Migration): name='competence_page', field=modelcluster.fields.ParentalKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='competences', to='learnpath.competencepage'), ), - migrations.AddField( - model_name='circle', - name='topic', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='circles', to='learnpath.topic'), + migrations.CreateModel( + name='Circle', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('description', models.TextField(blank=True, default='')), + ('goals', models.TextField(blank=True, default='')), + ('topic', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='circles', to='learnpath.topic')), + ], + options={ + 'verbose_name': 'Circle', + }, + bases=('wagtailcore.page',), ), ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0002_fullfillmentcriteria_sort_order.py b/server/vbv_lernwelt/learnpath/migrations/0002_fullfillmentcriteria_sort_order.py deleted file mode 100644 index 5fe74a62..00000000 --- a/server/vbv_lernwelt/learnpath/migrations/0002_fullfillmentcriteria_sort_order.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.12 on 2022-05-04 16:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('learnpath', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='fullfillmentcriteria', - name='sort_order', - field=models.IntegerField(blank=True, editable=False, null=True), - ), - ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0003_auto_20220512_1456.py b/server/vbv_lernwelt/learnpath/migrations/0003_auto_20220512_1456.py deleted file mode 100644 index 6e3dd602..00000000 --- a/server/vbv_lernwelt/learnpath/migrations/0003_auto_20220512_1456.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.12 on 2022-05-12 12:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('learnpath', '0002_fullfillmentcriteria_sort_order'), - ] - - operations = [ - migrations.RemoveField( - model_name='learningunit', - name='learning_sequence', - ), - migrations.CreateModel( - name='LearningPackage', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), - ('title', models.CharField(default='', max_length=256)), - ('learning_sequence', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='learning_packages', to='learnpath.learningsequence')), - ], - options={ - 'ordering': ['sort_order'], - 'abstract': False, - }, - ), - migrations.AddField( - model_name='learningunit', - name='learning_package', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='learning_units', to='learnpath.learningpackage'), - ), - ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0004_alter_learningpackage_learning_sequence.py b/server/vbv_lernwelt/learnpath/migrations/0004_alter_learningpackage_learning_sequence.py deleted file mode 100644 index fd4b51c7..00000000 --- a/server/vbv_lernwelt/learnpath/migrations/0004_alter_learningpackage_learning_sequence.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.12 on 2022-05-12 12:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('learnpath', '0003_auto_20220512_1456'), - ] - - operations = [ - migrations.AlterField( - model_name='learningpackage', - name='learning_sequence', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='learning_packages', to='learnpath.learningsequence'), - ), - ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0005_alter_learningunit_learning_package.py b/server/vbv_lernwelt/learnpath/migrations/0005_alter_learningunit_learning_package.py deleted file mode 100644 index 6a1128a7..00000000 --- a/server/vbv_lernwelt/learnpath/migrations/0005_alter_learningunit_learning_package.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.12 on 2022-05-12 12:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('learnpath', '0004_alter_learningpackage_learning_sequence'), - ] - - operations = [ - migrations.AlterField( - model_name='learningunit', - name='learning_package', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='learning_units', to='learnpath.learningpackage'), - ), - ] diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py index bbd4f161..326e6ebc 100644 --- a/server/vbv_lernwelt/learnpath/models.py +++ b/server/vbv_lernwelt/learnpath/models.py @@ -1,21 +1,16 @@ # Create your models here. from django.utils.text import slugify +from grapple.helpers import register_query_field +from wagtail.api import APIField from wagtail.core.blocks import StreamBlock from wagtail.core.fields import StreamField from wagtail.core.models import Page, Orderable -from wagtail.api import APIField from vbv_lernwelt.learnpath.models_competences import * -from vbv_lernwelt.learnpath.models_learning_unit_content import WebBasedTrainingBlock, VideoBlock -from grapple.helpers import register_query_field -from collections import OrderedDict -import graphene - -from grapple.models import ( - GraphQLString, GraphQLPage, - GraphQLStreamfield, GraphQLBoolean, GraphQLInt, GraphQLForeignKey, GraphQLField -) +from vbv_lernwelt.learnpath.models_learning_unit_content import RiseTrainingBlock, VideoBlock, PodcastBlock, \ + CompetenceBlock, ExerciseBlock, SelfEvaluationBlock, DocumentBlock, KnowledgeBlock +from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class @register_query_field("learning_path") @@ -28,7 +23,6 @@ class LearningPath(Page): subpage_types = ['learnpath.Circle'] - class Meta: verbose_name = "Learning Path" @@ -65,7 +59,6 @@ class Topic(Orderable): # parent_page_types = ['learnpath.LearningPath'] # subpage_types = ['learnpath.Circle'] - def full_clean(self, *args, **kwargs): self.slug = find_available_slug(Topic, slugify(self.title, allow_unicode=True)) super(Topic, self).full_clean(*args, **kwargs) @@ -77,9 +70,10 @@ class Topic(Orderable): return f"{self.title}" -class Circle(Page, Orderable): +class Circle(Page): description = models.TextField(default="", blank=True) goals = models.TextField(default="", blank=True) + topic = models.ForeignKey( 'learnpath.Topic', null=True, @@ -88,51 +82,21 @@ class Circle(Page, Orderable): related_name='circles' ) - parent_page_types = ['learnpath.Learningpath'] - subpage_types = ['learnpath.LearningUnit'] + parent_page_types = ['learnpath.LearningPath'] + subpage_types = ['learnpath.LearningSequence', 'learnpath.LearningUnit'] content_panels = Page.content_panels + [ FieldPanel('description'), FieldPanel('goals'), - InlinePanel('learning_sequences', label="Learning Sequences"), ] - # Export fields over the API api_fields = [ APIField('title'), APIField('description'), - APIField('topic'), - APIField('content_structure'), ] - @property - def content_structure(self): - learning_sequences = LearningSequence.objects.filter(circle_id=self.id).values() - learning_packages = LearningPackage.objects.filter(learning_sequence__circle_id=self.id).values() - learning_units = LearningUnit.objects.filter(learning_package__learning_sequence__circle_id=self.id).values('learning_package_id', 'title') - - content = OrderedDict() - content['learning_sequences'] = [] - - for learning_sequence in learning_sequences: - this_learning_packages = [] - related_learning_packages = [lp for lp in learning_packages if lp['learning_sequence_id'] == learning_sequence['id']] - for learning_package in related_learning_packages: - related_learning_units = [lu for lu in learning_units if - lu['learning_package_id'] == learning_package['id']] - this_learning_units = [learning_unit for learning_unit in related_learning_units] - learning_package['learning_units'] = this_learning_units - this_learning_packages.append(learning_package) - learning_sequence['learning_packages'] = this_learning_packages - content['learning_sequences'].append(learning_sequence) - - return content - - - - def full_clean(self, *args, **kwargs): - self.slug = find_available_slug(Circle, slugify(self.title, allow_unicode=True)) + # self.slug = find_available_slug(Circle, slugify(self.title, allow_unicode=True)) super(Circle, self).full_clean(*args, **kwargs) class Meta: @@ -142,116 +106,77 @@ class Circle(Page, Orderable): return f"{self.title}" -IN_CIRCLE = 'INCIRCLE' -START = 'START' -END = 'END' +class LearningSequence(Page): + icon = models.CharField(max_length=255, default="IconLsStart") -LEARNING_SEQUENCE_CATEGORIES = [ - (IN_CIRCLE, 'In Circle'), - (START, 'Start'), - (END, 'End') -] + parent_page_types = ['learnpath.Circle'] - -class LearningSequence(Orderable): - # TODO: How to do a icon choice field? - title = models.CharField(max_length=256, default='') - category = models.CharField(max_length=16, choices=LEARNING_SEQUENCE_CATEGORIES, default=IN_CIRCLE) - - circle = ParentalKey( - 'learnpath.Circle', - null=True, - blank=True, - on_delete=models.CASCADE, - related_name='learning_sequences', - ) - - panels = [FieldPanel('title'), FieldPanel('category'), FieldPanel('circle')] - - - api_fields = [ - APIField('title'), - APIField('category'), - APIField('learning_packages'), + panels = [ + FieldPanel('title'), + FieldPanel('icon'), ] - - class Meta: verbose_name = "Learning Sequence" def __str__(self): return f"{self.title}" + @classmethod + def get_serializer_class(cls): + return get_it_serializer_class(cls, field_names=['id', 'title', 'icon', 'slug', 'type', 'translation_key']) + + def get_admin_display_title(self): + return f'{self.icon} {self.draft_title}' + def full_clean(self, *args, **kwargs): - self.slug = find_available_slug(LearningSequence, slugify(self.title, allow_unicode=True)) super(LearningSequence, self).full_clean(*args, **kwargs) - -class LearningPackage(Orderable): - title = models.CharField(max_length=256, default='') - - learning_sequence = models.ForeignKey( - 'learnpath.LearningSequence', - null=True, - blank=True, - on_delete=models.SET_NULL, - related_name='learning_packages', - ) - panels = [FieldPanel('title')] - - api_fields = [ - APIField('title'), - APIField('my_title'), - ] - - @property - def my_title(self): - return self.title - - - def full_clean(self, *args, **kwargs): - self.slug = find_available_slug(LearningPackage, slugify(self.title, allow_unicode=True)) - super(LearningPackage, self).full_clean(*args, **kwargs) - - -class LearningUnit(Page, Orderable): +class LearningUnit(Page): """ This is a group of contents, with the übung and test it is one unit. ... more of a structural charactacter. """ # TODO: Review model architecture, is the stream field the right thing here? parent_page_types = ['learnpath.Circle'] - learning_package = models.ForeignKey( - 'learnpath.LearningPackage', - null=True, - blank=True, - on_delete=models.SET_NULL, - related_name='learning_units', - ) + subpage_types = [] + minutes = models.PositiveIntegerField(default=15) + package = models.CharField(max_length=255, default="", blank=True) content_blocks = [ - ('web_based_training', WebBasedTrainingBlock()), ('video', VideoBlock()), + ('rise_training', RiseTrainingBlock()), + ('podcast', PodcastBlock()), + ('competence', CompetenceBlock()), + ('exercise', ExerciseBlock()), + ('self_evaluation', SelfEvaluationBlock()), + ('document', DocumentBlock()), + ('knowledge', KnowledgeBlock()), ] - contents = StreamField(StreamBlock(content_blocks), - null=True, blank=True, min_num=1, max_num=1) + contents = StreamField( + StreamBlock(content_blocks), blank=False, min_num=1, max_num=1 + ) content_panels = [ FieldPanel('title', classname="full title"), - FieldPanel('learning_package'), + FieldPanel('minutes'), StreamFieldPanel('contents'), ] - api_fields = [ - APIField('title'), - APIField('contents'), - APIField('content_blocks'), - ] + def get_admin_display_title(self): + display_title = '' - subpage_types = [] + if self.package: + display_title += f'{self.package}: ' + + if len(self.contents) > 0: + display_title += f'{self.contents[0].block_type.capitalize()}: ' + + display_title += self.draft_title + + return display_title class Meta: verbose_name = "Learning Unit" @@ -260,11 +185,14 @@ class LearningUnit(Page, Orderable): self.slug = find_available_slug(LearningUnit, slugify(self.title, allow_unicode=True)) super(LearningUnit, self).full_clean(*args, **kwargs) + @classmethod + def get_serializer_class(cls): + return get_it_serializer_class(cls, field_names=['id', 'title', 'minutes', 'package', 'contents', 'slug', 'type', 'translation_key']) + def __str__(self): return f"{self.title}" - def find_available_slug(model, requested_slug, ignore_page_id=None): """ Finds an available slug within the specified parent. diff --git a/server/vbv_lernwelt/learnpath/models_learning_unit_content.py b/server/vbv_lernwelt/learnpath/models_learning_unit_content.py index 763d1668..5b29f647 100644 --- a/server/vbv_lernwelt/learnpath/models_learning_unit_content.py +++ b/server/vbv_lernwelt/learnpath/models_learning_unit_content.py @@ -1,51 +1,61 @@ -from django.db import models from wagtail.core import blocks -from wagtail.api import APIField -# 'video_block' class VideoBlock(blocks.StructBlock): # TODO: Possible video Types for the user, upload file, add URL - title = models.CharField(max_length=128, default="") - description = models.TextField(default="") + description = blocks.TextBlock() url = blocks.URLBlock() class Meta: icon = 'media' - def get_api_representation(self, value, context=None): - return {'sdfsdf': 1, - 'sldkfm': 3} - -RISE = 'rise' - -CONTENT_TYPE_CHOICES = ( - (RISE, 'Rise'), -) - -# 'Web based training Block' -class WebBasedTrainingBlock(blocks.StructBlock): +class RiseTrainingBlock(blocks.StructBlock): + description = blocks.TextBlock() url = blocks.URLBlock() - content_type = models.CharField( - max_length=100, - choices=CONTENT_TYPE_CHOICES, - default=RISE - ) class Meta: icon = 'media' - def get_api_representation(self, value, context=None): - return {'sdfsdf': 1, - 'sldkfm': 3} - - -# 'Transver Task' -class TranverTaskBlock(blocks.StructBlock): - title = models.CharField(max_length=128, default="") - description = models.TextField(default="") +class PodcastBlock(blocks.StructBlock): + description = blocks.TextBlock() + url = blocks.URLBlock() + + class Meta: + icon = 'media' + + +class CompetenceBlock(blocks.StructBlock): + description = blocks.TextBlock() + + class Meta: + icon = 'media' + + +class ExerciseBlock(blocks.StructBlock): + description = blocks.TextBlock() + + class Meta: + icon = 'media' + + +class SelfEvaluationBlock(blocks.StructBlock): + description = blocks.TextBlock() + + class Meta: + icon = 'media' + + +class DocumentBlock(blocks.StructBlock): + description = blocks.TextBlock() + + class Meta: + icon = 'media' + + +class KnowledgeBlock(blocks.StructBlock): + description = blocks.TextBlock() class Meta: icon = 'media' diff --git a/server/vbv_lernwelt/learnpath/serializer_helpers.py b/server/vbv_lernwelt/learnpath/serializer_helpers.py new file mode 100644 index 00000000..81bd41f9 --- /dev/null +++ b/server/vbv_lernwelt/learnpath/serializer_helpers.py @@ -0,0 +1,15 @@ +import wagtail.api.v2.serializers as wagtail_serializers + + +def get_it_serializer_class(model, field_names): + return wagtail_serializers.get_serializer_class(model, field_names=field_names, meta_fields=[], base=ItBaseSerializer) + + +class ItTypeField(wagtail_serializers.TypeField): + def to_representation(self, obj): + name = type(obj)._meta.app_label + '.' + type(obj).__name__ + return name + + +class ItBaseSerializer(wagtail_serializers.BaseSerializer): + type = ItTypeField(read_only=True) diff --git a/server/vbv_lernwelt/learnpath/serializers.py b/server/vbv_lernwelt/learnpath/serializers.py new file mode 100644 index 00000000..0e2201af --- /dev/null +++ b/server/vbv_lernwelt/learnpath/serializers.py @@ -0,0 +1,20 @@ +from rest_framework import serializers + +from vbv_lernwelt.learnpath.models import Circle +from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class + + +class CircleSerializer(get_it_serializer_class(Circle, [])): + children = serializers.SerializerMethodField() + + meta_fields = [] + + def get_children(self, obj): + return [c.specific.get_serializer_class()(c.specific).data for c in obj.get_children()] + + def get_meta_label(self, obj): + return obj._meta.label + + class Meta: + model = Circle + fields = ['id', 'title', 'slug', 'children', 'type'] diff --git a/server/vbv_lernwelt/learnpath/tests/competences_factories.py b/server/vbv_lernwelt/learnpath/tests/competences_factories.py index 6a043add..fd6ed29e 100644 --- a/server/vbv_lernwelt/learnpath/tests/competences_factories.py +++ b/server/vbv_lernwelt/learnpath/tests/competences_factories.py @@ -2,7 +2,7 @@ import factory import wagtail_factories from vbv_lernwelt.learnpath.models_competences import Competence, FullfillmentCriteria, CompetencePage -from vbv_lernwelt.learnpath.tests.learningpath_factories import LearningPathFactory +from vbv_lernwelt.learnpath.tests.learning_path_factories import LearningPathFactory class CompetencePageFactory(wagtail_factories.PageFactory): diff --git a/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py b/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py index 5e8a7527..e1a36f1a 100644 --- a/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py +++ b/server/vbv_lernwelt/learnpath/tests/create_default_learning_path.py @@ -1,20 +1,26 @@ import wagtail_factories -from wagtail.core.models import Site +from django.conf import settings +from wagtail.core.models import Site, Page +from vbv_lernwelt.core.admin import User from vbv_lernwelt.learnpath.models import LearningPath, Topic, Circle, LearningSequence, LearningUnit -from vbv_lernwelt.learnpath.tests.create_default_competences import create_default_competences -from vbv_lernwelt.learnpath.tests.learningpath_factories import LearningPathFactory, TopicFactory, CircleFactory, \ - LearningSequenceFactory, LearningUnitFactory, VideoBlockFactory, WebBasedTrainingBlockFactory, LearningPackageFactory +from vbv_lernwelt.learnpath.tests.learning_path_factories import LearningPathFactory, TopicFactory, CircleFactory, \ + LearningSequenceFactory, LearningUnitFactory, VideoBlockFactory, PodcastBlockFactory, CompetenceBlockFactory, \ + ExerciseBlockFactory, SelfEvaluationBlockFactory, DocumentBlockFactory -def create_default_learning_path(): +def create_default_learning_path(user=None): + if user is None: + user = User.objects.get(username='admin') + site = Site.objects.filter(is_default_site=True).first() if not site: site = wagtail_factories.SiteFactory(is_default_site=True) - site.port = 8000 - site.save() + if settings.APP_ENVIRONMENT == 'development': + site.port = 8000 + site.save() # create_default_competences() @@ -22,22 +28,10 @@ def create_default_learning_path(): tp = TopicFactory(title="Basis", is_visible=False, learning_path=lp) - circle_1 = CircleFactory(title="Basis", parent=lp, topic=tp, description="""In diesem Circle erklären wir dir, wie der Lehrgang - Versicherungsvermittler / in " aufgebaut ist. Zudem vermitteln wir dir die wichtigsten Grundlagen, - damit erfolgreich mit deinem Lernpfad starten kannst.""") - - ls_1 = LearningSequenceFactory(title='Einleitung', circle=circle_1) - - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=ls_1) - - lu_1 = LearningUnitFactory(title="Herzlich Willkommmen", parent=circle_1, learning_package=lpck_1) - lu_1 = LearningUnitFactory(title="Herzlich Willkommmen 1", parent=circle_1, learning_package=lpck_1) - lu_1 = LearningUnitFactory(title="Herzlich Willkommmen 2", parent=circle_1, learning_package=lpck_1) - - ls_2 = LearningSequenceFactory(title='Grundlagen', circle=circle_1) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=ls_2) - - lu_1 = LearningUnitFactory(title="Aber jetzt, Butter bei die Fische", parent=circle_1, learning_package=lpck_1) + circle_1 = CircleFactory(title="Basis", parent=lp, topic=tp, description=""" +In diesem Circle erklären wir dir, wie der Lehrgang +Versicherungsvermittler / in " aufgebaut ist. Zudem vermitteln wir dir die wichtigsten Grundlagen, +damit erfolgreich mit deinem Lernpfad starten kannst.""") tp = TopicFactory(title="Gewinnen von Kunden", learning_path=lp) @@ -60,7 +54,8 @@ von Neukunden zu benützen tp = TopicFactory(title="Beraten der Kunden", learning_path=lp) circle_3 = CircleFactory(title="Einstieg", parent=lp, topic=tp) - circle_4 = CircleFactory(title="Analyse", parent=lp, topic=tp, + + circe_analyse = CircleFactory(title="Analyse", parent=lp, topic=tp, description="""Nach dem Gespräch werten sie die Analyse aus und erstellen mit den zur Verfügung stehenden Systemen formal korrekte Lösungsvorschläge bzw. Ausschreibungen. Je nach Komplexität der Situation ziehen sie die nötigen @@ -73,93 +68,220 @@ von Neukunden zu benützen — Lösungsvorschläge zu skizzieren und zu visualisieren""") - sequence_1 = LearningSequenceFactory(title="Starten", circle=circle_4) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_1) + LearningSequenceFactory(title='Starten', parent=circe_analyse) + LearningUnitFactory( + title='Einleitung Circle "Anlayse"', + parent=circe_analyse, + minutes=15, + contents=[('video', VideoBlockFactory())] + ) - learning_unit = LearningUnitFactory(title='Einleitung Circle "Anlayse"', parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title='** Einstieg Video"', parent=circle_4, learning_package=lpck_1) - video_url = "https://www.vbv.ch/fileadmin/vbv/Videos/Statements_Externe/Janos_M/Testimonial_Janos_Mischler_PositiveEffekte.mp4" - video_title = "Ausbildung ist pflicht" - video_description = "Erfahren Sie, was für Janos Mischler die positiven Aspekte von ständiger Weiterbildung sind – aus fachlicher und aus persönlicher Sicht." - video_block = VideoBlockFactory(content_type="video", url=video_url, title=video_title, description=video_description) - learning_unit.contents.append(('video', video_block)) - learning_unit.save() + LearningSequenceFactory(title='Beobachten', parent=circe_analyse, icon='IconLsWatch') + LearningUnitFactory( + title='Ermittlung des Kundenbedarfs', + parent=circe_analyse, + package='Absicherung der Familie', + minutes=30, + contents=[('podcast', PodcastBlockFactory())] + ) + LearningUnitFactory( + title='Kundenbedürfnisse erkennen', + parent=circe_analyse, + package='Absicherung der Familie', + minutes=30, + contents=[('competence', CompetenceBlockFactory())] + ) + LearningUnitFactory( + title='Was braucht eine Familie?', + parent=circe_analyse, + package='Absicherung der Familie', + minutes=60, + contents=[('exercise', ExerciseBlockFactory())] + ) + LearningUnitFactory( + title='Selbsteinschätzung', + parent=circe_analyse, + package='Absicherung der Familie', + minutes=0, + contents=[('self_evaluation', SelfEvaluationBlockFactory())] + ) - learning_unit = LearningUnitFactory.create(title='** Web Based Training"', parent=circle_4, learning_package=lpck_1) - wbt_url = "web_based_trainings/rise_cmi5_test_export/scormcontent/index.html" - wbt_block = WebBasedTrainingBlockFactory(content_type="web_based_training", url=wbt_url) - learning_unit.contents.append(('web_based_training', wbt_block)) - learning_unit.save() + LearningSequenceFactory(title='Anwenden', parent=circe_analyse, icon='IconLsApply') + LearningUnitFactory( + title='Versicherungsbedarf für Familien', + parent=circe_analyse, + package='Prämien einsparen', + minutes=60, + contents=[('exercise', ExerciseBlockFactory())] + ) + LearningUnitFactory( + title='Alles klar?', + parent=circe_analyse, + package='Prämien einsparen', + minutes=60, + contents=[('exercise', ExerciseBlockFactory())] + ) + LearningUnitFactory( + title='Selbsteinschätzung', + parent=circe_analyse, + package='Prämien einsparen', + minutes=0, + contents=[('self_evaluation', SelfEvaluationBlockFactory())] + ) - learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4, learning_package=lpck_1) + LearningUnitFactory( + title='GmbH oder AG', + parent=circe_analyse, + package='Sich selbständig machen', + minutes=120, + contents=[('video', VideoBlockFactory())] + ) + LearningUnitFactory( + title='Tiertherapie Patrizia Feller', + parent=circe_analyse, + package='Sich selbständig machen', + minutes=120, + contents=[('exercise', ExerciseBlockFactory())] + ) + LearningUnitFactory( + title='Selbsteinschätzung', + parent=circe_analyse, + package='Sich selbständig machen', + minutes=0, + contents=[('self_evaluation', SelfEvaluationBlockFactory())] + ) - sequence_2 = LearningSequenceFactory.create(title="Beobachten", circle=circle_4) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_2) - - learning_unit = LearningUnitFactory.create(title="Mein Motorfahrzeug kaufen", parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title="Sich selbständig machen", parent=circle_4, learning_package=lpck_1) - - sequence_3 = LearningSequenceFactory.create(title="Anwenden", circle=circle_4) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_3) - - learning_unit = LearningUnitFactory.create(title="Nora kauft sich ein neues Auto", parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title="Manuel träumt von einem neuen Tesla", parent=circle_4, learning_package=lpck_1) - - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_3) - - learning_unit = LearningUnitFactory.create(title="Deine Erkenntnisse und Learnings", parent=circle_4, learning_package=lpck_1) - - sequence_4 = LearningSequenceFactory.create(title="Üben", circle=circle_4) - - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_4) - learning_unit = LearningUnitFactory.create(title="Ermittlung des Kundenbedarfs", parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title="Aktives Zuhören", parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title="In Bildern Sprechen", parent=circle_4, learning_package=lpck_1) - - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_4) - learning_unit = LearningUnitFactory.create(title="Priorisieren des Bedarfs", parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title="Zusammenfassung des Bedarfs", parent=circle_4, learning_package=lpck_1) - - sequence_5 = LearningSequenceFactory.create(title="Testen", circle=circle_4) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_5) - - learning_unit = LearningUnitFactory.create(title="Bedarfsfragen", parent=circle_4, learning_package=lpck_1) - learning_unit = LearningUnitFactory.create(title="Andwendung der Fragetechniken", parent=circle_4, learning_package=lpck_1) - - sequence_5 = LearningSequenceFactory.create(title="Vernetzen", circle=circle_4) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_5) - - learning_unit = LearningUnitFactory.create(title="Online Training", parent=circle_4, learning_package=lpck_1) - - sequence_6 = LearningSequenceFactory.create(title="Beenden", circle=circle_4) - lpck_1 = LearningPackageFactory(title="Wunderbar !", learning_sequence=sequence_6) - - learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4, learning_package=lpck_1) - - circle_5 = CircleFactory.create(title="Lösung", - parent=lp, - topic=tp, - goals="""— Die Daten des Kunden korrekt in die notwendigen Systeme einzutragen - — Fachspezialisten beizuziehen, falls dies angezeigt ist - — Mit den zur Verfügung stehenden Systemen korrekte Lösungsvorschläge - (z.B. Offerten oder Ausschreibungen) zu verfassen - — Falls nötig die Lösungsvorschläge dem Underwriting weiterzuleiten und - Unklarheiten zu bereinigen """) - - circle_6 = CircleFactory.create(title="Abschluss", - parent=lp, - topic=tp, - goals="""— Je nach Komplexität der Lösungsvorschläge (z.B. Offerten oder Offertvergleich) einen Fachspezialisten aufzubieten - — Sich kundenorientiert auf das Gespräch vorzubereiten und sich passend zu präsentieren""") - - tp = TopicFactory.create(title="Betreuen und Ausbauen des Kundenstamms", learning_path=lp) - circle_7 = CircleFactory.create(title="Betreuen", parent=lp, topic=tp) - - tp = TopicFactory.create(title="Prüfung", is_visible=False, learning_path=lp) - circle_7 = CircleFactory.create(title="Prüfungsvorbereitung", parent=lp, topic=tp) + LearningUnitFactory( + title='Motorfahrzeugversicherung', + parent=circe_analyse, + package='Auto verkaufen', + minutes=240, + contents=[('competence', CompetenceBlockFactory())] + ) + LearningUnitFactory( + title='Nora kauft sich ein neues Auto', + parent=circe_analyse, + package='Auto verkaufen', + minutes=60, + contents=[('podcast', PodcastBlockFactory())] + ) + LearningUnitFactory( + title='Ermittlung des Kundenbedarfs', + parent=circe_analyse, + package='Auto verkaufen', + minutes=120, + contents=[('document', DocumentBlockFactory())] + ) + LearningUnitFactory( + title='Motorfahrzeug kaufen', + parent=circe_analyse, + package='Auto verkaufen', + minutes=120, + contents=[('exercise', ExerciseBlockFactory())] + ) + LearningUnitFactory( + title='Selbsteinschätzung', + parent=circe_analyse, + package='Auto verkaufen', + minutes=0, + contents=[('self_evaluation', SelfEvaluationBlockFactory())] + ) + LearningSequenceFactory(title='Üben', parent=circe_analyse, icon='IconLsPractice') + LearningUnitFactory( + title='Hausrat', + parent=circe_analyse, + package='Kind zieht von zu Hause aus', + minutes=120, + contents=[('competence', CompetenceBlockFactory())] + ) + LearningUnitFactory( + title='Privathaftpflicht', + parent=circe_analyse, + package='Kind zieht von zu Hause aus', + minutes=60, + contents=[('competence', CompetenceBlockFactory())] + ) + LearningUnitFactory( + title='Kind zieht von zu Hause aus', + parent=circe_analyse, + package='Kind zieht von zu Hause aus', + minutes=60, + contents=[('competence', CompetenceBlockFactory())] + ) + LearningUnitFactory( + title='Selbsteinschätzung', + parent=circe_analyse, + package='Kind zieht von zu Hause aus', + minutes=0, + contents=[('self_evaluation', SelfEvaluationBlockFactory())] + ) + # learning_unit = LearningUnitFactory.create(title='** Einstieg Video"', parent=circle_4) + # video_url = "https://www.vbv.ch/fileadmin/vbv/Videos/Statements_Externe/Janos_M/Testimonial_Janos_Mischler_PositiveEffekte.mp4" + # video_title = "Ausbildung ist pflicht" + # video_description = "Erfahren Sie, was für Janos Mischler die positiven Aspekte von ständiger Weiterbildung sind – aus fachlicher und aus persönlicher Sicht." + # video_block = VideoBlockFactory(content_type="video", url=video_url, title=video_title, description=video_description) + # learning_unit.contents.append(('video', video_block)) + # learning_unit.save() + # + # learning_unit = LearningUnitFactory.create(title='** Web Based Training"', parent=circle_4) + # wbt_url = "web_based_trainings/rise_cmi5_test_export/scormcontent/index.html" + # wbt_block = WebBasedTrainingBlockFactory(content_type="web_based_training", url=wbt_url) + # learning_unit.contents.append(('web_based_training', wbt_block)) + # learning_unit.save() + # learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4) + # + # sequence_2 = LearningSequenceFactory.create(title="Beobachten", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Mein Motorfahrzeug kaufen", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Sich selbständig machen", parent=circle_4) + # + # sequence_3 = LearningSequenceFactory.create(title="Anwenden", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Nora kauft sich ein neues Auto", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Manuel träumt von einem neuen Tesla", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Deine Erkenntnisse und Learnings", parent=circle_4) + # + # sequence_4 = LearningSequenceFactory.create(title="Üben", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Ermittlung des Kundenbedarfs", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Aktives Zuhören", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="In Bildern Sprechen", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Priorisieren des Bedarfs", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Zusammenfassung des Bedarfs", parent=circle_4) + # + # sequence_5 = LearningSequenceFactory.create(title="Testen", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Bedarfsfragen", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Andwendung der Fragetechniken", parent=circle_4) + # + # sequence_5 = LearningSequenceFactory.create(title="Vernetzen", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Online Training", parent=circle_4) + # + # sequence_6 = LearningSequenceFactory.create(title="Beenden", parent=circle_4) + # learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4) + # + # circle_5 = CircleFactory.create(title="Lösung", + # parent=lp, + # topic=tp, + # goals="""— Die Daten des Kunden korrekt in die notwendigen Systeme einzutragen + # — Fachspezialisten beizuziehen, falls dies angezeigt ist + # — Mit den zur Verfügung stehenden Systemen korrekte Lösungsvorschläge + # (z.B. Offerten oder Ausschreibungen) zu verfassen + # — Falls nötig die Lösungsvorschläge dem Underwriting weiterzuleiten und + # Unklarheiten zu bereinigen """) + # + # circle_6 = CircleFactory.create(title="Abschluss", + # parent=lp, + # topic=tp, + # goals="""— Je nach Komplexität der Lösungsvorschläge (z.B. Offerten oder Offertvergleich) einen Fachspezialisten aufzubieten + # — Sich kundenorientiert auf das Gespräch vorzubereiten und sich passend zu präsentieren""") + # + # tp = TopicFactory.create(title="Betreuen und Ausbauen des Kundenstamms", learning_path=lp) + # circle_7 = CircleFactory.create(title="Betreuen", parent=lp, topic=tp) + # + # tp = TopicFactory.create(title="Prüfung", is_visible=False, learning_path=lp) + # circle_7 = CircleFactory.create(title="Prüfungsvorbereitung", parent=lp, topic=tp) + # all pages belong to 'admin' by default + Page.objects.update(owner=user) def delete_default_learning_path(): diff --git a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py new file mode 100644 index 00000000..da06e5aa --- /dev/null +++ b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py @@ -0,0 +1,101 @@ +import factory +import wagtail_factories + +from vbv_lernwelt.learnpath.models import LearningPath, Topic, Circle, LearningSequence, LearningUnit +from vbv_lernwelt.learnpath.models_learning_unit_content import VideoBlock, RiseTrainingBlock, PodcastBlock, \ + CompetenceBlock, ExerciseBlock, SelfEvaluationBlock, DocumentBlock, KnowledgeBlock + + +class LearningPathFactory(wagtail_factories.PageFactory): + title = "Versicherungsvermittler/in" + + class Meta: + model = LearningPath + + +class TopicFactory(factory.django.DjangoModelFactory): + title = "Gewinnen von Kunden" + is_visible = True + + class Meta: + model = Topic + + +class CircleFactory(wagtail_factories.PageFactory): + title = "Gewinnen" + + class Meta: + model = Circle + + +class LearningSequenceFactory(wagtail_factories.PageFactory): + title = "Grundlagen" + + class Meta: + model = LearningSequence + + +class LearningUnitFactory(wagtail_factories.PageFactory): + title = "Herzlich Willkommen" + + class Meta: + model = LearningUnit + + +class VideoBlockFactory(wagtail_factories.StructBlockFactory): + url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" + description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam" + + class Meta: + model = VideoBlock + + +class RiseTrainingBlockFactory(wagtail_factories.StructBlockFactory): + url = "https://www.example.com" + description = "Beispiel Rise Modul" + + class Meta: + model = RiseTrainingBlock + + +class PodcastBlockFactory(wagtail_factories.StructBlockFactory): + description = "Beispiel Podcast" + url = "https://docs.wagtail.org/en/stable/topics/streamfield.html" + + class Meta: + model = PodcastBlock + + +class CompetenceBlockFactory(wagtail_factories.StructBlockFactory): + description = "Beispiel Kompetenz" + + class Meta: + model = CompetenceBlock + + +class ExerciseBlockFactory(wagtail_factories.StructBlockFactory): + description = "Beispiel Aufgabe" + + class Meta: + model = ExerciseBlock + + +class SelfEvaluationBlockFactory(wagtail_factories.StructBlockFactory): + description = "Beispiel Selbsteinschätzung" + + class Meta: + model = SelfEvaluationBlock + + +class DocumentBlockFactory(wagtail_factories.StructBlockFactory): + description = "Beispiel Dokument" + + class Meta: + model = DocumentBlock + + +class KnowledgeBlockFactory(wagtail_factories.StructBlockFactory): + description = "Beispiel Wissen" + + class Meta: + model = KnowledgeBlock diff --git a/server/vbv_lernwelt/learnpath/tests/learningpath_factories.py b/server/vbv_lernwelt/learnpath/tests/learningpath_factories.py deleted file mode 100644 index 3e9ff9c8..00000000 --- a/server/vbv_lernwelt/learnpath/tests/learningpath_factories.py +++ /dev/null @@ -1,62 +0,0 @@ -import wagtail_factories -import factory -from vbv_lernwelt.learnpath.models import LearningPath, Topic, Circle, LearningSequence, LearningUnit, LearningPackage -from vbv_lernwelt.learnpath.models_learning_unit_content import VideoBlock, WebBasedTrainingBlock - - -class LearningPathFactory(wagtail_factories.PageFactory): - title = "Versicherungsvermittler/in" - - class Meta: - model = LearningPath - - -class TopicFactory(factory.django.DjangoModelFactory): - title = "Gewinnen von Kunden" - is_visible = True - - class Meta: - model = Topic - - -class CircleFactory(wagtail_factories.PageFactory): - title = "Gewinnen" - - class Meta: - model = Circle - - -class LearningSequenceFactory(factory.django.DjangoModelFactory): - title = "Grundlagen" - - class Meta: - model = LearningSequence - -class LearningPackageFactory(factory.django.DjangoModelFactory): - title = "Whatever" - - class Meta: - model = LearningPackage - - -class LearningUnitFactory(wagtail_factories.PageFactory): - title = "Herzlich Willkommen" - - class Meta: - model = LearningUnit - - -class VideoBlockFactory(wagtail_factories.StructBlockFactory): - title = "Ausbildung ist Pflicht" - url = "https://www.vbv.ch/fileadmin/vbv/Videos/Statements_Externe/Janos_M/Testimonial_Janos_Mischler_PositiveEffekte.mp4" - - class Meta: - model = VideoBlock - -class WebBasedTrainingBlockFactory(wagtail_factories.StructBlockFactory): - title = "Beispiel Rise Modul" - url = "https://docs.wagtail.org/en/stable/topics/streamfield.html" - - class Meta: - model = WebBasedTrainingBlock - diff --git a/server/vbv_lernwelt/learnpath/tests/test_api.py b/server/vbv_lernwelt/learnpath/tests/test_api.py index 05f08641..8b412f7e 100644 --- a/server/vbv_lernwelt/learnpath/tests/test_api.py +++ b/server/vbv_lernwelt/learnpath/tests/test_api.py @@ -1,5 +1,3 @@ -from django.test import TestCase - from rest_framework.test import APITestCase from vbv_lernwelt.core.admin import User @@ -14,8 +12,8 @@ class TestRetrieveLearingPathContents(APITestCase): def setUpClass(cls) -> None: super(TestRetrieveLearingPathContents, cls).setUpClass() create_locales_for_wagtail() - create_default_learning_path() create_default_users() + create_default_learning_path() def setUp(self) -> None: qs = LearningPath.objects.filter(title="Versicherungsvermittler/in") diff --git a/server/vbv_lernwelt/learnpath/tests/test_create_default_competences.py b/server/vbv_lernwelt/learnpath/tests/test_create_default_competences.py index 714dc179..64cdd3c5 100644 --- a/server/vbv_lernwelt/learnpath/tests/test_create_default_competences.py +++ b/server/vbv_lernwelt/learnpath/tests/test_create_default_competences.py @@ -1,10 +1,6 @@ -from django.conf import settings from django.test import TestCase -from wagtail.core.models import Locale -from vbv_lernwelt.learnpath.models import LearningPath from vbv_lernwelt.learnpath.tests.create_default_competences import create_default_competences -from vbv_lernwelt.learnpath.tests.create_default_learning_path import create_default_learning_path class TestCreateDefaultCompetences(TestCase): diff --git a/server/vbv_lernwelt/learnpath/urls.py b/server/vbv_lernwelt/learnpath/urls.py new file mode 100644 index 00000000..2bae3734 --- /dev/null +++ b/server/vbv_lernwelt/learnpath/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import url, include +from django.urls import path +from rest_framework.routers import DefaultRouter + +from . import views +from .views import circle_view + +urlpatterns = [ + path(r"api/circle//", circle_view, name="circle_view"), +] diff --git a/server/vbv_lernwelt/learnpath/views.py b/server/vbv_lernwelt/learnpath/views.py index 91ea44a2..4607a9d3 100644 --- a/server/vbv_lernwelt/learnpath/views.py +++ b/server/vbv_lernwelt/learnpath/views.py @@ -1,3 +1,13 @@ -from django.shortcuts import render - # Create your views here. +from rest_framework.decorators import api_view +from rest_framework.response import Response + +from vbv_lernwelt.learnpath.models import Circle +from vbv_lernwelt.learnpath.serializers import CircleSerializer + + +@api_view(['GET']) +def circle_view(request, slug): + circle = Circle.objects.get(slug=slug) + serializer = CircleSerializer(circle) + return Response(serializer.data) diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.eot new file mode 100755 index 00000000..43840179 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.ttf new file mode 100755 index 00000000..1f6e55fb Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.woff new file mode 100755 index 00000000..2b756c77 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.woff2 new file mode 100755 index 00000000..4db55870 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Black.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.eot new file mode 100755 index 00000000..6ccea109 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.ttf new file mode 100755 index 00000000..86cfc3bb Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.woff new file mode 100755 index 00000000..30caac65 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.woff2 new file mode 100755 index 00000000..8800f8ef Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BlackItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.eot new file mode 100755 index 00000000..65cc044a Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.ttf new file mode 100755 index 00000000..bc671d4a Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.woff new file mode 100755 index 00000000..60b9a814 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.woff2 new file mode 100755 index 00000000..9bca229a Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Bold.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.eot new file mode 100755 index 00000000..b27274c8 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.ttf new file mode 100755 index 00000000..36647edd Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.woff new file mode 100755 index 00000000..3d3fa6dc Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.woff2 new file mode 100755 index 00000000..90bd2212 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BoldItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.eot new file mode 100755 index 00000000..bf40f0d4 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.ttf new file mode 100755 index 00000000..38e22e57 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.woff new file mode 100755 index 00000000..33f16cb5 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.woff2 new file mode 100755 index 00000000..7b56d65a Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Book.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.eot new file mode 100755 index 00000000..805d1c3c Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.ttf new file mode 100755 index 00000000..910c5b77 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.woff new file mode 100755 index 00000000..295de32d Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.woff2 new file mode 100755 index 00000000..b5201016 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-BookItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.eot new file mode 100755 index 00000000..b779341e Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.ttf new file mode 100755 index 00000000..77392ae3 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.woff new file mode 100755 index 00000000..02e6f0c9 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.woff2 new file mode 100755 index 00000000..0747ca31 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Light.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.eot new file mode 100755 index 00000000..064d9a74 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.ttf new file mode 100755 index 00000000..f5cdbc86 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.woff new file mode 100755 index 00000000..1bd74a57 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.woff2 new file mode 100755 index 00000000..49e26fe6 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-LightItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.eot new file mode 100755 index 00000000..d62b9915 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.ttf new file mode 100755 index 00000000..e9ef17d1 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.ttf differ diff --git a/client/src/assets/styles/fonts/BuenosAires-Regular.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.woff old mode 100644 new mode 100755 similarity index 100% rename from client/src/assets/styles/fonts/BuenosAires-Regular.woff rename to server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.woff diff --git a/client/src/assets/styles/fonts/BuenosAires-Regular.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.woff2 old mode 100644 new mode 100755 similarity index 100% rename from client/src/assets/styles/fonts/BuenosAires-Regular.woff2 rename to server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Regular.woff2 diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.eot new file mode 100755 index 00000000..3ae174bc Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.ttf new file mode 100755 index 00000000..81374fe6 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.woff new file mode 100755 index 00000000..2e0889d8 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.woff2 new file mode 100755 index 00000000..39c0e920 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-RegularItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.eot new file mode 100755 index 00000000..78d1b865 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.ttf new file mode 100755 index 00000000..e0b5fe52 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.woff new file mode 100755 index 00000000..aa20c689 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.woff2 new file mode 100755 index 00000000..9cb4f1e9 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBold.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.eot new file mode 100755 index 00000000..d8eec76a Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.ttf new file mode 100755 index 00000000..00ae26e4 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.woff new file mode 100755 index 00000000..92b710b1 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.woff2 new file mode 100755 index 00000000..82dce38f Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-SemiBoldItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.eot new file mode 100755 index 00000000..7e394af0 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.ttf new file mode 100755 index 00000000..e8ba546d Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.woff new file mode 100755 index 00000000..9a054826 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.woff2 new file mode 100755 index 00000000..d6a56503 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-Thin.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.eot b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.eot new file mode 100755 index 00000000..ab6bfd00 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.eot differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.ttf b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.ttf new file mode 100755 index 00000000..b4c34807 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.ttf differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.woff b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.woff new file mode 100755 index 00000000..f6bdb10a Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.woff differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.woff2 b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.woff2 new file mode 100755 index 00000000..8a668321 Binary files /dev/null and b/server/vbv_lernwelt/static/fonts/BuenosAires/BuenosAires-ThinItalic.woff2 differ diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/demo.html b/server/vbv_lernwelt/static/fonts/BuenosAires/demo.html new file mode 100755 index 00000000..808df051 --- /dev/null +++ b/server/vbv_lernwelt/static/fonts/BuenosAires/demo.html @@ -0,0 +1,569 @@ + + + + + + + + + Transfonter demo + + + + +
+
+

Buenos Aires Bold

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: bold;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-Bold.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Light Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 300;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-LightItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Regular

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: normal;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-Regular.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Bold Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: bold;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-BoldItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires SemiBold

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 600;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-SemiBold.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires SemiBold Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 600;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-SemiBoldItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Thin

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 100;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-Thin.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Thin Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 100;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-ThinItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Book

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 300;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-Book.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Book Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 300;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-BookItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Light

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 300;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-Light.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Black

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 900;
+    font-style: normal;
+}
+
+<link rel="preload" href="BuenosAires-Black.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Black Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: 900;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-BlackItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+

Buenos Aires Regular Italic

+
.your-style {
+    font-family: 'Buenos Aires';
+    font-weight: normal;
+    font-style: italic;
+}
+
+<link rel="preload" href="BuenosAires-RegularItalic.woff2" as="font" type="font/woff2" crossorigin>
+
+

+ abcdefghijklmnopqrstuvwxyz
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ 0123456789.:,;()*!?'@#<>$%&^+-=~ +

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+

The quick brown fox jumps over the lazy dog.

+
+
+ +
+ + diff --git a/server/vbv_lernwelt/static/fonts/BuenosAires/stylesheet.css b/server/vbv_lernwelt/static/fonts/BuenosAires/stylesheet.css new file mode 100755 index 00000000..385f19d2 --- /dev/null +++ b/server/vbv_lernwelt/static/fonts/BuenosAires/stylesheet.css @@ -0,0 +1,182 @@ +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-Bold.eot'); + src: local('Buenos Aires Bold'), local('BuenosAires-Bold'), + url('BuenosAires-Bold.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-Bold.woff2') format('woff2'), + url('BuenosAires-Bold.woff') format('woff'), + url('BuenosAires-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-LightItalic.eot'); + src: local('Buenos Aires Light Italic'), local('BuenosAires-LightItalic'), + url('BuenosAires-LightItalic.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-LightItalic.woff2') format('woff2'), + url('BuenosAires-LightItalic.woff') format('woff'), + url('BuenosAires-LightItalic.ttf') format('truetype'); + font-weight: 300; + font-style: italic; + font-display: swap; +} + +/*@font-face {*/ +/* font-family: 'Buenos Aires';*/ +/* src: url('BuenosAires-Regular.eot');*/ +/* src: local('Buenos Aires Regular'), local('BuenosAires-Regular'),*/ +/* url('BuenosAires-Regular.eot?#iefix') format('embedded-opentype'),*/ +/* url('BuenosAires-Regular.woff2') format('woff2'),*/ +/* url('BuenosAires-Regular.woff') format('woff'),*/ +/* url('BuenosAires-Regular.ttf') format('truetype');*/ +/* font-weight: normal;*/ +/* font-style: normal;*/ +/* font-display: swap;*/ +/*}*/ + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-BoldItalic.eot'); + src: local('Buenos Aires Bold Italic'), local('BuenosAires-BoldItalic'), + url('BuenosAires-BoldItalic.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-BoldItalic.woff2') format('woff2'), + url('BuenosAires-BoldItalic.woff') format('woff'), + url('BuenosAires-BoldItalic.ttf') format('truetype'); + font-weight: bold; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-SemiBold.eot'); + src: local('Buenos Aires SemiBold'), local('BuenosAires-SemiBold'), + url('BuenosAires-SemiBold.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-SemiBold.woff2') format('woff2'), + url('BuenosAires-SemiBold.woff') format('woff'), + url('BuenosAires-SemiBold.ttf') format('truetype'); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-SemiBoldItalic.eot'); + src: local('Buenos Aires SemiBold Italic'), local('BuenosAires-SemiBoldItalic'), + url('BuenosAires-SemiBoldItalic.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-SemiBoldItalic.woff2') format('woff2'), + url('BuenosAires-SemiBoldItalic.woff') format('woff'), + url('BuenosAires-SemiBoldItalic.ttf') format('truetype'); + font-weight: 600; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-Thin.eot'); + src: local('Buenos Aires Thin'), local('BuenosAires-Thin'), + url('BuenosAires-Thin.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-Thin.woff2') format('woff2'), + url('BuenosAires-Thin.woff') format('woff'), + url('BuenosAires-Thin.ttf') format('truetype'); + font-weight: 100; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-ThinItalic.eot'); + src: local('Buenos Aires Thin Italic'), local('BuenosAires-ThinItalic'), + url('BuenosAires-ThinItalic.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-ThinItalic.woff2') format('woff2'), + url('BuenosAires-ThinItalic.woff') format('woff'), + url('BuenosAires-ThinItalic.ttf') format('truetype'); + font-weight: 100; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-Book.eot'); + src: local('Buenos Aires Book'), local('BuenosAires-Book'), + url('BuenosAires-Book.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-Book.woff2') format('woff2'), + url('BuenosAires-Book.woff') format('woff'), + url('BuenosAires-Book.ttf') format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-BookItalic.eot'); + src: local('Buenos Aires Book Italic'), local('BuenosAires-BookItalic'), + url('BuenosAires-BookItalic.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-BookItalic.woff2') format('woff2'), + url('BuenosAires-BookItalic.woff') format('woff'), + url('BuenosAires-BookItalic.ttf') format('truetype'); + font-weight: normal; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-Light.eot'); + src: local('Buenos Aires Light'), local('BuenosAires-Light'), + url('BuenosAires-Light.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-Light.woff2') format('woff2'), + url('BuenosAires-Light.woff') format('woff'), + url('BuenosAires-Light.ttf') format('truetype'); + font-weight: 300; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-Black.eot'); + src: local('Buenos Aires Black'), local('BuenosAires-Black'), + url('BuenosAires-Black.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-Black.woff2') format('woff2'), + url('BuenosAires-Black.woff') format('woff'), + url('BuenosAires-Black.ttf') format('truetype'); + font-weight: 900; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Buenos Aires'; + src: url('BuenosAires-BlackItalic.eot'); + src: local('Buenos Aires Black Italic'), local('BuenosAires-BlackItalic'), + url('BuenosAires-BlackItalic.eot?#iefix') format('embedded-opentype'), + url('BuenosAires-BlackItalic.woff2') format('woff2'), + url('BuenosAires-BlackItalic.woff') format('woff'), + url('BuenosAires-BlackItalic.ttf') format('truetype'); + font-weight: 900; + font-style: italic; + font-display: swap; +} + +/*@font-face {*/ +/* font-family: 'Buenos Aires';*/ +/* src: url('BuenosAires-RegularItalic.eot');*/ +/* src: local('Buenos Aires Regular Italic'), local('BuenosAires-RegularItalic'),*/ +/* url('BuenosAires-RegularItalic.eot?#iefix') format('embedded-opentype'),*/ +/* url('BuenosAires-RegularItalic.woff2') format('woff2'),*/ +/* url('BuenosAires-RegularItalic.woff') format('woff'),*/ +/* url('BuenosAires-RegularItalic.ttf') format('truetype');*/ +/* font-weight: normal;*/ +/* font-style: italic;*/ +/* font-display: swap;*/ +/*}*/ + diff --git a/server/vbv_lernwelt/static/vue/.gitkeep b/server/vbv_lernwelt/static/vue/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/templates/admin/index.html b/server/vbv_lernwelt/templates/admin/index.html new file mode 100644 index 00000000..61e80fee --- /dev/null +++ b/server/vbv_lernwelt/templates/admin/index.html @@ -0,0 +1,15 @@ +{% extends "admin/index.html" %} + +{% block content %} +
+ {% include "admin/app_list.html" with app_list=app_list show_changelinks=True %} + +
+
+ {% csrf_token %} + +
+
+
+ +{% endblock %} diff --git a/server/vbv_lernwelt/templates/base.html b/server/vbv_lernwelt/templates/base.html index d6cf109e..714c1c6e 100644 --- a/server/vbv_lernwelt/templates/base.html +++ b/server/vbv_lernwelt/templates/base.html @@ -8,37 +8,28 @@ - - {% block css %} + - - {% endblock %} {# Placed at the top of the document so pages load faster with defer #} - {% block javascript %} - - - {% endblock javascript %} -{% include "core/partials/header.html" %} -
{% if messages %} diff --git a/server/vbv_lernwelt/templates/vue/.gitkeep b/server/vbv_lernwelt/templates/vue/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tailwind.config.js b/tailwind.config.js index d1a6cdf4..a22e83ee 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,18 +1,57 @@ +const colors = require('tailwindcss/colors') + module.exports = { content: [ './client/index.html', './client/src/**/*.{vue,js,ts,jsx,tsx}', './server/vbv_lernwelt/**/*.{html,js}', -], + ], theme: { - colors: { - 'white': '#FFFFFF', - 'blue-dark': '#00224D' - }, fontFamily: { - sans: ['BuenosAires', 'sans-serif'], + sans: ['Buenos Aires', 'sans-serif'], }, - extend: {}, + extend: { + spacing: { + '128': '32rem', + } + }, + colors: { + transparent: 'transparent', + current: 'currentColor', + 'white': '#ffffff', + 'blue': { + 900: '#00224D', + }, + 'sky': { + 500: '#41B5FA', + }, + 'orange': { + 500: '#FE955A', + }, + 'green': { + 500: '#3EDF9C', + }, + 'red': { + 500: '#DE3618', + }, + 'gray': { + 100: '#EDF2F6', + 300: '#E0E5EC', + 500: '#B1C1CA', + 700: '#6F787E', + 900: '#0A0A0A', + }, + } }, - plugins: [], + safelist: [{ + pattern: /bg-(blue|sky|orange|green|red)-(500)/, + }, { + pattern: /bg-gray-(100|300|500|700|900)/, + }, + 'bg-blue-900', + ], + plugins: [ + require('@tailwindcss/typography'), + require('@tailwindcss/forms'), + ], } diff --git a/tailwind/input.css b/tailwind/input.css index 48373905..e4d1da7c 100644 --- a/tailwind/input.css +++ b/tailwind/input.css @@ -2,16 +2,57 @@ @tailwind components; @tailwind utilities; +html { + @apply text-gray-900 +} + @layer base { + h1 { - @apply text-4xl font-bold + @apply text-4xl md:text-5xl xl:text-7xl font-bold + } + + .heading-1 { + @apply text-4xl md:text-5xl xl:text-7xl font-bold } h2 { - @apply text-2xl font-semibold + @apply text-3xl xl:text-4xl font-bold + } + + .heading-2 { + @apply text-3xl xl:text-4xl font-bold } h3 { - @apply text-lg font-medium italic + @apply text-2xl font-bold + } + + .heading-3 { + @apply text-3xl xl:text-4xl font-bold + } + + .link { + @apply underline underline-offset-2 } } + +@layer components { + .circle-title { + @apply text-9xl font-bold + } + + .btn-primary { + @apply font-bold py-2 px-4 align-middle inline-block bg-blue-900 text-white border-2 border-blue-900 hover:bg-sky-500 + } + + .btn-secondary { + @apply font-bold py-2 px-4 align-middle inline-block bg-white text-blue-900 border-2 border-blue-900 hover:bg-sky-500 + } + + .btn-blue { + @apply font-bold py-2 px-4 align-middle inline-block bg-sky-500 text-blue-900 border-2 border-sky-500 hover:bg-blue-900 hover:text-white + } + +} +