-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
321 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
<script setup lang="ts"> | ||
/** | ||
* This component provides import of classes of students from Edison | ||
* into Kelvin using the INBUS API. | ||
* | ||
* It is only available to teachers, and it is accessible from the main page | ||
* next to class selection drop down menu. | ||
*/ | ||
import { computed, onMounted, ref } from 'vue'; | ||
import { csrfToken } from '../api.js'; | ||
import { ConcreteActivity, InbusSubjectVersion } from './inbusdto'; | ||
interface KelvinSubject { | ||
name: string; | ||
abbr: string; | ||
} | ||
interface Semester { | ||
pk: number; | ||
year: number; | ||
winter: boolean; | ||
} | ||
interface ImportResult { | ||
login: string; | ||
firstname: string; | ||
lastname: string; | ||
created: boolean; | ||
} | ||
interface Result { | ||
users: ImportResult[]; | ||
count: number; | ||
error?: string; | ||
} | ||
let subject_inbus_selected = ref<InbusSubjectVersion | null>(null); | ||
let subject_kelvin_selected = ref(null); | ||
let semester = ref(null); | ||
let busy = ref<boolean>(false); | ||
let semesters = ref(null); | ||
let semesters_loading = ref<boolean>(true); | ||
let inbus_and_kelvin_subjects_loading = ref<boolean>(true); | ||
let inbus_schedule_loaded = ref<boolean>(false); | ||
let subjects_inbus: InbusSubjectVersion[] | null = null; | ||
let subjects_inbus_filtered: InbusSubjectVersion[] | null = null; | ||
let subjects_kelvin: KelvinSubject[] | null = null; | ||
let subject_inbus_schedule = ref<ConcreteActivity[] | null>(null); | ||
let classes_to_import = ref([]); | ||
let result = ref<Result | null>(null); | ||
let canImport = computed(() => { | ||
return ( | ||
classes_to_import.value.length && !busy.value && subject_kelvin_selected.value && semester.value | ||
); | ||
}); | ||
function assembleRequest(url: string): Request { | ||
const headers: Headers = new Headers(); | ||
headers.set('Content-Type', 'application/json'); | ||
headers.set('Accept', 'application/json'); | ||
const request: Request = new Request(url, { | ||
method: 'GET', | ||
headers: headers | ||
}); | ||
return request; | ||
} | ||
function svcc2num(svcc: string): number { | ||
const [dept_code, version] = svcc.split('/'); | ||
const [dept, code] = dept_code.split('-'); | ||
const svcc_str = dept + code + version; | ||
const svcc_num = Number(svcc_str); | ||
return svcc_num; | ||
} | ||
async function loadInbusAndKelvinSubjects() { | ||
const res1 = await fetch('/api/inbus/subject_versions', {}); | ||
const res2 = await fetch('/api/subjects/all', {}); | ||
subjects_inbus = await res1.json(); | ||
const subjects_kelvin_resp = await res2.json(); | ||
subjects_kelvin = subjects_kelvin_resp.subjects; | ||
subjects_kelvin.sort((a, b) => { | ||
const name_a = a.name.toUpperCase(); | ||
const name_b = b.name.toUpperCase(); | ||
if (name_a < name_b) { | ||
return -1; | ||
} | ||
if (name_a > name_b) { | ||
return 1; | ||
} | ||
return 0; | ||
}); | ||
const subject_kelvin_abbrs = subjects_kelvin.map((s) => s.abbr); | ||
subjects_inbus_filtered = subjects_inbus.filter((subject_inbus) => | ||
subject_kelvin_abbrs.includes(subject_inbus.subject.abbrev) | ||
); | ||
subjects_inbus_filtered = subjects_inbus_filtered.sort( | ||
(a, b) => svcc2num(a.subjectVersionCompleteCode) - svcc2num(b.subjectVersionCompleteCode) | ||
); | ||
inbus_and_kelvin_subjects_loading.value = false; | ||
} | ||
function parseSemesters(semesters_data: Semester[]) { | ||
semesters.value = semesters_data.map((sm) => ({ | ||
pk: sm.pk, | ||
year: sm.year, | ||
winter: sm.winter, | ||
display: String(sm.year) + (sm.winter ? 'W' : 'S') | ||
})); | ||
semesters_loading.value = false; | ||
} | ||
async function loadSemesters() { | ||
const request = assembleRequest('/api/semesters'); | ||
const res = await fetch(request, {}); | ||
const semesters_data = await res.json(); | ||
parseSemesters(semesters_data['semesters']); | ||
} | ||
async function loadScheduleForSubjectVersionId() { | ||
inbus_schedule_loaded.value = false; | ||
const request = assembleRequest( | ||
'/api/inbus/schedule/subject/version/' + subject_inbus_selected.value.subjectVersionId | ||
); | ||
let res = await fetch(request, {}); | ||
subject_inbus_schedule.value = await res.json(); | ||
inbus_schedule_loaded.value = true; | ||
} | ||
async function importActivities() { | ||
busy.value = true; | ||
const req = { | ||
semester_id: semester.value, | ||
subject: subject_kelvin_selected.value, | ||
activities: classes_to_import.value | ||
}; | ||
//console.log(req); | ||
const res = await fetch('/api/import/activities', { | ||
method: 'POST', | ||
headers: { | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
'X-CSRFToken': csrfToken() | ||
}, | ||
body: JSON.stringify(req) | ||
}); | ||
result.value = await res.json(); | ||
busy.value = false; | ||
} | ||
function onInbusSubjectSelected(event: Event) { | ||
loadScheduleForSubjectVersionId(); | ||
} | ||
onMounted(() => { | ||
loadSemesters(); | ||
loadInbusAndKelvinSubjects(); | ||
}); | ||
</script> | ||
|
||
<template> | ||
<select v-if="!semesters_loading" v-model="semester"> | ||
<option v-for="item in semesters" :key="item.pk" :value="item.pk">{{ item.display }}</option> | ||
</select> | ||
|
||
<select v-if="!inbus_and_kelvin_subjects_loading" v-model="subject_kelvin_selected"> | ||
<option v-for="item in subjects_kelvin" :key="item.abbr" :value="item"> | ||
{{ item.abbr }} - {{ item.name }} | ||
</option> | ||
</select> | ||
|
||
<select | ||
v-if="!inbus_and_kelvin_subjects_loading" | ||
v-model="subject_inbus_selected" | ||
@change="onInbusSubjectSelected" | ||
> | ||
<option v-for="(item, index) in subjects_inbus_filtered" :key="index" :value="item"> | ||
{{ item.subjectVersionCompleteCode }} - {{ item.subject.abbrev }} - {{ item.subject.title }} | ||
</option> | ||
</select> | ||
|
||
<table v-if="inbus_schedule_loaded" class="table table-hover table-stripped table-sm"> | ||
<tbody> | ||
<tr v-for="ca in subject_inbus_schedule" :key="ca.concreteActivityId"> | ||
<td> | ||
<label> | ||
<input | ||
v-model="classes_to_import" | ||
type="checkbox" | ||
:value="ca.concreteActivityId" | ||
name="classesToImport" | ||
/> | ||
{{ ca.educationTypeAbbrev }} | ||
</label> | ||
</td> | ||
|
||
<td>{{ ca.educationTypeAbbrev }}/{{ ca.order }}, {{ ca.subjectVersionCompleteCode }}</td> | ||
|
||
<td> | ||
{{ ca.teacherFullNames }} | ||
</td> | ||
|
||
<td> | ||
{{ ca.weekDayAbbrev }} | ||
</td> | ||
|
||
<td> | ||
{{ ca.beginTime }} | ||
</td> | ||
|
||
<td> | ||
{{ ca.endTime }} | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
|
||
<button | ||
class="btn" | ||
:class="{ 'btn-success': canImport, 'btn-danger': !canImport }" | ||
:disabled="!canImport" | ||
@click="importActivities" | ||
> | ||
<span v-if="busy">Importing...</span> | ||
<span v-else>Import</span> | ||
</button> | ||
|
||
<div> | ||
<div v-if="result"> | ||
<div v-if="result.error" class="alert alert-danger" role="alert"> | ||
{{ result.error }} | ||
</div> | ||
<div v-else> | ||
<table class="table table-sm table-hover table-striped"> | ||
<thead> | ||
<tr> | ||
<th>Login</th> | ||
<th>First name</th> | ||
<th>Last name</th> | ||
<th>User created</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr v-for="item in result.users" :key="item.login"> | ||
<td>{{ item.login }}</td> | ||
<td>{{ item.firstname }}</td> | ||
<td>{{ item.lastname }}</td> | ||
<td>{{ item.created }}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style scoped></style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export interface ConcreteActivity { | ||
/** | ||
Concrete activity in schedule. | ||
*/ | ||
concreteActivityId: number; | ||
order: number; | ||
subjectVersionId: number; | ||
subjectVersionCompleteCode: string; | ||
educationTypeAbbrev: string; | ||
beginTime: string; | ||
endTime: string; | ||
weekDayAbbrev: string; | ||
teacherFullNames?: string; | ||
} | ||
|
||
interface InbusSubject { | ||
subjectId: number; | ||
code: string; | ||
abbrev: string; | ||
title: string; | ||
} | ||
|
||
export interface InbusSubjectVersion { | ||
subjectVersionId: number; | ||
subject: InbusSubject; | ||
subjectVersionCompleteCode: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{% extends 'web/layout.html' %} | ||
|
||
{% block fullcontent %} | ||
<kelvin-inbus-import></kelvin-inbus-import> | ||
{% endblock %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters