chartManD commited on
Commit
11f5edf
·
1 Parent(s): 6f7e77f

PF primera fase, creacion de lista sin findalizar sesion

Browse files
tecnicas/admin.py CHANGED
@@ -14,7 +14,7 @@ from .models import Producto, Participacion
14
 
15
  from .models import Orden, Posicion
16
 
17
- from .models import Dato, ValorDecimal, ValorBooleano, Calificacion
18
 
19
  # Register your models here.
20
  admin.site.register(CategoriaTecnica)
@@ -46,3 +46,4 @@ admin.site.register(Dato)
46
  admin.site.register(ValorDecimal)
47
  admin.site.register(ValorBooleano)
48
  admin.site.register(Calificacion)
 
 
14
 
15
  from .models import Orden, Posicion
16
 
17
+ from .models import Dato, ValorDecimal, ValorBooleano, Calificacion, ListaPalabras
18
 
19
  # Register your models here.
20
  admin.site.register(CategoriaTecnica)
 
46
  admin.site.register(ValorDecimal)
47
  admin.site.register(ValorBooleano)
48
  admin.site.register(Calificacion)
49
+ admin.site.register(ListaPalabras)
tecnicas/controllers/__init__.py CHANGED
@@ -46,4 +46,5 @@ from .views_controller.vocabulary_manage.list_vocabulary_controller import ListV
46
 
47
  from .api_controller.rating_sacales_controller import RatingScalesController
48
  from .api_controller.rating_cata_controller import RatingCataController
 
49
  from .views_controller.tester_list_controller import TesterListController
 
46
 
47
  from .api_controller.rating_sacales_controller import RatingScalesController
48
  from .api_controller.rating_cata_controller import RatingCataController
49
+ from .api_controller.rating_pf_list_controller import RatingPFListController
50
  from .views_controller.tester_list_controller import TesterListController
tecnicas/controllers/api_controller/rating_pf_list_controller.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import JsonResponse, HttpRequest
2
+ from django.db import transaction, IntegrityError
3
+ from tecnicas.models import Palabra, ListaPalabras, Participacion
4
+ from tecnicas.forms import ListWordsForm
5
+
6
+
7
+ class RatingPFListController():
8
+ def __init__(self):
9
+ pass
10
+
11
+ @staticmethod
12
+ def firstSaveList(request: HttpRequest, words: list, current_phase: int):
13
+ dic_words = {}
14
+ for index, word in enumerate(words, start=1):
15
+ dic_words[f"palabra_{index}"] = word
16
+
17
+ form = ListWordsForm(dic_words, new_words=words)
18
+
19
+ if form.is_valid():
20
+ participation = Participacion.objects.get(
21
+ id=request.session["id_participation"])
22
+
23
+ if not participation:
24
+ return JsonResponse({"error": "No está autorizado en la sesión"})
25
+
26
+ technique = participation.tecnica
27
+
28
+ list_words_tester: ListaPalabras
29
+ if current_phase == 1:
30
+ (list_words_tester, created) = ListaPalabras.objects.get_or_create(
31
+ tecnica=technique,
32
+ catador=request.user.user_catador,
33
+ es_final=False,
34
+ )
35
+ elif current_phase == 2:
36
+ (list_words_tester, created) = ListaPalabras.objects.get_or_create(
37
+ tecnica=technique,
38
+ catador=request.user.user_catador,
39
+ es_final=True,
40
+ )
41
+
42
+ added_words = addWordsToListWordsTester(
43
+ list_words=words, list_tester=list_words_tester)
44
+
45
+ response = JsonResponse({
46
+ "message": "Palabras guardadas con exito",
47
+ "words": [word.nombre_palabra for word in added_words]
48
+ })
49
+ else:
50
+ response = JsonResponse({"error": "Palabras invalidas"})
51
+
52
+ return response
53
+
54
+
55
+ @staticmethod
56
+ def addWordsToListWordsTester(list_words: list[str], list_tester: ListaPalabras):
57
+ # Normalizar
58
+ clean_words = [s.strip() for s in list_words if s.strip()]
59
+
60
+ # Obtener existentes
61
+ all_words = Palabra.objects.filter(nombre_palabra__in=clean_words)
62
+
63
+ names_words_exist = set(
64
+ all_words.values_list('nombre_palabra', flat=True))
65
+
66
+ # Determinar faltantes
67
+ missing_words = [
68
+ nombre for nombre in clean_words if nombre not in names_words_exist]
69
+
70
+ created_words = []
71
+
72
+ # Intentar crear missing_words
73
+ for nombre in missing_words:
74
+ try:
75
+ with transaction.atomic():
76
+ palabra, created = Palabra.objects.get_or_create(
77
+ nombre_palabra=nombre)
78
+ if created:
79
+ created_words.append(palabra)
80
+ except IntegrityError:
81
+ palabra = Palabra.objects.get(nombre_palabra=nombre)
82
+ created_words.append(palabra)
83
+
84
+ # Combinar todas (all_words + created_words)
85
+ all_new_words = list(all_words) + created_words
86
+
87
+ list_tester.palabras.set(all_new_words)
88
+
89
+ return all_new_words
tecnicas/controllers/views_controller/sessions_tester/tests_forms/test_pf_controller.py CHANGED
@@ -42,6 +42,17 @@ class TestPFController(GenetalTestController):
42
  self.context["form"] = ListWordsForm()
