Client can complete learning contents

This commit is contained in:
Daniel Egger 2022-06-08 17:47:08 +02:00
parent 0d54437bf8
commit c334c25f1c
8 changed files with 133 additions and 51 deletions

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
defineProps(['learningSequence'])
</script>
@ -28,7 +29,10 @@ defineProps(['learningSequence'])
v-for="learningContent in learningUnit.learningContents"
class="flex items-center gap-4 pb-3"
>
<it-icon-checkbox-unchecked/>
<div @click="$emit('toggleLearningContentCheckbox', learningContent)">
<it-icon-checkbox-checked v-if="learningContent.completed" />
<it-icon-checkbox-unchecked v-else />
</div>
<div>{{ learningContent.contents[0].type }}: {{ learningContent.title }}</div>
</div>

View File

@ -0,0 +1,53 @@
import { getCookieValue } from '@/router/guards';
class FetchError extends Error {
constructor(response, message = 'HTTP error ' + response.status) {
super(message);
this.response = response;
}
}
export const itFetch = (url, options) => {
return fetch(url, options).then(response => {
if (!response.ok) {
throw new FetchError(response);
}
return response;
});
};
export const itPost = (url, data, options) => {
options = Object.assign({}, options);
const headers = Object.assign({
Accept: 'application/json',
'Content-Type': 'application/json;charset=UTF-8',
}, options?.headers);
if (options?.headers) {
delete options.headers;
}
options = Object.assign({
method: 'POST',
headers: headers,
body: JSON.stringify(data)
}, options);
options.headers['X-CSRFToken'] = getCookieValue('csrftoken');
if (options.method === 'GET') {
delete options.body;
}
return itFetch(url, options).then((response) => {
return response.json().catch(() => {
return Promise.resolve(null);
});
});
};
export const itGet = (url) => {
return itPost(url, {}, {method: 'GET'});
};

View File

@ -22,7 +22,7 @@ export const redirectToLoginIfRequired: NavigationGuardWithThis<undefined> = (to
}
}
const getCookieValue = (cookieName: string): string => {
export const getCookieValue = (cookieName: string): string => {
// https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript
const cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)')
if (!cookieValue) {

View File

@ -1,9 +1,9 @@
<script>
import axios from 'axios';
import * as log from 'loglevel';
import MainNavigationBar from '../components/MainNavigationBar.vue';
import LearningSequence from '../components/circle/LearningSequence.vue';
import { itGet, itPost } from '../fetchHelpers';
export default {
components: { LearningSequence, MainNavigationBar },
@ -15,14 +15,23 @@ export default {
learningSequences: [],
}
},
methods: {
toggleLearningContentCheckbox(learningContent) {
log.debug('toggleLearningContentCheckbox', learningContent);
console.log(learningContent);
itPost('/api/completion/complete_learning_content/', {
learning_content_key: learningContent.translation_key,
}).then((data) => {
console.log(data);
});
}
},
mounted() {
log.debug('CircleView mounted', this.circleSlug);
axios({
method: 'get',
url: `/learnpath/api/circle/${this.circleSlug}/`,
}).then((response) => {
log.debug(response.data);
this.circleData = response.data;
itGet(`/learnpath/api/circle/${this.circleSlug}/`).then((data) => {
this.circleData = data;
itGet(`/api/completion/user_circle_completion/${this.circleData.translation_key}/`).then((completionData) => {
// aggregate wagtail data into LearningSequence > LearningUnit > LearningPackage hierarchy
let learningSequence = null;
@ -45,6 +54,9 @@ export default {
learningUnit = Object.assign(child, { learningContents: [] });
} else {
// must be a LearningContent
if (child.translation_key in completionData.json_data.completed_learning_contents) {
child.completed = true;
}
learningUnit.learningContents.push(child);
}
});
@ -68,6 +80,8 @@ export default {
log.debug(this.learningSequences);
});
});
}
}
@ -107,7 +121,7 @@ export default {
<div class="flex-auto bg-gray-100 px-4 py-8 lg:px-24">
<div v-for="learningSequence in learningSequences">
<LearningSequence :learning-sequence="learningSequence"></LearningSequence>
<LearningSequence :learning-sequence="learningSequence" @toggleLearningContentCheckbox="toggleLearningContentCheckbox"></LearningSequence>
</div>
</div>

View File

@ -264,7 +264,7 @@ FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),)
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly
SESSION_COOKIE_HTTPONLY = True
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = False
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter
SECURE_BROWSER_XSS_FILTER = True
# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options

View File

@ -1,7 +1,8 @@
from django.urls import path
from vbv_lernwelt.completion.views import complete_learning_content
from vbv_lernwelt.completion.views import complete_learning_content, request_user_circle_completion
urlpatterns = [
path(r"user_circle_completion/<uuid:circle_key>/", request_user_circle_completion, name="request_user_circle_completion"),
path(r"complete_learning_content/", complete_learning_content, name="complete_learning_content"),
]

View File

@ -11,6 +11,16 @@ from vbv_lernwelt.learnpath.models import LearningContent
logger = structlog.get_logger(__name__)
@api_view(['GET'])
def request_user_circle_completion(request, circle_key):
ucc = UserCircleCompletion.objects.get(
user=request.user,
circle_key=circle_key,
)
return Response(status=200, data=UserCircleCompletionSerializer(ucc).data)
@api_view(['POST'])
def complete_learning_content(request):
learning_content_key = request.data.get('learning_content_key')
@ -18,7 +28,7 @@ def complete_learning_content(request):
circle_key = learning_content.get_parent().translation_key
LearningContentCompletion.objects.create(
LearningContentCompletion.objects.get_or_create(
user=request.user,
learning_content_key=learning_content_key,
circle_key=circle_key,