emartinm / lsql

Aplicación web para el aprendizaje de las bases de datos
MIT License
4 stars 4 forks source link

Tarea sobre pistas (dificultad alta) #40

Closed emartinm closed 3 years ago

emartinm commented 3 years ago

Tarea para @Tamiflu9

Opción de pista en cada ejercicio para los alumnos que no sepan empezar o resolver un ejercicio. En el caso de tener un sistema de puntos se les restaría un porcentaje por cada ejercicio en el que han necesitado ayuda.

Cada alumno tiene una serie de "saldo" el cual recibe tras un aceptado, logros (subir tres seguidos bien, alguno por dificultad tener más premio), que los comentarios de ayuda aparezca un botoncito con un máximo de 3 ayudas y cada vez más caros (3 , 5 , 10 por ejemplo) y sean ayudas genéricas para cada ejercicio.

Pensar si queremos incluir un sistema de saldos y de dónde salen las pistas.

Tamiflu9 commented 3 years ago

Para esta tarea había pensado en tener un botón en problem.html

bombilla

Cuando lo pusas que salga otra ventana y dentro la pista o el resultado:

pista

Luego para las pistas, habría que decidir cuantas pistas como máximo tendrían los ejercicios y si en algunos casos al ser un ejercicios muy fácil dar una pista o si al llegar a un número concreto de envíos erróneos se podría dar el resultado directamente, sin contar luego nada a la puntuación.

Para almacenar las pistas había pensado en tener varios ficheros de pistas para cada ejercicio, al igual que cada ejercicio tiene un fichero con el create, insert, solution, etc.. meter otro con la pista. Habría que modificar la base de datos y el model , no?

jcorreas commented 3 years ago

Me parece muy bien la idea de la bombilla.

Creo que no es necesario ofrecer la posibilidad de dar muchas pistas, como máximo 3 está bien. Respecto a dar el resultado podría ser interesante en algunos casos, pero creo que no debería ser el estándar (es muy fácil que un alumno seleccione esa pista y se la pase a otro alumno que se lleva la puntuación...; Si 4 alumnos se reparten los ejercicios, pueden sacar un 7,5 cada uno sin ningún esfuerzo). Creo que debe ser una pista más, y que el profesor decida cada vez si incluye la "pista total" con el resultado. En mi caso ofrecería eso en muy pocos ejercicios muy sencillitos.

emartinm commented 3 years ago

Con respecto al modelo, creo que sería suficiente con añadir un nuevo campo a la clase Problem que sea la lista de pistas. Las pistas deberían ser representadas en Markdown, al igual que pasa con el enunciado; pero creo que no será necesario almacenarlas también en HTML porque se usarán menos y no me parece tan crítico perder un poco de tiempo en calcularlo.

Por uniformidad con lo que está haciendo Iván sobre tener varias BD iniciales, ese nuevo campo de texto podría contener las pistas separadas por alguna línea prefijada (p.ej. @@@@ next hint @@@@) y de ahí sacarlas cuando sea necesario (recomiendo un método hint_list() que devuelva una lista de HTML con las pistas). Desde el punto de vista del ZIP, se trataría de un fichero hints.md con es mismo formato de separadores. Y si no existe o está vacío no debería fallar nada, es un problema sin pistas.

Parece ser que lo más sensato es ir desbloqueando pistas con los envíos erróneos del estudiante, ¿no? En ese mismo "formato" se podría indicar a partir de qué número de envíos se muestra cada pista. Y que la bombilla indique cuántas pistas hay disponibles y muestre todas las que haya. ¿Cuando se resuelve correctamente el problema se deja de poder acceder a las pistas?

Si un problema no tiene pistas, entonces no se debería incluir la bombilla o debería estar desactivada. Y creo que también sería interesante que el mensaje con las pistas incluya información del tipo "X envíos fallidos más para obtener la siguiente pista".

El formato de pistas para un problema con 3 pistas (la primera disponible siempre, la siguiente tras 5 envío fallidos y la última tras 12 envíos fallidos) podría ser:

0
Es **importante** realizar LEFT JOIN.
@@@@ next hint @@@@
5
Recuerda eliminar los NULL y transformalos por 0. Puedes usar `NLV` o `COALESCE`.
@@@@ next hint @@@@
12
La cláusula `FROM` debe ser: `FROM Club LEFT JOIN Jugador ON Club.CIF = Jugador.Club`

En este caso el método hint_list() devolvería [(0, "Es importante..."), (5, "Recuerda..."), (12, "La cláusula...")]

Sobre la pista final, podríamos no hacer nada especial y que si el profesor quiere añadir la solución completa la ponga expresamente como pista en el listado. Y se podría simplificar añadiendo una etiqueta @@@@ solution @@@@ para no tener que hacer copia-pega. Es decir, algo como:

0
Es **importante** realizar LEFT JOIN.
@@@@ next hint @@@@
5
Recuerda eliminar los NULL y transformalos por 0. Puedes usar `NLV` o `COALESCE`.
@@@@ next hint @@@@
12
La cláusula `FROM` debe ser: `FROM Club LEFT JOIN Jugador ON Club.CIF = Jugador.Club`
@@@@ next hint @@@@
20
@@@@ solution @@@@

Y que a partir de 20 envíos fallidos muestre la solución oficial como pista, que se devolvería en el método hint_list().

Tamiflu9 commented 3 years ago

Había pensado que una forma más sencilla de administrar las pistas podría ser un nuevo modelo llamado Hint.

Campos:

Posibles funciones:

Habría que tener una variable en algún sitio para saber cuantas pistas ha usado el usuario o automáticamente cuando llegue a 5/12/20 que se muestre la pista.

Con esta estructura se facilita la gestión de las pistas al profesor, a la hora de añadir, eliminar o modificar alguna pista. Por ejemplo si hay una pista al quinto fallo, si el profesor ve que al quinto intento todos los alumnos consiguen el acierto en el ejercicio, quizás el profesor prefiera modificar la pista para que no sea tan sencillo. Esto sería muy fácil con esta estructura haciéndolo desde admin. También si el profesor ve que hay muchos alumnos con un número muy elevado de envíos erróneos, sería más sencillo tener la opción de mostrar la solución solo con añadir la pista desde admin.

También sería conveniente comprobar no solo el número de envíos fallidos, si no el número de intentos fallidos que tengan un código distinto, para que un alumno ponga la misma solución x veces y obtenga la pista fácilmente.

Creo que esta estructura sería más simple desde el punto de vista del profesor.