43
  self.context["initial_phase"] = True
44
 
 
 
 
 
 
 
 
 
 
 
 
45
  return render(request, self.current_directory, self.context)
46
 
47
  def getSecondPhase(self, request: HttpRequest):
@@ -53,13 +64,20 @@ class TestPFController(GenetalTestController):
53
  }
54
  return redirect(reverse(self.previus_directory, kwargs=params))
55
 
56
- list_words = list(
57
- ListaPalabras.objects.get(
 
 
 
 
 
 
58
  tecnica=self.session.tecnica,
59
  catador=request.user.user_catador,
60
  es_final=False
61
- ).palabras.all()
62
- )
 
63
 
64
  self.context["form"] = ListWordsForm()
65
  self.context["initial_phase"] = False
 
42
  self.context["form"] = ListWordsForm()
43
  self.context["initial_phase"] = True
44
 
45
+ try:
46
+ tester_list = ListaPalabras.objects.get(
47
+ tecnica=self.session.tecnica,
48
+ catador=request.user.user_catador,
49
+ es_final=False
50
+ )
51
+ list_words = list(tester_list.palabras.all())
52
+ self.context["words"] = list_words
53
+ except ListaPalabras.DoesNotExist:
54
+ self.context["words"] = []
55
+
56
  return render(request, self.current_directory, self.context)
57
 
58
  def getSecondPhase(self, request: HttpRequest):
 
64
  }
65
  return redirect(reverse(self.previus_directory, kwargs=params))
66
 
67
+ try:
68
+ tester_list = ListaPalabras.objects.get(
69
+ tecnica=self.session.tecnica,
70
+ catador=request.user.user_catador,
71
+ es_final=True
72
+ )
73
+ except ListaPalabras.DoesNotExist:
74
+ tester_list = ListaPalabras.objects.get(
75
  tecnica=self.session.tecnica,
76
  catador=request.user.user_catador,
77
  es_final=False
78
+ )
79
+
80
+ list_words = list(tester_list.palabras.all())
81
 
82
  self.context["form"] = ListWordsForm()
83
  self.context["initial_phase"] = False
tecnicas/forms/list_words_form.py CHANGED
@@ -7,7 +7,6 @@ class ListWordsForm(forms.Form):
7
 
8
  def __init__(self, *args, new_words=None, **kwargs):
9
  super().__init__(*args, **kwargs)
10
- new_words = new_words or []
11
 
12
  if not new_words:
13
  self.fields['nombre_palabra'] = forms.CharField(
@@ -22,7 +21,7 @@ class ListWordsForm(forms.Form):
22
  )
