|
@@ -5,54 +5,63 @@
|
|
</button>
|
|
</button>
|
|
</form>
|
|
</form>
|
|
<main class="container" @vue:mounted="mounted">
|
|
<main class="container" @vue:mounted="mounted">
|
|
- <form class="mb-4">
|
|
|
|
- <div class="row mb-3 form-box">
|
|
|
|
- <label for="alumno.grupo" class="col-sm-1 col-form-label form-group barra">Grupo</label>
|
|
|
|
- <div class="col-sm-10">
|
|
|
|
- <select name="alumno.grupo" id="alumno.grupo" class="form-control col-form-label"
|
|
|
|
- v-model="filter.grupo">
|
|
|
|
- <option :value="null">Todos los grupos</option>
|
|
|
|
- <option v-for="alumno in alumnos.filter(alumno => alumno.grupo)" :value="alumno.grupo">
|
|
|
|
- {{ alumno.grupo }}
|
|
|
|
- </option>
|
|
|
|
- </select>
|
|
|
|
|
|
+ <fieldset class="mb-4">
|
|
|
|
+ <legend v-if="alumno_y_clave">
|
|
|
|
+ {{ alumno_y_clave }}
|
|
|
|
+ </legend>
|
|
|
|
+ <legend v-else>
|
|
|
|
+ Gráficos
|
|
|
|
+ </legend>
|
|
|
|
+ <form>
|
|
|
|
+ <div class="row mb-3 form-box">
|
|
|
|
+ <label for="alumno.grupo" class="col-sm-1 col-form-label form-group barra">Grupo</label>
|
|
|
|
+ <div class="col-sm-10">
|
|
|
|
+ <select name="alumno.grupo" id="alumno.grupo" class="form-control col-form-label"
|
|
|
|
+ v-model="filter.grupo">
|
|
|
|
+ <option :value="null">Todos los grupos</option>
|
|
|
|
+ <option v-for="alumno in alumnos.filter(alumno => alumno.grupo)" :value="alumno.grupo">
|
|
|
|
+ {{ alumno.grupo }}
|
|
|
|
+ </option>
|
|
|
|
+ </select>
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
- </div>
|
|
|
|
- <div class="row mb-3 form-box">
|
|
|
|
- <label for="alumno" class="col-sm-1 col-form-label form-group barra">Alumno</label>
|
|
|
|
- <div class="col-sm-10">
|
|
|
|
- <input type="item.type" name="item.name" list="item.name" id="alumno"
|
|
|
|
- class="form-control col-form-label" @input="fetchDatos" v-model="filter.username">
|
|
|
|
- <datalist id="item.name">
|
|
|
|
- <option v-for="alumno in alumnos_por_grupo" :value="alumno.username">
|
|
|
|
- {{ alumno.nombre }}
|
|
|
|
- </option>
|
|
|
|
- </datalist>
|
|
|
|
|
|
+ <div class="row mb-3 form-box">
|
|
|
|
+ <label for="alumno" class="col-sm-1 col-form-label form-group barra">Alumno</label>
|
|
|
|
+ <div class="col-sm-10">
|
|
|
|
+ <input type="item.type" name="item.name" list="item.name" id="alumno"
|
|
|
|
+ class="form-control col-form-label" @input="fetchDatos" v-model="filter.username">
|
|
|
|
+ <datalist id="item.name">
|
|
|
|
+ <option v-for="alumno in alumnos_por_grupo" :value="alumno.username">
|
|
|
|
+ {{ alumno.nombre }}
|
|
|
|
+ </option>
|
|
|
|
+ </datalist>
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
- </div>
|
|
|
|
- <button type="reset" class="btn btn-danger btn-sm">
|
|
|
|
- <i class="fas fa-eraser"></i>
|
|
|
|
- </button>
|
|
|
|
- </form>
|
|
|
|
-
|
|
|
|
|
|
+ <!-- <button type="reset" class="btn btn-danger btn-sm">
|
|
|
|
+ <i class="fas fa-eraser"></i>
|
|
|
|
+ </button> -->
|
|
|
|
+ </form>
|
|
|
|
+ </fieldset>
|
|
<section v-if="datos != null">
|
|
<section v-if="datos != null">
|
|
<h2>Gráficos</h2>
|
|
<h2>Gráficos</h2>
|
|
- <div class="row">
|
|
|
|
- <div class="col-md-6">
|
|
|
|
- <canvas id="calificaciones" width="400" height="400" @vue:mounted="createGraph($el,
|
|
|
|
- datos.calificaciones.map(calificacion => calificacion.course_name),
|
|
|
|
- datos.calificaciones.map(calificacion => calificacion.calificación_final),
|
|
|
|
- 'bar', 'Calificaciones')">
|
|
|
|
- </div>
|
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
+ <div class="row d-flex justify-content-center">
|
|
|
|
+ <div class="col-md-8">
|
|
<canvas id="snapshot" width="400" height="400"
|
|
<canvas id="snapshot" width="400" height="400"
|
|
- @vue:mounted="createGraph($el, datos.timeline.map(snapshot => snapshot.created_at), datos.timeline.map(snapshot => snapshot.promedio_calificacion_final), 'line', 'Snapshot')">
|
|
|
|
|
|
+ @vue:mounted="createMultiLineGraph($el, datos.snapshots)">
|
|
|
|
+ </div>
|
|
|
|
+ <div class="col-md-8">
|
|
|
|
+ <div id="calificaciones"
|
|
|
|
+ @vue:mounted="createGraph($el, datos.calificaciones.map(calificacion => calificacion.course_name), datos.calificaciones.map(calificacion => calificacion.calificación_final))">
|
|
|
|
+ </div>
|
|
</div>
|
|
</div>
|
|
|
|
+
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
</main>
|
|
</main>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
|
|
+<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
|
|
|
+
|
|
<script>
|
|
<script>
|
|
const filter = PetiteVue.reactive({
|
|
const filter = PetiteVue.reactive({
|
|
grupo: null,
|
|
grupo: null,
|
|
@@ -75,6 +84,11 @@
|
|
|
|
|
|
return this.alumnos.flatMap(alumno => alumno.alumnos);
|
|
return this.alumnos.flatMap(alumno => alumno.alumnos);
|
|
},
|
|
},
|
|
|
|
+ get alumno_y_clave() { // returns string: (username) nombre
|
|
|
|
+ if (this.filter.username == null) return null;
|
|
|
|
+ const alumno = this.alumnos_por_grupo.find(alumno => alumno.username === this.filter.username);
|
|
|
|
+ return alumno ? `(${alumno.username}) ${alumno.nombre}` : null;
|
|
|
|
+ },
|
|
datos: null,
|
|
datos: null,
|
|
async mounted() {
|
|
async mounted() {
|
|
store.loading = true;
|
|
store.loading = true;
|
|
@@ -83,52 +97,76 @@
|
|
this.alumnos = data;
|
|
this.alumnos = data;
|
|
store.loading = false;
|
|
store.loading = false;
|
|
},
|
|
},
|
|
- createGraph($el, labels, data, type, title) {
|
|
|
|
- const backgroundColor = [
|
|
|
|
- 'rgba(255, 99, 132, 0.2)',
|
|
|
|
- 'rgba(54, 162, 235, 0.2)',
|
|
|
|
- 'rgba(255, 206, 86, 0.2)',
|
|
|
|
- 'rgba(75, 192, 192, 0.2)',
|
|
|
|
- 'rgba(153, 102, 255, 0.2)',
|
|
|
|
- 'rgba(255, 159, 64, 0.2)',
|
|
|
|
- 'rgba(255, 99, 132, 0.2)',
|
|
|
|
- 'rgba(54, 162, 235, 0.2)',
|
|
|
|
- 'rgba(255, 206, 86, 0.2)',
|
|
|
|
- 'rgba(75, 192, 192, 0.2)',
|
|
|
|
- ];
|
|
|
|
|
|
+ createGraph($el, labels, data) {
|
|
|
|
+ var options = {
|
|
|
|
+ series: [{
|
|
|
|
+ data: data
|
|
|
|
+ }],
|
|
|
|
+ chart: {
|
|
|
|
+ type: 'bar',
|
|
|
|
+ height: 350
|
|
|
|
+ },
|
|
|
|
+ plotOptions: {
|
|
|
|
+ bar: {
|
|
|
|
+ borderRadius: 4,
|
|
|
|
+ horizontal: true,
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ dataLabels: {
|
|
|
|
+ enabled: false
|
|
|
|
+ },
|
|
|
|
+ xaxis: {
|
|
|
|
+ categories: labels,
|
|
|
|
+ },
|
|
|
|
+ colors: [
|
|
|
|
+ 'rgba(54, 162, 235, 0.7)', // Blue
|
|
|
|
+ 'rgba(255, 99, 132, 0.7)', // Red
|
|
|
|
+ 'rgba(255, 159, 64, 0.7)', // Orange
|
|
|
|
+ 'rgba(255, 206, 86, 0.7)', // Yellow
|
|
|
|
+ 'rgba(75, 192, 192, 0.7)', // Green
|
|
|
|
+ 'rgba(153, 102, 255, 0.7)', // Purple
|
|
|
|
+ 'rgba(201, 203, 207, 0.7)', // gray
|
|
|
|
+ ]
|
|
|
|
+
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ var chart = new ApexCharts($el, options);
|
|
|
|
+ chart.render();
|
|
|
|
+ },
|
|
|
|
+ createMultiLineGraph($el, snapshots) {
|
|
|
|
+ const labels = snapshots.map(snapshot => snapshot.fecha);
|
|
|
|
+ const datasets = snapshots[0].calificaciones.map(calificacion => {
|
|
|
|
+ return {
|
|
|
|
+ label: calificacion.course_name,
|
|
|
|
+ data: snapshots.map(snapshot => snapshot.calificaciones.find(c => c.course_name === calificacion.course_name).calificación_final),
|
|
|
|
+ fill: false,
|
|
|
|
+ // borderColor: borderColor.slice(0, snapshots[0].calificaciones.length),
|
|
|
|
+ tension: 0.1,
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
|
|
- const borderColor = [
|
|
|
|
- 'rgba(255, 99, 132, 1)',
|
|
|
|
- 'rgba(54, 162, 235, 1)',
|
|
|
|
- 'rgba(255, 206, 86, 1)',
|
|
|
|
- 'rgba(75, 192, 192, 1)',
|
|
|
|
- 'rgba(153, 102, 255, 1)',
|
|
|
|
- 'rgba(255, 159, 64, 1)',
|
|
|
|
- 'rgba(255, 99, 132, 1)',
|
|
|
|
- 'rgba(54, 162, 235, 1)',
|
|
|
|
- 'rgba(255, 206, 86, 1)',
|
|
|
|
- 'rgba(75, 192, 192, 1)',
|
|
|
|
- ];
|
|
|
|
- if (this.datos == null) return;
|
|
|
|
- const ctx1 = $el.getContext('2d');
|
|
|
|
- const chart1 = new Chart(ctx1, {
|
|
|
|
- type: type,
|
|
|
|
|
|
+
|
|
|
|
+ const ctx = $el.getContext('2d');
|
|
|
|
+ const multiLineChart = new Chart(ctx, {
|
|
|
|
+ type: 'line',
|
|
data: {
|
|
data: {
|
|
- labels: labels,
|
|
|
|
- datasets: [{
|
|
|
|
- label: title,
|
|
|
|
- data: data,
|
|
|
|
- backgroundColor: backgroundColor.slice(0, data.length),
|
|
|
|
- borderColor: borderColor.slice(0, data.length),
|
|
|
|
- borderWidth: 1
|
|
|
|
- }]
|
|
|
|
|
|
+ labels: labels, // Las fechas de tus snapshots
|
|
|
|
+ datasets: datasets, // Un array de objetos, cada uno representando una materia
|
|
},
|
|
},
|
|
options: {
|
|
options: {
|
|
scales: {
|
|
scales: {
|
|
y: {
|
|
y: {
|
|
- beginAtZero: true
|
|
|
|
|
|
+ beginAtZero: true,
|
|
|
|
+ max: 1, // Asumiendo que las calificaciones están normalizadas entre 0 y 1
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ },
|
|
|
|
+ plugins: {
|
|
|
|
+ title: {
|
|
|
|
+ display: true,
|
|
|
|
+ text: 'Calificaciones por Materia a lo largo del Tiempo',
|
|
|
|
+ },
|
|
|
|
+ },
|
|
}
|
|
}
|
|
});
|
|
});
|
|
},
|
|
},
|
|
@@ -136,6 +174,7 @@
|
|
if (this.filter.username == null) return;
|
|
if (this.filter.username == null) return;
|
|
if (this.alumnos_por_grupo.find(alumno => alumno.username === this.filter.username) == null) return;
|
|
if (this.alumnos_por_grupo.find(alumno => alumno.username === this.filter.username) == null) return;
|
|
store.loading = true;
|
|
store.loading = true;
|
|
|
|
+ this.datos = null;
|
|
// params POST (formadata [username])
|
|
// params POST (formadata [username])
|
|
const params = new FormData();
|
|
const params = new FormData();
|
|
params.append('username', this.filter.username);
|
|
params.append('username', this.filter.username);
|