jcorreas commented 3 years ago

Me parecen bien los comentarios, aunque es mejor que la parte de implementación la revise Enrique.

Respecto a la modificación de pistas, hay que pensar si es adecuado modificarlas según las están resolviendo los alumnos, sobre todo si forman parte de la evaluación (para evitar que unos alumnos hayan tenido más ayuda que otros).

En cambio, está bien el comprobar que las soluciones no sean iguales, aunque el alumno también puede cambiar ligeramente (o aleatoriamente) su respuesta para obtener la pista. Por otra parte, la penalización sobre la nota debería ser suficientemente disuasoria.

Por último, una pregunta: el hecho de recibir ayuda en forma de pistas, ¿debería afectar a la obtención de logros / podio? Estar el primero en el podio recibiendo ayuda se debería considerar trampujas, no? :-)

Tamiflu9 commented 3 years ago

Respecto a la modificación de pistas, hay que pensar si es adecuado modificarlas según las están resolviendo los alumnos, sobre todo si forman parte de la evaluación (para evitar que unos alumnos hayan tenido más ayuda que otros).

Si es verdad que modificar una pista a mitad de curso cuando alguien ya ha tenido esa ayuda no sería justo para los demás alumnos.

En cambio, está bien el comprobar que las soluciones no sean iguales, aunque el alumno también puede cambiar ligeramente (o aleatoriamente) su respuesta para obtener la pista. Por otra parte, la penalización sobre la nota debería ser suficientemente disuasoria.

Bueno al hacer tantos envíos también les perjudica a la hora de sumarle puntos cada vez que falla un envío y tendría una puntuación mayor en el ranking. Con las pistas se podría sumar a la puntuación del ranking x puntos por cada pista que ha usado. Por ejemplo, que la pista 1 sean 5 puntos, la segunda 10.. o algo así.

Por último, una pregunta: el hecho de recibir ayuda en forma de pistas, ¿debería afectar a la obtención de logros / podio? Estar el primero en el podio recibiendo ayuda se debería considerar trampujas, no? :-)

Que esté el primero tampoco lo vería muy mal ya que luego su puntuación va a ser peor que la de otros alumnos.

emartinm commented 3 years ago

La información de las pistas se puede almacenar en un modelo diferente de Problema y la organización que propones funcionaría. Lo que no estoy tan seguro es que sea la más cómoda para el profesor. Si tenemos 30 problemas y cada uno tiene 3 pistas, encontrar una pista concreta será buscar en un listado de 90 entradas (con un poco de suerte ordenando por problema). Si lo configuras para permitir buscar por nombre de problema me parecería adecuado (creo que se puede, investiga un poco las vistas de admin). En cualquier caso, se debe permitir cargar un problema de un ZIP con un único fichero de pistas, y crear los objetos Hint adecuados. Y si se realizan actualizaciones tendrías que borrar todos los objetos Hint y crearlos de nuevo

Con respecto a los puntos que se restan, yo por ahora no lo contemplaría porque no tenemos esa noción en el juez. La puntuación del ranking únicamente mide el número de envíos incorrectos hasta el primer acierto de cada problema, y de hecho tener más puntos es peor que tener menos, pero solo se utilizan para romper el empate por número de envíos.

Las funciones available_hint(self, user, problem) y calculate_points(self) son de un objeto Hint concreto? Porque no les veo mucha conexión ahí. Si una pista es del problema X, no veo qué utilidad tiene invocar a available_hint de un problema Y. available_hints lo veo más como una función del usuario (pero no vamos a cambiar el modelo de usuario) o de problema como problem.available_hints(user). La decalculate_points` por ahora no la haría, pero también podría pertenecer a un objeto problema. Creo que es más cómodo, porque cuando renderizas el enunciado tendrás un objeto Problem del que sacar toda la información.

Lo de mirar envíos diferentes suena bien, pero en el momento que definamos una noción de equivalencia de envíos habrá quien haga el cambio mínimo para ser envíos diferentes :-) Por ahora no me preocuparía.

Ahora mismo el ranking únicamente considera envíos aceptados y fallidos. Si queremos meter las pistas aquí yo haría lo siguientes:

Esto de llevar la cuenta de cuántas pistas ha utilizado un usuario es algo que se debe almacenar en algún lado. Porque puedes tener la posibilidad de ver una pista pero nunca hacerlo, y eso no debe penalizar. Esto implica tener otro modelo (por ejemplo UsedHint) y complica un poco la página de enunciado, porque tiene que permitir la visualización de las pistas ya "utilizadas" pero si tienes otra disponible tienes que solicitarla de manera expresa.

Tamiflu9 commented 3 years ago

La información de las pistas se puede almacenar en un modelo diferente de Problema y la organización que propones funcionaría. Lo que no estoy tan seguro es que sea la más cómoda para el profesor. Si tenemos 30 problemas y cada uno tiene 3 pistas, encontrar una pista concreta será buscar en un listado de 90 entradas (con un poco de suerte ordenando por problema). Si lo configuras para permitir buscar por nombre de problema me parecería adecuado (creo que se puede, investiga un poco las vistas de admin).

He estado mirando y con lo de ' buscar por nombre de problema ' no se si te refieres a poder filtrar la lista que de las pistas y que aparezcan las del problema que quieras. Que con list_filter() entiendo que podría hacer que en el cuadro de filtrado apareciera la lista de los problemas, seleccionas el que quieras y aparecen las pistas de ese problema.

Las funciones available_hint(self, user, problem) y calculate_points(self) son de un objeto Hint concreto? Porque no les veo mucha conexión ahí. Si una pista es del problema X, no veo qué utilidad tiene invocar a available_hint de un problema Y. available_hints lo veo más como una función del usuario (pero no vamos a cambiar el modelo de usuario) o de problema como `problem.available_hints(user).

Si igual tiene mas sentido esto: problem.available_hints(user) , que sea una función de problema.

  • En el ranking, tener en cuenta el número de pistas utilizadas para penalizar su posición. Ahora mismo no sé cómo contabilizarlas, quizá sumar la actual puntuación con el número de pistas utilizadas para romper empates a número de problemas resueltos?

Si, lo veo una buena solución.

Esto de llevar la cuenta de cuántas pistas ha utilizado un usuario es algo que se debe almacenar en algún lado. Porque puedes tener la posibilidad de ver una pista pero nunca hacerlo, y eso no debe penalizar. Esto implica tener otro modelo (por ejemplo UsedHint) y complica un poco la página de enunciado, porque tiene que permitir la visualización de las pistas ya "utilizadas" pero si tienes otra disponible tienes que solicitarla de manera expresa.