23
  else:
24
  for index, name in enumerate(new_words, start=1):
25
- self.fields[f'nombre_palabra_{index}'] = forms.CharField(
26
  label=f'Palabra {index}',
27
  max_length=255,
28
  initial=name,
 
7
 
8
  def __init__(self, *args, new_words=None, **kwargs):
9
  super().__init__(*args, **kwargs)
 
10
 
11
  if not new_words:
12
  self.fields['nombre_palabra'] = forms.CharField(
 
21
  )
22
  else:
23
  for index, name in enumerate(new_words, start=1):
24
+ self.fields[f'palabra_{index}'] = forms.CharField(
25
  label=f'Palabra {index}',
26
  max_length=255,
27
  initial=name,
tecnicas/models/lista_palabras.py CHANGED
@@ -12,4 +12,4 @@ class ListaPalabras(models.Model):
12
  Palabra, related_name="lista_palabras")
13
 
14
  def __str__(self):
15
- return f"{self.tecnica.codigo_sesion} - {self.catador.user.username}"
 
12
  Palabra, related_name="lista_palabras")
13
 
14
  def __str__(self):
15
+ return f"{self.tecnica.sesion_tecnica.codigo_sesion} - {self.catador.user.username}"
tecnicas/static/js/pf-make-list.js CHANGED
@@ -2,6 +2,7 @@ const FORM_DESCRIBE = document.querySelector(".cts-form-pf-word");
2
  const BOX_WORDS = document.querySelector(".cts-box-words");
3
  const IMG_LIST = document.querySelector(".cts-img-list");
4
  const ERROR_INPUT_WORD = document.querySelector(".error-input-word");
 
5
 
6
  const WORDS = [];
7
  const STYLES_LI = [
@@ -116,7 +117,7 @@ function setupDescribeFormToAddWord() {
116
  if (!value) return;
117
 
118
  if (WORDS.includes(value)) {
119
- notifactionError("Esa palabra ya está en la lista");
120
  return;
121
  }
122
 
@@ -128,15 +129,83 @@ function setupDescribeFormToAddWord() {
128
  });
129
  }
130
 
131
- function notifactionError(messageError) {
132
- ERROR_INPUT_WORD.textContent = messageError;
133
- ERROR_INPUT_WORD.classList.remove("hidden");
 
 
 
 
 
 
 
134
  setTimeout(() => {
135
- ERROR_INPUT_WORD.textContent = "";
136
- ERROR_INPUT_WORD.classList.add("hidden");
137
  }, 3000);
138
  }
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  window.addEventListener("DOMContentLoaded", () => {
141
  initWordsFromBox();
142
  setupDescribeFormToAddWord();
 
2
  const BOX_WORDS = document.querySelector(".cts-box-words");
3
  const IMG_LIST = document.querySelector(".cts-img-list");
4
  const ERROR_INPUT_WORD = document.querySelector(".error-input-word");
5
+ const FORM_ACTION = document.querySelector(".form-actions");
6
 
7
  const WORDS = [];
8
  const STYLES_LI = [
 
117
  if (!value) return;
118
 
119
  if (WORDS.includes(value)) {
120
+ spanNotifaction("Esa palabra ya está en la lista");
121
  return;
122
  }
123
 
 
129
  });
130
  }
131
 
132
+ function spanNotifaction(messageError, isError = true) {
133
+ const span = document.createElement("span");
134
+ span.textContent = messageError;
135
+
136
+ const div = document.createElement("div");
137
+ div.classList.add("alert", isError ? "alert-error" : "alert-success");
138
+ div.appendChild(span);
139
+
140
+ ERROR_INPUT_WORD.append(div);
141
+
142
  setTimeout(() => {
143
+ ERROR_INPUT_WORD.removeChild(div);
 
144
  }, 3000);
145
  }
146
 
