Add Edoniq testblock
WIP: Add sso code Update content name WIP: Add redirect endpoint mend Fix after rebase WIP: Update model WIP: Add extended time test url Update trufflehog config, use sso Update test
This commit is contained in:
parent
f7de5bae47
commit
c140f225ea
|
|
@ -106,6 +106,9 @@
|
|||
"noDueDatesAvailable": "Keine Termine vorhanden",
|
||||
"showAllDueDates": "Alle Termine anzeigen"
|
||||
},
|
||||
"edoniqTest": {
|
||||
"qualifiesForExtendedTime": "Ich habe Anrecht auf einen Nachteilsausgleich."
|
||||
},
|
||||
"feedback": {
|
||||
"answers": "Antworten",
|
||||
"areYouSatisfied": "Wie zufrieden bist du?",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import LearningContentContainer from "@/pages/learningPath/learningContentPage/LearningContentContainer.vue";
|
||||
import DocumentListBlock from "@/pages/learningPath/learningContentPage/blocks/DocumentListBlock.vue";
|
||||
import TestBlock from "@/pages/learningPath/learningContentPage/blocks/TestBlock.vue";
|
||||
import EdoniqTestBlock from "@/pages/learningPath/learningContentPage/blocks/EdoniqTestBlock.vue";
|
||||
import { useCircleStore } from "@/stores/circle";
|
||||
import type { LearningContent, LearningContentType } from "@/types";
|
||||
import eventBus from "@/utils/eventBus";
|
||||
|
|
@ -35,7 +35,7 @@ const COMPONENTS: Record<LearningContentType, Component> = {
|
|||
"learnpath.LearningContentMediaLibrary": MediaLibraryBlock,
|
||||
"learnpath.LearningContentPlaceholder": PlaceholderBlock,
|
||||
"learnpath.LearningContentRichText": RichTextBlock,
|
||||
"learnpath.LearningContentTest": TestBlock,
|
||||
"learnpath.LearningContentEdoniqTest": EdoniqTestBlock,
|
||||
"learnpath.LearningContentVideo": VideoBlock,
|
||||
};
|
||||
const DEFAULT_BLOCK = PlaceholderBlock;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
<script setup lang="ts">
|
||||
import { useTranslation } from "i18next-vue";
|
||||
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
||||
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
|
||||
import type { LearningContentEdoniqTest } from "@/types";
|
||||
import { ref } from "vue";
|
||||
import * as log from "loglevel";
|
||||
import { itPost } from "@/fetchHelpers";
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const props = defineProps<{
|
||||
content: LearningContentEdoniqTest;
|
||||
}>();
|
||||
|
||||
const termsAccepted = ref(false);
|
||||
const extendedTimeTest = ref(false);
|
||||
|
||||
async function startTest() {
|
||||
log.info("start test", props.content);
|
||||
const response = await itPost("/api/core/edoniq-test/redirect/", {
|
||||
learning_content_id: props.content.id,
|
||||
extended_time_test: extendedTimeTest.value,
|
||||
});
|
||||
log.info(response.redirect_url);
|
||||
window.open(response.redirect_url, "_blank");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LearningContentSimpleLayout
|
||||
:title="props.content.title"
|
||||
:learning-content="props.content"
|
||||
>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div class="container-medium">
|
||||
<p
|
||||
v-if="props.content.description"
|
||||
class="default-wagtail-rich-text text-large my-4"
|
||||
v-html="props.content.description"
|
||||
></p>
|
||||
|
||||
<div class="my-8">
|
||||
<ItCheckbox
|
||||
v-if="props.content.checkbox_text"
|
||||
:checkbox-item="{
|
||||
label: props.content.checkbox_text,
|
||||
value: termsAccepted,
|
||||
checked: termsAccepted,
|
||||
}"
|
||||
@toggle="termsAccepted = !termsAccepted"
|
||||
/>
|
||||
</div>
|
||||
<div class="my-8">
|
||||
<ItCheckbox
|
||||
v-if="props.content.has_extended_time_test"
|
||||
:checkbox-item="{
|
||||
label: t('edoniqTest.qualifiesForExtendedTime'),
|
||||
value: extendedTimeTest,
|
||||
checked: extendedTimeTest,
|
||||
}"
|
||||
@toggle="extendedTimeTest = !extendedTimeTest"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="my-8">
|
||||
<button
|
||||
:disabled="!termsAccepted"
|
||||
class="btn-primary inline-flex items-center"
|
||||
@click="startTest()"
|
||||
>
|
||||
Test starten
|
||||
<it-icon-external-link class="it-icon ml-2 h-5 w-5"></it-icon-external-link>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</LearningContentSimpleLayout>
|
||||
</template>
|
||||
|
||||
<!--<template>-->
|
||||
|
||||
<!-- <div class="container-medium">-->
|
||||
|
||||
<!-- <div class="lg:mt-8">-->
|
||||
|
||||
<!-- <h1>{{ content.title }}</h1>-->
|
||||
|
||||
<!-- <p class="text-large my-4 lg:my-8">{{ value.description }}</p>-->
|
||||
|
||||
<!-- <a :href="value.url" target="_blank" class="button btn-primary">Test starten</a>-->
|
||||
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- </div>-->
|
||||
|
||||
<!--</template>-->
|
||||
|
||||
<!--<script setup lang="ts">-->
|
||||
|
||||
<!--import type { LearningContent } from "@/types";-->
|
||||
|
||||
<!--interface Value {-->
|
||||
|
||||
<!-- description: string;-->
|
||||
|
||||
<!-- url: string;-->
|
||||
|
||||
<!--}-->
|
||||
|
||||
<!--defineProps<{-->
|
||||
|
||||
<!-- value: Value;-->
|
||||
|
||||
<!-- content: LearningContent;-->
|
||||
|
||||
<!--}>();-->
|
||||
|
||||
<!--</script>-->
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
||||
import LearningContentSimpleLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentSimpleLayout.vue";
|
||||
import type { LearningContentTest } from "@/types";
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
content: LearningContentTest;
|
||||
}>();
|
||||
|
||||
const checked = ref(false);
|
||||
|
||||
function toggleChecked() {
|
||||
checked.value = !checked.value;
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
window.open(props.content.content_url, "_blank");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LearningContentSimpleLayout
|
||||
:title="props.content.title"
|
||||
:learning-content="props.content"
|
||||
>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div class="container-medium">
|
||||
<p
|
||||
v-if="props.content.description"
|
||||
class="default-wagtail-rich-text text-large my-4"
|
||||
v-html="props.content.description"
|
||||
></p>
|
||||
|
||||
<div class="my-8">
|
||||
<ItCheckbox
|
||||
v-if="props.content.checkbox_text"
|
||||
:checkbox-item="{
|
||||
label: props.content.checkbox_text,
|
||||
value: checked,
|
||||
checked: checked,
|
||||
}"
|
||||
@toggle="toggleChecked()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="my-8">
|
||||
<button
|
||||
:disabled="!checked"
|
||||
class="btn-primary inline-flex items-center"
|
||||
@click="startTest()"
|
||||
>
|
||||
Test starten
|
||||
<it-icon-external-link class="it-icon ml-2 h-5 w-5"></it-icon-external-link>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</LearningContentSimpleLayout>
|
||||
</template>
|
||||
|
|
@ -26,7 +26,7 @@ function isLearningContentType(object: any): object is LearningContent {
|
|||
object?.content_type === "learnpath.LearningContentMediaLibrary" ||
|
||||
object?.content_type === "learnpath.LearningContentPlaceholder" ||
|
||||
object?.content_type === "learnpath.LearningContentRichText" ||
|
||||
object?.content_type === "learnpath.LearningContentTest" ||
|
||||
object?.content_type === "learnpath.LearningContentEdoniqTest" ||
|
||||
object?.content_type === "learnpath.LearningContentVideo"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export type LearningContent =
|
|||
| LearningContentMediaLibrary
|
||||
| LearningContentPlaceholder
|
||||
| LearningContentRichText
|
||||
| LearningContentTest
|
||||
| LearningContentEdoniqTest
|
||||
| LearningContentVideo;
|
||||
|
||||
export type LearningContentType = LearningContent["content_type"];
|
||||
|
|
@ -93,9 +93,10 @@ export interface LearningContentRichText extends LearningContentInterface {
|
|||
readonly text: string;
|
||||
}
|
||||
|
||||
export interface LearningContentTest extends LearningContentInterface {
|
||||
readonly content_type: "learnpath.LearningContentTest";
|
||||
export interface LearningContentEdoniqTest extends LearningContentInterface {
|
||||
readonly content_type: "learnpath.LearningContentEdoniqTest";
|
||||
readonly checkbox_text: string;
|
||||
readonly has_extended_time_test: boolean;
|
||||
}
|
||||
|
||||
export interface LearningContentVideo extends LearningContentInterface {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function learningContentTypeData(
|
|||
return { title: t("mediaLibrary.title"), icon: "it-icon-lc-media-library" };
|
||||
case "learnpath.LearningContentVideo":
|
||||
return { title: t("learningContentTypes.video"), icon: "it-icon-lc-video" };
|
||||
case "learnpath.LearningContentTest":
|
||||
case "learnpath.LearningContentEdoniqTest":
|
||||
return { title: t("learningContentTypes.test"), icon: "it-icon-lc-test" };
|
||||
case "learnpath.LearningContentRichText":
|
||||
return { title: t("learningContentTypes.text"), icon: "it-icon-lc-resource" };
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -640,6 +640,11 @@ GRAPHENE = {
|
|||
# ],
|
||||
# }
|
||||
|
||||
# edoniq
|
||||
|
||||
EDONIQ_ENV = env("IT_EDONIQ_ENV", default="STEAGTEST")
|
||||
EDONIQ_CERTIFICATE = env("IT_EDONIQ_CERTIFICATE", default="")
|
||||
|
||||
# Notifications
|
||||
# django-notifications
|
||||
DJANGO_NOTIFICATIONS_CONFIG = {"SOFT_DELETE": True}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ from django.urls import include, path, re_path, register_converter
|
|||
from django.urls.converters import IntConverter
|
||||
from django.views import defaults as default_views
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django_ratelimit.exceptions import Ratelimited
|
||||
from graphene_django.views import GraphQLView
|
||||
from ratelimit.exceptions import Ratelimited
|
||||
|
||||
from vbv_lernwelt.assignment.views import request_assignment_completion_status
|
||||
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
|
||||
|
|
@ -41,6 +41,7 @@ from vbv_lernwelt.edoniq_test.views import (
|
|||
export_students,
|
||||
export_students_and_trainers,
|
||||
export_trainers,
|
||||
get_edoniq_token_redirect,
|
||||
)
|
||||
from vbv_lernwelt.feedback.views import (
|
||||
get_expert_feedbacks_for_course,
|
||||
|
|
@ -146,10 +147,9 @@ urlpatterns = [
|
|||
path(r'api/core/feedback/<str:course_session_id>/<str:circle_id>/', get_feedback_for_circle,
|
||||
name='feedback_for_circle'),
|
||||
|
||||
path("server/graphql/",
|
||||
csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
|
||||
|
||||
# edoniq test
|
||||
path(r'api/core/edoniq-test/redirect/', get_edoniq_token_redirect,
|
||||
name='get_edoniq_token_redirect'),
|
||||
path(r'api/core/edoniq-test/export-users/', export_students, name='edoniq_export_students'),
|
||||
path(r'api/core/edoniq-test/export-trainers/', export_trainers, name='edoniq_export_trainers'),
|
||||
path(r'api/core/edoniq-test/export-users-trainers/', export_students_and_trainers,
|
||||
|
|
@ -172,6 +172,8 @@ urlpatterns = [
|
|||
name="t2l_sync",
|
||||
),
|
||||
|
||||
path("server/graphql/",
|
||||
csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
|
||||
# testing and debug
|
||||
path('server/raise_error/',
|
||||
user_passes_test(lambda u: u.is_superuser, login_url='/login/')(
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
# This file is autogenerated by pip-compile with python 3.10
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file=requirements-dev.txt requirements-dev.in
|
||||
# pip-compile requirements-dev.in
|
||||
#
|
||||
aniso8601==9.0.1
|
||||
# via graphene
|
||||
anyascii==0.3.1
|
||||
anyascii==0.3.2
|
||||
# via wagtail
|
||||
anyio==3.5.0
|
||||
anyio==3.7.1
|
||||
# via watchfiles
|
||||
appnope==0.1.3
|
||||
# via ipython
|
||||
|
|
@ -16,64 +16,64 @@ argon2-cffi==21.3.0
|
|||
# via -r requirements.in
|
||||
argon2-cffi-bindings==21.2.0
|
||||
# via argon2-cffi
|
||||
asgiref==3.5.0
|
||||
asgiref==3.7.2
|
||||
# via django
|
||||
astroid==2.11.2
|
||||
astroid==2.15.6
|
||||
# via pylint
|
||||
asttokens==2.0.5
|
||||
asttokens==2.2.1
|
||||
# via stack-data
|
||||
async-timeout==4.0.2
|
||||
# via redis
|
||||
attrs==21.4.0
|
||||
attrs==23.1.0
|
||||
# via
|
||||
# jsonschema
|
||||
# pytest
|
||||
# referencing
|
||||
# usort
|
||||
authlib==1.0.0
|
||||
authlib==1.2.1
|
||||
# via -r requirements.in
|
||||
azure-core==1.26.4
|
||||
azure-core==1.29.1
|
||||
# via
|
||||
# azure-identity
|
||||
# azure-storage-blob
|
||||
azure-identity==1.13.0
|
||||
azure-identity==1.14.0
|
||||
# via -r requirements.in
|
||||
azure-storage-blob==12.16.0
|
||||
azure-storage-blob==12.17.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-storages
|
||||
backcall==0.2.0
|
||||
# via ipython
|
||||
beautifulsoup4==4.9.3
|
||||
beautifulsoup4==4.11.2
|
||||
# via wagtail
|
||||
black==22.10.0
|
||||
black==23.7.0
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# ufmt
|
||||
boto3==1.26.11
|
||||
boto3==1.28.23
|
||||
# via -r requirements.in
|
||||
botocore==1.29.11
|
||||
botocore==1.31.23
|
||||
# via
|
||||
# boto3
|
||||
# s3transfer
|
||||
brotli==1.0.9
|
||||
# via whitenoise
|
||||
build==0.8.0
|
||||
build==0.10.0
|
||||
# via pip-tools
|
||||
caprover-api @ git+https://github.com/iterativ/Caprover-API.git@5013f8fc929e8e3281b9d609e968a782e8e99530
|
||||
# via -r requirements-dev.in
|
||||
certifi==2021.10.8
|
||||
certifi==2023.7.22
|
||||
# via
|
||||
# requests
|
||||
# sentry-sdk
|
||||
cffi==1.15.0
|
||||
cffi==1.15.1
|
||||
# via
|
||||
# argon2-cffi-bindings
|
||||
# cryptography
|
||||
cfgv==3.3.1
|
||||
# via pre-commit
|
||||
charset-normalizer==2.0.12
|
||||
charset-normalizer==3.2.0
|
||||
# via requests
|
||||
click==8.1.1
|
||||
click==8.1.6
|
||||
# via
|
||||
# -r requirements.in
|
||||
# black
|
||||
|
|
@ -83,17 +83,13 @@ click==8.1.1
|
|||
# ufmt
|
||||
# usort
|
||||
# uvicorn
|
||||
concurrent-log-handler==0.9.20
|
||||
concurrent-log-handler==0.9.24
|
||||
# via -r requirements.in
|
||||
coreapi==2.3.3
|
||||
# via djangorestframework-stubs
|
||||
coreschema==0.0.4
|
||||
# via coreapi
|
||||
coverage==6.3.2
|
||||
coverage==7.2.7
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# django-coverage-plugin
|
||||
cryptography==36.0.2
|
||||
cryptography==41.0.3
|
||||
# via
|
||||
# authlib
|
||||
# azure-identity
|
||||
|
|
@ -104,15 +100,15 @@ decorator==5.1.1
|
|||
# via
|
||||
# ipdb
|
||||
# ipython
|
||||
deprecated==1.2.13
|
||||
# via redis
|
||||
dill==0.3.4
|
||||
defusedxml==0.7.1
|
||||
# via willow
|
||||
dill==0.3.7
|
||||
# via pylint
|
||||
distlib==0.3.4
|
||||
distlib==0.3.7
|
||||
# via virtualenv
|
||||
dj-database-url==1.0.0
|
||||
dj-database-url==2.0.0
|
||||
# via -r requirements.in
|
||||
django==3.2.13
|
||||
django==3.2.20
|
||||
# via
|
||||
# -r requirements.in
|
||||
# dj-database-url
|
||||
|
|
@ -138,27 +134,26 @@ django==3.2.13
|
|||
# graphene-django
|
||||
# jsonfield
|
||||
# wagtail
|
||||
# wagtail-grapple
|
||||
# wagtail-localize
|
||||
django-click==2.3.0
|
||||
# via -r requirements.in
|
||||
django-cors-headers==3.11.0
|
||||
django-cors-headers==4.2.0
|
||||
# via -r requirements.in
|
||||
django-coverage-plugin==2.0.2
|
||||
django-coverage-plugin==3.1.0
|
||||
# via -r requirements-dev.in
|
||||
django-csp==3.7
|
||||
# via -r requirements.in
|
||||
django-debug-toolbar==3.2.4
|
||||
django-debug-toolbar==4.1.0
|
||||
# via -r requirements-dev.in
|
||||
django-extensions==3.2.0
|
||||
django-extensions==3.2.3
|
||||
# via -r requirements-dev.in
|
||||
django-filter==21.1
|
||||
django-filter==23.2
|
||||
# via wagtail
|
||||
django-ipware==4.0.2
|
||||
django-ipware==5.0.0
|
||||
# via -r requirements.in
|
||||
django-jsonform==2.17.0
|
||||
django-jsonform==2.18.0
|
||||
# via -r requirements.in
|
||||
django-model-utils==4.2.0
|
||||
django-model-utils==4.3.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-notifications-hq
|
||||
|
|
@ -168,64 +163,70 @@ django-notifications-hq==1.8.2
|
|||
# via -r requirements.in
|
||||
django-permissionedforms==0.1
|
||||
# via wagtail
|
||||
django-ratelimit==3.0.1
|
||||
django-ratelimit==4.1.0
|
||||
# via -r requirements.in
|
||||
django-redis==5.2.0
|
||||
django-redis==5.3.0
|
||||
# via -r requirements.in
|
||||
django-storages[azure]==1.13.1
|
||||
django-storages[azure]==1.13.2
|
||||
# via -r requirements.in
|
||||
django-stubs==1.10.1
|
||||
django-stubs==4.2.3
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# djangorestframework-stubs
|
||||
django-stubs-ext==0.4.0
|
||||
django-stubs-ext==4.2.2
|
||||
# via django-stubs
|
||||
django-taggit==2.1.0
|
||||
django-taggit==4.0.0
|
||||
# via wagtail
|
||||
django-treebeard==4.5.1
|
||||
django-treebeard==4.7
|
||||
# via wagtail
|
||||
django-watchfiles @ https://github.com/q0w/django-watchfiles/archive/issue-1.zip
|
||||
# via -r requirements-dev.in
|
||||
djangorestframework==3.13.1
|
||||
djangorestframework==3.14.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# drf-spectacular
|
||||
# wagtail
|
||||
djangorestframework-stubs==1.4.0
|
||||
djangorestframework-stubs==3.14.2
|
||||
# via -r requirements-dev.in
|
||||
draftjs-exporter==2.1.7
|
||||
# via wagtail
|
||||
drf-spectacular==0.22.0
|
||||
drf-spectacular==0.26.4
|
||||
# via -r requirements.in
|
||||
environs==9.5.0
|
||||
# via -r requirements.in
|
||||
et-xmlfile==1.1.0
|
||||
# via openpyxl
|
||||
executing==0.8.3
|
||||
exceptiongroup==1.1.2
|
||||
# via
|
||||
# anyio
|
||||
# pytest
|
||||
executing==1.2.0
|
||||
# via stack-data
|
||||
factory-boy==3.2.1
|
||||
factory-boy==3.3.0
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# wagtail-factories
|
||||
faker==13.3.4
|
||||
faker==19.3.0
|
||||
# via factory-boy
|
||||
filelock==3.6.0
|
||||
filelock==3.12.2
|
||||
# via virtualenv
|
||||
flake8==4.0.1
|
||||
filetype==1.2.0
|
||||
# via willow
|
||||
flake8==6.1.0
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# flake8-isort
|
||||
flake8-isort==4.1.1
|
||||
flake8-isort==6.0.0
|
||||
# via -r requirements-dev.in
|
||||
gitdb==4.0.9
|
||||
gitdb==4.0.10
|
||||
# via gitdb2
|
||||
gitdb2==4.0.2
|
||||
# via gitpython
|
||||
gitpython==3.0.6
|
||||
# via trufflehog
|
||||
graphene==3.2.2
|
||||
graphene==3.3
|
||||
# via graphene-django
|
||||
graphene-django==3.0.0
|
||||
graphene-django==3.1.5
|
||||
# via wagtail-grapple
|
||||
graphql-core==3.2.3
|
||||
# via
|
||||
|
|
@ -236,63 +237,59 @@ graphql-relay==3.2.0
|
|||
# via
|
||||
# graphene
|
||||
# graphene-django
|
||||
gunicorn==20.1.0
|
||||
gunicorn==21.2.0
|
||||
# via -r requirements.in
|
||||
h11==0.13.0
|
||||
h11==0.14.0
|
||||
# via uvicorn
|
||||
html5lib==1.1
|
||||
# via wagtail
|
||||
httptools==0.4.0
|
||||
httptools==0.6.0
|
||||
# via uvicorn
|
||||
identify==2.4.12
|
||||
identify==2.5.26
|
||||
# via pre-commit
|
||||
idna==3.3
|
||||
idna==3.4
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
inflection==0.5.1
|
||||
# via drf-spectacular
|
||||
iniconfig==1.1.1
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
ipdb==0.13.9
|
||||
ipdb==0.13.13
|
||||
# via -r requirements-dev.in
|
||||
ipython==8.2.0
|
||||
ipython==8.14.0
|
||||
# via ipdb
|
||||
isodate==0.6.1
|
||||
# via azure-storage-blob
|
||||
isort==5.10.1
|
||||
isort==5.12.0
|
||||
# via
|
||||
# flake8-isort
|
||||
# pylint
|
||||
itypes==1.2.0
|
||||
# via coreapi
|
||||
jedi==0.18.1
|
||||
jedi==0.19.0
|
||||
# via ipython
|
||||
jinja2==3.1.1
|
||||
# via coreschema
|
||||
jmespath==1.0.1
|
||||
# via
|
||||
# boto3
|
||||
# botocore
|
||||
jsonfield==3.1.0
|
||||
# via django-notifications-hq
|
||||
jsonschema==4.4.0
|
||||
jsonschema==4.19.0
|
||||
# via drf-spectacular
|
||||
jsonschema-specifications==2023.7.1
|
||||
# via jsonschema
|
||||
l18n==2021.3
|
||||
# via wagtail
|
||||
lazy-object-proxy==1.7.1
|
||||
lazy-object-proxy==1.9.0
|
||||
# via astroid
|
||||
libcst==0.4.7
|
||||
libcst==1.0.1
|
||||
# via
|
||||
# ufmt
|
||||
# usort
|
||||
markupsafe==2.1.1
|
||||
# via jinja2
|
||||
marshmallow==3.15.0
|
||||
marshmallow==3.20.1
|
||||
# via environs
|
||||
matplotlib-inline==0.1.3
|
||||
matplotlib-inline==0.1.6
|
||||
# via ipython
|
||||
mccabe==0.6.1
|
||||
mccabe==0.7.0
|
||||
# via
|
||||
# flake8
|
||||
# pylint
|
||||
|
|
@ -300,127 +297,127 @@ moreorless==0.4.0
|
|||
# via
|
||||
# ufmt
|
||||
# usort
|
||||
msal==1.22.0
|
||||
msal==1.23.0
|
||||
# via
|
||||
# azure-identity
|
||||
# msal-extensions
|
||||
msal-extensions==1.0.0
|
||||
# via azure-identity
|
||||
mypy==0.942
|
||||
mypy==1.4.1
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# django-stubs
|
||||
# djangorestframework-stubs
|
||||
mypy-extensions==0.4.3
|
||||
mypy-extensions==1.0.0
|
||||
# via
|
||||
# black
|
||||
# mypy
|
||||
# typing-inspect
|
||||
nodeenv==1.6.0
|
||||
nodeenv==1.8.0
|
||||
# via pre-commit
|
||||
openpyxl==3.1.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# wagtail
|
||||
packaging==21.3
|
||||
packaging==23.1
|
||||
# via
|
||||
# black
|
||||
# build
|
||||
# gunicorn
|
||||
# marshmallow
|
||||
# pytest
|
||||
# pytest-sugar
|
||||
# redis
|
||||
parso==0.8.3
|
||||
# via jedi
|
||||
pathspec==0.9.0
|
||||
pathspec==0.11.2
|
||||
# via
|
||||
# black
|
||||
# trailrunner
|
||||
pep517==0.12.0
|
||||
# via build
|
||||
pexpect==4.8.0
|
||||
# via ipython
|
||||
pickleshare==0.7.5
|
||||
# via ipython
|
||||
pillow==9.0.1
|
||||
pillow==10.0.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pillow-heif
|
||||
# wagtail
|
||||
pip-tools==6.9.0
|
||||
pillow-heif==0.13.0
|
||||
# via willow
|
||||
pip-tools==7.3.0
|
||||
# via -r requirements-dev.in
|
||||
platformdirs==2.5.1
|
||||
platformdirs==3.10.0
|
||||
# via
|
||||
# black
|
||||
# pylint
|
||||
# virtualenv
|
||||
pluggy==1.0.0
|
||||
pluggy==1.2.0
|
||||
# via pytest
|
||||
polib==1.1.1
|
||||
polib==1.2.0
|
||||
# via wagtail-localize
|
||||
portalocker==2.4.0
|
||||
portalocker==2.7.0
|
||||
# via
|
||||
# concurrent-log-handler
|
||||
# msal-extensions
|
||||
pre-commit==2.17.0
|
||||
pre-commit==3.3.3
|
||||
# via -r requirements-dev.in
|
||||
promise==2.3
|
||||
# via graphene-django
|
||||
prompt-toolkit==3.0.28
|
||||
prompt-toolkit==3.0.39
|
||||
# via ipython
|
||||
psycopg2-binary==2.9.3
|
||||
psycopg2-binary==2.9.7
|
||||
# via -r requirements.in
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
pure-eval==0.2.2
|
||||
# via stack-data
|
||||
py==1.11.0
|
||||
# via pytest
|
||||
pycodestyle==2.8.0
|
||||
pycodestyle==2.11.0
|
||||
# via flake8
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
pyflakes==2.4.0
|
||||
pycryptodome==3.18.0
|
||||
# via -r requirements.in
|
||||
pyflakes==3.1.0
|
||||
# via flake8
|
||||
pygments==2.11.2
|
||||
pygments==2.16.1
|
||||
# via ipython
|
||||
pyjwt[crypto]==2.7.0
|
||||
pyjwt[crypto]==2.8.0
|
||||
# via msal
|
||||
pylint==2.13.4
|
||||
pylint==2.17.5
|
||||
# via
|
||||
# pylint-django
|
||||
# pylint-plugin-utils
|
||||
pylint-django==2.5.3
|
||||
# via -r requirements-dev.in
|
||||
pylint-plugin-utils==0.7
|
||||
pylint-plugin-utils==0.8.2
|
||||
# via pylint-django
|
||||
pyparsing==3.0.7
|
||||
# via packaging
|
||||
pyrsistent==0.18.1
|
||||
# via jsonschema
|
||||
pytest==7.1.3
|
||||
pyproject-hooks==1.0.0
|
||||
# via build
|
||||
pytest==7.4.0
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# pytest-django
|
||||
# pytest-sugar
|
||||
pytest-django==4.5.2
|
||||
# via -r requirements-dev.in
|
||||
pytest-sugar==0.9.4
|
||||
pytest-sugar==0.9.7
|
||||
# via -r requirements-dev.in
|
||||
python-dateutil==2.8.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# botocore
|
||||
# faker
|
||||
python-dotenv==0.20.0
|
||||
python-dotenv==1.0.0
|
||||
# via
|
||||
# environs
|
||||
# uvicorn
|
||||
python-http-client==3.3.7
|
||||
# via sendgrid
|
||||
python-json-logger==2.0.2
|
||||
python-json-logger==2.0.7
|
||||
# via -r requirements.in
|
||||
python-slugify==6.1.1
|
||||
python-slugify==8.0.1
|
||||
# via -r requirements.in
|
||||
pytz==2022.1
|
||||
pytz==2023.3
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django
|
||||
|
|
@ -428,94 +425,97 @@ pytz==2022.1
|
|||
# django-notifications-hq
|
||||
# djangorestframework
|
||||
# l18n
|
||||
pyyaml==6.0
|
||||
pyyaml==6.0.1
|
||||
# via
|
||||
# caprover-api
|
||||
# drf-spectacular
|
||||
# libcst
|
||||
# pre-commit
|
||||
# uvicorn
|
||||
redis==4.2.1
|
||||
redis==4.6.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-redis
|
||||
requests==2.27.1
|
||||
referencing==0.30.2
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
requests==2.31.0
|
||||
# via
|
||||
# azure-core
|
||||
# caprover-api
|
||||
# coreapi
|
||||
# djangorestframework-stubs
|
||||
# msal
|
||||
# wagtail
|
||||
s3transfer==0.6.0
|
||||
rpds-py==0.9.2
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
s3transfer==0.6.1
|
||||
# via boto3
|
||||
sendgrid==6.9.7
|
||||
sendgrid==6.10.0
|
||||
# via -r requirements.in
|
||||
sentry-sdk==1.5.8
|
||||
sentry-sdk==1.29.2
|
||||
# via -r requirements.in
|
||||
six==1.16.0
|
||||
# via
|
||||
# asttokens
|
||||
# azure-core
|
||||
# azure-identity
|
||||
# django-coverage-plugin
|
||||
# html5lib
|
||||
# isodate
|
||||
# l18n
|
||||
# promise
|
||||
# python-dateutil
|
||||
# virtualenv
|
||||
smmap==5.0.0
|
||||
# via gitdb
|
||||
sniffio==1.2.0
|
||||
sniffio==1.3.0
|
||||
# via anyio
|
||||
soupsieve==2.3.2.post1
|
||||
soupsieve==2.4.1
|
||||
# via beautifulsoup4
|
||||
sqlparse==0.4.2
|
||||
sqlparse==0.4.4
|
||||
# via
|
||||
# django
|
||||
# django-debug-toolbar
|
||||
stack-data==0.2.0
|
||||
stack-data==0.6.2
|
||||
# via ipython
|
||||
starkbank-ecdsa==2.2.0
|
||||
# via sendgrid
|
||||
stdlibs==2022.6.8
|
||||
stdlibs==2022.10.9
|
||||
# via usort
|
||||
structlog==21.5.0
|
||||
structlog==23.1.0
|
||||
# via -r requirements.in
|
||||
swapper==1.3.0
|
||||
# via django-notifications-hq
|
||||
telepath==0.2
|
||||
telepath==0.3.1
|
||||
# via wagtail
|
||||
termcolor==1.1.0
|
||||
termcolor==2.3.0
|
||||
# via pytest-sugar
|
||||
testfixtures==6.18.5
|
||||
# via flake8-isort
|
||||
text-unidecode==1.3
|
||||
# via
|
||||
# graphene-django
|
||||
# python-slugify
|
||||
toml==0.10.2
|
||||
# via
|
||||
# ipdb
|
||||
# pre-commit
|
||||
# usort
|
||||
# via usort
|
||||
tomli==2.0.1
|
||||
# via
|
||||
# black
|
||||
# build
|
||||
# django-stubs
|
||||
# ipdb
|
||||
# mypy
|
||||
# pep517
|
||||
# pip-tools
|
||||
# pylint
|
||||
# pyproject-hooks
|
||||
# pytest
|
||||
tomlkit==0.11.5
|
||||
# via ufmt
|
||||
trailrunner==1.2.1
|
||||
tomlkit==0.12.1
|
||||
# via
|
||||
# pylint
|
||||
# ufmt
|
||||
trailrunner==1.4.0
|
||||
# via
|
||||
# ufmt
|
||||
# usort
|
||||
traitlets==5.1.1
|
||||
traitlets==5.9.0
|
||||
# via
|
||||
# ipython
|
||||
# matplotlib-inline
|
||||
|
|
@ -523,14 +523,23 @@ trufflehog==2.2.1
|
|||
# via -r requirements-dev.in
|
||||
trufflehogregexes==0.0.7
|
||||
# via trufflehog
|
||||
types-pytz==2021.3.6
|
||||
types-pytz==2023.3.0.0
|
||||
# via django-stubs
|
||||
types-pyyaml==6.0.5
|
||||
# via django-stubs
|
||||
typing-extensions==4.5.0
|
||||
types-pyyaml==6.0.12.11
|
||||
# via
|
||||
# django-stubs
|
||||
# djangorestframework-stubs
|
||||
types-requests==2.31.0.2
|
||||
# via djangorestframework-stubs
|
||||
types-urllib3==1.26.25.14
|
||||
# via types-requests
|
||||
typing-extensions==4.7.1
|
||||
# via
|
||||
# asgiref
|
||||
# astroid
|
||||
# azure-core
|
||||
# azure-storage-blob
|
||||
# dj-database-url
|
||||
# django-stubs
|
||||
# django-stubs-ext
|
||||
# djangorestframework-stubs
|
||||
|
|
@ -538,63 +547,60 @@ typing-extensions==4.5.0
|
|||
# mypy
|
||||
# typing-inspect
|
||||
# ufmt
|
||||
# uvicorn
|
||||
# wagtail-localize
|
||||
typing-inspect==0.8.0
|
||||
typing-inspect==0.9.0
|
||||
# via libcst
|
||||
ufmt==2.0.1
|
||||
ufmt==2.2.0
|
||||
# via -r requirements-dev.in
|
||||
uritemplate==4.1.1
|
||||
# via
|
||||
# coreapi
|
||||
# drf-spectacular
|
||||
urllib3==1.26.9
|
||||
# via drf-spectacular
|
||||
urllib3==1.26.16
|
||||
# via
|
||||
# botocore
|
||||
# requests
|
||||
# sentry-sdk
|
||||
usort==1.0.5
|
||||
usort==1.0.7
|
||||
# via ufmt
|
||||
uvicorn[standard]==0.18.3
|
||||
uvicorn[standard]==0.23.2
|
||||
# via -r requirements.in
|
||||
uvloop==0.16.0
|
||||
uvloop==0.17.0
|
||||
# via uvicorn
|
||||
virtualenv==20.14.0
|
||||
virtualenv==20.24.2
|
||||
# via pre-commit
|
||||
wagtail==4.2.2
|
||||
wagtail==5.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# wagtail-factories
|
||||
# wagtail-grapple
|
||||
# wagtail-headless-preview
|
||||
# wagtail-localize
|
||||
wagtail-factories==4.0.0
|
||||
wagtail-factories==4.1.0
|
||||
# via -r requirements.in
|
||||
wagtail-grapple==0.19.2
|
||||
wagtail-grapple==0.20.0
|
||||
# via -r requirements.in
|
||||
wagtail-headless-preview==0.4.0
|
||||
wagtail-headless-preview==0.6.0
|
||||
# via wagtail-grapple
|
||||
wagtail-localize==1.5
|
||||
wagtail-localize==1.5.1
|
||||
# via -r requirements.in
|
||||
watchfiles==0.17.0
|
||||
watchfiles==0.19.0
|
||||
# via
|
||||
# django-watchfiles
|
||||
# uvicorn
|
||||
wcwidth==0.2.5
|
||||
wcwidth==0.2.6
|
||||
# via prompt-toolkit
|
||||
webencodings==0.5.1
|
||||
# via html5lib
|
||||
websockets==10.2
|
||||
websockets==11.0.3
|
||||
# via uvicorn
|
||||
wheel==0.37.1
|
||||
wheel==0.41.1
|
||||
# via pip-tools
|
||||
whitenoise[brotli]==6.0.0
|
||||
whitenoise[brotli]==6.5.0
|
||||
# via -r requirements.in
|
||||
willow==1.4.1
|
||||
willow[heif]==1.6.1
|
||||
# via wagtail
|
||||
wrapt==1.14.0
|
||||
# via
|
||||
# astroid
|
||||
# deprecated
|
||||
wrapt==1.15.0
|
||||
# via astroid
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ structlog
|
|||
python-json-logger
|
||||
concurrent-log-handler
|
||||
python-dateutil
|
||||
pycryptodome
|
||||
|
||||
wagtail>=4
|
||||
wagtail-factories>=4
|
||||
|
|
|
|||
|
|
@ -2,75 +2,77 @@
|
|||
# This file is autogenerated by pip-compile with python 3.10
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file=requirements.txt requirements.in
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
aniso8601==9.0.1
|
||||
# via graphene
|
||||
anyascii==0.3.1
|
||||
anyascii==0.3.2
|
||||
# via wagtail
|
||||
anyio==3.5.0
|
||||
anyio==3.7.1
|
||||
# via watchfiles
|
||||
argon2-cffi==21.3.0
|
||||
# via -r requirements.in
|
||||
argon2-cffi-bindings==21.2.0
|
||||
# via argon2-cffi
|
||||
asgiref==3.5.0
|
||||
asgiref==3.7.2
|
||||
# via django
|
||||
async-timeout==4.0.2
|
||||
# via redis
|
||||
attrs==21.4.0
|
||||
# via jsonschema
|
||||
authlib==1.0.0
|
||||
attrs==23.1.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
authlib==1.2.1
|
||||
# via -r requirements.in
|
||||
azure-core==1.26.4
|
||||
azure-core==1.29.1
|
||||
# via
|
||||
# azure-identity
|
||||
# azure-storage-blob
|
||||
azure-identity==1.13.0
|
||||
azure-identity==1.14.0
|
||||
# via -r requirements.in
|
||||
azure-storage-blob==12.16.0
|
||||
azure-storage-blob==12.17.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-storages
|
||||
beautifulsoup4==4.9.3
|
||||
beautifulsoup4==4.11.2
|
||||
# via wagtail
|
||||
boto3==1.26.11
|
||||
boto3==1.28.23
|
||||
# via -r requirements.in
|
||||
botocore==1.29.11
|
||||
botocore==1.31.23
|
||||
# via
|
||||
# boto3
|
||||
# s3transfer
|
||||
brotli==1.0.9
|
||||
# via whitenoise
|
||||
certifi==2021.10.8
|
||||
certifi==2023.7.22
|
||||
# via
|
||||
# requests
|
||||
# sentry-sdk
|
||||
cffi==1.15.0
|
||||
cffi==1.15.1
|
||||
# via
|
||||
# argon2-cffi-bindings
|
||||
# cryptography
|
||||
charset-normalizer==2.0.12
|
||||
charset-normalizer==3.2.0
|
||||
# via requests
|
||||
click==8.1.1
|
||||
click==8.1.6
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-click
|
||||
# uvicorn
|
||||
concurrent-log-handler==0.9.20
|
||||
concurrent-log-handler==0.9.24
|
||||
# via -r requirements.in
|
||||
cryptography==36.0.2
|
||||
cryptography==41.0.3
|
||||
# via
|
||||
# authlib
|
||||
# azure-identity
|
||||
# azure-storage-blob
|
||||
# msal
|
||||
# pyjwt
|
||||
deprecated==1.2.13
|
||||
# via redis
|
||||
dj-database-url==1.0.0
|
||||
defusedxml==0.7.1
|
||||
# via willow
|
||||
dj-database-url==2.0.0
|
||||
# via -r requirements.in
|
||||
django==3.2.13
|
||||
django==3.2.20
|
||||
# via
|
||||
# -r requirements.in
|
||||
# dj-database-url
|
||||
|
|
@ -91,21 +93,20 @@ django==3.2.13
|
|||
# graphene-django
|
||||
# jsonfield
|
||||
# wagtail
|
||||
# wagtail-grapple
|
||||
# wagtail-localize
|
||||
django-click==2.3.0
|
||||
# via -r requirements.in
|
||||
django-cors-headers==3.11.0
|
||||
django-cors-headers==4.2.0
|
||||
# via -r requirements.in
|
||||
django-csp==3.7
|
||||
# via -r requirements.in
|
||||
django-filter==21.1
|
||||
django-filter==23.2
|
||||
# via wagtail
|
||||
django-ipware==4.0.2
|
||||
django-ipware==5.0.0
|
||||
# via -r requirements.in
|
||||
django-jsonform==2.17.0
|
||||
django-jsonform==2.18.0
|
||||
# via -r requirements.in
|
||||
django-model-utils==4.2.0
|
||||
django-model-utils==4.3.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-notifications-hq
|
||||
|
|
@ -115,36 +116,40 @@ django-notifications-hq==1.8.2
|
|||
# via -r requirements.in
|
||||
django-permissionedforms==0.1
|
||||
# via wagtail
|
||||
django-ratelimit==3.0.1
|
||||
django-ratelimit==4.1.0
|
||||
# via -r requirements.in
|
||||
django-redis==5.2.0
|
||||
django-redis==5.3.0
|
||||
# via -r requirements.in
|
||||
django-storages[azure]==1.13.1
|
||||
django-storages[azure]==1.13.2
|
||||
# via -r requirements.in
|
||||
django-taggit==2.1.0
|
||||
django-taggit==4.0.0
|
||||
# via wagtail
|
||||
django-treebeard==4.5.1
|
||||
django-treebeard==4.7
|
||||
# via wagtail
|
||||
djangorestframework==3.13.1
|
||||
djangorestframework==3.14.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# drf-spectacular
|
||||
# wagtail
|
||||
draftjs-exporter==2.1.7
|
||||
# via wagtail
|
||||
drf-spectacular==0.22.0
|
||||
drf-spectacular==0.26.4
|
||||
# via -r requirements.in
|
||||
environs==9.5.0
|
||||
# via -r requirements.in
|
||||
et-xmlfile==1.1.0
|
||||
# via openpyxl
|
||||
factory-boy==3.2.1
|
||||
exceptiongroup==1.1.2
|
||||
# via anyio
|
||||
factory-boy==3.3.0
|
||||
# via wagtail-factories
|
||||
faker==13.11.1
|
||||
faker==19.3.0
|
||||
# via factory-boy
|
||||
graphene==3.2.2
|
||||
filetype==1.2.0
|
||||
# via willow
|
||||
graphene==3.3
|
||||
# via graphene-django
|
||||
graphene-django==3.0.0
|
||||
graphene-django==3.1.5
|
||||
# via wagtail-grapple
|
||||
graphql-core==3.2.3
|
||||
# via
|
||||
|
|
@ -155,15 +160,15 @@ graphql-relay==3.2.0
|
|||
# via
|
||||
# graphene
|
||||
# graphene-django
|
||||
gunicorn==20.1.0
|
||||
gunicorn==21.2.0
|
||||
# via -r requirements.in
|
||||
h11==0.13.0
|
||||
h11==0.14.0
|
||||
# via uvicorn
|
||||
html5lib==1.1
|
||||
# via wagtail
|
||||
httptools==0.4.0
|
||||
httptools==0.6.0
|
||||
# via uvicorn
|
||||
idna==3.3
|
||||
idna==3.4
|
||||
# via
|
||||
# anyio
|
||||
# requests
|
||||
|
|
@ -177,13 +182,15 @@ jmespath==1.0.1
|
|||
# botocore
|
||||
jsonfield==3.1.0
|
||||
# via django-notifications-hq
|
||||
jsonschema==4.4.0
|
||||
jsonschema==4.19.0
|
||||
# via drf-spectacular
|
||||
jsonschema-specifications==2023.7.1
|
||||
# via jsonschema
|
||||
l18n==2021.3
|
||||
# via wagtail
|
||||
marshmallow==3.15.0
|
||||
marshmallow==3.20.1
|
||||
# via environs
|
||||
msal==1.22.0
|
||||
msal==1.23.0
|
||||
# via
|
||||
# azure-identity
|
||||
# msal-extensions
|
||||
|
|
@ -193,48 +200,49 @@ openpyxl==3.1.2
|
|||
# via
|
||||
# -r requirements.in
|
||||
# wagtail
|
||||
packaging==21.3
|
||||
packaging==23.1
|
||||
# via
|
||||
# gunicorn
|
||||
# marshmallow
|
||||
# redis
|
||||
pillow==9.0.1
|
||||
pillow==10.0.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pillow-heif
|
||||
# wagtail
|
||||
polib==1.1.1
|
||||
pillow-heif==0.13.0
|
||||
# via willow
|
||||
polib==1.2.0
|
||||
# via wagtail-localize
|
||||
portalocker==2.4.0
|
||||
portalocker==2.7.0
|
||||
# via
|
||||
# concurrent-log-handler
|
||||
# msal-extensions
|
||||
promise==2.3
|
||||
# via graphene-django
|
||||
psycopg2-binary==2.9.3
|
||||
psycopg2-binary==2.9.7
|
||||
# via -r requirements.in
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
pyjwt[crypto]==2.7.0
|
||||
pycryptodome==3.18.0
|
||||
# via -r requirements.in
|
||||
pyjwt[crypto]==2.8.0
|
||||
# via msal
|
||||
pyparsing==3.0.7
|
||||
# via packaging
|
||||
pyrsistent==0.18.1
|
||||
# via jsonschema
|
||||
python-dateutil==2.8.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# botocore
|
||||
# faker
|
||||
python-dotenv==0.20.0
|
||||
python-dotenv==1.0.0
|
||||
# via
|
||||
# environs
|
||||
# uvicorn
|
||||
python-http-client==3.3.7
|
||||
# via sendgrid
|
||||
python-json-logger==2.0.2
|
||||
python-json-logger==2.0.7
|
||||
# via -r requirements.in
|
||||
python-slugify==6.1.1
|
||||
python-slugify==8.0.1
|
||||
# via -r requirements.in
|
||||
pytz==2022.1
|
||||
pytz==2023.3
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django
|
||||
|
|
@ -242,95 +250,100 @@ pytz==2022.1
|
|||
# django-notifications-hq
|
||||
# djangorestframework
|
||||
# l18n
|
||||
pyyaml==6.0
|
||||
pyyaml==6.0.1
|
||||
# via
|
||||
# drf-spectacular
|
||||
# uvicorn
|
||||
redis==4.2.1
|
||||
redis==4.6.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-redis
|
||||
requests==2.27.1
|
||||
referencing==0.30.2
|
||||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
requests==2.31.0
|
||||
# via
|
||||
# azure-core
|
||||
# msal
|
||||
# wagtail
|
||||
s3transfer==0.6.0
|
||||
rpds-py==0.9.2
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
s3transfer==0.6.1
|
||||
# via boto3
|
||||
sendgrid==6.9.7
|
||||
sendgrid==6.10.0
|
||||
# via -r requirements.in
|
||||
sentry-sdk==1.5.8
|
||||
sentry-sdk==1.29.2
|
||||
# via -r requirements.in
|
||||
six==1.16.0
|
||||
# via
|
||||
# azure-core
|
||||
# azure-identity
|
||||
# html5lib
|
||||
# isodate
|
||||
# l18n
|
||||
# promise
|
||||
# python-dateutil
|
||||
sniffio==1.2.0
|
||||
sniffio==1.3.0
|
||||
# via anyio
|
||||
soupsieve==2.3.2.post1
|
||||
soupsieve==2.4.1
|
||||
# via beautifulsoup4
|
||||
sqlparse==0.4.2
|
||||
sqlparse==0.4.4
|
||||
# via django
|
||||
starkbank-ecdsa==2.2.0
|
||||
# via sendgrid
|
||||
structlog==21.5.0
|
||||
structlog==23.1.0
|
||||
# via -r requirements.in
|
||||
swapper==1.3.0
|
||||
# via django-notifications-hq
|
||||
telepath==0.2
|
||||
telepath==0.3.1
|
||||
# via wagtail
|
||||
text-unidecode==1.3
|
||||
# via
|
||||
# graphene-django
|
||||
# python-slugify
|
||||
typing-extensions==4.5.0
|
||||
typing-extensions==4.7.1
|
||||
# via
|
||||
# asgiref
|
||||
# azure-core
|
||||
# azure-storage-blob
|
||||
# dj-database-url
|
||||
# uvicorn
|
||||
# wagtail-localize
|
||||
uritemplate==4.1.1
|
||||
# via drf-spectacular
|
||||
urllib3==1.26.9
|
||||
urllib3==1.26.16
|
||||
# via
|
||||
# botocore
|
||||
# requests
|
||||
# sentry-sdk
|
||||
uvicorn[standard]==0.18.3
|
||||
uvicorn[standard]==0.23.2
|
||||
# via -r requirements.in
|
||||
uvloop==0.16.0
|
||||
uvloop==0.17.0
|
||||
# via uvicorn
|
||||
wagtail==4.2.2
|
||||
wagtail==5.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# wagtail-factories
|
||||
# wagtail-grapple
|
||||
# wagtail-headless-preview
|
||||
# wagtail-localize
|
||||
wagtail-factories==4.0.0
|
||||
wagtail-factories==4.1.0
|
||||
# via -r requirements.in
|
||||
wagtail-grapple==0.19.2
|
||||
wagtail-grapple==0.20.0
|
||||
# via -r requirements.in
|
||||
wagtail-headless-preview==0.4.0
|
||||
wagtail-headless-preview==0.6.0
|
||||
# via wagtail-grapple
|
||||
wagtail-localize==1.5
|
||||
wagtail-localize==1.5.1
|
||||
# via -r requirements.in
|
||||
watchfiles==0.17.0
|
||||
watchfiles==0.19.0
|
||||
# via uvicorn
|
||||
webencodings==0.5.1
|
||||
# via html5lib
|
||||
websockets==10.2
|
||||
websockets==11.0.3
|
||||
# via uvicorn
|
||||
whitenoise[brotli]==6.0.0
|
||||
whitenoise[brotli]==6.5.0
|
||||
# via -r requirements.in
|
||||
willow==1.4.1
|
||||
willow[heif]==1.6.1
|
||||
# via wagtail
|
||||
wrapt==1.14.0
|
||||
# via deprecated
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
|||
from django.shortcuts import render
|
||||
from django.template import loader
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from ratelimit.decorators import ratelimit
|
||||
from django_ratelimit.decorators import ratelimit
|
||||
from rest_framework import authentication
|
||||
from rest_framework.decorators import (
|
||||
api_view,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
|||
CircleFactory,
|
||||
LearningContentAssignmentFactory,
|
||||
LearningContentAttendanceCourseFactory,
|
||||
LearningContentEdoniqTestFactory,
|
||||
LearningContentFeedbackFactory,
|
||||
LearningContentLearningModuleFactory,
|
||||
LearningContentMediaLibraryFactory,
|
||||
|
|
@ -309,6 +310,16 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
|||
slug__startswith="test-lehrgang-assignment-fahrzeug-mein-erstes-auto"
|
||||
),
|
||||
),
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Wissens- und Verständnisfragen",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
extended_time_test_url="https://exam2.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
)
|
||||
|
||||
PerformanceCriteriaFactory(
|
||||
parent=CompetencePage.objects.get(competence_id="X1"),
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
|||
LearningContentAssignmentFactory,
|
||||
LearningContentAttendanceCourseFactory,
|
||||
LearningContentDocumentListFactory,
|
||||
LearningContentEdoniqTestFactory,
|
||||
LearningContentFeedbackFactory,
|
||||
LearningContentMediaLibraryFactory,
|
||||
LearningContentPlaceholderFactory,
|
||||
LearningContentTestFactory,
|
||||
LearningPathFactory,
|
||||
LearningSequenceFactory,
|
||||
LearningUnitFactory,
|
||||
|
|
@ -329,14 +329,14 @@ In diesem Circle erfährst du wie die überbetrieblichen Kurse aufgebaut sind. Z
|
|||
],
|
||||
)
|
||||
LearningUnitFactory(title="Kompetenznachweis", title_hidden=True, parent=circle)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Wissens- und Verständnisfragen",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
content_url="https://exam.vbv-afa.ch/",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
)
|
||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||
LearningContentFeedbackFactory(
|
||||
|
|
@ -444,14 +444,14 @@ Dans ce cercle, tu apprendras comment les cours interentreprises sont structuré
|
|||
LearningUnitFactory(
|
||||
title="Contrôle de compétences", title_hidden=True, parent=circle
|
||||
)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Questions de connaissances et de compréhension",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
content_url="https://exam.vbv-afa.ch/",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
)
|
||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||
LearningContentFeedbackFactory(
|
||||
|
|
@ -559,14 +559,14 @@ In questo Circle imparerai come sono strutturati i corsi interaziendali. Imparer
|
|||
LearningUnitFactory(
|
||||
title="Controllo delle competenze", title_hidden=True, parent=circle
|
||||
)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Domande di conoscenza e di comprensione",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
content_url="https://exam.vbv-afa.ch/",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
)
|
||||
LearningUnitFactory(title="Feedback", title_hidden=True, parent=circle)
|
||||
LearningContentFeedbackFactory(
|
||||
|
|
@ -679,14 +679,15 @@ In diesem Circle lernst du die wichtigsten Grundlagen bezüglich Versicherungswi
|
|||
)
|
||||
LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end")
|
||||
LearningUnitFactory(title="Kompetenznachweis", title_hidden=True, parent=circle)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Wissens- und Verständnisfragen",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
content_url="https://exam.vbv-afa.ch/",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096523730,2147466125",
|
||||
extended_time_test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1691157696911,2147478636",
|
||||
)
|
||||
LearningUnitFactory(title="Reflexion", title_hidden=True, parent=circle)
|
||||
LearningContentAssignmentFactory(
|
||||
|
|
@ -787,14 +788,15 @@ Dans ce cercle, tu apprends les bases les plus importantes en matière d'assuran
|
|||
LearningUnitFactory(
|
||||
title="Contrôle de compétences", title_hidden=True, parent=circle
|
||||
)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Questions de connaissances et de compréhension",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
content_url="https://exam.vbv-afa.ch/",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096523730,2147466125",
|
||||
extended_time_test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1691157696911,2147478636",
|
||||
)
|
||||
LearningUnitFactory(title="Réflexion", title_hidden=True, parent=circle)
|
||||
LearningContentAssignmentFactory(
|
||||
|
|
@ -895,14 +897,15 @@ In questo Circle imparerai le basi più importanti del settore assicurativo e de
|
|||
LearningUnitFactory(
|
||||
title="Controllo delle competenze", title_hidden=True, parent=circle
|
||||
)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Domande di conoscenza e di comprensione",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
content_url="https://exam.vbv-afa.ch/",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096523730,2147466125",
|
||||
extended_time_test_url="https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1691157696911,2147478636",
|
||||
)
|
||||
LearningUnitFactory(title="Riflessione", title_hidden=True, parent=circle)
|
||||
LearningContentAssignmentFactory(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
# Generated by Django 3.2.20 on 2023-08-17 13:14
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("learnpath", "0003_auto_20230810_0817"),
|
||||
("course", "0003_alter_coursecompletion_additional_json_data"),
|
||||
("duedate", "0002_alter_duedate_start"),
|
||||
("course_session", "0003_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="CourseSessionEdoniqTest",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"course_session",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="course.coursesession",
|
||||
),
|
||||
),
|
||||
(
|
||||
"due_date",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="edoniq_test_due_date",
|
||||
to="duedate.duedate",
|
||||
),
|
||||
),
|
||||
(
|
||||
"learning_content",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="learnpath.learningcontentedoniqtest",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -152,3 +152,22 @@ class CourseSessionAssignment(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return f"{self.course_session} - {self.learning_content}"
|
||||
|
||||
|
||||
class CourseSessionEdoniqTest(models.Model):
|
||||
course_session = models.ForeignKey(
|
||||
"course.CourseSession",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
learning_content = models.ForeignKey(
|
||||
"learnpath.LearningContentEdoniqTest",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
due_date = models.OneToOneField(
|
||||
"duedate.DueDate",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="edoniq_test_due_date",
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.course_session} - {self.learning_content}"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from wagtail.models import Page
|
|||
from vbv_lernwelt.duedate.models import DueDate
|
||||
from vbv_lernwelt.learnpath.models import (
|
||||
LearningContentAttendanceCourse,
|
||||
LearningContentTest,
|
||||
LearningContentEdoniqTest,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ class DueDateAdmin(admin.ModelAdmin):
|
|||
csd = DueDate.objects.get(id=object_id)
|
||||
kwargs["queryset"] = Page.objects.descendant_of(
|
||||
csd.course_session.course.coursepage
|
||||
).exact_type(LearningContentAttendanceCourse, LearningContentTest)
|
||||
).exact_type(LearningContentAttendanceCourse, LearningContentEdoniqTest)
|
||||
else:
|
||||
kwargs["queryset"] = Page.objects.none()
|
||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.20 on 2023-08-17 13:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("duedate", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="duedate",
|
||||
name="start",
|
||||
field=models.DateTimeField(blank=True, db_index=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LearnpathConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
|
||||
name = "vbv_lernwelt.edoniq_test"
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import base64
|
||||
from datetime import datetime
|
||||
|
||||
import structlog
|
||||
from Crypto.Cipher import PKCS1_v1_5
|
||||
from Crypto.PublicKey import RSA
|
||||
from django.conf import settings
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
CIPHER_TRANSFORMATION = "RSA/ECB/PKCS1Padding"
|
||||
|
||||
|
||||
def create_token(user_id: str):
|
||||
try:
|
||||
token = (
|
||||
f"user:{user_id};system:{settings.EDONIQ_ENV};date:"
|
||||
+ str(int(datetime.now().timestamp()))
|
||||
+ ";"
|
||||
)
|
||||
|
||||
encrypted = _encrypt_message(
|
||||
token, settings.EDONIQ_CERTIFICATE.replace("\\n", "\n")
|
||||
)
|
||||
return str(base64.b64encode(encrypted), "utf-8")
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(
|
||||
"get_edoniq_test_fail",
|
||||
label="edoniq_test_api",
|
||||
error=e,
|
||||
)
|
||||
|
||||
|
||||
def _encrypt_message(token: str, key: str):
|
||||
key = RSA.import_key(key)
|
||||
plainbytes = token.encode("utf-8")
|
||||
cipherbytes = _encrypt(plainbytes, key)
|
||||
encrypted_token = base64.b64encode(cipherbytes)
|
||||
return encrypted_token
|
||||
|
||||
|
||||
def _encrypt(token, key):
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
return cipher.encrypt(token)
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
from rest_framework.test import APITestCase
|
||||
|
||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||
from vbv_lernwelt.learnpath.models import LearningContentEdoniqTest
|
||||
|
||||
|
||||
class RedirectTestCase(APITestCase):
|
||||
def setUp(self) -> None:
|
||||
create_default_users()
|
||||
create_test_course(with_sessions=True)
|
||||
|
||||
def test_redirects_normal_test(self):
|
||||
test = LearningContentEdoniqTest.objects.get(
|
||||
title="Wissens- und Verständnisfragen"
|
||||
)
|
||||
data = {"learning_content_id": test.id, "extended_time_test": False}
|
||||
self.client.login(username="admin", password="test")
|
||||
self._test_redirect(
|
||||
data,
|
||||
"https://exam.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
)
|
||||
|
||||
def test_redirects_extended_test(self):
|
||||
test = LearningContentEdoniqTest.objects.get(
|
||||
title="Wissens- und Verständnisfragen"
|
||||
)
|
||||
data = {"learning_content_id": test.id, "extended_time_test": True}
|
||||
self.client.login(username="admin", password="test")
|
||||
self._test_redirect(
|
||||
data,
|
||||
"https://exam2.vbv-afa.ch/e-tutor/v4/user/course/pre_course_object?aid=1689096897473,2147466097",
|
||||
)
|
||||
|
||||
def _test_redirect(self, data, expected_url):
|
||||
response = self.client.post(
|
||||
"/api/core/edoniq-test/redirect/", data, format="json"
|
||||
)
|
||||
self.assertEqual(200, response.status_code)
|
||||
content = response.json()
|
||||
self.assertTrue(content["redirect_url"].startswith(expected_url))
|
||||
|
|
@ -1,3 +1,14 @@
|
|||
import structlog
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.decorators import api_view
|
||||
|
||||
from vbv_lernwelt.course.permissions import has_course_access_by_page_request
|
||||
from vbv_lernwelt.edoniq_test.edoniq_sso import create_token
|
||||
from vbv_lernwelt.learnpath.models import LearningContentEdoniqTest
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
import csv
|
||||
from datetime import date
|
||||
from itertools import chain
|
||||
|
|
@ -15,6 +26,62 @@ UK_COURSE_IDS = [COURSE_UK, COURSE_UK_FR, COURSE_UK_IT]
|
|||
DEFAULT_EXCLUDED_DOMAINS = ["eiger-versicherung.ch", "assurance.ch", "example.com"]
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
def get_edoniq_token_redirect(request):
|
||||
page_id = request.data.get("learning_content_id", "")
|
||||
extended_time_test = request.data.get("extended_time_test", False)
|
||||
|
||||
logger.debug(
|
||||
"get_edoniq_test_redirect",
|
||||
label="edoniq_test_api",
|
||||
page_id=page_id,
|
||||
user_id=request.user.id,
|
||||
extended_time_test=extended_time_test,
|
||||
)
|
||||
|
||||
if not page_id:
|
||||
return JsonResponse({"error": "learning_content_id is required"}, status=400)
|
||||
|
||||
try:
|
||||
test_page = get_object_or_404(LearningContentEdoniqTest, id=page_id)
|
||||
|
||||
if not has_course_access_by_page_request(request, test_page):
|
||||
logger.debug(
|
||||
"get_edoniq_test_redirect_permission_denied",
|
||||
label="edoniq_test_api",
|
||||
page_id=page_id,
|
||||
page_slug=test_page.slug,
|
||||
page_title=test_page.title,
|
||||
user_id=request.user.id,
|
||||
)
|
||||
raise PermissionDenied()
|
||||
|
||||
url = (
|
||||
test_page.test_url
|
||||
if not extended_time_test
|
||||
else test_page.extended_time_test_url
|
||||
)
|
||||
token = create_token(
|
||||
str(request.user.id),
|
||||
)
|
||||
redirect_url = f"{url}&ssoToken={token}"
|
||||
response_data = {"redirect_url": redirect_url}
|
||||
|
||||
logger.debug(
|
||||
"get_edoniq_test_redirect_permission_success",
|
||||
label="edoniq_test_api",
|
||||
page_id=page_id,
|
||||
page_slug=test_page.slug,
|
||||
page_title=test_page.title,
|
||||
user_id=request.user.id,
|
||||
)
|
||||
return JsonResponse(response_data, status=200)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return JsonResponse({"error": str(e)}, status=404)
|
||||
|
||||
|
||||
@staff_member_required
|
||||
def export_students(request):
|
||||
course_session_users = fetch_course_session_users(UK_COURSE_IDS)
|
||||
|
|
@ -35,6 +102,14 @@ def export_students_and_trainers(request):
|
|||
return generate_export_response(course_session_users)
|
||||
|
||||
|
||||
def fetch_course_session_users(courses: List[int]):
|
||||
# if a user is in multiple courses, he should be exported multiple times
|
||||
# todo: check if this is the case otherwise use .distinct("user")
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session__course__id__in=courses, role=CourseSessionUser.Role.MEMBER
|
||||
).order_by("user__email")
|
||||
|
||||
|
||||
def fetch_course_session_users(
|
||||
courses: List[int], role=CourseSessionUser.Role.MEMBER, excluded_domains=None
|
||||
):
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ from vbv_lernwelt.course.models import CourseCategory, CoursePage
|
|||
from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||
CircleFactory,
|
||||
LearningContentAssignmentFactory,
|
||||
LearningContentEdoniqTestFactory,
|
||||
LearningContentFeedbackFactory,
|
||||
LearningContentLearningModuleFactory,
|
||||
LearningContentMediaLibraryFactory,
|
||||
LearningContentPlaceholderFactory,
|
||||
LearningContentRichTextFactory,
|
||||
LearningContentTestFactory,
|
||||
LearningContentVideoFactory,
|
||||
LearningPathFactory,
|
||||
LearningSequenceFactory,
|
||||
|
|
@ -353,13 +353,13 @@ def create_circle_fahrzeug(lp, title="Fahrzeug"):
|
|||
title="Praxisauftrag",
|
||||
parent=circle,
|
||||
)
|
||||
LearningContentTestFactory(
|
||||
LearningContentEdoniqTestFactory(
|
||||
title="Fachcheck Fahrzeug",
|
||||
parent=circle,
|
||||
description=RichText(
|
||||
"<p>Teste dein Wissen mit dem Fachcheck. Der Test kann nur einmal durchgeführt werden.</p>"
|
||||
),
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.",
|
||||
checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden und die Redlichkeitserklärung akzeptiert habe.",
|
||||
content_url="https://s3.eu-central-1.amazonaws.com/myvbv-wbt.iterativ.ch/fachcheck-fahrzeug-xapi-LFv8YiyM/index.html#/",
|
||||
)
|
||||
LearningContentAssignmentFactory(
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ from vbv_lernwelt.learnpath.models import (
|
|||
LearningContentAssignment,
|
||||
LearningContentAttendanceCourse,
|
||||
LearningContentDocumentList,
|
||||
LearningContentEdoniqTest,
|
||||
LearningContentFeedback,
|
||||
LearningContentLearningModule,
|
||||
LearningContentMediaLibrary,
|
||||
LearningContentPlaceholder,
|
||||
LearningContentRichText,
|
||||
LearningContentTest,
|
||||
LearningContentVideo,
|
||||
LearningPath,
|
||||
LearningSequence,
|
||||
|
|
@ -46,7 +46,7 @@ class LearningContentInterface(CoursePageInterface):
|
|||
return LearningContentPlaceholderObjectType
|
||||
elif isinstance(instance, LearningContentRichText):
|
||||
return LearningContentRichTextObjectType
|
||||
elif isinstance(instance, LearningContentTest):
|
||||
elif isinstance(instance, LearningContentEdoniqTest):
|
||||
return LearningContentTestObjectType
|
||||
elif isinstance(instance, LearningContentVideo):
|
||||
return LearningContentVideoObjectType
|
||||
|
|
@ -104,7 +104,7 @@ class LearningContentMediaLibraryObjectType(DjangoObjectType):
|
|||
|
||||
class LearningContentTestObjectType(DjangoObjectType):
|
||||
class Meta:
|
||||
model = LearningContentTest
|
||||
model = LearningContentEdoniqTest
|
||||
interfaces = (LearningContentInterface,)
|
||||
fields = []
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
# Generated by Django 3.2.13 on 2023-08-10 06:17
|
||||
|
||||
import django.db.models.deletion
|
||||
import wagtail.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("course", "0003_alter_coursecompletion_additional_json_data"),
|
||||
("wagtailcore", "0083_workflowcontenttype"),
|
||||
("wagtailredirects", "0008_add_verbose_name_plural"),
|
||||
("wagtailforms", "0005_alter_formsubmission_form_data"),
|
||||
("duedate", "0001_initial"),
|
||||
("learnpath", "0002_alter_learningcontentassignment_assignment_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="LearningContentEdoniqTest",
|
||||
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)),
|
||||
("description", wagtail.fields.RichTextField(blank=True)),
|
||||
("content_url", models.TextField(blank=True)),
|
||||
("has_course_completion_status", models.BooleanField(default=True)),
|
||||
(
|
||||
"can_user_self_toggle_course_completion",
|
||||
models.BooleanField(default=False),
|
||||
),
|
||||
("checkbox_text", models.TextField(blank=True)),
|
||||
("test_url", models.TextField(blank=True)),
|
||||
("extended_time_test_url", models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("wagtailcore.page",),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="LearningContentTest",
|
||||
),
|
||||
]
|
||||
|
|
@ -77,7 +77,7 @@ class Circle(CourseBasePage):
|
|||
"learnpath.LearningContentMediaLibrary",
|
||||
"learnpath.LearningContentPlaceholder",
|
||||
"learnpath.LearningContentRichText",
|
||||
"learnpath.LearningContentTest",
|
||||
"learnpath.LearningContentEdoniqTest",
|
||||
"learnpath.LearningContentVideo",
|
||||
]
|
||||
|
||||
|
|
@ -321,19 +321,28 @@ class LearningContentMediaLibrary(LearningContent):
|
|||
can_user_self_toggle_course_completion = models.BooleanField(default=True)
|
||||
|
||||
|
||||
class LearningContentTest(LearningContent):
|
||||
class LearningContentEdoniqTest(LearningContent):
|
||||
serialize_field_names = LearningContent.serialize_field_names + [
|
||||
"checkbox_text",
|
||||
"has_extended_time_test",
|
||||
]
|
||||
parent_page_types = ["learnpath.Circle"]
|
||||
subpage_types = []
|
||||
|
||||
checkbox_text = models.TextField(blank=True)
|
||||
test_url = models.TextField(blank=True)
|
||||
extended_time_test_url = models.TextField(blank=True)
|
||||
|
||||
content_panels = LearningContent.content_panels + [
|
||||
FieldPanel("checkbox_text", classname="Text"),
|
||||
FieldPanel("test_url", classname="Text"),
|
||||
FieldPanel("extended_time_test_url", classname="Text"),
|
||||
]
|
||||
|
||||
@property
|
||||
def has_extended_time_test(self):
|
||||
return bool(self.extended_time_test_url)
|
||||
|
||||
|
||||
class LearningContentRichText(LearningContent):
|
||||
text = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ from vbv_lernwelt.learnpath.models import (
|
|||
LearningContentAssignment,
|
||||
LearningContentAttendanceCourse,
|
||||
LearningContentDocumentList,
|
||||
LearningContentEdoniqTest,
|
||||
LearningContentFeedback,
|
||||
LearningContentLearningModule,
|
||||
LearningContentMediaLibrary,
|
||||
LearningContentPlaceholder,
|
||||
LearningContentRichText,
|
||||
LearningContentTest,
|
||||
LearningContentVideo,
|
||||
LearningPath,
|
||||
LearningSequence,
|
||||
|
|
@ -149,14 +149,14 @@ class LearningContentMediaLibraryFactory(wagtail_factories.PageFactory):
|
|||
model = LearningContentMediaLibrary
|
||||
|
||||
|
||||
class LearningContentTestFactory(wagtail_factories.PageFactory):
|
||||
class LearningContentEdoniqTestFactory(wagtail_factories.PageFactory):
|
||||
title = "Fachcheck"
|
||||
minutes = 0
|
||||
content_url = ""
|
||||
description = RichText("")
|
||||
|
||||
class Meta:
|
||||
model = LearningContentTest
|
||||
model = LearningContentEdoniqTest
|
||||
|
||||
|
||||
class LearningContentRichTextFactory(wagtail_factories.PageFactory):
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ env/docker_local.env
|
|||
server/vbv_lernwelt/assignment/creators/create_assignments.py
|
||||
server/vbv_lernwelt/static/
|
||||
server/vbv_lernwelt/media/
|
||||
server/vbv_lernwelt/edoniq_test/certificates/test.key
|
||||
supabase.md
|
||||
scripts/supabase/init.sql
|
||||
ramon.wenger@iterativ.ch.gpg
|
||||
|
|
|
|||
Loading…
Reference in New Issue