Entonces, tendría que hacer dos modelos nuevos, uno para las pistas y otro para las pistas usadas?

emartinm commented 3 years ago

He estado mirando y con lo de ' buscar por nombre de problema ' no se si te refieres a poder filtrar la lista que de las pistas y que aparezcan las del problema que quieras. Que con list_filter() entiendo que podría hacer que en el cuadro de filtrado apareciera la lista de los problemas, seleccionas el que quieras y aparecen las pistas de ese problema.

Sí, me refiero a facilitar la vida al profesor en el interfaz de administrador, para saber fácilmente cuántas pistas hay de un problema concreto. Sé que se puede personalizar esa interfaz, y lo que propones si funciona sería bastante cómodo.

Entonces, tendría que hacer dos modelos nuevos, uno para las pistas y otro para las pistas usadas?

Si tenemos que almacenar qué pistas existen (definición) y quién ha visto de verdad cada pista disponible (acceso o visualización) sería una opción válida, similar a lo que hizo Daniel con los logros. Podría haber otras alternativas, pero esta tendría de bueno:

Tamiflu9 commented 3 years ago

Al hacer otra vez las migraciones en otro entorno virtual me da este error:

errores

He borrado todo varias veces e incluso he borrado el repositorio de github y he hecho un nuevo fork y todo y me sigue dando el mismo error.

Tamiflu9 commented 3 years ago

De momento lo que tengo hecho es que aparezcan todas las pistas usadas por un usurario (imagen2). Estoy viendo como hacer el pedir una cuando pulsas el botón de "Pista". Había pensado en usar JavaScript pero igual es un poco complicado.. Que para poder ver qué pista mostrar en ese momento habría que pasarle la lista de pistas a la función "damePista()" para no se como hacerlo.

`

` (código) problem

Esta sería la vista cuando no tienes ninguna pista disponible. pista1

Esta sería la vista cuando ya has pedido una pista. pista2

emartinm commented 3 years ago

Con respecto a las migraciones, hay un jaleo que no sé cómo se puede haber creado: tienes una migración pendiente y esa migración pendiente no se puede aplicar porque trata de crear una tabla que ya existe. ¿Cómo se ha llegado ahí? Realmente no lo sé. Busca a ver si se se pueden revertir las migraciones tuyas aplicadas, borra todas los ficheros PY con las migraciones que hayas generado tú, vuelve a crearla con makemigrations y aplícala. Si no puedes revertir las migraciones, tendrás que vaciar toda la BD de PosgreSQL y volver a crear todo desde cero, incluyendo superuser, demás usuarios, colecciones, problemas, etc.

La última migración oficial es la https://github.com/emartinm/lsql/blob/master/lsql/judge/migrations/0026_auto_20210407_1044.py, así que todas las que tengas después de la 0026 serán tuyas.

emartinm commented 3 years ago

Sobre lo de pedir pista, si queremos controlar bien quién visualiza cada pista, habrá que realizar una petición al servidor sí o sí y manejar la respuesta con JavaScript para modificar el HTML y mostrar el popup. Como es una petición que cambia el estado de la aplicación web, será una petición POST. Como realizar envíos también es una petición asíncrona, tener actualizado el número de envíos que te faltan para la nueva pista puede ser un dolor de cabeza. Así que propongo que se maneje todo dentro de la petición de pista nueva.

Esta petición sería asíncrona a una ruta /lsql/getHint y llevaría dos parémtros:

Esta petición sería contestada con un JSON que contendría la información necesaria:

emartinm commented 3 years ago

Con respecto al interfaz, cuando no hay ninguna pista disponible creo que habría que indicarlo con un mensaje Todavía no tienes ninguna pista disponible para este problema. Pulsa el botón "Solicitar pista" para obtener una pista. Ten en cuenta que solicitar pistas puede afectar a tu posición en la clasificación. La última parte creo que debería aparecer siempre, como aviso del impacto de las pistas.

También cambiaría el botón a "Solicitar pista", separaría Pista y el número de pista (ahora sale "Pista1"). Y ten cuidado para mostrar el HTML y no el markdown de la pista. Como es algo que siempre se usará, puede ser interesante almacenar el texto en HTML de la pista en la BD, al igual que se hace con los problemas.

Tamiflu9 commented 3 years ago

Sobre lo de pedir pista, si queremos controlar bien quién visualiza cada pista, habrá que realizar una petición al servidor sí o sí y manejar la respuesta con JavaScript para modificar el HTML y mostrar el popup. Como es una petición que cambia el estado de la aplicación web, será una petición POST. Como realizar envíos también es una petición asíncrona, tener actualizado el número de envíos que te faltan para la nueva pista puede ser un dolor de cabeza. Así que propongo que se maneje todo dentro de la petición de pista nueva.

Esta petición sería asíncrona a una ruta /lsql/getHint y llevaría dos parémtros:

  • user
  • problem

Esta petición sería contestada con un JSON que contendría la información necesaria:

  • Si todavía no puede ver ninguna pista nueva, se recibiría el número de envíos que faltan para indicárselo al usuario
  • Si ya no hay más pistas que mostrar para ese usuario y problema, se indicaría. Sería más usable detectar esa situación y deshabilitar/borrar el botón de pista nueva en ese caso.
  • Si el usuario puede ver una nueva pista para ese problema, se devolvería la nueva pista y se almacenaría la visualización de esa pista por ese usuario en la BD. En este caso puede ser más cómodo devolver dentro del JSON toda la lista de pistas disponibles incluyendo la última que acaba de pedir, para que el código JS sea más sencillo (reemplazar todas las pistas actuales por las nuevas)

Vale, entiendo que hay que hacer una nueva ruta en urls.py:

urls

Luego en el botón que tengo en problem.py llamo a la función de views.py que llama la ruta:

problem

La función deviews.py:

views

Y el JSON que se devuelve lo recoge la función que tendría en submit.js y ahí analizaría el JSON y devolvería un html modificado con las pistas actualizadas?

Y luego para almacenar las pistas nuevas, se haría en la función deviews.py o en la de submit.js?

emartinm commented 3 years ago

Viendo el listado de rutas, quizá preferiría el nombre lsql/hint/<problem> porque 'getHint` es bastante diferente.