147
+ async function sendWordsToSave() {
148
+ if (!WORDS.length) {
149
+ spanNotifaction("Debe existir al menos una palabra en la lista");
150
+ return;
151
+ }
152
+
153
+ const currentPhase = parseInt(
154
+ document.querySelector(".cts-phase-pf").dataset.phase
155
+ );
156
+
157
+ const csrfToken = document.querySelector("[name=csrfmiddlewaretoken]").value;
158
+
159
+ const requestData = {
160
+ phase: currentPhase,
161
+ words: WORDS,
162
+ };
163
+
164
+ const URL = "/cata/testers/api/ratingword/pf/list";
165
+
166
+ try {
167
+ const response = await fetch(URL, {
168
+ method: "POST",
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ "X-CSRFToken": csrfToken,
172
+ "X-Requested-With": "XMLHttpRequest",
173
+ },
174
+ body: JSON.stringify(requestData),
175
+ });
176
+
177
+ if (!response.ok) {
178
+ spanNotifaction("Fallo con la respuesta recibida");
179
+ return;
180
+ }
181
+
182
+ const result = await response.json();
183
+
184
+ const messError = result.error;
185
+
186
+ if (messError) {
187
+ spanNotifaction(messError);
188
+ return;
189
+ }
190
+
191
+ spanNotifaction(result.message, false);
192
+ const addedWords = result.words;
193
+ WORDS.length = 0;
194
+ addedWords.forEach((word) => WORDS.push(word));
195
+ renderWords();
196
+ } catch (err) {
197
+ console.error(err);
198
+ spanNotifaction("Error en la respuesta del servidor");
199
+ }
200
+ }
201
+
202
+ // function setUpFormAction() {
203
+ // const input = FORM_ACTION.querySelector(".input-action");
204
+ // input.action = "";
205
+ // input.value = "finish_session";
206
+ // FORM_ACTION.submit();
207
+ // }
208
+
209
  window.addEventListener("DOMContentLoaded", () => {
210
  initWordsFromBox();
211
  setupDescribeFormToAddWord();
tecnicas/static/js/test-cata.js CHANGED
@@ -100,8 +100,6 @@ document.addEventListener("DOMContentLoaded", () => {
100
  const result = await response.json();
101
  const messError = result.error;
102
 
103
- console.log(messError);
104
-
105
  if (messError) {
106
  message.textContent = messError;
107
  return;
 
100
  const result = await response.json();
101
  const messError = result.error;
102
 
 
 
103
  if (messError) {
104
  message.textContent = messError;
105
  return;
tecnicas/templates/tecnicas/forms_tester/test_pf_list_words.html CHANGED
@@ -7,7 +7,7 @@
7
  {% block content %}
8
  <article class="cts-container-main">
9
  <article class="cts-wrap-content text-black max-w-4xl">
10
- <header class="text-center flex-row w-full items-stretch flex justify-around flex-wrap gap-2">
11
  <h1 class="rounded font-bold text-2xl bg-surface-ligt p-4 flex-1">
12
  Sesión usando <br>técnica
13
  <span class="uppercase">{{ session.tecnica.tipo_tecnica }}</span>
@@ -30,12 +30,7 @@
30
  </section>
31
 
32
  {% if error %}
33
- <hr>
34
- <article class="bg-red-600 p-4 text-white rounded-xl ct-notification-error">
35
- <p class="block font-sans text-white text-xl antialiased font-bold uppercase tracking-wider text-center">
36
- {{ error }}
37
- </p>
38
- </article>
39
  {% endif %}
40
 
41
  <article class="rounded flex flex-col gap-4">
@@ -47,14 +42,14 @@
47
  <section class="flex items-center justify-center flex-wrap gap-4 bg-surface-ligt p-2 rounded-lg">
48
  {% if initial_phase %}
49
  <p class="text-xl font-bold text-center">
50
- Fase 1:
51
  </p>
52
  <p class="text-xl font-bold text-center">
53
  Lista inicial
54
  </p>
55
  {% else %}
56
  <p class="text-xl font-bold text-center">
57
- Fase 2:
58
  </p>
59
  <p class="text-xl font-bold text-center">
60
  Lista Final
@@ -63,7 +58,9 @@
63
  </section>
64
  </article>
65
 
66
- <article class="cts-list-words">
 
 
67
  <article class="flex gap-4 flex-wrap">
68
  <section class="bg-surface-card p-4 rounded-lg text-center space-y-4 sm:w-[300px] w-full flex-shrink-0">
69
  <h3 class="text-xl font-bold border-b-1">Describe el producto</h3>
@@ -82,13 +79,11 @@
82
  <button type="submit" class="cts-btn-general-compress cts-btn-primary btn-push w-full py-2">
83
  Agregar
84
  </button>
85
- <p class="error-input-word p-2 bg-red-500 rounded-lg hidden text-white font-semibold">
86
- </p>
87
  </form>
88
  </section>
89
 
90
  <section
91
- class="bg-surface-card p-4 rounded-lg text-center w-[400px] max-w-[400px] h-[400px] min-h-[400px] max-h-[600px] flex-shrink-0 overflow-y-auto">
92
  <div>
93
  <p class="text-xl font-bold pb-3">Lista de palabras</p>
94
  <hr>
@@ -117,8 +112,30 @@
117
  </ul>
118
  </section>
119
  </article>
 
120
 
 
 
 
 
 
 
 
 
 
121
  </article>
 
 
 
 
 
 
 
 
 
 
 
 
122
  </article>
123
  </article>
124
  {% endblock %}
 
7
  {% block content %}
8
  <article class="cts-container-main">
9
  <article class="cts-wrap-content text-black max-w-4xl">
10
+ <header class="text-center flex-row max-sm:flex-col w-full items-stretch flex justify-around flex-wrap gap-2">
11
  <h1 class="rounded font-bold text-2xl bg-surface-ligt p-4 flex-1">
12
  Sesión usando <br>técnica
13
  <span class="uppercase">{{ session.tecnica.tipo_tecnica }}</span>
 
30
  </section>
31
 
32
  {% if error %}
33
+ {% include "../components/error-message.html" with message=error %}
 
 
 
 
 
34
  {% endif %}
35
 
36
  <article class="rounded flex flex-col gap-4">
 
42
  <section class="flex items-center justify-center flex-wrap gap-4 bg-surface-ligt p-2 rounded-lg">
43
  {% if initial_phase %}
44
  <p class="text-xl font-bold text-center">
45
+ Fase <span class="cts-phase-pf" data-phase="1">1</span>:
46
  </p>
47
  <p class="text-xl font-bold text-center">
48
  Lista inicial
49
  </p>
50
  {% else %}
51
  <p class="text-xl font-bold text-center">
52
+ Fase <span class="cts-phase-pf" data-phase="2">2</span>:
53
  </p>
54
  <p class="text-xl font-bold text-center">
55
  Lista Final
 
58
  </section>
59
  </article>
60
 
61
+ <div class="error-input-word toast toast-bottom toast-center"></div>
62
+
63
+ <section class="cts-list-words space-y-4">
64
  <article class="flex gap-4 flex-wrap">
65
  <section class="bg-surface-card p-4 rounded-lg text-center space-y-4 sm:w-[300px] w-full flex-shrink-0">
66
  <h3 class="text-xl font-bold border-b-1">Describe el producto</h3>
 
79
  <button type="submit" class="cts-btn-general-compress cts-btn-primary btn-push w-full py-2">
80
  Agregar
81
  </button>
 
 
82
  </form>
83
  </section>
84
 
85
  <section
86
+ class="bg-surface-card p-4 rounded-lg text-center flex-1 h-[400px] min-h-[400px] max-h-[600px] flex-shrink-0 overflow-y-auto">
87
  <div>
88
  <p class="text-xl font-bold pb-3">Lista de palabras</p>
89
  <hr>
 
112
  </ul>
113
  </section>
114
  </article>
115
+ </section>
116
 
117
+ <article class="flex max-sm:flex-col gap-4">
118
+ <button type="button" class="cts-btn-general-compress cts-btn-primary btn-push py-2 px-4 flex-1"
119
+ onclick="sendWordsToSave()">
120
+ Guardar y comprobar palabras actuales
121
+ </button>
122
+
123
+ <button type="button" class="cts-btn-general-compress cts-btn-secondary btn-push py-2 px-4 flex-1">
124
+ Finalizar la sesión
125
+ </button>
126
  </article>
127
+
128
+ <div role="alert" class="alert alert-warning">
129
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none"
130
+ viewBox="0 0 24 24">
131
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
132
+ d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
133
+ </svg>
134
+ <span class="text-lg">
135
+ Asegúrese de guardas las palabras si va salir de la sesión con el botón “Salir de la sesión”, de lo
136
+ contrario las palabras no se guardaran
137
+ </span>
138
+ </div>
139
  </article>
140
  </article>
141
  {% endblock %}
tecnicas/urls.py CHANGED
@@ -139,4 +139,8 @@ urlpatterns = [
139
  path("testers/api/ratingword/cata",
140
  views.ratingWordCata,
141
  name="api_rating_word_cata"),
 
 
 
 
142
  ]
 
139
  path("testers/api/ratingword/cata",
140
  views.ratingWordCata,
141
  name="api_rating_word_cata"),
142
+
143
+ path("testers/api/ratingword/pf/list",
144
+ views.apiListWordsPF,
145
+ name="api_rating_word_pf_list"),
146
  ]
tecnicas/views/__init__.py CHANGED
@@ -25,6 +25,7 @@ from .vocabulary_management.list_vocabulary import listVocabulary
25
  from .apis.api_tag import newTag
26
  from .apis.api_words import words
27
  from .apis.api_words import wordsVocabulary
 
28
  from .apis.rating_word_scales import ratingWordScales
29
  from .apis.rating_word_cata import ratingWordCata
30
 
 
25
  from .apis.api_tag import newTag
26
  from .apis.api_words import words
27
  from .apis.api_words import wordsVocabulary
28
+ from .apis.api_list_words_pf import apiListWordsPF
29
  from .apis.rating_word_scales import ratingWordScales
30
  from .apis.rating_word_cata import ratingWordCata
31
 
tecnicas/views/apis/api_list_words_pf.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from django.http import HttpRequest, JsonResponse
2
+ from tecnicas.utils import general_error
3
+ from tecnicas.controllers import RatingPFListController
4
+ import json
5
+
6
+
7
+ def apiListWordsPF(req: HttpRequest):
8
+ if req.method == "POST":
9
+ try:
10
+ data = json.loads(req.body.decode("utf-8"))
11
+ raw_words = data.get("words", [])
12
+ phase = data.get("phase", [])
13
+
14
+ response = RatingPFListController.firstSaveList(
15
+ request=req, current_phase=phase, words=raw_words)
16
+ return response
17
+ except Exception as e:
18
+ print("Error:", e)
19
+ return JsonResponse({"error": "Error procesando datos"}, status=400)
20
+ else:
21
+ return JsonResponse({"error": "Método no permitido"}, status=405)
tecnicas/views/apis/rating_word_cata.py CHANGED
@@ -14,10 +14,8 @@ def ratingWordCata(req: HttpRequest):
14
  response = RatingCataController.saveRatingWords(
15
  request=req, data_words=raw_words, data_prodct=raw_product)
16
  return response
17
- return JsonResponse({"message": "Error procesando datos"})
18
  except Exception as e:
19
  print("Error:", e)
20
  return JsonResponse({"error": "Error procesando datos"}, status=400)
21
-
22
  else:
23
  return JsonResponse({"error": "Método no permitido"}, status=405)
 
14
  response = RatingCataController.saveRatingWords(
15
  request=req, data_words=raw_words, data_prodct=raw_product)
16
  return response
 
17
  except Exception as e:
18
  print("Error:", e)
19
  return JsonResponse({"error": "Error procesando datos"}, status=400)
 
20
  else:
21
  return JsonResponse({"error": "Método no permitido"}, status=405)