Skip to content

Commit

Permalink
Basic QTI zip loading prototype.
Browse files Browse the repository at this point in the history
  • Loading branch information
rtibbles committed May 10, 2021
1 parent a1b0b62 commit 8153bf9
Show file tree
Hide file tree
Showing 20 changed files with 829 additions and 2 deletions.
4 changes: 4 additions & 0 deletions kolibri/core/assets/src/core-app/urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ const urls = {
}
return generateUrl(this.__mediaUrl, { url });
},
rawStorageUrl(fileId, extension) {
const filename = `${fileId}.${extension}`;
return generateUrl(this.__contentUrl, { url: `${filename[0]}/${filename[1]}/${filename}` });
},
storageUrl(fileId, extension, embeddedFilePath = '') {
const filename = `${fileId}.${extension}`;
if (['zip', 'h5p'].includes(extension)) {
Expand Down
79 changes: 79 additions & 0 deletions kolibri/core/content/migrations/0028_qti_format_preset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2021-05-10 21:49
from __future__ import unicode_literals

from django.db import migrations
from django.db import models


class Migration(migrations.Migration):

dependencies = [
("content", "0027_channelmetadata_tagline"),
]

operations = [
migrations.AlterField(
model_name="file",
name="preset",
field=models.CharField(
blank=True,
choices=[
("high_res_video", "High Resolution"),
("low_res_video", "Low Resolution"),
("video_thumbnail", "Thumbnail"),
("video_subtitle", "Subtitle"),
("video_dependency", "Video (dependency)"),
("audio", "Audio"),
("audio_thumbnail", "Thumbnail"),
("audio_dependency", "audio (dependency)"),
("document", "Document"),
("epub", "ePub Document"),
("document_thumbnail", "Thumbnail"),
("exercise", "Exercise"),
("exercise_thumbnail", "Thumbnail"),
("exercise_image", "Exercise Image"),
("exercise_graphie", "Exercise Graphie"),
("channel_thumbnail", "Channel Thumbnail"),
("topic_thumbnail", "Thumbnail"),
("html5_zip", "HTML5 Zip"),
("html5_dependency", "HTML5 Dependency (Zip format)"),
("html5_thumbnail", "HTML5 Thumbnail"),
("h5p", "H5P Zip"),
("h5p_thumbnail", "H5P Thumbnail"),
("qti", "QTI Zip"),
("qti_thumbnail", "QTI Thumbnail"),
("slideshow_image", "Slideshow Image"),
("slideshow_thumbnail", "Slideshow Thumbnail"),
("slideshow_manifest", "Slideshow Manifest"),
],
max_length=150,
),
),
migrations.AlterField(
model_name="localfile",
name="extension",
field=models.CharField(
blank=True,
choices=[
("mp4", "MP4 Video"),
("webm", "WEBM Video"),
("vtt", "VTT Subtitle"),
("mp3", "MP3 Audio"),
("pdf", "PDF Document"),
("jpg", "JPG Image"),
("jpeg", "JPEG Image"),
("png", "PNG Image"),
("gif", "GIF Image"),
("json", "JSON"),
("svg", "SVG Image"),
("perseus", "Perseus Exercise"),
("graphie", "Graphie Exercise"),
("zip", "HTML5 Zip"),
("h5p", "H5P"),
("epub", "ePub Document"),
],
max_length=40,
),
),
]
Empty file.
19 changes: 19 additions & 0 deletions kolibri/plugins/qti_viewer/assets/src/mixins/domMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export default {
props: {
dom: {
required: true,
},
qtiFile: {
required: true,
},
filePath: {
required: true,
type: String,
},
},
computed: {
children() {
return Array.from(this.dom.children);
},
},
};
13 changes: 13 additions & 0 deletions kolibri/plugins/qti_viewer/assets/src/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import QTIViewer from './views/QTIViewer';
import ContentRendererModule from 'content_renderer_module';

class QTIViewerModule extends ContentRendererModule {
get rendererComponent() {
QTIViewer.contentModule = this;
return QTIViewer;
}
}

const qtiViewer = new QTIViewerModule();

export { qtiViewer as default };
46 changes: 46 additions & 0 deletions kolibri/plugins/qti_viewer/assets/src/views/AssessmentItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<template>

<div>
{{ title }}

<ItemBody
v-if="itemBody"
:key="identifier"
:dom="itemBody"
:qtiFile="qtiFile"
:filePath="filePath"
@submit="$emit('submit', $event)"
/>
</div>

</template>


<script>
import domMixin from '../mixins/domMixin';
import ItemBody from './ItemBody';
export default {
name: 'AssessmentItem',
components: {
ItemBody,
},
mixins: [domMixin],
computed: {
itemBody() {
return this.dom && this.dom.getElementsByTagName('itemBody')[0];
},
identifier() {
return this.dom && this.dom.getAttribute('identifier');
},
title() {
return (this.dom && this.dom.getAttribute('title')) || '';
},
},
};
</script>


<style lang="scss" scoped></style>
58 changes: 58 additions & 0 deletions kolibri/plugins/qti_viewer/assets/src/views/AssessmentItemRef.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>

<AssessmentItem
v-if="assessmentItem"
:dom="assessmentItem"
:qtiFile="qtiFile"
:filePath="filePath"
/>

</template>


<script>
import domMixin from '../mixins/domMixin';
import AssessmentItem from './AssessmentItem';
export default {
name: 'AssessmentItemRef',
components: {
AssessmentItem,
},
mixins: [domMixin],
data() {
return {
assessmentItem: null,
};
},
computed: {
href() {
const relativeHref = this.dom && this.dom.getAttribute('href');
if (relativeHref) {
const path = new URL(
relativeHref,
new URL(this.filePath, 'http://b.b/')
).pathname.substring(1);
return path;
}
return '';
},
},
created() {
const resourceFile = this.qtiFile.file(this.href);
if (!resourceFile) {
throw new ReferenceError(`file ${this.href} not found`);
}
resourceFile.async('string').then(qtiXML => {
const parser = new DOMParser();
const DOM = parser.parseFromString(qtiXML.trim(), 'text/xml');
this.assessmentItem = DOM.getElementsByTagName('assessmentItem')[0];
});
},
};
</script>


<style lang="scss" scoped></style>
55 changes: 55 additions & 0 deletions kolibri/plugins/qti_viewer/assets/src/views/AssessmentSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>

<div>
<AssessmentItem
v-if="assessmentItem"
:dom="assessmentItem"
:qtiFile="qtiFile"
:filePath="filePath"
/>
<AssessmentItemRef
v-else-if="assessmentItemRef"
:dom="assessmentItemRef"
:qtiFile="qtiFile"
:filePath="filePath"
/>
</div>

</template>


<script>
import domMixin from '../mixins/domMixin';
import AssessmentItem from './AssessmentItem';
import AssessmentItemRef from './AssessmentItemRef';
export default {
name: 'AssessmentSection',
components: {
AssessmentItem,
AssessmentItemRef,
},
mixins: [domMixin],
data() {
return {
assessmentItemIndex: 0,
};
},
computed: {
assessmentItems() {
return this.dom && Array.from(this.dom.getElementsByTagName('assessmentItem'));
},
assessmentItem() {
return this.assessmentItems && this.assessmentItems[this.assessmentItemIndex];
},
assessmentItemRefs() {
return this.dom && Array.from(this.dom.getElementsByTagName('assessmentItemRef'));
},
assessmentItemRef() {
return this.assessmentItemRefs && this.assessmentItemRefs[this.assessmentItemIndex];
},
},
};
</script>
58 changes: 58 additions & 0 deletions kolibri/plugins/qti_viewer/assets/src/views/AssessmentTest.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>

<div>
<AssessmentSection
v-if="assessmentSection"
:dom="assessmentSection"
:qtiFile="qtiFile"
:filePath="filePath"
/>
<KButton @click="previous">
Back
</KButton>
<KButton @click="next">
Continue
</KButton>
</div>

</template>


<script>
import domMixin from '../mixins/domMixin';
import AssessmentSection from './AssessmentSection';
export default {
name: 'AssessmentTest',
components: {
AssessmentSection,
},
mixins: [domMixin],
data() {
return {
testPartIndex: 0,
sectionIndex: 0,
};
},
computed: {
testParts() {
return this.dom && Array.from(this.dom.getElementsByTagName('testPart'));
},
testPart() {
return this.testParts && this.testParts[this.testPartIndex];
},
assessmentSections() {
return Array.from(this.testPart.getElementsByTagName('assessmentSection'));
},
assessmentSection() {
return this.assessmentSections[this.sectionIndex];
},
},
methods: {
next() {},
previous() {},
},
};
</script>
Loading

0 comments on commit 8153bf9

Please sign in to comment.