La idea del formulario es correcta pero no sé si se están mezclando cosas. Al pulsar el botón se debe hacer una invocación a tu función JS, que debe hacer una petición asíncrona y el callback que reciba el JSON debe modificar la página HTML y mostrar el popup. No es necesario pasar el usuario porque esa petición asíncrona será realizada por el usuario que pide la pista, pero sí hay que pasar el ID de problema sobre el que se pide pista.

Para devolver un JSON siempre debes crear un diccionario Python, para que lo que devuelvas tenga siempre la misma estructura. Piensa y define bien qué campos necesitas y avísanos por aquí sobre ese esquema. El código de get_hint() que adjuntas está bien como mock para pruebas pero debería consultar las pistas disponibles para detectar si se puede mostrar una nueva o cuántos intentos faltan para el siguiente. Es decir, que no esté atornillado a 5 envíos, 12 envíos, 20 envíos. Tampoco supongas que las pistas están ordenadas de menor a mayor número de envíos, porque un profesor las puede crear en cualquier orden y un problema puede tener un número indeterminado de pistas, p.ej. 8.

emartinm commented 3 years ago

Para que no se nos olvide lo que hemos comentado, he cambiado esta tarea a dificultad alta. Se dividiría en 3 PR:

  1. Tener pistas asociadas a un problema y almacenar los usos de las pistas. Que un usuario pueda ver las pistas usadas en un determinado problema y pedir una nueva. Las pistas se añaden manualmente desde el interfaz de administrador. Idealmente el texto de una pista debe ser Markdown para permitir usar negrita, tablas, imágenes remotas, etc.
  2. Que la información de las pistas esté dentro de los ZIP con información del problema, dentro de un único fichero hints.md.
  3. Hacer una página de pistas del usuario (similar a los logros) donde un usuario vea qué pistas ha utilizado
emartinm commented 3 years ago

Otra que cosa que se ha comentado pero que no he recordado meter en la división de PR sería modificar los rankings para que tengan en cuenta el número de pistas utilizadas. Creo que el orden de los usuarios en el ranking debería tener en cuenta primero el número de problemas resueltos (como hasta ahora). En caso de empate, tendríamos que jugar con el número de envíos incorrectos realizado y el número de pistas utilizadas. @jcorreas @Tamiflu9 ¿qué creéis que debería penalizar más: el número de envíos incorrectos que has hecho o el número de pistas que has utilizado? ¿Pensáis que no debería penalizarse, ya que para conseguir pista ya "han pagado" en envíos incorrectos? ¿Considerar que utilizar una pista es tan malo como X envíos incorrectos? Cualquier propuesta es bienvenida.

Actualizo la propuesta de PR de esta tarea:

  1. Tener pistas asociadas a un problema y almacenar los usos de las pistas. Que un usuario pueda ver las pistas usadas en un determinado problema y pedir una nueva. Las pistas se añaden manualmente desde el interfaz de administrador. Idealmente el texto de una pista debe ser Markdown para permitir usar negrita, tablas, imágenes remotas, etc.
  2. Que la información de las pistas esté dentro de los ZIP con información del problema, dentro de un único fichero hints.md.
  3. Hacer una página de pistas del usuario (similar a los logros) donde un usuario vea qué pistas ha utilizado
  4. Actualizar la generación de rankings para que tenga en cuenta en el orden el número de pistas utilizadas por cada usuario (si al final se decide que se debe tener en cuenta). Si hay que tocar este código, propongo refactorizar el código que genera la lista de usuarios y moverla al modelo colección, de tal manera que se obtenga con collection.ranking(group, start_date, end_date)
Tamiflu9 commented 3 years ago

Yo creo que con penalizar ya el número de envíos erróneos bastaría, ya que para obtener la pista necesitas hacer un determinado número de envíos. Creo que si al final se penaliza el hacer x números de envíos erróneos y además el pedir una pista, dejarán de usar las pistas para que no les penalice más en el ranking.

emartinm commented 3 years ago

Supongo que también entrará en juego el peso que tenga el uso de una pista. Si penaliza como 10 envíos fallidos, seguro que prefieren hacer los 10 envíos fallidos. Si penaliza lo mismo que un envío fallido, seguramente prefieran la pista porque si les ayuda a superar el ejercicio eso sí que tiene un impacto grande en el ranking. Y si el ranking no vale para nota, es posible que usen todas las pistas disponibles sin problema.

En este momento yo estaría a favor de que usar una pista penalizase igual que hacer un envío fallido.

jcorreas commented 3 years ago

Yo creo que utilizar una pista tiene que penalizar algo. Si no penaliza nada, entonces siempre se utilizarían pistas, porque no cuesta nada pedirla.

En cambio, si cuesta (reputación negativa, en este caso), entonces si le falta poco al alumno para sacarlo, puede que se arriesgue e intente resolverlo sin utilizar la pista.

Por otra parte, si se fija el precio de la pista a 1pista = 1fallo, entonces es trabajo del profesor poner pistas que no den demasiadas pistas.

Tamiflu9 commented 3 years ago

Contabilizar la pista como un fallo al igual que un envío erróneo lo veo bien. Que si un alumno ha hecho el mismo número de envíos fallidos que otro y el segundo ha usado una pista, no sería justo estar empatados.

Tamiflu9 commented 3 years ago

Con respecto a las migraciones, hay un jaleo que no sé cómo se puede haber creado: tienes una migración pendiente y esa migración pendiente no se puede aplicar porque trata de crear una tabla que ya existe. ¿Cómo se ha llegado ahí? Realmente no lo sé. Busca a ver si se se pueden revertir las migraciones tuyas aplicadas, borra todas los ficheros PY con las migraciones que hayas generado tú, vuelve a crearla con makemigrations y aplícala. Si no puedes revertir las migraciones, tendrás que vaciar toda la BD de PosgreSQL y volver a crear todo desde cero, incluyendo superuser, demás usuarios, colecciones, problemas, etc.

Para vaciar la base de datos como lo hago? Que me sigue dando errores al hacer las migraciones.. No se si se hace desde la página de PosgreSQL, que he intentado hacerlo desde ahí y yo no veo nada o no se donde buscar

pg

emartinm commented 3 years ago

Yo nunca he usado pgAdmin, así que no puedo indicarte cómo se hará. Quizá puedas mirar en Schemas a ver qué esquemas tienes. Si hay uno que contenga las tablas del juez es ese esquema el que debes eliminar. También puedes conectarte con python manage.py dbshell y borrar el esquema con DROP SCHEMA o las tablas una a una con DROP TABLE.

