Skip to content
Snippets Groups Projects
Commit 05086310 authored by robinwilliam.hundt's avatar robinwilliam.hundt
Browse files

Added frontend part for import ui

parent f3d982d4
No related branches found
No related tags found
No related merge requests found
Pipeline #110769 failed
This commit is part of merge request !192. Comments created here will be created in the context of that merge request.
...@@ -256,6 +256,10 @@ export async function fetchStudentExportData (options: StudentExportOptions): Pr ...@@ -256,6 +256,10 @@ export async function fetchStudentExportData (options: StudentExportOptions): Pr
return (await ax.post('/api/export/json/', options)).data return (await ax.post('/api/export/json/', options)).data
} }
export async function importData (data: Object): Promise<AxiosResponse<void>> {
return ax.post('/api/import/', data)
}
// Note, this interface does not represent all of the returned data, // Note, this interface does not represent all of the returned data,
// but only the fields which have to be transformed for deanonymisation // but only the fields which have to be transformed for deanonymisation
export interface InstanceExportData { export interface InstanceExportData {
......
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
<slot name="toolbar-center"/> <slot name="toolbar-center"/>
<div class="toolbar-content"> <div class="toolbar-content">
<v-menu bottom offset-y v-if="!isStudent"> <v-menu bottom offset-y v-if="!isStudent">
<v-btn slot="activator" color="cyan" style="text-transform: none"> <v-btn id="user-options" slot="activator" color="cyan" style="text-transform: none">
{{ userRole }} | {{ username }} <v-icon>arrow_drop_down</v-icon> {{ userRole }} | {{ username }} <v-icon>arrow_drop_down</v-icon>
</v-btn> </v-btn>
<user-options class="mt-1" v-if="!isStudent"/> <user-options class="mt-1" v-if="!isStudent"/>
......
<template>
<v-dialog v-model="show" width="30%">
<v-card>
<v-card-title class="title">Import data</v-card-title>
<v-card-text>
<p>
You can use this component to import data into Grady.
You can use
<a
href="https://gitlab.gwdg.de/grady-corp/rusty-hektor"
target="_blank"
>rusty-hektor</a> to convert
and pseudonomize ILIAS output.
</p>
<file-select v-model="hektorFile" display-text="Select json file" />
</v-card-text>
<v-card-actions>
<v-btn @click="submitData" :loading="loading" id="submit-import">Import</v-btn>
<v-btn @click="$emit('hide')" color="red">Cancel</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import FileSelect from "@/components/util/FileSelect.vue";
import { importData } from "@/api";
export default {
name: "ImportDialog",
components: {
FileSelect
},
data: () => {
return {
show: true,
loading: false,
hektorFile: null
};
},
methods: {
async submitData() {
this.loading = true
let data;
try {
data = await this.readFile();
data = JSON.parse(data)
} catch (error) {
this.$notify({
type: 'error',
title: 'Error reading import file',
text: error.message
})
this.loading = false
return
}
try {
await importData(data)
this.$emit('imported')
this.$notify({
title: 'Successfully imported data. Please log out and in again.',
type: 'success'
})
} finally {
this.loading = false
}
},
readFile() {
const fileReader = new FileReader();
return new Promise((resolve, reject) => {
fileReader.onload = event => {
resolve(event.target.result);
};
fileReader.onerror = () => {
fileReader.abort();
reject(new Error("Problem parsing input file."));
};
fileReader.readAsText(this.hektorFile)
});
}
},
watch: {
show(val) {
if (!val) {
this.$emit("hide");
}
}
}
};
</script>
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<v-list-tile <v-list-tile
v-if="opt.condition()" v-if="opt.condition()"
@click="opt.action" @click="opt.action"
:id="opt.id"
:key="i" :key="i"
> >
{{opt.display}} {{opt.display}}
...@@ -17,11 +18,12 @@ ...@@ -17,11 +18,12 @@
<script> <script>
import PasswordChangeDialog from '@/components/PasswordChangeDialog' import PasswordChangeDialog from '@/components/PasswordChangeDialog'
import ImportDialog from '@/components/ImportDialog'
import { Authentication } from '@/store/modules/authentication' import { Authentication } from '@/store/modules/authentication'
import { deleteAllActiveAssignments } from '@/api' import { deleteAllActiveAssignments } from '@/api'
export default { export default {
name: 'UserOptions', name: 'UserOptions',
components: { PasswordChangeDialog }, components: { PasswordChangeDialog, ImportDialog },
data () { data () {
return { return {
displayComponent: null, displayComponent: null,
...@@ -29,12 +31,20 @@ export default { ...@@ -29,12 +31,20 @@ export default {
{ {
display: 'Change password', display: 'Change password',
action: () => { this.displayComponent = PasswordChangeDialog }, action: () => { this.displayComponent = PasswordChangeDialog },
condition: () => !Authentication.isStudent condition: () => !Authentication.isStudent,
id: "change-password-list-tile"
}, },
{ {
display: 'Free all reserved submissions', display: 'Free all reserved submissions',
action: deleteAllActiveAssignments, action: deleteAllActiveAssignments,
condition: () => Authentication.isReviewer condition: () => Authentication.isReviewer,
id: "free-assignments-list-tile"
},
{
display: 'Import data',
action: () => { this.displayComponent = ImportDialog },
condition: () => Authentication.isReviewer,
id: "import-data-list-tile"
} }
] ]
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<span v-if="value">Selected: {{value.name}}</span> <span v-if="value">Selected: {{value.name}}</span>
<span v-else>{{displayText}}</span> <span v-else>{{displayText}}</span>
</div> </div>
<input type="file" @change="handleFileChange"/> <input id="file-input" type="file" @change="handleFileChange"/>
</label> </label>
</template> </template>
...@@ -17,7 +17,6 @@ export default { ...@@ -17,7 +17,6 @@ export default {
default: 'Select File' default: 'Select File'
} }
}, },
methods: { methods: {
handleFileChange (e) { handleFileChange (e) {
this.$emit('input', e.target.files[0]) this.$emit('input', e.target.files[0])
......
<template> <template>
<component :is="startPage"> <component :is="startPage"/>
</component>
</template> </template>
<script> <script>
......
<template> <template>
<div> <div v-if="dataLoaded">
<v-layout row wrap> <v-layout row wrap>
<v-flex lg5 md9 xs12> <v-flex lg5 md9 xs12>
<correction-statistics class="ma-4"></correction-statistics> <correction-statistics class="ma-4"></correction-statistics>
...@@ -10,22 +10,66 @@ ...@@ -10,22 +10,66 @@
</v-layout> </v-layout>
<SubmissionTypesOverview class="ma-4"/> <SubmissionTypesOverview class="ma-4"/>
</div> </div>
<v-layout v-else justify-center class="mt-4 pt-4">
<import-dialog
v-if="showImportDialog"
@hide="showImportDialog = false"
@imported="importDone"
/>
<v-card class="import-card">
<v-card-title class="title">
Import data
</v-card-title>
<v-card-text>
It looks like this instance doesn't contain any data.
Would you like to import some?
</v-card-text>
<v-card-actions class="justify-center">
<v-btn @click="showImportDialog = true" class="info">Import data</v-btn>
</v-card-actions>
</v-card>
</v-layout>
</template> </template>
<script> <script>
import CorrectionStatistics from '@/components/CorrectionStatistics' import CorrectionStatistics from '@/components/CorrectionStatistics'
import ImportDialog from '@/components/ImportDialog'
import SubscriptionList from '@/components/subscriptions/SubscriptionList' import SubscriptionList from '@/components/subscriptions/SubscriptionList'
import SubmissionTypesOverview from '@/components/submission_type/SubmissionTypesOverview' import SubmissionTypesOverview from '@/components/submission_type/SubmissionTypesOverview'
import { getters } from '../../store/getters'
import { Subscriptions } from '../../store/modules/subscriptions'
export default { export default {
components: { components: {
ImportDialog,
SubmissionTypesOverview, SubmissionTypesOverview,
SubscriptionList, SubscriptionList,
CorrectionStatistics }, CorrectionStatistics },
name: 'reviewer-start-page' name: 'reviewer-start-page',
data: () => {
return {
showImportDialog: false,
dataImported: false
}
},
computed: {
dataLoaded () {
return Object.keys(getters.state.submissionTypes).length !== 0 || this.dataImported
}
},
methods: {
importDone() {
this.dataImported = true
Subscriptions.RESET_STATE()
}
}
} }
</script> </script>
<style scoped> <style scoped>
.import-card {
width: 30%;
}
</style> </style>
{
"meta": {
"version": "3.0.0"
},
"module": {
"module_reference": "B.Inf.1801",
"total_score": 50,
"pass_score": 25,
"pass_only": false
},
"submission_types": [
{
"name": "Eine Bibliothek für Permutationen (I1-ID: l120mlc005h0)",
"full_score": 50,
"description": "A <b>description</b>!",
"solution": "Blub",
"programming_language": "java"
}
],
"students": [
{
"fullname": "Test, User",
"identifier": "20000000",
"username": "TU20000000",
"submissions": [
{
"code": "234;",
"type": "Eine Bibliothek für Permutationen (I1-ID: l120mlc005h0)",
"tests": {}
}
]
}
]
}
import os
from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from core import models
from functional_tests.util import (login, create_browser, query_returns_object,
reset_browser_after_test)
from util import factory_boys as fact
JSON_EXPORT_FILE = os.path.join(os.path.dirname(__file__), 'data/hektor.json')
class TestImport(LiveServerTestCase):
browser: webdriver.Firefox = None
username = None
password = None
role = None
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.browser = create_browser()
@classmethod
def tearDownClass(cls):
super().tearDownClass()
cls.browser.quit()
def setUp(self):
super().setUp()
self.username = 'rev'
self.password = 'p'
fact.UserAccountFactory(
username=self.username,
password=self.password,
role=models.UserAccount.REVIEWER
)
def tearDown(self):
reset_browser_after_test(self.browser, self.live_server_url)
def _login(self):
login(self.browser, self.live_server_url, self.username, self.password)
def test_reviewer_can_import_data(self):
self._login()
self.browser.find_element_by_id("user-options").click()
self.browser.find_element_by_id("import-data-list-tile").click()
file_input = self.browser.find_element_by_id("file-input")
file_input.send_keys(JSON_EXPORT_FILE)
self.browser.find_element_by_id("submit-import").click()
WebDriverWait(self.browser, 20).until(query_returns_object(models.SubmissionType))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment