graph.html 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <form action="/" method="post">
  2. <input type="hidden" name="page" value="menu">
  3. <button class="btn btn-primary mb-4">
  4. <i class="fas fa-arrow-left"></i> Regresar
  5. </button>
  6. </form>
  7. <main class="container" @vue:mounted="mounted">
  8. <fieldset class="mb-4">
  9. <legend v-if="alumno_y_clave">
  10. {{ alumno_y_clave }}
  11. </legend>
  12. <legend v-else>
  13. Gráficos
  14. </legend>
  15. <form>
  16. <div class="row mb-3 form-box">
  17. <label for="alumno.grupo" class="col-sm-1 col-form-label form-group barra">Grupo</label>
  18. <div class="col-sm-10">
  19. <select name="alumno.grupo" id="alumno.grupo" class="form-control col-form-label"
  20. v-model="filter.grupo">
  21. <option :value="null">Todos los grupos</option>
  22. <option v-for="alumno in alumnos.filter(alumno => alumno.grupo)" :value="alumno.grupo">
  23. {{ alumno.grupo }}
  24. </option>
  25. </select>
  26. </div>
  27. </div>
  28. <div class="row mb-3 form-box">
  29. <label for="alumno" class="col-sm-1 col-form-label form-group barra">Alumno</label>
  30. <div class="col-sm-10">
  31. <input type="item.type" name="item.name" list="item.name" id="alumno"
  32. class="form-control col-form-label" @input="fetchDatos" v-model="filter.username">
  33. <datalist id="item.name">
  34. <option v-for="alumno in alumnos_por_grupo" :value="alumno.username">
  35. {{ alumno.nombre }}
  36. </option>
  37. </datalist>
  38. </div>
  39. </div>
  40. <!-- <button type="reset" class="btn btn-danger btn-sm">
  41. <i class="fas fa-eraser"></i>
  42. </button> -->
  43. </form>
  44. </fieldset>
  45. <section v-if="datos != null">
  46. <h2>Gráficos</h2>
  47. <div class="row d-flex justify-content-center">
  48. <div class="col-md-8">
  49. <canvas id="snapshot" width="400" height="400"
  50. @vue:mounted="createMultiLineGraph($el, datos.snapshots)">
  51. </div>
  52. <div class="col-md-8">
  53. <div id="calificaciones"
  54. @vue:mounted="createGraph($el, datos.calificaciones.map(calificacion => calificacion.course_name), datos.calificaciones.map(calificacion => calificacion.calificación_final))">
  55. </div>
  56. </div>
  57. </div>
  58. </section>
  59. </main>
  60. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  61. <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
  62. <script>
  63. const filter = PetiteVue.reactive({
  64. grupo: null,
  65. username: null,
  66. });
  67. const graph = PetiteVue.reactive({
  68. calificaciones: null,
  69. snapshot: null,
  70. })
  71. PetiteVue.createApp({
  72. store,
  73. filter,
  74. graph,
  75. alumnos: [], // schema: { grupo: string, alumnos: Array<{ nombre: string, username: string }> }
  76. get alumnos_por_grupo() { // returns Array<{ nombre: string, username: string }>
  77. if (this.filter.grupo)
  78. return this.alumnos.find(alumno => alumno.grupo === this.filter.grupo).alumnos;
  79. return this.alumnos.flatMap(alumno => alumno.alumnos);
  80. },
  81. get alumno_y_clave() { // returns string: (username) nombre
  82. if (this.filter.username == null) return null;
  83. const alumno = this.alumnos_por_grupo.find(alumno => alumno.username === this.filter.username);
  84. return alumno ? `(${alumno.username}) ${alumno.nombre}` : null;
  85. },
  86. datos: null,
  87. async mounted() {
  88. store.loading = true;
  89. const response = await fetch('fetch/alumnos.php');
  90. const data = await response.json();
  91. this.alumnos = data;
  92. store.loading = false;
  93. },
  94. createGraph($el, labels, data) {
  95. var options = {
  96. series: [{
  97. data: data
  98. }],
  99. chart: {
  100. type: 'bar',
  101. height: 350
  102. },
  103. plotOptions: {
  104. bar: {
  105. borderRadius: 4,
  106. horizontal: true,
  107. }
  108. },
  109. dataLabels: {
  110. enabled: false
  111. },
  112. xaxis: {
  113. categories: labels,
  114. },
  115. colors: [
  116. 'rgba(54, 162, 235, 0.7)', // Blue
  117. 'rgba(255, 99, 132, 0.7)', // Red
  118. 'rgba(255, 159, 64, 0.7)', // Orange
  119. 'rgba(255, 206, 86, 0.7)', // Yellow
  120. 'rgba(75, 192, 192, 0.7)', // Green
  121. 'rgba(153, 102, 255, 0.7)', // Purple
  122. 'rgba(201, 203, 207, 0.7)', // gray
  123. ]
  124. };
  125. var chart = new ApexCharts($el, options);
  126. chart.render();
  127. },
  128. createMultiLineGraph($el, snapshots) {
  129. const labels = snapshots.map(snapshot => snapshot.fecha);
  130. const datasets = snapshots[0].calificaciones.map(calificacion => {
  131. return {
  132. label: calificacion.course_name,
  133. data: snapshots.map(snapshot => snapshot.calificaciones.find(c => c.course_name === calificacion.course_name).calificación_final),
  134. fill: false,
  135. // borderColor: borderColor.slice(0, snapshots[0].calificaciones.length),
  136. tension: 0.1,
  137. }
  138. });
  139. const ctx = $el.getContext('2d');
  140. const multiLineChart = new Chart(ctx, {
  141. type: 'line',
  142. data: {
  143. labels: labels, // Las fechas de tus snapshots
  144. datasets: datasets, // Un array de objetos, cada uno representando una materia
  145. },
  146. options: {
  147. scales: {
  148. y: {
  149. beginAtZero: true,
  150. max: 1, // Asumiendo que las calificaciones están normalizadas entre 0 y 1
  151. }
  152. },
  153. plugins: {
  154. title: {
  155. display: true,
  156. text: 'Calificaciones por Materia a lo largo del Tiempo',
  157. },
  158. },
  159. }
  160. });
  161. },
  162. async fetchDatos() {
  163. if (this.filter.username == null) return;
  164. if (this.alumnos_por_grupo.find(alumno => alumno.username === this.filter.username) == null) return;
  165. store.loading = true;
  166. this.datos = null;
  167. // params POST (formadata [username])
  168. const params = new FormData();
  169. params.append('username', this.filter.username);
  170. const response = await fetch('fetch/calificaciones.php', {
  171. method: 'POST',
  172. body: params,
  173. });
  174. const data = await response.json();
  175. this.datos = data;
  176. store.loading = false;
  177. /* this.createGraph(); */
  178. }
  179. }).mount()
  180. </script>