Tamiflu9 commented 3 years ago

Ah vale si, borrando las tablas de Hints y UsedHint si que funciona. Ahora ya hace las migraciones bien y no me salta ningún error.

Tamiflu9 commented 3 years ago

Para devolver un JSON siempre debes crear un diccionario Python, para que lo que devuelvas tenga siempre la misma estructura. Piensa y define bien qué campos necesitas y avísanos por aquí sobre ese esquema. El código de get_hint() que adjuntas está bien como mock para pruebas pero debería consultar las pistas disponibles para detectar si se puede mostrar una nueva o cuántos intentos faltan para el siguiente. Es decir, que no esté atornillado a 5 envíos, 12 envíos, 20 envíos. Tampoco supongas que las pistas están ordenadas de menor a mayor número de envíos, porque un profesor las puede crear en cualquier orden y un problema puede tener un número indeterminado de pistas, p.ej. 8.

Como hablamos el miércoles, creo que para el diccionario sería suficiente almacenar solo la lista de las pistas y el mensaje en caso de que se vaya a usar. Así si la lista esta vacía se devolvería el mensaje y si no, la lista con las pistas.

Para la función de views.py así estaría mejor? Primero añado todas las pistas que ya se han usado a la lista de pistas del diccionario y luego recorro la lista de las pistas para ver si hay una que no se ha usado, si no hay ninguna se quedaría el mensaje inicial de que no hay mas pistas, si hay una pista que no se ha usado o se añade la pista a la lista de pistas usadas ya que esa es la que se va a usar o si no tiene el número de envíos necesarios se cambiaría el mensaje informando de cuantos envíos le falta para poder ver una pista nueva.

vi

emartinm commented 3 years ago

Ten cuidado que no es lo mismo decir "no hay pistas para este problema" (tiene 0 pistas) que "no hay más pistas para este problema** (tiene más de 0 pero ya las has visto todas).

Usar un entero 0 o 1 a modo de booleano es muy de C de los años 70. Si es algo cierto o falso mejor usa bool. Y para qué sirve si lo cambias a 1 y justo depués terminas la función con un return?

No estoy muy seguro de qué añade el append de la línea 584. Añadiría un objeto UsedHint, pero entiendo que lo que quieres añadir es el texto del objeto Hint asociado. Ojo que el texto de una pista no es texto plano sino Markdown, así que lo que debes añadir es el HTML generado a partir del Markdown y no el texto plano. Es decir, el texto de una pista podría ser el siguiente, y la lista de pistas que ve el usuario debería contener Club en negrita:

Ten cuidado con la clave primaria de la tabla **Club**

Tampoco estoy seguro de que la comparación sea de igualdad. Si llevas 57 envíos fallidos y no has pedido ninguna pista, cuando pidas la primera deberías ver la que menos envíos necesite (p.ej. 3), luego la de 5 y luego la de 10. En ese orden. Ten cuidado con esta situación, debería ser un test para comprobar que no se visualizan las 3 de golpe y van a apareciendo en el orden adecuado.

Finalmente, ¿en qué orden aparecen las pistas vistas de used_hinst? Añade un order by para garantizarlo.

Tamiflu9 commented 3 years ago

Ten cuidado que no es lo mismo decir "no hay pistas para este problema" (tiene 0 pistas) que "no hay más pistas para este problema** (tiene más de 0 pero ya las has visto todas).

Usar un entero 0 o 1 a modo de booleano es muy de C de los años 70. Si es algo cierto o falso mejor usa bool. Y para qué sirve si lo cambias a 1 y justo depués terminas la función con un return?

Vale si, no tiene sentido..

No estoy muy seguro de qué añade el append de la línea 584. Añadiría un objeto UsedHint, pero entiendo que lo que quieres añadir es el texto del objeto Hint asociado. Ojo que el texto de una pista no es texto plano sino Markdown, así que lo que debes añadir es el HTML generado a partir del Markdown y no el texto plano. Es decir, el texto de una pista podría ser el siguiente, y la lista de pistas que ve el usuario debería contener Club en negrita:

Ten cuidado con la clave primaria de la tabla **Club**

Ah vale si es verdad, entonces en los modelos de las pistas tengo que almacenar el text_html y text_md como en los problemas? Y cuando haga de leer de los zips usar la función de markdown_to_html?

Tampoco estoy seguro de que la comparación sea de igualdad. Si llevas 57 envíos fallidos y no has pedido ninguna pista, cuando pidas la primera deberías ver la que menos envíos necesite (p.ej. 3), luego la de 5 y luego la de 10. En ese orden. Ten cuidado con esta situación, debería ser un test para comprobar que no se visualizan las 3 de golpe y van a apareciendo en el orden adecuado.

Ah vale es verdad, que si no le daría la pista para esos fallos y no la primera pista que debería dar.

Finalmente, ¿en qué orden aparecen las pistas vistas de used_hinst? Añade un order by para garantizarlo.

Vale si, que igual están de mayor a menor ordenadas.

Tamiflu9 commented 3 years ago

La idea del formulario es correcta pero no sé si se están mezclando cosas. Al pulsar el botón se debe hacer una invocación a tu función JS, que debe hacer una petición asíncrona y el callback que reciba el JSON debe modificar la página HTML y mostrar el popup. No es necesario pasar el usuario porque esa petición asíncrona será realizada por el usuario que pide la pista, pero sí hay que pasar el ID de problema sobre el que se pide pista.

Vale para la función JS, no se muy bien como hacerlo. Tengo la función show_hit() y en la variable myjson guardo el json que se devuelve la llamada a la función get_hint() que tengo el views.py. Después me quedo con la lista de las pistas y el mensaje.

function get_hit(){
    // Get json with the hints
    let myjson = $('#hintpoint').val();
    var list_hints = myjson.pistas
    var msg = myjson.msg

    if ( list_hints.length > 0) {
        /*para las pistas*/

    } else {
        /*para el mensaje*/
    }
}

Para mostrar una cosa u otra, debo generar un html o como cambio el contenido del popup?

Lo que he contrado para modificar html con javascript es esto document.getElementById(id).innerHTML = nuevo HTML, estaría bien? donde pone nuevo HTML pondría o la lista de las pistas o el mensaje que fuera.

emartinm commented 3 years ago

Ah vale si es verdad, entonces en los modelos de las pistas tengo que almacenar el text_html y text_md como en los problemas? Y cuando haga de leer de los zips usar la función de markdown_to_html?

Sí, es una posibilidad para amortizar costes, por si acaso hay alguna pista especialmente larga no tener que transformarla cada vez que se visualiza.

Para mostrar una cosa u otra, debo generar un html o como cambio el contenido del popup Lo que he contrado para modificar html con javascript es esto document.getElementById(id).innerHTML = nuevo HTML, estaría bien? donde pone nuevo HTML pondría o la lista de las pistas o el mensaje que fuera.

Dado que Bootstrap usa JQuery, mi recomendación es que uses JQuery para modificar el HTML. Si ves la función show_modal de submit.js ahí ves cómo cambio el texto de los elementos <div> de identificador modal_title y modal_message que existen en la plantilla problem.html. La función show_feedback borra el código HTML de un <div> y luego lo establece al código HTML que le llega en el JSON. Pero ojo que este JSON contiene una cadena de texto de código HTML, no texto plano. Si quieres usarlo igual (quizá lo más recomendable) tu JSON tendría que contener dos campos:

Por si te sirve de guía, este es un ejemplo de JSON que devuelve submit

{
  "veredict": "WA",
  "title": "Resultados incorrectos",
  "message": "Tu código SQL ha generado resultados erróneos. Consulta el cuadro rojo en la parte inferior de la página para ver los detalles.",
  "feedback": "<div id=\"result\">\n\n<p>\n    Faltan algunas filas que deberían aparecer.\n</p>\n<p>\n    <strong>Resultado generado por tu código:</strong>\n    \n\n\n\n\n<table class=\"table table-striped\">\n\n    <thead class=\"thead-dark\">\n        <tr>\n        \n            <th scope=\"col\">CIF</th>\n        \n            <th scope=\"col\">NOMBRE</th>\n        \n            <th scope=\"col\">SEDE</th>\n        \n            <th scope=\"col\">NUM_SOCIOS</th>\n        \n        </tr>\n    </thead>\n    \n    <tbody>\n    \n        \n            \n                <tr>\n            \n            \n                <td>11111111X</td>\n            \n                <td>Real Madrid CF</td>\n            \n                <td>Concha Espina</td>\n            \n                <td>70000</td>\n            \n            </tr>\n        \n            \n                <tr>\n            \n            \n                <td>11111112X</td>\n            \n                <td>Futbol Club Barcelona</td>\n            \n                <td>Aristides Maillol</td>\n            \n                <td>80000</td>\n            \n            </tr>\n        \n    </tbody>\n</table>\n\n\n\n\n\n</p>\n<p>\n    <strong>Filas que faltan:</strong>\n    \n\n\n\n\n<table class=\"table\">\n\n    <thead class=\"thead-dark\">\n        <tr>\n        \n            <th scope=\"col\">CIF</th>\n        \n            <th scope=\"col\">NOMBRE</th>\n        \n            <th scope=\"col\">SEDE</th>\n        \n            <th scope=\"col\">NUM_SOCIOS</th>\n        \n        </tr>\n    </thead>\n    \n    <tbody>\n    \n        \n             \n                <tr class=\"marked_row\">\n            \n            \n                <td>11111113X</td>\n            \n                <td>Paris Saint-Germain Football Club</td>\n            \n                <td>Rue du Commandant Guilbaud</td>\n            \n                <td>1000</td>\n            \n            </tr>\n        \n    </tbody>\n</table>\n\n\n\n\n\n</p>\n\n</div>\n\n\n\n"
}
Tamiflu9 commented 3 years ago

Me sale este error que no se muy bien de que es. No se si es por la url: path('hint/<int:problem_id>', views.get_hint, name='get_hint')

El error me deja de salir si quito esto del botón de 'solicitar pista' de problem.html : <input type="hidden" id="hintpoint" name="hintpoint" value="{% url 'judge:hint' problem.pk %}">

error

En problem.html tengo esto, no se si para las pistas tengo que dejar lo que tenía antes, que era un bucle que recorría las pistas usadas que cogía de una función que había creado en la función show_probem() de views.py:

problem.used_hints = UsedHint.objects.filter(problem=problem, user=request.user)

O no hace falta si al pedir pista se va actualizando el html con las pistas e inicialmente los alumnos no tendrían ninguna pista.

problem

Aquí uso el $('#hint_text').append(html); que he visto con el append() se puede ir acumulando los elementos, como es en el caso de las pistas, para que se creen los <div></div> necesarios.

submit

En views.py:

  1. En el diccionario no se muy bien como almacenar las pistas para tener el nombre y a descripción. Si tengo algo así:

    data = {'pista' = [ { name: 'pista1', description: 'ayuda de la pista1' }, 
                             { name: 'pista2', description: 'ayuda de la pista2' }, 
                             { name: 'pista3', description: 'ayuda de la pista3' } ] , 
        'msg' = 'bla bla bla' }

    Se accedería como lo hago arriba: data[0].name y data[0].description .

  2. Para comprobar el caso que me dijiste de que un alumno haga 20 envíos pero que no haya pedido ninguna pista y solicita una, que le de la primera y no la pista que correspondería al tener 20 envíos fallidos. Intento quedarme con la ultima pista que tenga en UsedHint y coger la pista de Hint que tenga el num_submit (número de envío fallidos) más alto que el de la ultima pista usada. Acabo de ver que igual debería añadir el caso de que no haya ninguna pista usada y en ese caso se compararía con 0.

views

Tamiflu9 commented 3 years ago

Por otro lado la vista de las pistas de momento se vería así:

pistas

He añadido como AVISO el mensaje que me dijiste advirtiendo que si usan una pista afectará a su calificación. El apartado de INFO lo he dejado para los mensajes de 'no hay más pistas disponibles' o 'este problema no tiene pistas' y luego con pistas se vería:

pista2

emartinm commented 3 years ago

Me sale este error que no se muy bien de que es. No se si es por la url:

Tu ruta se llama judge:get_hint pero al usarla en el botón usas judge:hint, por eso falla.

O no hace falta si al pedir pista se va actualizando el html con las pistas e inicialmente los alumnos no tendrían ninguna pista.

Cuando se cargue el enunciado de un problema, la página ya debe venir pre-cargada con el listado de pistas que el usuario ya ha desbloqueado. Es decir, no debería tener que pulsar el botón Solicitar pista para ver las pistas que pidió en el pasado porque eso no es pedir pista (que tiene un precio) sino recuperar lo que ya ha pagado. Así que esa plantilla debería cargar las pistas originales de problem.used_hints (ordenadas, claro)

Se accedería como lo hago arriba: data[0].name y data[0].description .

¿Era una pregunta? data no es una lista sino un diccionario que contiene un campo con una lista, sería data["pistas"][0]["name"]. Pero cuando hagas pruebas te darás cuenta de esos fallos. Si únicamente añades pista tras pista, "pistas" no tiene por qué ser una lista de todas las pistas sino únicamente de la última. Si reemplazas todas las pistas que existen por lo que te llegue en el JSON, necesitas enviarlas todas como hasta ahora. Pero quizá puedas hacer que el JSON contenga el código HTML directamente y solo tengas que pegarlo sin generarlo.

Para comprobar el caso que me dijiste de que un alumno haga 20 envíos pero que no haya pedido ninguna pista y solicita una, que le de la primera y no la pista que correspondería al tener 20 envíos fallidos. Intento quedarme con la ultima pista que tenga en UsedHint y coger la pista de Hint que tenga el num_submit (número de envío fallidos) más alto que el de la ultima pista usada.

Para un problema puedes obtener todas las pistas que existen (ordenadas), las pistas que el usuario X ha solicitado para ese problema (ordenadas también) y sin necesidad de bucle ya podrás acceder a qué pista sería la siguiente a considerar únicamente por posición (o si ya las ha agotado). Luego solo debes comprobar que tiene o no envíos suficientes para desbloquearla. Tu código me resulta excesivamente complejo/largo para lo poco que hay que hacer (no sé si se me está escapando algo).

Acabo de ver que igual debería añadir el caso de que no haya ninguna pista usada y en ese caso se compararía con 0.

Eso hay que tenerlo en cuenta, claro.

Por otro lado la vista de las pistas de momento se vería así:

Los rectángulos de colores me resultan visualmente incómodos. Ten en cuenta que las pistas pueden ser HTML complejo (imagínate una lista numerada seguida de un fragmento de código). Haz pruebas para ver cómo se visualizan varias pistas largas en tu ventana, y si queda clara la separación entre ellas.

Tamiflu9 commented 3 years ago

He terminado los procesos de Oracle y he reiniciado y al conectarme ahora me sale este error: también he cambiado la contraseña de Oracle en las variables de entorno, he reiniciado otra vez y me sigue saliendo.

or

Y por otra parte, las vistas del juez me salen sin la cabecera

pn

Tamiflu9 commented 3 years ago

He buscado por internet y he probado varias cosas, pero nada.

Aquí alterar la tabla para poner otra contraseña:

(base) C:\Users\tamara>sqlplus "/as sysdba"

SQL*Plus: Release 11.2.0.2.0 Production on MiÚ Abr 28 20:11:34 2021

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

SQL> ALTER USE SYSTEM IDENTIFIED BY SYSTEM;
ALTER USE SYSTEM IDENTIFIED BY SYSTEM
      *
ERROR at line 1:
ORA-00940: invalid ALTER command

SQL> ALTER USER SYSTEM IDENTIFIED BY SYSTEM;

User altered.

SQL> COMMIT
  2  ;

Commit complete.

SQL> EXIT
Disconnected from Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

(base) C:\Users\tamara>SQLPLUS

SQL*Plus: Release 11.2.0.2.0 Production on MiÚ Abr 28 20:13:42 2021

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

Enter user-name: SYSTEM
Enter password:
ERROR:
ORA-01017: invalid username/password; logon denied

Enter user-name: SYSTEM
Enter password:
ERROR:
ORA-01017: invalid username/password; logon denied

Enter user-name: system
Enter password:
ERROR:
ORA-01017: invalid username/password; logon denied

SP2-0157: unable to CONNECT to ORACLE after 3 attempts, exiting SQL*Plus

(base) C:\Users\tamara>

También he visto que decían que puede ser porque en Oracle distingue entre matúsculas y minúsculas. Cambiando a false el parámetro en teoría a la gente le iba..

(base) C:\Users\tamara>sqlplus "/as sysdba"

SQL*Plus: Release 11.2.0.2.0 Production on MiÚ Abr 28 20:17:09 2021

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

SQL> SHOW PARAMETER SEC_CASE_SENSITIVE_LOGON

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
sec_case_sensitive_logon             boolean     TRUE

SQL> ALTER SYSTEM SET SEC_CASE_SENSITIVE_LOGON = FALSE;

System altered.

SQL> ALTER USER SYSTEM IDENTIFIED BY SYSTEM;

User altered.

SQL> COMMIT
  2  ;

Commit complete.

SQL> EXIT
Disconnected from Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production

(base) C:\Users\tamara>SQLPLUS

SQL*Plus: Release 11.2.0.2.0 Production on MiÚ Abr 28 20:20:16 2021

Copyright (c) 1982, 2014, Oracle.  All rights reserved.

Enter user-name: SYSTEM
Enter password:
ERROR:
ORA-01017: invalid username/password; logon denied

Enter user-name: SYSTEM
Enter password:
ERROR:
ORA-01017: invalid username/password; logon denied

Enter user-name: system
Enter password:
ERROR:
ORA-01017: invalid username/password; logon denied

SP2-0157: unable to CONNECT to ORACLE after 3 attempts, exiting SQL*Plus

(base) C:\Users\tamara>
emartinm commented 3 years ago

Al cambiar la clave yo siempre lo he hecho poniéndola entre comillas. Pero intenta crear un usuario administrador SQLAB_GESTOR en lugar de usar SYS y SYSTEM. Accede como sysdba y ejecuta lo siguiente:

CREATE TABLESPACE SQLAB DATAFILE 'SQLAB' SIZE 1024M AUTOEXTEND OFF;

CREATE USER SQLAB_GESTOR IDENTIFIED BY "elpassword" DEFAULT TABLESPACE SQLAB TEMPORARY TABLESPACE TEMP QUOTA UNLIMITED ON SQLAB;

GRANT DBA to SQLAB_GESTOR;

-- Evita que las contraseñas caduquen a los 180 días
ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

Después deberías poder acceder como usuario SQLAB_GESTOR y contraseña elpassword.

emartinm commented 3 years ago

Y por otra parte, las vistas del juez me salen sin la cabecera

¿No existe menú o el menú aparece en letras blancas sobre fondo blanco? ¿Hay enlaces en la parte superior izquierda que puedas puedas pinchar aunque no veas? Deberías ver algo como esto:

menu

emartinm commented 3 years ago

Iván ha encontrado un problema al pasar los tests que únicamente se presenta en Windows y por eso no puedo detectarlo ni depurarlo. La información está en https://github.com/emartinm/lsql/issues/62#issuecomment-829571844 y justo debajo propongo una solución temporal para que os funcione en Windows: comentar una línea en vuestra copia local pero nunca incluir ese cambio en los PR.

Tamiflu9 commented 3 years ago

Al cambiar la clave yo siempre lo he hecho poniéndola entre comillas. Pero intenta crear un usuario administrador SQLAB_GESTOR en lugar de usar SYS y SYSTEM. Accede como sysdba y ejecuta lo siguiente:

CREATE TABLESPACE SQLAB DATAFILE 'SQLAB' SIZE 1024M AUTOEXTEND OFF;

CREATE USER SQLAB_GESTOR IDENTIFIED BY "elpassword" DEFAULT TABLESPACE SQLAB TEMPORARY TABLESPACE TEMP QUOTA UNLIMITED ON SQLAB;

GRANT DBA to SQLAB_GESTOR;

-- Evita que las contraseñas caduquen a los 180 días
ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

Después deberías poder acceder como usuario SQLAB_GESTOR y contraseña elpassword.

Vale si que me ha dejado crearlo y luego acceder con ese usuario. Lo he hecho desde el Prompt de Anaconda, no habría ningún problema no?

Ahora ya si me sale el encabezado de la página. Que antes era que me salía todo en blanco pero si podía pinchar sobre los enlaces y me llevaba a la página que fuera. Lo que no se ha arreglado es a la hora de subir ejercicios, que me sale error de conexión:

pn

Ahora el error que me da es este:

or

emartinm commented 3 years ago

Según parece, la cuenta a la que se trata de acceder está bloqueada. ¿Desde SQLDeveloper eres capaz de conectarte? Porque el bloqueo debería ser el mismo independientemente de donde se conecte. Busca por ese código de error porque seguro que encuentras algo.

Tamiflu9 commented 3 years ago

He hecho SQL> ALTER USER SQLAB_GESTOR ACCOUNT UNLOCK; y en SQLDeveloper si me deja crear una conexión con ese usuario, pero en el juez me sigue dando el mismo error. También he probado a hacerlo con SYSTEM pero nada.

Se conecta con el usuario SLAB_GESTOR or

Pero en el juez ahora me sale el mismo error de antes.. or1

emartinm commented 3 years ago

Yo revisaría con mucho detalle los credenciales que tienes configurados en tus variables de entorno, porque no tiene sentido que puedas acceder a mano pero no desde el código que usa el mismo servidor. Desde un terminal Python revisa con cuidado estas variables:

os.environ['ORACLE_USER']
os.environ['ORACLE_PASS']
os.environ['ORACLE_SERVER']
os.environ['ORACLE_PORT']
os.environ['ORACLE_SID']

Ten cuidado con mayúsculas/minúsculas, espacios al principio y final y cualquier otra cosas que pueda hacerlas diferentes de las que usas para conectarte a mano.

Tamiflu9 commented 3 years ago

Y donde debería poner esos comandos? Que no me lo reconoce en ningún lado.

IMG_20210501_181631

emartinm commented 3 years ago

Es código Python, así que en un terminal como el que te sale con python manage.py shell

emartinm commented 3 years ago

He estado pensando sobre la ventana modal de las pistas y la información que debería contener. Creo que debería estar compuesta por 3 o 4 zonas:

  1. Aviso estático de que se pueden pedir pistas y que las pistas influyen en la clasificación
  2. Información estática "Este problema tiene un total de X pistas disponibles"
  3. Zona dinámica de pistas "Pista 1", "Pista 2", etc. que puede estar vacía
  4. Zona dinámica donde aparecerá el mensaje resultante al pedir pista. Si se devuelve pista con éxito, este apartado no se muestra. Si faltan X envíos para la siguiente pista, se dice aquí. Si ya no hay más pistas, se dice aquí también.

Las zonas 1 y 2 son estáticas y nunca cambian. Por eso sería posible fusionarlas en una única zona.

La zona 3 empezará vacía y se va extendiendo con pistas según se van pidiendo.

La zona 4 se reemplaza completamente, y debería estar en la parte abajo del todo. Solo debería ser visible si hay algo que mostrar.

¿Qué os parece este comportamiento @Tamiflu9 @jcorreas? Espero que sea coherente con lo que hemos hablado en las reuniones.

Tamiflu9 commented 3 years ago

Si, así es como estaba haciéndolo más o menos. Ahora mismo quedaría así:

Cando te faltan X envíos: p1

Cuando el ejercicio no tiene ninguna pista aparece el botón desactivado y con el mensaje 'Sin ayuda disponible': p2

Y cuando pides pistas sería así (tengo que cambiar todavía el mensaje de la información que no esta actualizado) https://user-images.githubusercontent.com/33600064/117186426-1a8fb200-addb-11eb-9d9d-91223d793388.mp4

Igual se podrían juntar la zona 1 y 2 o cambiar algún mensaje si no os convencen.

emartinm commented 3 years ago

Igual se podrían juntar la zona 1 y 2 o cambiar algún mensaje si no os convencen.

Déjalo como está, no te preocupes.

Lo que sí que cambiaría es la frase "Todavía no has usado ninguna pista" por "Todavía no has solicitado ninguna pista", que es el verbo que se usa en el resto de la ventana.

Tamiflu9 commented 3 years ago

Vale lo he cambiado. Dejo el enlace a un video que no me dejaba subirlo. Quedaría así:

https://drive.google.com/file/d/1MlTqlXuDIXchwDWEw1Y95lMlx60nr1aI/view?usp=sharing

emartinm commented 3 years ago

Me queda un poco raro que cuando te dé la última pista el botón siga activo, y cuando se vuelva a pulsar se desactive sin darte ninguna información de qué está pasando. ¿Se podría desactivar directamente al recibir la última pista, añadiendo el mensaje de que no hay más pistas disponibles? Así se desactiva el botón, se explica y no hace falta una interacción adicional.

emartinm commented 3 years ago

Otra cosa que he pensado es sacar esos dos primeros mensajes estáticos de la tabla y mostrarlos en un alert de tipo info (https://getbootstrap.com/docs/5.0/components/alerts/) que es una manera muy visual de mostrar información relevante durante toda la vida de la ventana modal. De esa manera dejamos la tabla únicamente para lo dinámico: las pistas y el mensaje de que no hay más pistas o que quedan X envíos para la siguiente pista.