jfarray xiomarablanco commited on
Commit
51a4fb0
0 Parent(s):

Duplicate from xiomarablanco/plentas

Browse files

Co-authored-by: Xiomara Patricia Blanco Valencia <xiomarablanco@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ api/StudentAnswers/*
2
+ archivos/OutputFiles2/*
3
+ codeScripts/__pycache__/*
4
+ codeScripts/Dependencies/__pycache__/*
5
+ __pycache__/*
README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: PLENTAS
3
+ colorFrom: gray
4
+ colorTo: red
5
+ sdk: gradio
6
+ app_file: app.py
7
+ pinned: false
8
+ license: other
9
+ duplicated_from: xiomarablanco/plentas
10
+ ---
api/ApiStudentsDict.json ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "respuesta": "",
4
+ "hashed_id": "BECERRA JARAMILLO, DANIEL ISAI",
5
+ "TableIndex": 0
6
+ },
7
+ {
8
+ "respuesta": "Profesor buenas tardes,� Adjunto solo los archivos en excel que me permiti� subir la plataforma. El jupyter-notebook� no me lo permiti� subir ni siquiera en formato html; env�o todo estos dos �ltimos archivos a trav�s de tutor. Muchas gracias.",
9
+ "hashed_id": "BERRIO ZAPATA, SURLEY YANSURY",
10
+ "TableIndex": 1
11
+ },
12
+ {
13
+ "respuesta": "Buenos d�as estimado profe,� A continuaci�n adjunto la tarea correspondiente a la actividad 1.� La subo en formato HTML el Jupyter Notebook esto debido a que al trasnformarlo en PDF se da�aba totalmente la salidas de las celdas. Adem�s, como es de su conocimiento no se nos permite subir comprimidos.� Quedo atento a sus comentarios.� Saludos cordiales,� Alexander Camacho.�",
14
+ "hashed_id": "CAMACHO PONCE, ALEXANDER",
15
+ "TableIndex": 2
16
+ },
17
+ {
18
+ "respuesta": "",
19
+ "hashed_id": "CARDONA YARA, FRANKLIN ANDRES",
20
+ "TableIndex": 3
21
+ },
22
+ {
23
+ "respuesta": "Buenas tardes Profe, Envio los archivos requeridos de forma individual dado que no pude subir el .zip Quedo atento a comentarios, Miguel Chacon",
24
+ "hashed_id": "CHACON CALA, MIGUEL ANDRES",
25
+ "TableIndex": 4
26
+ },
27
+ {
28
+ "respuesta": "Buenas noches profe, Adjunto el cuaderno de Jupyter exportado c�mo PDF. Puede verlo tambi�n desde la siguiente URL de google colab, d�nde se visualiza mejor: https://colab.research.google.com/drive/1zDG9VwzY1ojDQDVezm8LH8EgXVTBokBG. Adicionalmente, adjunto los 3 archivos de excel generados para las probabilidades de transici�n, emisi�n y viterbi. Gracias por su atenci�n y saludos!",
29
+ "hashed_id": "CHACON ORDONEZ, JOSE MIGUEL",
30
+ "TableIndex": 5
31
+ },
32
+ {
33
+ "respuesta": "",
34
+ "hashed_id": "CHIRINOS VALERO, LUIS",
35
+ "TableIndex": 6
36
+ },
37
+ {
38
+ "respuesta": "",
39
+ "hashed_id": "CIFUENTES DURANGO, VICTOR ALFONSO",
40
+ "TableIndex": 7
41
+ },
42
+ {
43
+ "respuesta": "Adjunto soluci�n",
44
+ "hashed_id": "CORONADO MOREIRA, CRISTOPHER GEOVANNY",
45
+ "TableIndex": 8
46
+ },
47
+ {
48
+ "respuesta": "",
49
+ "hashed_id": "CUARTAS VALDERRAMA, MANUEL ALEJANDRO",
50
+ "TableIndex": 9
51
+ },
52
+ {
53
+ "respuesta": "Buen dia, Envio los excel de las matrices y el codigo en HTML, ya que el PDF me dio en primer lugar problemas por el tama�o y en segundo lugar al comprimirlo, no acept� el formato",
54
+ "hashed_id": "DUQUE AGUILERA, ADRIAN",
55
+ "TableIndex": 10
56
+ },
57
+ {
58
+ "respuesta": "Hola Profesor gusto en saludarlo Anexo los archivos correspondientes a la actividad El cuaderno no me dej� subirlo a la plataforma Espero que sea de su agrado Saludos cordiales",
59
+ "hashed_id": "FERRER ACOSTA, MISAEL JOAQUIN",
60
+ "TableIndex": 11
61
+ },
62
+ {
63
+ "respuesta": "Estimado profesor, Adjunto los documentos de la actividad 1. Pablo Garcia",
64
+ "hashed_id": "GARCIA VELEZ, PABLO ALFONSO",
65
+ "TableIndex": 12
66
+ },
67
+ {
68
+ "respuesta": "Buen D�a profesor Se adjunta para su validaci�n. No se sube el HTML export compilado, porque de ser asi, pesa mas de 50MB, se sube sin compilar. � Cordialmente, CRISTIAN CAMILO GARZON TAMAYO crittan01@gmail.com CC 1032414179 +57 312 583 5649 Bogot�- Colombia",
69
+ "hashed_id": "GARZON TAMAYO, CRISTIAN CAMILO",
70
+ "TableIndex": 13
71
+ },
72
+ {
73
+ "respuesta": "",
74
+ "hashed_id": "GIRALDO OSORIO, CARLOS ANDRES",
75
+ "TableIndex": 14
76
+ },
77
+ {
78
+ "respuesta": "Estimado: adjunto los documentos solicitados de la actividad 1. � atte Victor Gomez",
79
+ "hashed_id": "GOMEZ FARFAN, VICTOR",
80
+ "TableIndex": 15
81
+ },
82
+ {
83
+ "respuesta": "",
84
+ "hashed_id": "grades.xls",
85
+ "TableIndex": 16
86
+ },
87
+ {
88
+ "respuesta": "",
89
+ "hashed_id": "GUAMAN LOPEZ, ANDREA SILVANA",
90
+ "TableIndex": 17
91
+ },
92
+ {
93
+ "respuesta": "",
94
+ "hashed_id": "GUTIERREZ QUEZADA, LUIS MANUEL",
95
+ "TableIndex": 18
96
+ },
97
+ {
98
+ "respuesta": "Buenas tardes Adjunto archivo en formato html de la actividad 1. Saludos � � � � � �",
99
+ "hashed_id": "JAIMES HUAMAN, ROGER",
100
+ "TableIndex": 19
101
+ },
102
+ {
103
+ "respuesta": "",
104
+ "hashed_id": "JIMENEZ QUEZADA, EDWIN",
105
+ "TableIndex": 20
106
+ },
107
+ {
108
+ "respuesta": "Buenas tardes Profesor Alberto. Anexo archivos de la Actividad 01 de�Etiquetado morfosint�ctico. El Notebook lo he convertido a HTML debido a que no permite extensi�n ipynb. Que tengas excelente d�a Profesor, EdgarL �",
109
+ "hashed_id": "LEAL GELVEZ, EDGAR",
110
+ "TableIndex": 21
111
+ },
112
+ {
113
+ "respuesta": "",
114
+ "hashed_id": "LUCIANI LUJAN, RICHARD MICHAEL",
115
+ "TableIndex": 22
116
+ },
117
+ {
118
+ "respuesta": "",
119
+ "hashed_id": "MARIN SARRIA, GEMBER",
120
+ "TableIndex": 23
121
+ },
122
+ {
123
+ "respuesta": "",
124
+ "hashed_id": "MONTANO PULZARA, NESTOR",
125
+ "TableIndex": 24
126
+ },
127
+ {
128
+ "respuesta": "se adjunta actividad",
129
+ "hashed_id": "MOSQUERA ASIMBAYA, JOSE ALEJANDRO",
130
+ "TableIndex": 25
131
+ },
132
+ {
133
+ "respuesta": "",
134
+ "hashed_id": "MOSQUERA DIAZ, JAVIER",
135
+ "TableIndex": 26
136
+ },
137
+ {
138
+ "respuesta": "Buenas Tardes, le envio la actividad 1 �",
139
+ "hashed_id": "ONA SERRANO, JORGE",
140
+ "TableIndex": 27
141
+ },
142
+ {
143
+ "respuesta": "",
144
+ "hashed_id": "OVIEDO HERRERA, PAUL",
145
+ "TableIndex": 28
146
+ },
147
+ {
148
+ "respuesta": "Buen d�a, Ante la dificultad para subir el c�digo fue necesario exportarlo en formato HTML, aunque el despligue de los datos generado hizo extenso el archivo se tiene todos los componentes solicitados, adem�s fue necesario cargar los archivos generados por separado.",
149
+ "hashed_id": "PATINO TERAN, CARLOS ARMANDO",
150
+ "TableIndex": 29
151
+ },
152
+ {
153
+ "respuesta": "Buenas tardes, Dado que no me deja cargar el archivo en formato .ipynb, entonces adjunto el enlace en google colab de la actividad. Adem�s, se adjunto en formato pdf la actividad. https://colab.research.google.com/drive/1IwW-s5kpRqPSysGxcn5vl6dlwRtlr-k4?usp=sharing Saludos Cordiales.",
154
+ "hashed_id": "PINTO NIETO, JOSUE DAVID",
155
+ "TableIndex": 30
156
+ },
157
+ {
158
+ "respuesta": "Buenos dias, estimado profesor� � Cordial saludo� � A continuaci�n presento la entrega correspondiente a la actividad 1, adjunto 3 formatos de excel ( probabilidades y Viterbi) ademas del codigo en formato HTML y uno en PDF( Con recortes)��puesto que en formato del Notebook no fue posible cargarlo. � Atentamente� � German A. Portilla Gonzalez�",
159
+ "hashed_id": "PORTILLA GONZALEZ, GERMAN ARLEY",
160
+ "TableIndex": 31
161
+ },
162
+ {
163
+ "respuesta": "",
164
+ "hashed_id": "ROJAS ONCOY, ALVARO ELIAS",
165
+ "TableIndex": 32
166
+ },
167
+ {
168
+ "respuesta": "El sistema no permitio enviar el archivo .zip. En caso de algun problema igual dejo el link al folder Google Drive donde estan todos los archivos. https://drive.google.com/drive/folders/1_No8RFn3QJiNlqzY6NS2tEFimwgqegxQ?usp=sharing Saludos,� Cesar",
169
+ "hashed_id": "RON CUSME, CESAR ANDRES",
170
+ "TableIndex": 33
171
+ },
172
+ {
173
+ "respuesta": "",
174
+ "hashed_id": "ROSERO VACA, DARWIN",
175
+ "TableIndex": 34
176
+ },
177
+ {
178
+ "respuesta": "Estimado profesor, Buenas tardes, entrego actividad. Saludos Cordiales Jorge Santiago Salas",
179
+ "hashed_id": "SALAS ONA, JORGE",
180
+ "TableIndex": 35
181
+ },
182
+ {
183
+ "respuesta": "",
184
+ "hashed_id": "SANCHEZ CASTRO, AUGUSTO",
185
+ "TableIndex": 36
186
+ },
187
+ {
188
+ "respuesta": "",
189
+ "hashed_id": "SANCHEZ VARGAS, LUIS CARLOS",
190
+ "TableIndex": 37
191
+ },
192
+ {
193
+ "respuesta": "",
194
+ "hashed_id": "TARAZONA CUADROS, SONIA SMITH",
195
+ "TableIndex": 38
196
+ },
197
+ {
198
+ "respuesta": "No permite cargar archivos con extensi�n :�(.ipynb) se sube como HTML",
199
+ "hashed_id": "TENESACA GOMEZ, EDWIN",
200
+ "TableIndex": 39
201
+ },
202
+ {
203
+ "respuesta": "Buenas tardes, Adjunto los documentos: 1) Actividad resuelta en Jupyter Notebook�en formato (.html, por restricci�n de tipo de archivo para la carga), usando como base el archivo proporcionado y el corpus (.txt, mia07_t3_tra_Corpus-tagged). El�Jupyter Notebook�contiene: El c�digo en Python que permite�resolver la actividad, debidamente comentado. Todos los resultados que se solicita en el enunciado,�que genera el c�digo implementado. Las respuestas a las preguntas planteadas en el enunciado. 2) Hoja de c�lculo de Microsoft Excel (.xlsx) que muestra�las tablas de�las probabilidades de emisi�n. 3) Hoja de c�lculo de Microsoft Excel (.xlsx) que muestra�las tablas de�las probabilidades de transici�n. 4) Hoja de c�lculo de Microsoft Excel (.xlsx) que muestra�las tablas de�la matriz de probabilidades de la ruta Viterbi. � Quedo atento,",
204
+ "hashed_id": "TORRES REINA, ROGER NEIL",
205
+ "TableIndex": 40
206
+ },
207
+ {
208
+ "respuesta": "",
209
+ "hashed_id": "YANCHAPAXI GARCIA, ALEX STEEVEN",
210
+ "TableIndex": 41
211
+ },
212
+ {
213
+ "respuesta": "Buenas noches , un gusto saludarle , me permito adjuntar la tarea correspondiente a la actividad 1. Se me presento un problema al subir el archivo , me permito adjuntar el link del drive para la descarga por favor https://drive.google.com/file/d/1yQ27Sne2OwE0ATxYYBp1UvLUPWIWfDkZ/view?usp=sharing Una estupenda noche.� �",
214
+ "hashed_id": "ZAPATA PANELUISA, LUIS GABRIEL",
215
+ "TableIndex": 42
216
+ },
217
+ {
218
+ "respuesta": "Saludos env�o la tarea.",
219
+ "hashed_id": "ZHERDMANT CEVALLOS, ERICK JOEL",
220
+ "TableIndex": 43
221
+ }
222
+ ]
api/requirements.txt ADDED
File without changes
app.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ from flask import jsonify
4
+ from sentence_transformers import SentenceTransformer, InputExample, util
5
+ from codeScripts.utils import save_json, load_json, create_file_path
6
+ from plentas import Plentas
7
+ import pandas as pd
8
+ import zipfile
9
+ import os
10
+
11
+ def Main(configuration, uploadedFile):
12
+
13
+ error = ""
14
+ modelResult = ""
15
+
16
+ configuration_dict = json.loads(configuration)
17
+
18
+ try:
19
+ uploadedFilePath = uploadedFile.name
20
+
21
+ config_json = load_json("configV2.json")
22
+
23
+ #configuring plentas methodology
24
+ response = Plentas(config_json[0], [answersTodict(uploadedFilePath), createTeacherJson(configuration_dict)])
25
+ # #overwriting the custom settings for the settings from the api
26
+ response.setApiSettings(configuration)
27
+
28
+ modelResult = jsonify(response.processApiData())
29
+ except Exception as e:
30
+ error = "Oops: " + str(e)
31
+
32
+ return [error, modelResult]
33
+
34
+ def createTeacherJson(configuration):
35
+ """
36
+ This function extracts the information about the subquestions and subanswers and puts them in the correct format.
37
+ Inputs:
38
+ config: The configured info from the api.
39
+ Outputs:
40
+ teachersJson: The generated dictionary with the subquestions.
41
+ """
42
+ teachersJson = {"enunciado": "", "minipreguntas":[], "keywords":""}
43
+
44
+ #5 is the maximum number of permitted subquestions in the configuration2 page
45
+
46
+ for i in range(5):
47
+
48
+ try:
49
+ teachersJson["minipreguntas"].append({
50
+ "minipregunta": configuration["minip" + str(i+1)],
51
+ "minirespuesta": configuration["minir" + str(i+1)]
52
+ })
53
+
54
+ except:
55
+ break
56
+
57
+ return teachersJson
58
+
59
+ def extractZipData(ruta_zip):
60
+ """
61
+ This function extracts the students's answers from the zip file (the one the teacher has in the task section).
62
+ Inputs:
63
+ ruta_zip: The path inherited from answersTodict
64
+ """
65
+ #defining the path where the extracted info is to be stored
66
+ ruta_extraccion = create_file_path("StudentAnswers/", doctype= 1)
67
+ #extracting the info
68
+ archivo_zip = zipfile.ZipFile(ruta_zip, "r")
69
+ try:
70
+ archivo_zip.extractall(pwd=None, path=ruta_extraccion)
71
+ except:
72
+ pass
73
+ archivo_zip.close()
74
+
75
+ def removeHtmlFromString(string):
76
+ """
77
+ This function removes the html tags from the student's response.
78
+ Inputs:
79
+ -string: The student's response
80
+ Outputs:
81
+ -new_string: The filtered response
82
+ """
83
+ string = string.encode('utf-8', 'replace')
84
+ string = string.decode('utf-8', 'replace')
85
+ new_string = ""
86
+ skipChar = 0
87
+ for char in string:
88
+ if char == "<":
89
+ skipChar = 1
90
+ elif char == ">":
91
+ skipChar = 0
92
+ else:
93
+ if not skipChar:
94
+ new_string = new_string+char
95
+
96
+ new_string = new_string.encode('utf-8', 'replace')
97
+ new_string = new_string.decode('utf-8', 'replace')
98
+ return new_string
99
+
100
+ def answersTodict(zip_path):
101
+ """
102
+ This function extracts the students's answers and stacks them in one specific format so that it can be processed next.
103
+ Inputs:
104
+ ruta_zip: The path where the zip file is stored
105
+ Outputs:
106
+ studentAnswersDict: The dictionary with all the responses
107
+ """
108
+ #extracting the data
109
+ extractZipData(zip_path)
110
+
111
+ studentAnswersDict = []
112
+
113
+ #stacking the information of each extracted folder
114
+ for work_folder in os.listdir(create_file_path("StudentAnswers/", doctype= 1)):
115
+ for student, indx in zip(os.listdir(create_file_path("StudentAnswers/" + work_folder, doctype= 1)), range(len(os.listdir(create_file_path("StudentAnswers/" + work_folder, doctype= 1))))):
116
+ student_name = student.split("(")
117
+ student_name = student_name[0]
118
+ try:
119
+ #opening the file
120
+
121
+ #fichero = open(create_file_path("StudentAnswers/" + work_folder + "/" + student + "/" + 'comments.txt', doctype= 1))
122
+ #where the actual response is
123
+ fichero = open(create_file_path("StudentAnswers/" + work_folder + "/" + student + "/" + 'Adjuntos del envio/Respuesta enviada', doctype= 1), encoding='utf-8')
124
+ #reading it
125
+ lineas = fichero.readlines()
126
+
127
+ #removing html
128
+ lineas[0] = removeHtmlFromString(lineas[0])
129
+
130
+ #saving it
131
+ studentAnswersDict.append({"respuesta":lineas[0], "hashed_id":student_name, "TableIndex":indx})
132
+
133
+ except:
134
+ studentAnswersDict.append({"respuesta":"", "hashed_id":student_name, "TableIndex":indx})
135
+
136
+ #saving the final dictionary
137
+ save_json(create_file_path('ApiStudentsDict.json', doctype= 1),studentAnswersDict)
138
+ return studentAnswersDict
139
+
140
+ configuration = gr.inputs.Textbox(lines=10, placeholder="JSON de Configuración")
141
+ zipFileInput = gr.inputs.File(label="ZIP file")
142
+ #dataFrameOutput = gr.outputs.Dataframe(headers=["Resultados"], max_rows=20, max_cols=None, overflow_row_behaviour="paginate", type="pandas", label="Resultado")
143
+
144
+ labelOutput = gr.outputs.Label(num_top_classes=None, type="auto", label="")
145
+ labelError = gr.outputs.Label(num_top_classes=None, type="auto", label="")
146
+
147
+ iface = gr.Interface(fn=Main
148
+ , inputs=[configuration, zipFileInput]
149
+ , outputs=[labelError, labelOutput]
150
+ , title = "PLENTAS"
151
+ )
152
+
153
+ iface.launch(share = False,enable_queue=True, show_error =True, server_port=7870)
codeScripts/Dependencies/BERT-models/Prueba3/JoinedSubjects2.json ADDED
The diff for this file is too large to render. See raw diff
 
codeScripts/Dependencies/Dataset_train_BERT.json ADDED
The diff for this file is too large to render. See raw diff
 
codeScripts/Dependencies/Metodos_Filtrado_ScoreporMinipregunta.json ADDED
@@ -0,0 +1,446 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [{
2
+ "47431449K": {
3
+ "respuesta_completa": "Una arquitectura Réplica-Set permite implementar, tal como indica su nombre, la replicación de datos de una base de datos a otra. Esto, además de para tener una base de datos como respaldo, sirve como método de recuperación automático de datos cuando se produce un fallo. \nLa arquitectura de Réplica-Set necesita como mínimo tres nodos; el primario, el secundario y el árbitro. La función del nodo árbitro es la de decidir cuál de los otros dos nodos es el primario y cuál el secundario.\nCuando el nodo primario deja de tener disponibilidad, el nodo secundario asume su función.\nLa principal diferencia con mongodump es que éste realiza una copia de seguridad de los datos cuando se ejecuta el correspondiente comando, mientras que la arquitectura Réplica-Set se encuentra constantemente replicando los datos entre los dos nodos.",
4
+ "m1_score": 1.0,
5
+ "m2_score": 0.5,
6
+ "m3_score": 1.0,
7
+ "m4_score": 1.0,
8
+ "resp_m1": "",
9
+ "resp_m2": "",
10
+ "resp_m3": "",
11
+ "resp_m4": "",
12
+ "hashed_id": "61d68c1e8cd66a2cff6b98a539a30d37"
13
+ },
14
+ "47855702Q": {
15
+ "respuesta_completa": "Cuando hablamos de Replica-set, nos referimos a la generación de una BBDD a partir de un volcado de datos. Esto implica que mínimo tienen que haber 2 servidores (puede que haya 3 o más). En el caso de que hubieran tres el primario es donde se escribiría y los otros dos (secundarios) recibirían actualizaciones inmediatas de los datos, es decir se realizan replicas inmediatas. \nReplica set nos permite tener una mayor disponibilidad, ya que en el caso de fallo del servidor primario, es decir, dejara de enviar heartbeats, uno de los secundarios pasaría a ser primario. La elección se hace a través de un proceso de votación y en el caso de que hubiera empate, sería el nodo árbitro quien decidiría quien es el primario. \nAsimismo, ofrece respaldo porque ante una caída del primario siempre se tendrá una copia de los datos. Aunque no nos protege de la inserción o borrado de datos, por ese motivo es importante contar con un dump. Mongodump realiza un volcado de datos y se almacena en el disco duro, generando archivos BSON o JSON de la BBDD. \nPor último, ofrece balanceo de carga, ya que el primario propaga los datos pero la lectura puede ser en el primario o en los secundarios. Este proceso es transparente al cliente. ",
16
+ "m1_score": 1.0,
17
+ "m2_score": 1.0,
18
+ "m3_score": 1.0,
19
+ "m4_score": 1.0,
20
+ "resp_m1": "",
21
+ "resp_m2": "",
22
+ "resp_m3": "",
23
+ "resp_m4": "",
24
+ "hashed_id": "b5372c107dc97e91722398e3a9b57f08"
25
+ },
26
+ "79002636M": {
27
+ "respuesta_completa": "Un replica-set es una configuración de servidores de replicación. Puedes tener 1 nodo primario y n secundarios. Para configuración mínima hacen falta 1 primario y dos secundarios. El primario es un servidor de MongoDB donde escribes y lees y los secundarios están conectados al primario de tal forma que cada vez que el primario recibe un dato lo propaga a los secundarios, de tal forma que tenemos copias en tiempo real. \nAnte una corrupción del primario siempre tendremos una copia de los datos.\nSi el primario deja de funcionar uno de los secundarios se convierte en primario. La elección del nuevo primario se hace a través de una votación de los secundarios y cuando el que era primario se reconecte será secundario.\nUna ventaja del replica-set es el balanceo de carga ya que la escritura siempre será desde el primario, pero la lectura puede ser desde los secundarios.\nLa principal diferencia con mongodump es que con este lo que obtenemos es una foto de los datos es decir que si modificamos algo después de haber hecho el dump la copia anterior quedará desactualizada y con replica-set tenemos siempre una copia actualizada de los datos.",
28
+ "m1_score": 1.0,
29
+ "m2_score": 1.0,
30
+ "m3_score": 1.0,
31
+ "m4_score": 1.0,
32
+ "resp_m1": "",
33
+ "resp_m2": "",
34
+ "resp_m3": "",
35
+ "resp_m4": "",
36
+ "hashed_id": "d2b0d015ec81981be5ac318b6ff2c30e"
37
+ },
38
+ "02739491F": {
39
+ "respuesta_completa": "Una arquitectura replica set en mongo DB permite realizar backup de los documentos de una colección determinada con la que estamos trabajando. La principal diferencia con mongo dump es que este último crea una copia de seguridad completa de todas las colecciones de nuestra BBDD y con un replica set estamos replicando automáticamente la información de una colección determinada, especificada por nosotros en los ficheros de configuración de nuestro replica-set. Son necesarios tres nodos para una configuración mínima: un nodo primario que decide cuál es la información que se ha de replicar, un nodo secundario que replica la información indicada por el primario y un nodo árbitro es el encargado de decidir qué nodo es primario y cuál es secundario. Esta situación ocurre cuando el nodo primario deja de tener disponibilidad: el árbitro observará los nodos existentes y decidirá quien ha de ser el nuevo nodo primario en función de la disponibilidad de la que dispongan",
40
+ "m1_score": 0.75,
41
+ "m2_score": 0.5,
42
+ "m3_score": 0.75,
43
+ "m4_score": 0.0,
44
+ "resp_m1": "",
45
+ "resp_m2": "",
46
+ "resp_m3": "",
47
+ "resp_m4": "",
48
+ "hashed_id": "d53c53e45fe8364078094fe28ec52c6f"
49
+ },
50
+ "32082430Y": {
51
+ "respuesta_completa": "Replica-Set es una característica de MongoDB que permite la redundancia de datos e incrementar la disponibilidad de los datos. Esta arquitectura está basada en un modelo Maestro-Esclavo.\nEste modelo permite la recuperación de fallos de forma automática, y es la forma de implementar la replicación de datos en mongoDB.\nLa forma de configurar un replica-set es atreves de tres nodos, dos nodos serán instancias de mongod con acceso a los datos que replicarán los datos del nodo primario, mientras que uno de ellos será el nodo primario sobre el que se realizarán las consultas y modificaciones.\nEn caso de que el nodo primario deje de tener disponibilidad, uno de los otros dos nodos secundarios pasa a ser el primario a través de una votación. Para este tipo de votaciones se suele asignar a uno de los dos secundarios como árbitro, con prioridad 0 sobre los demás y se encargará de desempatar.\nA diferencia de mongodump, que se centra sobre todo en guardar backups de los datos, Replica-Set es una arquitectura robusta frente a fallos, y el proceso de replicación o backup en los nodos secundarios se realiza automáticamente.",
52
+ "m1_score": 1.0,
53
+ "m2_score": 1.0,
54
+ "m3_score": 1.0,
55
+ "m4_score": 0.0,
56
+ "resp_m1": "",
57
+ "resp_m2": "",
58
+ "resp_m3": "",
59
+ "resp_m4": "",
60
+ "hashed_id": "b05a860f2007c94a3ee35a06752a5c14"
61
+ },
62
+ "45891666L": {
63
+ "respuesta_completa": "Replica set permite una recuperación a fallos de forma automática, y es la forma más adecuada de implementar replicación de datos en MongoDB. Son necesarios 3 nodos: dos nodos son instancias de mongod con acceso a los datos, uno de los nodos será el primario y el otro replica las operaciones que se realizan en el primario. Y el tercer nodo se llama árbitro, se utiliza solo para decidir qué nodo debe ser el primario. Cuando el nodo primario no tiene disponibilidad, este se cambia por el nodo con disponibilidad, para saber la disponibilidad se envían pings cada dos segundos. Mongodump realiza una copia de seguridad de los datos conectándose a una instancia de mongod, mientras que replica-set esta directamente conectado con otro nodo en el cual se hace la replicación en cada operación automáticamente",
64
+ "m1_score": 1.0,
65
+ "m2_score": 0.5,
66
+ "m3_score": 1.0,
67
+ "m4_score": 1.0,
68
+ "resp_m1": "",
69
+ "resp_m2": "",
70
+ "resp_m3": "",
71
+ "resp_m4": "",
72
+ "hashed_id": "f67d6366cd6c0b9e461783351902eefe"
73
+ },
74
+ "71453891-K": {
75
+ "respuesta_completa": "El concepto de Replica-set es un refinamiento del modelo Maestro-esclavo, las funciones principales del conjunto de réplicas es permitir la recuperación a fallos de manera automática y es una de las formas de añadir replicación a datos en MongoDB. \nLa recomendación en replica-set es de una configuración mínima de tres nodos. En este caso uno de ellos es el nodo primario, sobre este nodo actúa un nodo secundario que replicará las operaciones que se vayan a realizar en el primario, por lo que actuará en caso de no disponibilidad del primario. \nUno de los métodos de respaldo es mongodump este hace una copia de seguridad en ese instante de los datos conectándose a una instancia mongo que se esté ejecutando, por otro lado, con las réplicas mantienen la información duplicada en todo momento por varios nodos.",
76
+ "m1_score": 1.0,
77
+ "m2_score": 0.5,
78
+ "m3_score": 0.5,
79
+ "m4_score": 0.25,
80
+ "resp_m1": "",
81
+ "resp_m2": "",
82
+ "resp_m3": "",
83
+ "resp_m4": "",
84
+ "hashed_id": "d0d16cf33b031602f1b818a8190fc59f"
85
+ },
86
+ "50345097Y": {
87
+ "respuesta_completa": "La principal aplicación de Replica-Set en MongoDB es la de proporcionar respaldo a los datos. Está basado en un modelo maestro-esclavo. En esta arquitectura existe la figura de un nodo primario (nodo maestro). Esta figura no es estática, los nodos se intercambian pings cada dos segundos para conocer la disponibilidad de cada uno. El número mínimo de nodos que se necesitan para un Replica-Set en MongoDB son tres nodos. Dos nodos son nodos de datos, donde uno es el primario y otro el secundario (réplica del primario). El tercer nodo es un nodo árbitro. En caso de que el nodo primario deje de tener disponibilidad, este nodo árbitro asignará la función de nodo primario a otro nodo. La principal diferencia con mongodump es que este comando lleva a cabo una foto estática de la base de datos en el momento en el que se ejecuta, y extrae unos ficheros BSON y JSON con los datos que se encuentran en la BBDD en el momento de la ejecución. Por su parte, Replica-Set funciona en tiempo real, es decir, los datos se encuentran respaldados continuamente, lo que conlleva una menor pérdida de datos en caso de incidencia",
88
+ "m1_score": 1.0,
89
+ "m2_score": 0.5,
90
+ "m3_score": 0.5,
91
+ "m4_score": 1.0,
92
+ "resp_m1": "",
93
+ "resp_m2": "",
94
+ "resp_m3": "",
95
+ "resp_m4": "",
96
+ "hashed_id": "2e7405e83d52a3f78f50c8ca6f16d961"
97
+ },
98
+ "12425296y": {
99
+ "respuesta_completa": "La función de un replica-set es permitir la redundancia de datos, incrementando así la disponibilidad siguiendo un modelo maestro-esclavo. Para una mínima configuración es necesario que existan 3 nodos, uno principal y dos secundarios (el nodo principal manda replicas a los nodos secundarios y así se consigue una “copia de seguridad” en caso de caída del principal) uno de los secundarios tomará el papel de árbitro en el caso de que haya una caída, ya que cuando el nodo principal deja de tener disponibilidad, será uno de los nodos secundarios el que pase al puesto de principal. Esto puede dar lugar a empate, es por ello que se establece un árbitro.\nEs importante tener en cuenta la diferencia con mongoDump. Mongodumop realiza una copia de seguridad fija (se puede hacer símil a una foto), mientras que el replica-set sufre cambios en “streaming”. Se actualiza constantemente",
100
+ "m1_score": 1.0,
101
+ "m2_score": 1.0,
102
+ "m3_score": 1.0,
103
+ "m4_score": 1.0,
104
+ "resp_m1": "",
105
+ "resp_m2": "",
106
+ "resp_m3": "",
107
+ "resp_m4": "",
108
+ "hashed_id": "b8af34d45326efd631958c2e4c4ca125"
109
+ },
110
+ "72102993V": {
111
+ "respuesta_completa": "Un mecanismo que nos sirve para aumentar la disponibilidad es la replicación de información. Esto, consiste en replicar o distribuir los datos de los que disponemos. La arquitectura Replica-Set nos sirve para replicar el conjunto de datos en varias instancias o servidores para aumentar la disponibilidad de los datos. Esto se consigue creando nodos que repliquen la información para que, en caso de caída de uno de los nodos o pérdida, esta siga disponible. Pese a que en otras técnicas de replicación la configuración mínima consiste en dos nodos, uno principal y otro secundario, en el replica-set en mongodb la configuración está conformada por tres nodos, uno principal, otro secundario y un tercero que actúa como árbitro (desempate) en las votaciones. En caso de que el primario se caiga, el nodo secundario se convierte en primario. La principal diferencia con mongodump es que la replicación de la información es dinámica, es decir, no tienes que ejecutar un comando para realizar la copia de seguridad ni otro para recuperarla",
112
+ "m1_score": 1.0,
113
+ "m2_score": 0.5,
114
+ "m3_score": 1.0,
115
+ "m4_score": 1.0,
116
+ "resp_m1": "",
117
+ "resp_m2": "",
118
+ "resp_m3": "",
119
+ "resp_m4": "",
120
+ "hashed_id": "07dcf54484cec166857559a76ff4afe8"
121
+ },
122
+ "51106382Z": {
123
+ "respuesta_completa": "Consiste en un mecanismo mediante el cual, se tiene una recuperación automática frente a fallos a través de la redundancia de los datos. El mecanismo se basa en tener un nodo primario que va replicando sus datos a los nodos secundarios, estos se ocuparán en todo momento de tener una copia actualizada de dichos datos, de este modo en caso de fallo del nodo primario, alguno de estos sea elegido como el nuevo nodo primario y de este modo se logre alta disponibilidad frente a fallos. Los nodos secundarios pueden ser de tipo: prioridad 0 los cuales no pueden ser primarios, ocultos, retrasados o árbitros.\nAdemás de la redundancia de los datos y la alta disponibilidad, esta arquitectura nos aporta la ventaja de balancear la carga automáticamente entre los nodos del replica-set en caso de lectura, aumentando así el rendimiento. Cabe destacar que para la escritura únicamente se usa el nodo primario.\nA la hora de tener una configuración mínima, se requiere un mínimo de 3 nodos, los cuales son 1 primario y 2 secundarios.\nLa principal diferencia con mongodump es que a pesar de poderse replicar los datos y, restaurar con mongostore, es un proceso manual y con la configuración replica-set se tiene una recuperación automática frente a fallos",
124
+ "m1_score": 1.0,
125
+ "m2_score": 1.0,
126
+ "m3_score": 1.0,
127
+ "m4_score": 1.0,
128
+ "resp_m1": "",
129
+ "resp_m2": "",
130
+ "resp_m3": "",
131
+ "resp_m4": "",
132
+ "hashed_id": "79afddaced6c773e45347fd7982fb54d"
133
+ },
134
+ "54294839G": {
135
+ "respuesta_completa": "En MongoDB se ha hecho un refinamiento sobre el modelo “Maestro-Esclavo” y se le ha definido como conjunto de replicas o replica-set. Este permite una recuperación a fallos de forma automática, y es la forma recomendada de implementar replicaciones de datos en MongoDB.\nUna característica principal es que no definen un nodo maestro estéticamente, sino que es asignado al azar y esta asignación se actualiza dependiendo de la disponibilidad del nodo.\nLa configuración mínima recomendada para un replica-set consiste en tres nodos: dos serán instancias de MongoDB con acceso a los datos; uno de estos será el nodo primario mientras que el otro replicará las operaciones que se realicen sobre el primario. El tercer nodo se denomina arbitro, solamente se utiliza para decidir que nodo debe ser asignado como primario. En caso de pérdida, el nodo “secundario” pasara a ser el primario ya que ha realizado las réplicas de todas las operaciones del nodo primario.\nLa principal diferencia con Mongodump es que hace una copia de seguridad de los datos conectándose a una instancia de mongod o mongos en ejecución. Esto permite crear una copia de seguridad para un servidor, una base de datos o una colección completa. Se puede ejecutar sin argumentos y esto creará una copia de seguridad en la base de datos de la carpeta dump/ siempre y cuando la carpeta se encuentre en el sistema local.",
136
+ "m1_score": 0.75,
137
+ "m2_score": 0.5,
138
+ "m3_score": 1.0,
139
+ "m4_score": 0.5,
140
+ "resp_m1": "",
141
+ "resp_m2": "",
142
+ "resp_m3": "",
143
+ "resp_m4": "",
144
+ "hashed_id": "bc0ec2cd7a91c72da74e5d7a4ec67a9e"
145
+ },
146
+ "79136144K": {
147
+ "respuesta_completa": "El objetivo del replica-set es crear un ambiente donde se de una recuperación automática frente a fallos en el sistema. Se consigue mediante la redundancia de datos. Existe un nodo primario que va replicando sus datos en los nodos secundarios. Esta copia siempre va a estar actualizada. Mediante la replica-set de Mongo se consigue una alta disponibilidad frente a los fallos que puedan ocurrir en el sistema. Otra ventaja es que se balancea la carga automáticamente entre los nodos del replica-set lo que hace que aumente nuestro rendimiento. \nPara una configuración mínimas son necesarios 3 nodos, 1 el primario y 2 secundarios. Respecto a los 2 secundarios uno se llamara secundario y el otro será llama árbitro. El arbitro únicamente decide que nodo tiene que ser empleado como primario. Si el nodo primario falla, uno de esos nodos secundarios será elegido como el nuevo nodo primario.\n\nMongodump también puede hacer una copia de seguridad de los datos y la puede restaurar con mongostore. La principal diferencia es que éste es un proceso manual que tiene que hacer el propio usuario. Sin embargo con la configuraciñon replica-set ya se tiene una recuperación automática frente a los fallos que puedan ocurrir.",
148
+ "m1_score": 1.0,
149
+ "m2_score": 0.5,
150
+ "m3_score": 1.0,
151
+ "m4_score": 1.0,
152
+ "resp_m1": "",
153
+ "resp_m2": "",
154
+ "resp_m3": "",
155
+ "resp_m4": "",
156
+ "hashed_id": "b5b1b7329e33e1ba1459273adae97e78"
157
+ },
158
+ "73133351K": {
159
+ "respuesta_completa": "Un replica-set consiste en la replicación de servidores. Se puede implementar en MongoDB y se trata de una refinación del modelo maestro-esclavo. El replica-set permite la redundancia de datos y la alta disponibilidad. Además, sirve como medida anti-caída de un servidor y también para implementar la replicación. \nLa configuración mínima de un replica-set consta de tres nodos: un nodo primario, un nodo secundario y un nodo árbitro. El nodo primario se escoge a la hora de crear el replica-set. \nEn caso de que el nodo primario falle se realiza una votación donde el nodo árbitro se encarga de elegir al nuevo nodo primario. La elección la toma en función del número de votos de cada uno de los nodos secundarios y de sus características, ya que hay nodos que no pueden ser nunca nodo primario, por ejemplo, el nodo árbitro o un nodo que esté oculto. \nMongodump es un comando que se emplea para realizar una copia de seguridad (volcado de datos) de la base de datos tal y como si se tomara una fotografía. Sin embargo, un replica-set permite tener una imagen actualizada en todos los nodos pertenecientes al conjunto sin necesidad de realizar una copia.",
160
+ "m1_score": 1.0,
161
+ "m2_score": 0.5,
162
+ "m3_score": 0.5,
163
+ "m4_score": 1.0,
164
+ "resp_m1": "",
165
+ "resp_m2": "",
166
+ "resp_m3": "",
167
+ "resp_m4": "",
168
+ "hashed_id": "53ebdb2239cc53c714efaaa26e576c4c"
169
+ },
170
+ "31008668E": {
171
+ "respuesta_completa": "El método de replica-set permite una recuperación a fallos de forma automática, y es la forma recomendada de implementar una replicación de los datos en MongoDB. \nPara una configuración mínima es necesario 3 nodos, los cuales dos nodos son instancias de mongod con acceso a los datos, uno de estos nodos es el nodo primario y el otro nodo replica las operaciones que se realizan sobre el primario. El tercer nodo es llamado, árbitro, y su única función es decidir qué nodo deber ser asignado como primario. Cuando un nodo primario deja de tener disponibilidad, es cuando actúa el tercer nodo árbitro, que decide quien pasa a ser el nodo primario. \nLa principal diferencia con mongodump, es que esté realiza una copia de seguridad de los datos conectándose a una instancia de mongod o mongos en ejecución, esto permite realizar una copia de seguridad para un servidor o base de datos, o permite utilizar una consulta para copiar solo una parte de la colección",
172
+ "m1_score": 0.75,
173
+ "m2_score": 0.5,
174
+ "m3_score": 0.5,
175
+ "m4_score": 0.75,
176
+ "resp_m1": "",
177
+ "resp_m2": "",
178
+ "resp_m3": "",
179
+ "resp_m4": "",
180
+ "hashed_id": "40f310f15c326b49d534adb6e15cf073"
181
+ },
182
+ "53630942 W": {
183
+ "respuesta_completa": "Replicaset es una característica de MongoDB que permite la replicación de los datos para incrementar su disponibilidad permitiendo la recuperación de datos frente a fallos. La arquitectura básica esta conformada por tres nodos (Primario-Secundario-Árbitro), es decir, nodo primario recibe las operaciones de escritura y estas son replicadas al secundario automáticamente, obteniendo así los mismos datos en ambos. La selección de los roles se realiza de manera aleatoria. Sin embargo, el nodo árbitro se utiliza para decidir que nodo debe ser primario en caso de que el primario deje de tener disponibilidad. Por tanto, en caso de fallo del primario asumiría dicho rol el secundario. \nAdicionalmente, MongoDB tiene una funcionalidad “mongodump” que permite respaldar los datos actuales de la BBDD, es decir, realiza una foto fija de la misma y esta es guardada y exportada en archivos para en caso de pérdida poder recuperar la última “foto/copia”. La principal diferencia con el replicaset es que “mongodump” interrumpe el servicio hasta que manualmente volvemos a recuperar la copia realizada, además que con esta funcionalidad podemos perder datos ya que no se replica de manera constante o en “streaming” como si lo hace el replicaset.",
184
+ "m1_score": 1.0,
185
+ "m2_score": 1.0,
186
+ "m3_score": 0.5,
187
+ "m4_score": 1.0,
188
+ "resp_m1": "",
189
+ "resp_m2": "",
190
+ "resp_m3": "",
191
+ "resp_m4": "",
192
+ "hashed_id": "b987f8fb47ae6497f73ee45146d8b75a"
193
+ },
194
+ "22756875P": {
195
+ "respuesta_completa": "La función de un replica-set en MongoDB es la de dar respaldo a fallos de forma automática. La configuración mínima para un replica-set, y que por defecto aparece configurado así, es de 3 nodos. Estos 3 nodos están enviándose pings cada dos segundos para conocer si están disponibles (no se han caído).\nLos 3 nodos son los siguientes:\n-\tPrimario: es elegido de forma dinámica. Es el que gestiona las operaciones.\n-\tSecundario: realiza las réplicas de escritura sobre el nodo primario, de esta forma, si se cae el primario, el secundario ocupa su puesto y el sistema sigue funcionando.\n-\tÁrbitro: se ocupa de elegir quien de los dos nodos va a ser el primario.\nLa principal diferencia con mongodump es que mongodump realiza una copia de una base de datos, colección o subcolección que se le indique, almacenando esta en un directorio. El replica-set en cambio funciona como respaldo automático en caso de caída de algún servidor.",
196
+ "m1_score": 1.0,
197
+ "m2_score": 0.5,
198
+ "m3_score": 0.0,
199
+ "m4_score": 1.0,
200
+ "resp_m1": "",
201
+ "resp_m2": "",
202
+ "resp_m3": "",
203
+ "resp_m4": "",
204
+ "hashed_id": "56b47fc533b4f14267f5887cb9103b05"
205
+ },
206
+ "26500378P": {
207
+ "respuesta_completa": "MongoDB proporciona varios mecanismos de respaldo para los datos. Una de ellas es la capacidad de replicación o redundancia de los datos. Para ello, MongoDB utiliza la arquitectura réplica-set, que es un refinamiento de la arquitectura maestro servidor. \nEn replica-set, un nodo primario, o maestro, es elegido por el resto de los nodos en base a la disponibilidad de este. Un conjunto replica-set está compuesto, en su configuración mínima por tres nodos: dos de ellos serán el primario y el secundario, nodo en el que se realiza la replicación de las operaciones sobre el nodo primario. El tercer nodo, llamado arbitrario, se encargará de decidir quién será el nodo primario, en caso de empate en la votación. La votación se llevará a cabo si el nodo primario deja de estar disponible. Para comprobar el estado del sistema, los nodos se hacen ping entre ellos cada 2 segundos.\nLa diferencia de este método de replicación respecto a mongodump, es que mongodump crea una copia de seguridad que puede restaurarse en otro mongo utilizando mongorestore. Replica-set proporciona una replicación completa de un nodo primario para poder sustituirse inmediatamente en caso de que deje de estar disponible.",
208
+ "m1_score": 1.0,
209
+ "m2_score": 0.5,
210
+ "m3_score": 1.0,
211
+ "m4_score": 1.0,
212
+ "resp_m1": "",
213
+ "resp_m2": "",
214
+ "resp_m3": "",
215
+ "resp_m4": "",
216
+ "hashed_id": "624a61346ee721323b887af8fed7eac7"
217
+ },
218
+ "77839987-Y": {
219
+ "respuesta_completa": "Un Replica-Set, como su nombre indica, es un sistema de replicación específico de MongoDB. Se basa en la arquitectura Maestro-Esclavo. Esto es, las escrituras son recibidas por un nodo Primario (Maestro) y este se encarga de comunicar su estado mediante un heartbeat a los Secundario (Esclavos), que se actualizan (con un intervalo de tiempo) al encontrar cambios. \nSe caracteriza porque el nodo Primario no es estático, se escoge mediante una votación interna. Aunque se pueden configurar prioridades dentro de cada nodo, de manera predeterminada cualquier nodo puede ser Primario. De este modo, si se pierde la disponibilidad del primario, se sigue manteniendo el sistema, sin tener que restablecer nada. \nEn este sistema la configuración mínima es de 3 nodos, un Primario, un Secundario y un nodo Árbitro. Que no será Primario nunca, solo se encarga de mantener la conexión y aportar su voto en la elección del Maestro. ",
220
+ "m1_score": 0.75,
221
+ "m2_score": 0.5,
222
+ "m3_score": 1.0,
223
+ "m4_score": 0.0,
224
+ "resp_m1": "",
225
+ "resp_m2": "",
226
+ "resp_m3": "",
227
+ "resp_m4": "",
228
+ "hashed_id": "375c9f81952e5da9fc1e5f5bf9fcf0bb"
229
+ },
230
+ "X3109431S": {
231
+ "respuesta_completa": "La función de un replica-set es la distribución de los datos en diferentes servidores para los cuáles existen tres nodos para una configuración mínima. Habrá dos nodos de almacenamiento, de los cuáles uno será el primario y otro el de replicación. El primario recibe las consultas y replica las operaciones al nodo de replicación. El tercer nodo será el que recibe el nombre de árbitro y decide qué nodo debe actuar como primario o como replicación. Si el nodo primario deja de tener disponibilidad, el nodo de replicación ocupará su lugar. Además, la comunicación entre los nodos se lleva a cabo través de pings (avisos) cada 2 segundos para confirmar que todos ellos siguen activos.\nMongodump permite exportar la base de datos, colección o parte de la base de datos a otro servidor, pero no garantiza la disponibilidad en paralelo además de que no tiene por qué tener la base de datos completa en otro servidor mientras que el replica-set consiste precisamente en poder recuperar la base de datos completa",
232
+ "m1_score": 0.5,
233
+ "m2_score": 0.5,
234
+ "m3_score": 1.0,
235
+ "m4_score": 0.75,
236
+ "resp_m1": "",
237
+ "resp_m2": "",
238
+ "resp_m3": "",
239
+ "resp_m4": "",
240
+ "hashed_id": "403a05fd0bcd4addf67ce479533c4159"
241
+ },
242
+ "49125521M": {
243
+ "respuesta_completa": "Un replica-set (conjunto de réplicas) es un mecanismo mediante el cuál permite la redundancia y el incremento de la disponibilidad de los datos. Además, permite una recuperación a fallos de forma automática, y es una de las formas más recomendadas de implementar replicación de datos en MongoDB.\nEste consiste en tener un nodo primario que replica sus datos a los nodos secundarios, manteniendo así una copia actualizada de dichos datos. Para la configuración mínima de un replica-set es necesario tres nodos, uno primario y dos secundarios, los cuáles se van actualizando. En el caso de que el nodo primario dejara de tener disponibilidad, automáticamente se elije uno de los nodos secundarios, siempre que no tenga “priority: 0” (este no podrá ser nunca nodo primario, es conocido como árbitro y solo interviene en la elección del nodo primario).\nPor otro lado, existe una herramienta en MongoDB que permite realizar copias de seguridad de las bases de datos, esta es mongodump, y se restaura con mongorestore; pero esto es un proceso manual y no de manera automática como la recuperación del replica-set",
244
+ "m1_score": 1.0,
245
+ "m2_score": 1.0,
246
+ "m3_score": 1.0,
247
+ "m4_score": 1.0,
248
+ "resp_m1": "",
249
+ "resp_m2": "",
250
+ "resp_m3": "",
251
+ "resp_m4": "",
252
+ "hashed_id": "e364b8a8d0183da2d9676d8907b95e55"
253
+ },
254
+ "25605969T": {
255
+ "respuesta_completa": "La función de un replica-set es dar respaldo a los datos. Un replica-set es una estructura que se crea para que en caso de error en el nodo primario no perdamos toda la información. \nPara crear un replica set son necesarios 3 nodos, el nodo primario, el nodo secundario y el arbitro. \nLa función que tienen estos nodos es: \n- nodo primario: guardar la información. \n- nodo secundario: hacer una copia de esta información y mantenerla hasta que sea necesario, que por caída del nodo primario se ponga en funcionamiento. \n- arbitro: encargado de nombrar al nodo secundario que comienza a actuar en caso de que el primario deje de tener disponibilidad. \nMongodump es un buen método de respaldo de la información, pero realiza la copia de seguridad en un momento dado, a diferencia de replica- set que se mantiene actualizada. ",
256
+ "m1_score": 0.0,
257
+ "m2_score": 0.0,
258
+ "m3_score": 0.0,
259
+ "m4_score": 0.0,
260
+ "resp_m1": "",
261
+ "resp_m2": "",
262
+ "resp_m3": "",
263
+ "resp_m4": "",
264
+ "hashed_id": "cc74af2802f44d4dd32ff43c7035e086"
265
+ },
266
+ "20904222M": {
267
+ "respuesta_completa": "MongoDB nos ofrece una forma de mantener seguros nuestros datos mediante la redundancia de los mismos, ofreciendo alta disponibilidad sobre ellos. \nMongoDB ha realizado un refinamiento sobre el sistema Maestro-Esclavo, en el cual el nodo maestro recibe y ejecuta las instrucciones y el esclavo las repite.\nUn replicaset consta debe constar de un mínimo de tres nodos: el primario (recibe y ejecuta las instrucciones), el secundario (repite lo que haga el primario) y el árbitro (en caso de que falle el primario, vota para cual debe sustituirle). \nEntre los nodos primario, secundario y árbitro, se envían “pings” o avisos para comprobar que todos continúan conectados.\nEn el caso de que existan más nodos secundarios, y se cayese el primario, todos los secundarios pueden votar para elegir cual se convierte en primario, pero no todos los secundarios pueden convertirse en primarios. El árbitro sirve en caso de que exista un empate.\nLa diferencia con mongodump radica en que mongodump realiza una copia de los datos, pero no existe esta arquitectura en línea para comprobar todos los pasos que realiza el primario y la comprobación de qué nodos se encuentran activos. Sin embargo, ambos son formas de garantizar la seguridad de los datos.",
268
+ "m1_score": 1.0,
269
+ "m2_score": 0.5,
270
+ "m3_score": 1.0,
271
+ "m4_score": 1.0,
272
+ "resp_m1": "",
273
+ "resp_m2": "",
274
+ "resp_m3": "",
275
+ "resp_m4": "",
276
+ "hashed_id": "9086ffc9bd11901abadbe95477d2292c"
277
+ },
278
+ "02720025E": {
279
+ "respuesta_completa": "La arquitectura replica set es una evolución del sistema de Replicación de archivos Maestro-Esclavo. Con la arquitectura replica-set somos capaces de tener toda la información en tiempo real duplicada en diferentes nodos.\nUna correcta arquitectura de replica-set debe contar con mínimo tres nodos que se comunican cada dos segundos: árbitro (que no almacena datos sino que decide qué otro nodo es el primario), primario (principal fuente de acceso a los datos) y secundario (que replica todo lo que hace el nodo primario).\nMongodump, sin embargo, se usa para crear una copia de seguridad de una instancia en ejecución según la necesidad (colecciones completas, base de datos…) en un momento de tiempo determinado.",
280
+ "m1_score": 1.0,
281
+ "m2_score": 0.5,
282
+ "m3_score": 0.5,
283
+ "m4_score": 1.0,
284
+ "resp_m1": "",
285
+ "resp_m2": "",
286
+ "resp_m3": "",
287
+ "resp_m4": "",
288
+ "hashed_id": "1c349c3eb06025e5ccc0a215cc9ad35f"
289
+ },
290
+ "54172161P": {
291
+ "respuesta_completa": "Un replica-set es un mecanismo de respaldo de MongoDB que consiste en interconectar un conjunto de nodos o servidores siguiendo el modelo maestro-esclavo. De este conjunto de nodos o servidores interconectados (al menos dos), uno de ellos es elegido nodo primario y, a parte de ese, debe haber, al menos, un nodo secundario. El nodo primario será el que responda ante operaciones CRUD realizadas por las aplicaciones cliente y deberá propagar las modificaciones de los datos a los nodos secundarios, de modo que cada uno de ellos tenga siempre una imagen actualizada de los datos. Este mecanismo aporta alta disponibilidad, consistencia y robustez frente a fallos en los servidores. En caso de que el nodo primario caiga, uno de los secundarios disponibles será elegido primario en una votación, siendo elegido este cuando consiga mayoría absoluta. Para las votaciones existe un tipo de nodo especial: el nodo árbitro. Este tiene el único papel de decidir en las votaciones. Además de este existen otros nodos especiales, como son el nodo retrasado (tiene una imagen retrasada del conjunto de datos), nodo de prioridad 0 (este nunca puede ser presidente), nodo oculto (no aparece visible para los usuarios), etc. \n\nExiste otro mecanismo de respaldo llamado mongodump, el cual realiza una fotografía de un momento concreto de todas las bases de datos, de algunas bases de datos o algunas colecciones dentro de una base de datos, según como indique el usuario. En caso de pérdida o daño en los datos, se podría hacer una restauración con el comando mongorestore según la fotografía de los datos en el momento que se hizo. \n\nLa diferencia fundamental entre los replicasets y mongodump es que mientras los replicaset realizan copias continuas y automáticas de los datos del nodo principal en otros nodos (copias actualizadas), mongodump es una copia de un momento concreto (no actualizada).",
292
+ "m1_score": 1.0,
293
+ "m2_score": 1.0,
294
+ "m3_score": 1.0,
295
+ "m4_score": 1.0,
296
+ "resp_m1": "",
297
+ "resp_m2": "",
298
+ "resp_m3": "",
299
+ "resp_m4": "",
300
+ "hashed_id": "653e6dc27f1031bcd8e1c1fa62070649"
301
+ },
302
+ "02224902_20210320213352": {
303
+ "respuesta_completa": "Una arquitectura replica-set es una alternativa muy recomendada que ofrece MongoDB para respaldar las bases de datos en caso de fallo o caída de un servidor, ofreciendo alta disponibilidad de los datos y recuperación a fallos de forma automática. La arquitectura mínima de un replica-set es:\n•\t2 nodos con los que podremos acceder a los datos. Uno será el principal con el que podremos trabajar y el segundo será el secundario que replicará las acciones que realicemos.\n•\tEl tercer nodo deberemos configurarlo en su creación, denominado arbitrario. Solo decide que nodo es asignado como primario y se comunica con los otros 2 cada cierto intervalo de tiempo. \nEn caso de fallo del nodo principal, el replica-set detecta la desconexión, cambiando al secundario a realizar de principal. Automáticamente, tenemos acceso a las bases de datos pudiendo trabajar con el nuevo principal. Si se reconectara el nodo caído este pasaría a ser el secundario replicando al principal. \nLa principal diferencia de un Replica-set con otra alternativa de respaldo como Mongodump (exportación de bases de datos a un fichero) es que automatizamos la recuperación porque con el otro método deberíamos importar los datos manualmente. Además, podríamos sufrir una pérdida de datos dependiendo de cuando se realizo la exportación. ",
304
+ "m1_score": 1.0,
305
+ "m2_score": 0.5,
306
+ "m3_score": 1.0,
307
+ "m4_score": 1.0,
308
+ "resp_m1": "",
309
+ "resp_m2": "",
310
+ "resp_m3": "",
311
+ "resp_m4": "",
312
+ "hashed_id": "8eaadfcb8902c35bc11e5ae1c8639892"
313
+ },
314
+ "75923577W": {
315
+ "respuesta_completa": "La arquitectura ‘Replica-Set’ de MongoDB hace alusión a una de las configuraciones estructurales de respaldo que ofrece esta base de datos NoSQL, la cuál hace permite asegurar en un mayor grado la disponibilidad de los datos. \nDicha configuración está basada en la definición de tres tipos de nodos en su sistema de almacenamiento distribuido, cada uno con una función propia dentro de la tarea de respaldo y que son los siguientes:\n-\tNodo principal: constituye el ‘datanode’ de almacenamiento principal, es decir, es en éste donde se leen y escriben directamente los datos.\n-\tNodo secundario: hace la función ‘datanode’ de respaldo del nodo principal, es decir, recibe la información generada por el nodo principal con el retraso/’delay’ establecido por el intervalo de ‘Pings’ enviados entre los mismos.\n-\tNodo árbitro: se limita únicamente de administrar y votar/asignar el papel (principal, secundario) de los nodos de tal manera que se garantice la mayor disponibilidad posible.\nEsta configuración de respaldo requiere por tanto de un mínimo de 3 nodos para su funcionamiento en producción, siendo posible la configuración de muchos más por supuesto, gracias a escalabilidad horizontal que ofrece la base de datos MongoDB. Podríamos sumar más máquinas/nodos que nos permitan cumplir con el factor de replicación (nº de nodos de respaldo) que se requiera.\nEn el caso que uno de los nodos elegidos como principal, se caía/pierda disponibilidad el nodo árbitro actúa nombrando un nuevo nodo principal (normalmente según el índice de probabilidad del resto de nodos para ser elegidos como principal ‘votes’) y considerando que el nodo secundario del principal ‘caído’, dispone del respaldo y metadatos generados por el mismo.\nLa principal diferencia que separa esta configuración de réplicas a la funcionalidad ofrecida por ‘mongodump’, es el dinamismo que ofrece, al disponer de un coordinador/’árbitro’ dentro del propio sistema de almacenamiento distribuido, que además está configurado para que tome un comportamiento de preventivo y reactivo, en caso de fallo de los nodos, para el aseguramiento de la disponibilidad de la base de datos, pudiendo ‘reestablecerse de manera ágil’ (cuestión de segundos). Mientras que ‘mongodump’ es una alternativa de respaldo mucho más cercana a las copias de directorio convencionales, ya que se base en la copia de todos o parte de los datos en directorio externos, algo que ofrece seguridad frente a la recuperación, pero no agilidad en caso de necesidad de restauración, ya que en este caso, necesitaría hacer uso de ‘mongorestore’ para recuperar el respaldo generado por ‘mongodump’ en la ubicación concreta.",
316
+ "m1_score": 0.5,
317
+ "m2_score": 0.25,
318
+ "m3_score": 0.25,
319
+ "m4_score": 0.5,
320
+ "resp_m1": "",
321
+ "resp_m2": "",
322
+ "resp_m3": "",
323
+ "resp_m4": "",
324
+ "hashed_id": "761c1c9b8459ec1b129a8d9b5d587f57"
325
+ },
326
+ "80098232L": {
327
+ "respuesta_completa": "Un replicaset consiste en un sistema de respaldo de mongoDB, que basado en un modelo Maestro-Esclavo refinado cuya función es evitar la caída del sistema por fallo en la máquina donde corre el DBMS. Está compuesto de, al menos, de 3 réplicas que interactúan entre ellas cada 3 segundos mediante ‘pings’ (un replicaset será primario, otro secundario y el tercero o árbitro tan solo sirve para votar cuál de ellos será el primario (el árbitro no puede ser primario)). Los nodos votan al primario y el que gana la votación ejercerá como tal hasta el momento en el que se detecte que este se ha caído. En ese caso, el nodo secundario pasará a ser primario y el antiguo primario, cuando vuelva a estar operativo, entrará como secundario. Lo importante de este proceso es que toda la información (datos, actualizaciones, etc) que le llegaba al primario, la recibía también el nodo secundario, de forma que en pocos segundos se recuperaría el proceso. \nEn el caso del replicaset el problema se resuelve de manera instantánea, mientras que con las funciones mongodump y mongorestore es totalmente manual y hay que prever la situación realizando copias previamente",
328
+ "m1_score": 1.0,
329
+ "m2_score": 0.5,
330
+ "m3_score": 1.0,
331
+ "m4_score": 0.5,
332
+ "resp_m1": "",
333
+ "resp_m2": "",
334
+ "resp_m3": "",
335
+ "resp_m4": "",
336
+ "hashed_id": "c7daf68382ba769cc135ad1301141fb8"
337
+ },
338
+ "48234395Z": {
339
+ "respuesta_completa": "Replica-set es un método de replicación utilizado por MongoDB para dar respaldo a los datos de la BBDD. Se trata de un método de redundancia de datos para dar respaldo y aumentar la disponibilidad de los mismos y poder seguir trabajando pese a que uno de los nodos se caiga. Además, mejora el rendimiento puesto que solo es necesario escribir en el nodo principal, pero podemos leer de cualquiera de los nodos. Para una configuración mínima es necesario tener mínimo 3 nodos, 1 primario y el resto secundarios.\nSi el nodo primario cae y deja de tener disponibilidad el resto de nodos se dan cuenta gracias al heartbeat que se envían cada 3 segundos y en ese momento serán los secundarios los que decidirán mediante votación cuál de ellos pasa a ser el nuevo nodo principal.\nEl método de copia de seguridad mongodump solo sirve para hacer una copia de una foto fija a la BBDD si se hacen modificaciones posteriores al restaurar la BBDD se habrán perdido mientras que con un replica-set los demás nodos están constantemente actualizados. Por el contrario, si borramos un elemento y futuramente queremos recuperarlo con replica-set no podremos mientras que con mongodump lo recuperariamos",
340
+ "m1_score": 1.0,
341
+ "m2_score": 1.0,
342
+ "m3_score": 1.0,
343
+ "m4_score": 1.0,
344
+ "resp_m1": "",
345
+ "resp_m2": "",
346
+ "resp_m3": "",
347
+ "resp_m4": "",
348
+ "hashed_id": "2fb405439e8aaae1d342716e1b595d83"
349
+ },
350
+ "45574617W": {
351
+ "respuesta_completa": "La arquitectura de Réplica-Set en MongoDB permite la redundancia de datos e incrementa la disponibilidad de los datos. Es una mejora sobre el modelo de Maestro-Esclavo, y es lo que ha definido como conjunto réplica -set. Con este método se permite recuperar de forma automática una recuperación a fallos.\nUna característica de los replica set es que no definen un nodo maestro estático, es asignado al azar y esta asignación se actualiza en función de la disponibilidad de los nodos. Los nodos mantienen el control unos sobre otros. Para crear un conjunto réplica set es necesario tener tres nodos (configuración mínima recomendable) de los cuáles dos nodos serán instancias de mongod con acceso a los datos; uno de estos será el nodo primario y el otro replicará las operaciones. El tercer nodo es el llamado nodo arbitrario y se utiliza para decidir qué nodo deber asignado como principal.\nCuando el nodo primario deja de tener disponibilidad el nodo secundario pasa a ser el principal ya que tenemos la figura del nodo arbitrario.\nLa principal diferencia con mongodump es que es un proceso automático, sin tener que realizar las operaciones de movimiento de las copias de seguridad. ",
352
+ "m1_score": 1.0,
353
+ "m2_score": 0.5,
354
+ "m3_score": 1.0,
355
+ "m4_score": 0.5,
356
+ "resp_m1": "",
357
+ "resp_m2": "",
358
+ "resp_m3": "",
359
+ "resp_m4": "",
360
+ "hashed_id": "d1bff6709d7728e5a45af677ae0b75c1"
361
+ },
362
+ "48091233G": {
363
+ "respuesta_completa": "Replica-set en mongoDB se basa en un mecanismo de recuperación automática frente a fallos mediante la redundancia de los datos. Dicho mecanismo se basa en tener un nodo primario que va replicando los datos a los nodos secundarios, los cuáles en todo momento mantendrán una copia actualizada de todos los datos. En caso de fallo del nodo primario, alguno de los secundarios es elegido como nuevo nodo primario. Este mecanismo permite una alta redundancia de los datos y una alta disponibilidad, una arquitectura replica-set nos brinda la ventaja de balancear la carga automáticamente entre los nodos del replica-set. \nPara una configuración adecuada se requieren mínimo 3 nodos: 1 nodo primario, uno secundario y un árbitro. \nMongoDB también dispone de “mongodump” para replicarl os datos y “mongorestore” para restaurarlos",
364
+ "m1_score": 1.0,
365
+ "m2_score": 0.5,
366
+ "m3_score": 1.0,
367
+ "m4_score": 0.5,
368
+ "resp_m1": "",
369
+ "resp_m2": "",
370
+ "resp_m3": "",
371
+ "resp_m4": "",
372
+ "hashed_id": "6c474f1ce11f4c3c4afb5c683a46754c"
373
+ },
374
+ "49224226V": {
375
+ "respuesta_completa": "La función de un replica-set es ofrecer garantías frente a la posibilidad de caída de un nodo, son necesarios mínimo tres nodos para realizar un despliegue de mongo con replica-set. Cuando un nodo primario cae, los nodos secundarios votan un nuevo nodo primario. Mongodump se realiza una vez y no se actualiza automáticamente, mientras que un replica-set se configura con un tiempo determinado tras el cual realiza las mismas operaciones que el nodo primario.",
376
+ "m1_score": 0.25,
377
+ "m2_score": 1.0,
378
+ "m3_score": 1.0,
379
+ "m4_score": 0.5,
380
+ "resp_m1": "",
381
+ "resp_m2": "",
382
+ "resp_m3": "",
383
+ "resp_m4": "",
384
+ "hashed_id": "197202cada30464d0dccd61b6d0ba4db"
385
+ },
386
+ "77386633M": {
387
+ "respuesta_completa": "Una Replica-Set en MongoDB es una arquitectura en la que se garantiza la alta disponibilidad de los datos y protección frente a fallos. \nPara configurar un Replica-Set en MongoDB hacen falta mínimo 3 nodos. Uno primario, otro secundario que realiza las mismas operaciones que el primerio y un nodo árbitro que decide quién es el primario.\nSi un nodo primario se cae cuando el nodo árbitro se dé cuenta, le comunicará a un nodo secundario que debe pasar a ser primario. El tiempo en el que esto ocurra depende de la frecuencia con la que se haya configurado la comunicación entre los nodos, que por defecto es 2 segundos.\nSu principal diferencia con mongodump es que mongodump realiza una copia de la base de datos cuando lo ejecutamos, mientras que Replica-Set va copiando en los nodos todos los cambios que vayamos realizando en la base de datos",
388
+ "m1_score": 1.0,
389
+ "m2_score": 0.5,
390
+ "m3_score": 1.0,
391
+ "m4_score": 1.0,
392
+ "resp_m1": "",
393
+ "resp_m2": "",
394
+ "resp_m3": "",
395
+ "resp_m4": "",
396
+ "hashed_id": "b03dbc813da7be54a82dfe329a859b44"
397
+ },
398
+ "72834193R": {
399
+ "respuesta_completa": "El Replica-Set de MongoDB es una medida de respaldo que permite tener los datos duplicados en varios nodos. Además, es un refinamiento del modelo Maestro-Esclavo, con la diferencia principal de que no existe un nodo maestro estático como tal. La configuración mínima de esta arquitectura contendrá tres nodos (uno se asignará como primario de manera aleatoria, uno de los secundarios replicará las modificaciones realizadas y el ultimo nodo será el árbitro). Una de las funciones principales de esta arquitectura es la alta disponibilidad de los datos, ya que si un nodo primario deja de tener disponibilidad se elige un nuevo nodo primario mediante una votación. Esta disponibilidad se obtiene mediante el envió de ping entre nodos que se realiza cada dos segundos. Además, se consigue mantener una consistencia entre los nodos y existe un balanceo de carga (las escrituras solo se realizan en el nodo primario). Mongodump realiza una foto o instantánea de la base de datos, mientras que en un conjunto replica-set cualquier modificación de los datos se replica en los demás nodos. De esta manera, se dispone de la información de la base de dato totalmente actualizada en el caso de que algún nodo deje de estar disponible. ",
400
+ "m1_score": 1.0,
401
+ "m2_score": 0.5,
402
+ "m3_score": 1.0,
403
+ "m4_score": 1.0,
404
+ "resp_m1": "",
405
+ "resp_m2": "",
406
+ "resp_m3": "",
407
+ "resp_m4": "",
408
+ "hashed_id": "915ceea06887d6f09ba9b6428e304511"
409
+ },
410
+ "44645938V": {
411
+ "respuesta_completa": "Replica set se trata de una arquitectura de sistemas pensada para MongoDb que tiene como fin mejorar el rendimiento y robustez del sistema, convirtiéndose en un sistema de alta disponibilidad. Se basa en un sistema maestro esclavo donde un nodo maestro va comunicando los cambios a su conjunto de nodos esclavos, manteniendo varias instancias actualizadas al mismo tiempo y pudiendo leer los datos de cualquiera de ellas, lo que nos permite un balanceo de carga (las escrituras se realizan a través del maestro). En caso de fallo del maestro, el resto de nodos elegirían un nuevo maestro mediante un sistema de votos.\nEn cuanto a las diferentes configuraciones la mínima sería aquella en la que existe un único nodo maestro y un nodo esclavo, mientras que la más típica sería un maestro, un esclavo y un árbitro, este último únicamente tiene la función de participar en la votación de un nuevo nodo maestro en caso de caída del sistema, es decir no contiene datos.\nLa diferencia con mongodump es que mientras este crea copias de seguridad y las restaura en caso de fallo (perdiendo los datos desde la última copia), replica-set se levanta en unos minutos con la información actualizada.",
412
+ "m1_score": 1.0,
413
+ "m2_score": 1.0,
414
+ "m3_score": 1.0,
415
+ "m4_score": 1.0,
416
+ "resp_m1": "",
417
+ "resp_m2": "",
418
+ "resp_m3": "",
419
+ "resp_m4": "",
420
+ "hashed_id": "64128cc76e7db51256e042661fff77cb"
421
+ },
422
+ "47921622H": {
423
+ "respuesta_completa": "La arquitectura replica-set es un refinamiento de MongoDB del modelo Maestro-Esclavo, en el que una instancia maestra recibe las operaciones y las replica en la instancia esclava. Este modelo permite que haya respaldo de los datos, es decir, que existan copias de restauración que se actualicen dinámicamente según ocurren las operaciones para poder recuperar los datos en caso de fallo.\nEn el caso del replica-set, la configuración mínima es de tres instancias: una primaria, una secundaria y una árbitro. La primaria recibirá las operaciones y las transmitirá al resto de instancias secundarias. El nodo árbitro sólo tendrá poderes de voto (no almacena datos) para elegir un nuevo primario en caso de que el actual falle. Es decir, ante fallo de la instancia principal, una de las secundarias (sin retraso en la actualización y no oculta) tomará el rol de primaria tras unas elecciones.\nEl punto fuerte de esta configuración es que la copia de seguridad se actualiza en el secundario según ocurren las operaciones, mientras que otros métodos de respaldo como el comando mongodump sólo generan una copia de seguridad puntual, es decir, con los datos que haya en el momento de correr el comando",
424
+ "m1_score": 1.0,
425
+ "m2_score": 1.0,
426
+ "m3_score": 1.0,
427
+ "m4_score": 1.0,
428
+ "resp_m1": "",
429
+ "resp_m2": "",
430
+ "resp_m3": "",
431
+ "resp_m4": "",
432
+ "hashed_id": "4f0da78d71f8c741ac8add282a1d1a77"
433
+ },
434
+ "77394225F": {
435
+ "respuesta_completa": "La replicación es una técnica de respaldo que consiste en realizar una copia de los datos en varios nodos (servidores) cada vez que se realiza una escritura en el nodo primario. Esta copia se efectúa de forma automática, no de forma manual como en el volcado de datos (dump). \nUn réplica set ofrece respaldo (ante una caída del nodo primario se tiene una copia de los datos), disponibilidad (ante una caída del nodo primario, un secundario pasará a primario) y balanceo de carga (la escritura se realiza en el nodo primario y la lectura en el primario o secundario/s).\nLa configuración mínima de un réplica set consta de:\n- 1 nodo primario, donde se pueden realizar operaciones de escritura y lectura.\n- 1 nodo secundario, donde solo se pueden leer datos.\n- 1 nodo árbitro, el cual establece con su voto de desempate qué nodo secundario debe pasar a primario ante una caída del nodo primario.\nEn caso de caída del servidor primario, los nodos secundarios lanzan un voto para determinar qué nodo debe ser primario. El nodo secundario con más votos pasará a primario, y el antiguo nodo primario cuando se reincorpore lo hará como secundario.",
436
+ "m1_score": 1.0,
437
+ "m2_score": 0.5,
438
+ "m3_score": 1.0,
439
+ "m4_score": 1.0,
440
+ "resp_m1": "",
441
+ "resp_m2": "",
442
+ "resp_m3": "",
443
+ "resp_m4": "",
444
+ "hashed_id": "2a6cb06588a72b29abded5a54d4991c6"
445
+ }
446
+ }]
codeScripts/Dependencies/SentenceTransformer2.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from distutils.filelist import FileList
2
+ import json
3
+ from datasets import load_dataset
4
+ from sklearn.model_selection import train_test_split
5
+ import pandas as pd
6
+ import os
7
+
8
+ from json import encoder
9
+
10
+ from codeScripts.utils import save_json, load_json, create_file_path
11
+
12
+ #variables que tengo que hacer parametrizables:
13
+ fileList = ["__appcache__/biConNotaAnon.json", "__appcache__/metodos-de-captura-conNota-Anon.json"]
14
+ path_created_dataset = create_file_path("JoinedSubjects2.json", doctype=4)
15
+
16
+ #los parámetros de SentTransf_train
17
+
18
+ def getJsonInfo(fileName):
19
+ subject_fileDataset = {'train': load_json(fileName)}
20
+ samples = []
21
+
22
+ for i in range (0,len(subject_fileDataset["train"])): #len(subject1)
23
+ hashed_id = subject_fileDataset["train"][i]['hashed_id']
24
+ keywords = subject_fileDataset["train"][i]['metadata']['keywords']
25
+ mark = subject_fileDataset["train"][i]['nota']
26
+ question = subject_fileDataset["train"][i]['metadata']['enunciado']
27
+ responseStudent = subject_fileDataset["train"][i]['respuesta']
28
+ responseTeacher = ""
29
+ for j in range(0,len(subject_fileDataset["train"][i]['metadata']['minipreguntas'])):
30
+ responseTeacher = responseTeacher + subject_fileDataset["train"][i]['metadata']['minipreguntas'][j]['minirespuesta']
31
+
32
+ ie = {'responseTeacher': responseTeacher,
33
+ 'responseStudent': responseStudent,
34
+ 'mark': mark,
35
+ 'hashed_id': hashed_id,
36
+ 'keywords': keywords
37
+ }
38
+
39
+ samples.append(ie)
40
+
41
+ return samples
42
+
43
+
44
+ def PreparingDataSet():
45
+ #Creating a list with the necesarry fields
46
+ first_iter = 1
47
+ for subject in fileList:
48
+ if first_iter:
49
+ subjectFileList = getJsonInfo(subject)
50
+ first_iter = 0
51
+ else:
52
+ subjectFileList = subjectFileList + getJsonInfo(subject)
53
+
54
+ #Splitting the dataset into train,valid and test data
55
+ data_train ,data_test = train_test_split(subjectFileList,test_size=0.3)
56
+ data_train ,data_valid = train_test_split(data_train,test_size=0.1)
57
+
58
+ data = {'train': data_train
59
+ ,'test': data_test
60
+ ,'valid': data_valid
61
+ }
62
+
63
+ save_json(path_created_dataset, data)
64
+
65
+
66
+ import json
67
+ import math
68
+ import pandas as pd
69
+ from datasets import load_dataset,Dataset,DatasetDict
70
+ from sklearn.model_selection import train_test_split
71
+ from sklearn.metrics import mean_absolute_error, mean_squared_error, mean_squared_log_error, mean_absolute_percentage_error, r2_score, roc_curve
72
+ from sentence_transformers import SentenceTransformer, InputExample, losses, util, evaluation, models
73
+ from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator
74
+ from torch.utils.data import DataLoader
75
+ from torch import nn
76
+
77
+ import datasets
78
+ import sklearn
79
+ import sentence_transformers
80
+ import torch
81
+
82
+
83
+
84
+ class SentTransf_train():
85
+ def __init__(self, modelsToTrain = [
86
+ {"checkPoint": "distiluse-base-multilingual-cased-v1", "fromScratch": False},
87
+ {"checkPoint": "paraphrase-multilingual-MiniLM-L12-v2", "fromScratch": False},
88
+ {"checkPoint": "paraphrase-multilingual-mpnet-base-v2", "fromScratch": False},
89
+ {"checkPoint": "all-distilroberta-v1", "fromScratch": False},
90
+ {"checkPoint": "bert-base-multilingual-uncased", "fromScratch": True},
91
+ {"checkPoint": "dccuchile/bert-base-spanish-wwm-uncased", "fromScratch": True}
92
+ ], epochsToTest = [1,5,10,30,50,100], saving_path = create_file_path('', doctype=4)):
93
+
94
+ #modelsToTrain = [{"checkPoint": "distiluse-base-multilingual-cased-v1", "fromScratch": False}]
95
+ #epochsToTest = [1]
96
+ self.saving_path = saving_path
97
+ self.data_train = self.__getDatasetPartition(path_created_dataset, "train")
98
+ self.data_test = self.__getDatasetPartition(path_created_dataset, "test")
99
+ self.data_valid = self.__getDatasetPartition(path_created_dataset, "valid")
100
+ #epochsToTest = [1,5,10,30,50,100]
101
+ #Get evaluator
102
+ evaluator = self.__CreateModelEvaluationData()
103
+
104
+ #Train the models
105
+ for model in modelsToTrain:
106
+ for epochs in epochsToTest:
107
+ self.__TrainModel(model["checkPoint"], evaluator, epochs, model["fromScratch"])
108
+
109
+ def __getDatasetPartition(self, fileName, split):
110
+ subject1_fileDataset = load_dataset("json", data_files=fileName, split="train")
111
+ samples = []
112
+
113
+ for i in range (0,len(subject1_fileDataset[split][0])): #len(subject1)
114
+ mark = subject1_fileDataset[split][0][i]['mark']
115
+ responseStudent = subject1_fileDataset[split][0][i]['responseStudent']
116
+ responseTeacher = subject1_fileDataset[split][0][i]['responseTeacher']
117
+
118
+ ie = InputExample(texts=[responseTeacher, responseStudent], label=mark)
119
+ samples.append(ie)
120
+
121
+ return samples
122
+ def __CreateModelEvaluationData(self):
123
+ sentences1 = []
124
+ sentences2 = []
125
+ scores = []
126
+
127
+ for i in range (0,len(self.data_valid)):
128
+ sentences1.append(self.data_valid[i].texts[0])
129
+ sentences2.append(self.data_valid[i].texts[1])
130
+ scores.append(self.data_valid[i].label)
131
+
132
+ evaluator = evaluation.EmbeddingSimilarityEvaluator(sentences1, sentences2, scores)
133
+ return evaluator
134
+
135
+ def __TrainModel(self, checkpoint, evaluator, epochs, fromScratch):
136
+ batch_size = int(len(self.data_train) * 0.1)
137
+ #Create the model from checkpoint
138
+ if (not fromScratch):
139
+ model = SentenceTransformer(checkpoint)
140
+ else:
141
+ word_embedding_model = models.Transformer(checkpoint, max_seq_length=256)
142
+ pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension())
143
+ dense_model = models.Dense(in_features=pooling_model.get_sentence_embedding_dimension(), out_features=256, activation_function=nn.Tanh())
144
+ model = SentenceTransformer(modules=[word_embedding_model, pooling_model, dense_model])
145
+
146
+ train_dataloader = DataLoader(self.data_train, shuffle=True, batch_size=batch_size)
147
+ train_loss = losses.CosineSimilarityLoss(model)
148
+
149
+ #Fit the model
150
+ local_model_path = self.saving_path + 'Model_' + checkpoint + '/' + str(epochs) + '_Epochs'
151
+ warmup_steps = math.ceil(len(train_dataloader) * epochs * 0.1) #10% of train data for warm-up
152
+ evaluation_steps = int(len(train_dataloader)*0.1)
153
+ print(len(train_dataloader),warmup_steps,evaluation_steps)
154
+ model.fit(train_objectives=[(train_dataloader, train_loss)]
155
+ , epochs=epochs
156
+ , warmup_steps=warmup_steps
157
+ , evaluator=evaluator
158
+ , evaluation_steps=evaluation_steps
159
+ ,output_path=local_model_path
160
+ ,save_best_model=True)
161
+
162
+ try:
163
+ os.mkdir(self.saving_path + "models")
164
+ except:
165
+ pass
166
+
167
+ model.save(self.saving_path + "models/" +checkpoint+ str("-Epochs-") + str(epochs))
168
+
169
+
170
+ import json
171
+ import math
172
+ import pandas as pd
173
+ from datasets import load_dataset,Dataset,DatasetDict
174
+ from sklearn.model_selection import train_test_split
175
+ from sklearn.metrics import mean_absolute_error, mean_squared_error, mean_squared_log_error, mean_absolute_percentage_error, r2_score, roc_curve
176
+ from sentence_transformers import SentenceTransformer, InputExample, losses, util, evaluation
177
+ from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator
178
+ from torch.utils.data import DataLoader
179
+
180
+
181
+ class SentTransf_test():
182
+ def __init__(self, modelsToTest = ['distiluse-base-multilingual-cased-v1'
183
+ ,'paraphrase-multilingual-MiniLM-L12-v2'
184
+ ,'paraphrase-multilingual-mpnet-base-v2'
185
+ ,'all-distilroberta-v1'
186
+ ,'bert-base-multilingual-uncased'
187
+ ,'dccuchile_bert-base-spanish-wwm-uncased'
188
+ ], epochsToTest = [1,5,10,30,50,100], save_path = create_file_path('', doctype=4)[:-1]):
189
+
190
+ self.modelsToTest = modelsToTest
191
+ self.epochsToTest = epochsToTest
192
+ self.save_path = save_path
193
+ self.data_test = self.__getDatasetPartition(path_created_dataset, "test")
194
+ #modelsToTest = ['distiluse-base-multilingual-cased-v1']
195
+ #epochsToTest = [1]
196
+
197
+ def similarity(self,model_path, text1, text2):
198
+ #local_model_path = self.save_path + '/' + checkpoint + '/' + str(epochs) + '_Epochs'
199
+
200
+ model = SentenceTransformer(model_path)
201
+ #Compute embedding for both lists
202
+ embeddings1 = model.encode(text1, convert_to_tensor=True)
203
+ embeddings2 = model.encode(text2, convert_to_tensor=True)
204
+
205
+ #Compute cosine-similarits
206
+ cosine_score = util.cos_sim(embeddings1, embeddings2)
207
+ return cosine_score
208
+
209
+
210
+ def test_model(self):
211
+ try:
212
+ os.mkdir(self.save_path + '/tests')
213
+ except:
214
+ pass
215
+
216
+ self.model_name = []
217
+ self.epochs = []
218
+ self.metricMAE = []
219
+ self.metricMSE = []
220
+ self.metricRMSE = []
221
+ self.metricRMSLE = []
222
+ self.metricMAPE = []
223
+ self.metricR2 = []
224
+ #Train & Test the model
225
+ cnt=0
226
+ for checkpoint in self.modelsToTest:
227
+
228
+ #checkpoint = 'Model_' + checkpoint.replace('/','_')
229
+ checkpoint = 'Model_' + checkpoint
230
+ df = pd.DataFrame(columns=['Sentence1', 'Sentence2', 'Hashed_id', 'Mark'])
231
+ dfMetrics = pd.DataFrame(columns=['Model','Epochs', 'MAE', 'MSE', 'RMSE', 'RMSLE', 'MAPE', 'R2'])
232
+
233
+ for epochs in self.epochsToTest:
234
+ self.__TestModel(checkpoint, self.data_test, epochs, df)
235
+ self.model_name.append(checkpoint)
236
+
237
+ #Save Score Results file
238
+ df.to_csv(self.save_path + '/tests/' + checkpoint +'_Scores_Results.csv', index=False, sep=';', encoding='utf-8')
239
+
240
+ #Save Metrics file
241
+ dfMetrics['Model'] = self.model_name
242
+ dfMetrics['Epochs'] = self.epochs
243
+ dfMetrics['MAE'] = self.metricMAE
244
+ dfMetrics['MSE'] = self.metricMSE
245
+ dfMetrics['RMSE'] = self.metricRMSE
246
+ dfMetrics['RMSLE'] = self.metricRMSLE
247
+ dfMetrics['MAPE'] = self.metricMAPE
248
+ dfMetrics['R2'] = self.metricR2
249
+ dfMetrics.to_csv(self.save_path + '/tests/All_Metrics_Results.csv', index=False, sep=';', encoding='utf-8')
250
+
251
+
252
+
253
+
254
+ def __getDatasetPartition(self, fileName, split):
255
+ subject1_fileDataset = load_dataset("json", data_files=fileName, split="train")
256
+ samples = []
257
+
258
+ for i in range (0,len(subject1_fileDataset[split][0])): #len(subject1)
259
+ hashed_id = subject1_fileDataset[split][0][i]['hashed_id']
260
+ mark = subject1_fileDataset[split][0][i]['mark']
261
+ responseStudent = subject1_fileDataset[split][0][i]['responseStudent']
262
+ responseTeacher = subject1_fileDataset[split][0][i]['responseTeacher']
263
+
264
+ ie = InputExample(guid= hashed_id, texts=[responseTeacher, responseStudent], label=mark)
265
+ samples.append(ie)
266
+
267
+ return samples
268
+
269
+ def __TestModel(self, checkpoint, data, epochs, df):
270
+ #Load model
271
+ #local_model_path = 'jfarray' + '/' + checkpoint + '_' + str(epochs) + '_Epochs'
272
+
273
+
274
+ local_model_path = self.save_path + '/' + checkpoint + '/' + str(epochs) + '_Epochs'
275
+
276
+ model = SentenceTransformer(local_model_path)
277
+
278
+ hashed_ids = []
279
+ sentences1 = []
280
+ sentences2 = []
281
+ marks = []
282
+ scores = []
283
+ marksFloat = []
284
+ scoresFloat = []
285
+
286
+ for i in range (0,len(data)): #len(data)
287
+ sentences1.append(data[i].texts[0])
288
+ sentences2.append(data[i].texts[1])
289
+
290
+ #Compute embedding for both lists
291
+ embeddings1 = model.encode(sentences1, convert_to_tensor=True)
292
+ embeddings2 = model.encode(sentences2, convert_to_tensor=True)
293
+
294
+ #Compute cosine-similarits
295
+ cosine_scores = util.cos_sim(embeddings1, embeddings2)
296
+
297
+ #Output the pairs with their score
298
+ for i in range(len(sentences1)):
299
+ hashed_ids.append(data[i].guid)
300
+ marks.append(str(data[i].label).replace('.',','))
301
+ marksFloat.append(data[i].label)
302
+ scores.append(str(round(cosine_scores[i][i].item(),3)).replace('.',','))
303
+ scoresFloat.append(round(cosine_scores[i][i].item(),3))
304
+
305
+ #Save scores in the file
306
+ df['Hashed_id'] = hashed_ids
307
+ df['Mark'] = marks
308
+ df['Score_' + str(epochs)] = scores
309
+ df['Sentence1'] = sentences1
310
+ df['Sentence2'] = sentences2
311
+
312
+ self.epochs.append(str(epochs))
313
+ #Calculate metrics 'MAE', 'MSE', 'RMSE', 'RMSLE', 'MAPE', 'R2'
314
+ self.metricMAE.append(str(mean_absolute_error(marksFloat, scoresFloat)).replace('.',','))
315
+ self.metricMSE.append(str(mean_squared_error(marksFloat, scoresFloat, squared = True)).replace('.',','))
316
+ self.metricRMSE.append(str(mean_squared_error(marksFloat, scoresFloat, squared = False)).replace('.',','))
317
+ try:
318
+ self.metricRMSLE.append(str(mean_squared_log_error(marksFloat, scoresFloat)).replace('.',','))
319
+ except:
320
+ self.metricRMSLE.append('-')
321
+
322
+ self.metricMAPE.append(str(mean_absolute_percentage_error(marksFloat, scoresFloat)).replace('.',','))
323
+ self.metricR2.append(str(r2_score(marksFloat, scoresFloat)).replace('.',','))
324
+
325
+ #Evaluate Model this test data
326
+ batch_size = 15 #Initializes the batch size with the same value as the training
327
+ test_evaluator = EmbeddingSimilarityEvaluator.from_input_examples(self.data_test, batch_size=batch_size, name= checkpoint)
328
+
329
+ test_evaluator(model, output_path= self.save_path + '/tests/')
330
+
codeScripts/OrtographicExceptions.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ["excel", "jupyter", "notebook", "html", "pdf", "zip", "url", "google", "colab", "https", "com", "drive", "viterbi", "gmail", "cc", "morfosintáctico", "ipynb", "txt" "python", "microsoft", "xlsx"]
codeScripts/methodologyPlentas.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from codeScripts.rubrics import *
3
+
4
+ class PlentasMethodology():
5
+ """
6
+ The methodology developed in Plentas consists in iteratively compute the similarity between two sentences: the baseline response and one variable group of sentences from the student's response. The goal of it is to identify in the text the subresponses to each subquestion so the answer-question similarity is better obtained and thus the overall calculated similarity fits better to the expected one.
7
+
8
+ Inputs:
9
+ -settings: The settings from the config json and the api.
10
+ """
11
+ def __init__(self, settings):
12
+ self.settings = settings
13
+ self.maxSimilarity = -99999
14
+ self.SemanticLevel = Semantica2(self.settings)
15
+
16
+
17
+ def getSimilarity(self, sentences, similarityMethod):
18
+ """
19
+ This function calculates the similarity between two responses using the Plentas methodology
20
+
21
+ Inputs:
22
+ sentences: pre-processed sentences of the student's response
23
+ similarityMethod: choose between spacy or bert
24
+ Outputs:
25
+ similarity: an array of the generated similarity for each subquestion
26
+ """
27
+
28
+ #Initializing the similarity array so if the student's response is blank default content is output
29
+ similarity = np.zeros(len(self.settings.indice_minipreguntas))
30
+
31
+ #obtaining the similarity for each subquestion
32
+ for minirespuesta, minipregunta in zip(self.settings.minirespuestas, self.settings.indice_minipreguntas):
33
+ self.SemanticLevel.output.initInforms(self.settings.studentID, minipregunta, similarityMethod)
34
+
35
+ #if the response is not blank ...
36
+ if len(sentences) >= 1 and sentences[0] != '':
37
+
38
+ self.maxSimilarity = -99999
39
+
40
+ #varying the group of sentences
41
+ for agrupation in list(range(self.settings.minAgrupation,self.settings.maxAgrupation)):
42
+
43
+ #varying the size of the group of sentences
44
+ for s in range(len(sentences)):
45
+ try:
46
+ #extracting the sentences
47
+ r_alumno, r_label = self.__Line2LineAnalysis__(sentences, s, agrupation)
48
+ #computing its similarity
49
+ #similar = self.__computeSimilarity__(r_alumno, minirespuesta, similarityMethod)
50
+ similar = self.SemanticLevel.computeSimilarity(r_alumno, minirespuesta, similarityMethod)
51
+
52
+ self.SemanticLevel.output.updateInformsBucle(self.settings.studentID, minipregunta, r_alumno, r_label, agrupation, similar, similarityMethod, 1 if similar > self.maxSimilarity else 0)
53
+
54
+ #storing the highest
55
+ if similar > self.maxSimilarity:
56
+ self.maxSimilarity = similar
57
+
58
+ except:
59
+ break
60
+
61
+ #stacking the similarity of each subquestion
62
+ similarity[int(minipregunta[12:])] = self.maxSimilarity
63
+
64
+ return similarity
65
+
66
+ def __Line2LineAnalysis__(self, sentences, size, start):
67
+ """
68
+ This function extracts the required group of sentences from a response.
69
+ Inputs:
70
+ -sentences: the array of sentences from the student's response
71
+ -size: the max number of sentences to extract.
72
+ -start: the array position from where to start extracting
73
+ Outputs:
74
+ respuesta_alumno: the extracted sentences
75
+ r_name: the label of those sentences (their position in the response and, thus, in the input array)
76
+ """
77
+ new_respuesta = ""
78
+ breaking_variable = sentences[size+start-1]
79
+ for line in sentences[size:size+start]:
80
+ new_respuesta= new_respuesta + line + '. '
81
+
82
+ respuesta_alumno = new_respuesta.lower()
83
+
84
+ if start == 1:
85
+ r_name = "Line " + str(size+1)
86
+
87
+ else:
88
+ r_name = "Lines " + str(size+1) + " - " + str(size+start)
89
+
90
+ return respuesta_alumno, r_name
91
+
92
+ def EvaluationMethod(self, studentID, response, similarity_array, similarity_type = "spacy"):
93
+ notaSemantica = 0
94
+ esSuperior = 0
95
+ esIntermedio = 0
96
+ for umbralL, umbralH in zip(self.SemanticLevel.output.min_umbral, self.SemanticLevel.output.max_umbral):
97
+ for minipregunta, similarity in zip(self.settings.indice_minipreguntas, similarity_array):
98
+ print(minipregunta, similarity)
99
+ if similarity >= umbralL:
100
+ if similarity <= umbralH:
101
+ if not esSuperior:
102
+ esIntermedio = 1
103
+ else:
104
+ esIntermedio = 0
105
+ esSuperior = 1
106
+
107
+
108
+ if esSuperior:
109
+ notaSemantica +=1
110
+ elif esIntermedio:
111
+ notaSemantica += 0.5
112
+
113
+ esSuperior = 0
114
+ esIntermedio = 0
115
+
116
+ notaSemantica = notaSemantica/len(self.settings.indice_minipreguntas)
117
+ self.SemanticLevel.output.updateInforms(studentID, umbralL, umbralH, notaSemantica, similarity_type, response)
118
+
119
+ #Descomment if analyzing more than one threshold option
120
+ #if umbralL == 0.3 and umbralH == 0.7:
121
+ #notaGuardar = notaSemantica
122
+
123
+
124
+ #notaSemantica = 0
125
+ #return notaGuardar
126
+ return notaSemantica
127
+
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+
136
+
codeScripts/rubrics.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spacy
2
+ #spacy model imported in the init of class Semantica2
3
+ #nlp = spacy.load('es_core_news_sm')
4
+ #nlp = spacy.load('es_core_news_md')
5
+ #nlp = spacy.load('es_core_news_lg')
6
+
7
+ from codeScripts.Dependencies.SentenceTransformer2 import *
8
+ from codeScripts.rubricsOut import SemanticOutput, OrtographicOutput, SintacticOutput
9
+ from codeScripts.utils import spelling_corrector, mu_index, FHuertas_index, check_senteces_words, save_json,create_file_path
10
+
11
+ #Done
12
+
13
+ class Semantica2():
14
+ """
15
+ This class allows to compute the semantic level of the rubric
16
+ """
17
+ #funciones para la extraccion/generacion de kw --- no integradas
18
+ def __init__(self, settings):
19
+
20
+ self.output = SemanticOutput(settings)
21
+ self.spacy_model = spacy.load(settings.spacy_package)
22
+ self.settings = settings
23
+
24
+ def computeSimilarity(self,sentences1,sentences2,similarityMethod):
25
+ """
26
+ This function applies a defined method to obtain the similarity between two sentences
27
+ Inputs:
28
+ -sentences1: First set of sentences to compare
29
+ -sentences2: Second set of sentences to compare
30
+ -similarityMethod: The inherited similarity method selected in getSimilarity
31
+ Outputs:
32
+ -similar: The similarity score
33
+ """
34
+ if similarityMethod.lower() == "spacy":
35
+ r1 = self.spacy_model(sentences1)
36
+ r2 = self.spacy_model(sentences2)
37
+ similar = round(r1.similarity(r2), 3)
38
+ else:
39
+ similar = self.settings.BertModels_glbl.similarity(self.settings.model_path, sentences1, sentences2)[0][0].item()
40
+
41
+ return similar
42
+
43
+ class Ortografia2 ():
44
+ """
45
+ This class allows to compute the ortographic level of the rubric
46
+ """
47
+ def __init__(self, settings):
48
+ self.output = OrtographicOutput()
49
+ self.settings = settings
50
+
51
+ def Evaluation(self, respuesta_alumno):
52
+ """
53
+ This method is used for calculating the ortographic mark.
54
+ Inputs:
55
+ respuesta_alumno: The student's answer.
56
+ Outputs:
57
+ nota_Orto: The generated mark.
58
+ """
59
+ #some exceptions that should not be considered as mistakes
60
+ exceptionsTxt = open('codeScripts/OrtographicExceptions.txt', encoding="utf-8")
61
+ exceptions = exceptionsTxt.readline()
62
+ exceptionsTxt.close()
63
+
64
+ nota_Orto = 0
65
+ if respuesta_alumno == "":
66
+ self.output.number_mistakes.append(0)
67
+ self.settings.faltasOrto = 0
68
+ else:
69
+ #obtaining the number of mistakes using hunspell
70
+ errores, mistakes = spelling_corrector(respuesta_alumno, self.settings.hunspell_aff, self.settings.hunspell_dic)
71
+ for mistake in mistakes:
72
+ if mistake not in exceptions:
73
+ if mistake not in self.output.mistakes:
74
+ self.output.mistakes.append(mistake)
75
+ else:
76
+ errores-=1
77
+
78
+ #storing the mistakes for debugging purposes
79
+ self.output.number_mistakes.append(errores)
80
+
81
+ #counting the number of errors when surpassing a fixed number of them (config parameter)
82
+
83
+ if errores <= self.settings.FaltasSalvaguarda:
84
+ nota_Orto = self.settings.PesoOrtografia
85
+ self.settings.faltasOrto = 0
86
+ else:
87
+ self.settings.faltasOrto = errores
88
+
89
+ #computing the mark applying rubric weights
90
+ try:
91
+ rel = self.settings.PesoOrtografia/self.settings.NMaxErrores
92
+ nota_Orto = self.settings.PesoOrtografia - (errores - self.settings.FaltasSalvaguarda) * rel
93
+ except:
94
+ nota_Orto = 0
95
+
96
+ if nota_Orto < 0:
97
+ nota_Orto = 0
98
+
99
+
100
+ return nota_Orto
101
+
102
+ def SaveMistakes(self):
103
+ """
104
+ This method is used for saving some information about the students' found mistakes
105
+ """
106
+ save_json(create_file_path('RecopiledMistakes.json', doctype= 2),self.output.mistakes, False)
107
+ save_json(create_file_path('NumberMistakes.json', doctype= 2),self.output.number_mistakes, False)
108
+
109
+ class Sintaxis2():
110
+ """
111
+ This class allows to compute the syntactic level of the rubric
112
+ """
113
+ def __init__(self, settings):
114
+ self.output = SintacticOutput()
115
+ self.settings = settings
116
+
117
+ def Evaluation(self, respuesta_alumno):
118
+ """
119
+ This method is used for calculating the syntactic mark.
120
+ Inputs:
121
+ respuesta_alumno: The student's answer.
122
+ Outputs:
123
+ nota_Sintaxis: The generated mark.
124
+ """
125
+ if respuesta_alumno == '':
126
+ self.output.leg_FH.append(0)
127
+ self.output.leg_mu.append(0)
128
+ self.settings.palabrasPorFrase = 0
129
+ return 0
130
+ else:
131
+ #obtaining FH and mu indexes
132
+ sentencesLenght, wordsLenght, syll, letter_per_word = check_senteces_words(respuesta_alumno)
133
+ FH, _ = FHuertas_index(sentencesLenght, wordsLenght, syll)
134
+ mu, _ = mu_index(sentencesLenght, wordsLenght, letter_per_word)
135
+
136
+ #storing the indexes
137
+ self.output.leg_FH.append(FH)
138
+ self.output.leg_mu.append(mu)
139
+
140
+ #calculating the grade
141
+ nota_Sintaxis = (self.settings.PesoSintaxis/2) * FH/80 + (self.settings.PesoSintaxis/2) * mu/60
142
+ if nota_Sintaxis > self.settings.PesoSintaxis:
143
+ nota_Sintaxis = self.settings.PesoSintaxis
144
+
145
+ self.settings.palabrasPorFrase = round(wordsLenght/sentencesLenght,2)
146
+ return nota_Sintaxis
147
+
148
+ def saveResults(self):
149
+ """
150
+ This method is used for saving some information about the students' readability
151
+ """
152
+ self.output.saveLegibilityResults()
153
+
154
+ def GenerateFeedback(settings, respuesta, OrtoMark, SintMark, SemanMarkSpacy, SemanMarkBert):
155
+ """
156
+ This function is used to analyze grades and generate appropriate feedback based on sentences.
157
+ Inputs:
158
+ -settings: The settings of the experiment.
159
+ -respuesta: The student's response.
160
+ -OrtoMark: The calification of the ortographic level.
161
+ -SintMark: The calification of the syntactic level.
162
+ -SemanMarkSpacy: The calification of the semantic level - spacy.
163
+ -SemanMarkBert: The calification of the semantic level - bert.
164
+ Outputs:
165
+ -feedback: The generated feedback.
166
+ """
167
+ feedback = ""
168
+ #if blank response
169
+ if respuesta == "":
170
+ feedback = feedback + "Respuesta en blanco"
171
+ else:
172
+ #pre-defined sentences in case: <5 - <10 - 10
173
+ if settings.Ortografia:
174
+ feedback = feedback + "\nNivel ortográfico: \n"
175
+ if OrtoMark < settings.PesoOrtografia/2:
176
+ feedback = feedback + "El estudiante cometió " + str(settings.faltasOrto) + " faltas de ortografía y no alcanzó el aprobado en este nivel de la rúbrica. El sistema reconoce un fallo si se ha producido un error en acento de puntuación o en una palabra no recogida en un diccionario español. \n"
177
+ elif OrtoMark < settings.PesoOrtografia:
178
+ feedback = feedback + "El estudiante cometió " + str(settings.faltasOrto) + " faltas de ortografía. El sistema reconoce un fallo si se ha producido un error en acento de puntuación o en una palabra no recogida en un diccionario español. \n"
179
+ else:
180
+ feedback = feedback + "El estudiante redactó su respuesta sin aparentes fallos de ortografía. \n"
181
+
182
+
183
+ if settings.Sintaxis:
184
+ feedback = feedback + "\nNivel sintáctico: \n"
185
+ if SintMark < settings.PesoSintaxis/2:
186
+ feedback = feedback + "La respuesta del estudiante incluye elementos que dificultan su correcta legibilidad y no alcanzó el aprobado en este nivel de la rúbrica. Hay " + str(settings.palabrasPorFrase) + " palabras por frase. El sistema tiene en consideración para la legibilidad el promedio de sílabas por palabra y la media de palabras por frase. Además, el uso de guiones o puntos es desaconsejable. \n"
187
+ elif SintMark < settings.PesoSintaxis:
188
+ feedback = feedback + "La respuesta del estudiante incluye elementos que dificultan su correcta legibilidad. Hay " + str(settings.palabrasPorFrase) + " palabras por frase. El sistema tiene en consideración para la legibilidad el promedio de sílabas por palabra y la media de palabras por frase. Además, el uso de guiones o puntos es desaconsejable. \n"
189
+ else:
190
+ feedback = feedback + "La respuesta del estudiante fue redactada con aparente buena legibilidad \n"
191
+
192
+ if settings.Semantica:
193
+ feedback = feedback + "\nNivel semántico, primer modelo: \n"
194
+ if SemanMarkSpacy < settings.PesoSemantics/2:
195
+ feedback = feedback + "El estudiante no responde correctamente a todas las minipreguntas y no alcanzó el aprobado en este nivel de la rúbrica. Debería profundizar más en " + settings.minipreguntasMalSpacy + ". El sistema evalúa una respuesta en base a la similitud que existe entre ésta y la respuesta de referencia de cada minipregunta. \n"
196
+ elif SemanMarkSpacy < settings.PesoSemantics:
197
+ feedback = feedback + "El estudiante no responde correctamente a todas las minipreguntas. Para mejorar podría profundizar más en " + settings.minipreguntasMalSpacy + ". El sistema evalúa una respuesta en base a la similitud que existe entre ésta y la respuesta de referencia de cada minipregunta. \n"
198
+ else:
199
+ feedback = feedback + "El estudiante responde a cada minipregunta de forma similar a la respuesta de referencia. \n"
200
+
201
+ feedback = feedback + "\nNivel semántico, segundo modelo: \n"
202
+ if SemanMarkBert < settings.PesoSemantics/2:
203
+ feedback = feedback + "El estudiante no responde correctamente a todas las minipreguntas y no alcanzó el aprobado en este nivel de la rúbrica. Debería profundizar más en " + settings.minipreguntasMalBert + ". El sistema evalúa una respuesta en base a la similitud que existe entre ésta y la respuesta de referencia de cada minipregunta.\n"
204
+ elif SemanMarkBert < settings.PesoSemantics:
205
+ feedback = feedback + "El estudiante no responde correctamente a todas las minipreguntas. Para mejorar podría profundizar más en " + settings.minipreguntasMalBert + ". El sistema evalúa una respuesta en base a la similitud que existe entre ésta y la respuesta de referencia de cada minipregunta. \n"
206
+ else:
207
+ feedback = feedback + "El estudiante responde a cada minipregunta de forma similar a la respuesta de referencia. \n"
208
+
209
+ feedback = feedback + "\n\nPor favor, si observa alguna diferencia significativa entre la calificación de ambos modelos, entre a valorar la nota del estudiante. "
210
+
211
+
212
+ return feedback
codeScripts/rubricsOut.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import seaborn as sns
2
+ import matplotlib.pyplot as plt
3
+ import pandas as pd
4
+ import copy
5
+
6
+ from codeScripts.Dependencies.SentenceTransformer2 import *
7
+ from codeScripts.utils import create_file_path, clean_words, save_json
8
+
9
+ #Done
10
+
11
+ class SemanticOutput():
12
+ """
13
+ Class to store the semantic processing and extract results
14
+ """
15
+ def __init__(self, settings):
16
+
17
+ self.nota_spacy = dict()
18
+ self.nota_spacy["spacy"] = dict()
19
+ self.nota_spacy["bert"] = dict()
20
+
21
+ self.nota_spacy_experimento = dict()
22
+ self.nota_spacy_experimento["spacy"] = dict()
23
+ self.nota_spacy_experimento["bert"] = dict()
24
+
25
+ self.identifyLineofResponse = dict()
26
+ self.identifyLineofResponse["spacy"] = dict()
27
+ self.identifyLineofResponse["bert"] = dict()
28
+
29
+ self.identifyLineofResponse_toexcel = []
30
+
31
+ self.notas_calculadas = dict()
32
+ self.notas_calculadas["spacy"] = dict()
33
+ self.notas_calculadas["bert"] = dict()
34
+
35
+ self.min_umbral = []
36
+ self.max_umbral = []
37
+ r= settings.UmbralesSimilitud.split(",")
38
+ for i in r:
39
+ c_w= clean_words(i)
40
+ self.min_umbral.append(float(c_w[0]+'.'+c_w[1]))
41
+ self.max_umbral.append(float(c_w[2]+'.'+c_w[3]))
42
+ self.notas_calculadas["spacy"]['Umbral ' + c_w[0]+'.'+c_w[1] + ' - ' + c_w[2]+'.'+c_w[3]] = []
43
+ self.notas_calculadas["bert"]['Umbral ' + c_w[0]+'.'+c_w[1] + ' - ' + c_w[2]+'.'+c_w[3]] = []
44
+
45
+
46
+ #variables taken from the settings
47
+ self.answersDF_json2 = dict()
48
+ self.answersDF_json2["spacy"] = dict()
49
+ self.answersDF_json2["bert"] = dict()
50
+
51
+ self.indiceMinipreguntas = settings.indice_minipreguntas
52
+ print("AAAAAAAAAAAA")
53
+ print(self.indiceMinipreguntas)
54
+ self.LofRespThreshold = settings.LofRespThreshold
55
+
56
+ self.indx = 1
57
+
58
+ def __createDict__(self, nota_spacy:dict(), studentID, minipregunta, similarity_type, type = 0):
59
+
60
+ if studentID not in nota_spacy[similarity_type].keys():
61
+ nota_spacy[similarity_type][studentID] = dict()
62
+
63
+ if type == 0:
64
+ nota_spacy[similarity_type][studentID][minipregunta]= []
65
+ else:
66
+ nota_spacy[similarity_type][studentID][minipregunta]= dict()
67
+ return nota_spacy
68
+
69
+ def __plotHistogram__(self, save_file, x):
70
+ """
71
+ Generates an histogram of the given data.
72
+ Inputs:
73
+ save_file: The path where the histogram is to be generated.
74
+ x: The data to be represented.
75
+ """
76
+ ax= sns.histplot(
77
+ data = x,
78
+ stat = "count",
79
+ kde = True,
80
+ color = "black"
81
+ )
82
+ ax.set(xlabel='Deviation', ylabel='Count')
83
+
84
+ figure = ax.get_figure()
85
+ figure.savefig(create_file_path(save_file,3))
86
+ del figure
87
+ ax.cla()
88
+
89
+ def initInforms(self, studentID, minipregunta, similarity_type):
90
+ """
91
+ This function is for initializing the variables where data is to be stored.
92
+ Inputs:
93
+ studentID: The id of the student
94
+ minipregunta: The minipregunta that is being studied
95
+ """
96
+ #identificar donde está la respuesta por minipreguta
97
+ self.identifyLineofResponse = self.__createDict__(self.identifyLineofResponse, studentID, minipregunta, similarity_type, 1)
98
+
99
+ #almacenar notas del evaluation process
100
+ #self.nota_spacy_experimento = self.__createDict__(self.nota_spacy_experimento, studentID, similarity_type, 1)
101
+
102
+ self.nota_spacy_experimento[similarity_type][studentID] = dict()
103
+
104
+ #Almacenar similitudes por minipregunta
105
+ self.nota_spacy = self.__createDict__(self.nota_spacy, studentID, minipregunta, similarity_type)
106
+
107
+ #separar y almacenar una a una las lineas de la respuesta
108
+ self.answersDF_json2[similarity_type][studentID] = dict()
109
+ self.answersDF_json2[similarity_type][studentID]["respuesta"] = dict()
110
+
111
+ def updateInformsBucle(self, studentID, minipregunta, response, response_label, numberOfSentences, similarity, similarity_type, isMaxSimil):
112
+ """
113
+ This function is the previous needed step before using updateInforms. Stores the important iterative-generated information
114
+ Inputs:
115
+ -studentID: The id of the student
116
+ -minipregunta: The minipregunta that is being studied
117
+ -response: The student's response
118
+ -response_label: The generated label that indicates the sentence number of the extracted response in the text.
119
+ -numberOfSentences: The number of splitted sentences.
120
+ -similarity: The obtained similarity score.
121
+ -isMaxSimil: If the similarity score is the highest obtained at the moment or not.
122
+ """
123
+ #Storing the similarity score obtained for only one sentence
124
+ if numberOfSentences == 1:
125
+ self.identifyLineofResponse[similarity_type][studentID][minipregunta][str(self.indx)] = dict()
126
+ self.identifyLineofResponse[similarity_type][studentID][minipregunta][str(self.indx)]["Similitud"] = similarity
127
+ self.identifyLineofResponse[similarity_type][studentID][minipregunta][str(self.indx)]["Frase"] = response
128
+ self.identifyLineofResponse[similarity_type][studentID][minipregunta][str(self.indx)]["Lineas"] = response_label
129
+
130
+ self.answersDF_json2[similarity_type][studentID]["respuesta"][self.indx] = response
131
+ self.indx+=1
132
+ else:
133
+ self.indx = 1
134
+
135
+ #storing the maximum similarity for each set of sentences length
136
+ if isMaxSimil:
137
+ self.nota_spacy_experimento[similarity_type][studentID][str(numberOfSentences)] = dict()
138
+ self.nota_spacy_experimento[similarity_type][studentID][str(numberOfSentences)]["MaxSimilitud"] = similarity
139
+ self.nota_spacy_experimento[similarity_type][studentID][str(numberOfSentences)]["Frase"] = response
140
+ self.nota_spacy_experimento[similarity_type][studentID][str(numberOfSentences)]["Lineas"] = response_label
141
+
142
+ #storing the similarity in every case
143
+ self.nota_spacy[similarity_type][studentID][minipregunta].append([response, None, None] if response == "" else [response, similarity, response_label])
144
+
145
+ def updateInforms(self, studentID, umbralL, umbralH, calculatedMark, similarity_type, response = ""):
146
+ """
147
+ This function is to store the obtained results from the processing of one response.
148
+ Inputs:
149
+ -studentID: The id of the student
150
+ -umbralL: The fixed low threshold (config json)
151
+ -umbralH: The fixed high threshold (config json)
152
+ -calculatedMark: The calculated mark.
153
+ -response: The student's response
154
+ """
155
+ print("ZZZZZ")
156
+ print(similarity_type)
157
+ #storing calculated marks
158
+ self.notas_calculadas[similarity_type]['Umbral ' + str(umbralL) + ' - ' + str(umbralH)].append(0 if response == "" else calculatedMark/len(self.indiceMinipreguntas))
159
+
160
+ #storing where the model thought the answer was
161
+ for minipregunta in self.indiceMinipreguntas:
162
+ print("EEEEE")
163
+ print(self.identifyLineofResponse)
164
+ aux = copy.deepcopy(self.identifyLineofResponse)
165
+ for indx in aux[similarity_type][studentID][minipregunta].keys():
166
+ if abs(self.identifyLineofResponse[similarity_type][studentID][minipregunta][indx]["Similitud"] - self.nota_spacy_experimento[similarity_type][studentID]["1"]["MaxSimilitud"]) > 0.075:
167
+ del self.identifyLineofResponse[similarity_type][studentID][minipregunta][indx]
168
+
169
+ #Getting the number of the guess
170
+ if response == "":
171
+ self.identifyLineofResponse_toexcel.append([minipregunta, ""])
172
+ else:
173
+ max_n = -999999
174
+ indx_queue = 0
175
+ queue = []
176
+ highlightedrows = ""
177
+ highlightedmarks = ""
178
+
179
+ for iter in self.identifyLineofResponse[similarity_type][studentID][minipregunta].keys():
180
+ for indx in self.identifyLineofResponse[similarity_type][studentID][minipregunta].keys():
181
+ if self.identifyLineofResponse[similarity_type][studentID][minipregunta][indx]["Similitud"] > max_n and not indx in queue and self.identifyLineofResponse[similarity_type][studentID][minipregunta][indx]["Similitud"]>self.LofRespThreshold:
182
+ max_n = self.identifyLineofResponse[similarity_type][studentID][minipregunta][indx]["Similitud"]
183
+ indx_queue = indx
184
+ queue.append(indx_queue)
185
+ highlightedrows = highlightedrows + str(indx_queue) + " "
186
+ highlightedmarks = highlightedmarks + str(max_n) + " "
187
+ max_n = -999999
188
+ indx_queue = 0
189
+
190
+ self.identifyLineofResponse_toexcel.append([minipregunta, highlightedrows, highlightedmarks])
191
+ highlightedrows = ""
192
+ highlightedmarks = ""
193
+ queue = []
194
+
195
+ def saveSimilarityResults(self, settings, similarity_type):
196
+ """
197
+ Saves the recopiled data in the corresponding format and path differentiating the types of semantic calculation.
198
+ Inputs:
199
+ -settings: system settings.
200
+ -similarity_type: "spacy" if similarity is being calculated from Spacy (if it is not, bert is selected)
201
+ """
202
+ savePrefix = "Spacy - " if similarity_type == "spacy" else str(settings.modelr) + str(settings.epochr) + " - "
203
+
204
+ #previous name - "AnalisisSemantico.json"
205
+ save_json(create_file_path(savePrefix + "SimilitudPorConjunto.json",2), self.nota_spacy[similarity_type])
206
+ save_json(create_file_path(savePrefix + "MaxSimilitudPorConjunto.json",2), self.nota_spacy_experimento[similarity_type])
207
+ save_json(create_file_path(savePrefix + "LineaRespuesta.json",2), self.identifyLineofResponse[similarity_type])
208
+ save_json(create_file_path(savePrefix + "RespuestaSeparadaPorFrases.json",2), self.answersDF_json2[similarity_type])
209
+
210
+
211
+ Notasdf = pd.DataFrame()
212
+ for intervaloUmbral in self.notas_calculadas[similarity_type]:
213
+ Notasdf[intervaloUmbral] = self.notas_calculadas[similarity_type][intervaloUmbral]
214
+
215
+ Notasdf.to_excel(create_file_path(savePrefix +'NotasCalculadas.xlsx',2), sheet_name='notas')
216
+
217
+ #self.__plotHistogram__(savePrefix + "HistogramaNotasGeneradas.png", self.notas_calculadas[similarity_type])
218
+
219
+ class SintacticOutput():
220
+ """
221
+ Class to store the sintactic processing
222
+ """
223
+ def __init__(self):
224
+ self.leg_FH =[]
225
+ self.leg_mu = []
226
+
227
+ def saveLegibilityResults(self):
228
+ """
229
+ Saves the recopiled data in the corresponding format.
230
+ """
231
+ save_json(create_file_path("FH-Readability.json",2), self.leg_FH, False)
232
+ save_json(create_file_path("mu-Readability.json",2), self.leg_mu, False)
233
+
234
+ x = []
235
+ for i in range(len(self.leg_FH)):
236
+ x.append(i)
237
+ plt.figure(figsize=(15,7))
238
+ plt.plot(x, self.leg_FH, label = "FH", color = (0.1,0.1,0.1))
239
+ plt.plot(x, self.leg_mu, '--', label = "mu", color = (0.5,0.5,0.5))
240
+ plt.xlabel("Student")
241
+ plt.ylabel("Legibility (0-100)")
242
+ plt.legend(loc=1)
243
+ plt.title("FH vs mu")
244
+ plt.xticks(rotation=-45)
245
+ plt.grid()
246
+ plt.savefig(create_file_path("Img_FHvsMu.png",3))
247
+ plt.cla()
248
+
249
+ class OrtographicOutput():
250
+ """
251
+ Class to store the ortographic processing
252
+ """
253
+ def __init__(self):
254
+ self.notaOrtografia = []
255
+ self.mistakes = []
256
+ self.number_mistakes = []
257
+
258
+ def saveOrtographicResults(self):
259
+ """
260
+ Saves the ortographic generated marks.
261
+ """
262
+ save_json(create_file_path("NotasOrtografia.json",2), self.notaOrtografia, False)
263
+
codeScripts/settings.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import json
3
+
4
+ from codeScripts.Dependencies.SentenceTransformer2 import *
5
+ from codeScripts.utils import load_json, create_file_path
6
+
7
+ class GetSettings():
8
+ """
9
+ This class stores the selected settings for the current experiment
10
+ """
11
+ def __init__(self, config_settings, studentsData):
12
+
13
+ #extracting the settings from the configuration document
14
+ self.__getConfigSettings(config_settings)
15
+
16
+ #getting the responses to study
17
+ self.__getDatatoStudy(studentsData)
18
+
19
+ def __getDatatoStudy(self, data):
20
+ if data[0] == None:
21
+ #extracting the info from the path in the config json
22
+ self.__getData(self.json_file_in)
23
+ else:
24
+ #extracting the info from the selected file in the api
25
+ self.__getApiData(data)
26
+
27
+ def setApiSettings(self, api_settings):
28
+ """
29
+ This function is to overwrite the parameters with the selected values from the api
30
+ Inputs:
31
+ -api_settings: dictionary with the stored parameters from the api
32
+ """
33
+ #transforming string dict into dict
34
+ api_settings = json.loads(api_settings)
35
+
36
+ self.PesoOrtografia = api_settings["ortographyPercentage"]
37
+ self.PesoSintaxis = api_settings["syntaxPercentage"]
38
+ self.PesoSemantics = api_settings["semanticPercentage"]
39
+ self.rango_ID = api_settings["students"]
40
+
41
+ def __getConfigSettings(self, df):
42
+ """
43
+ This method is used to import the settings from the config json
44
+ Inputs:
45
+ -df: The dataframe where the config json data is loaded
46
+ """
47
+
48
+ #+++ General settings +++
49
+
50
+ #path where the dataset is stored
51
+ self.json_file_in = df["ruta_fichero_entrada"]
52
+ #path where output is to be stored
53
+ self.json_file_out = df["ruta_fichero_salida"]
54
+
55
+ #path to access hunspell components
56
+ self.hunspell_aff = df["ruta_hunspell"]["aff"]
57
+ self.hunspell_dic = df["ruta_hunspell"]["dic"]
58
+
59
+ #range of students to study ---- Will be overwritten from api
60
+ if df["Parametros_Analisis"]["estudiantes"]["Todos"]:
61
+ self.rango_ID = "All"
62
+ else:
63
+ self.rango_ID = df["Parametros_Analisis"]["estudiantes"]["ID_rango"]
64
+
65
+
66
+ self.minAgrupation = int(df["Parametros_Analisis"]["Semantica"]["frases"]["Agrupacion"]["Minimo"])
67
+ self.maxAgrupation = int(df["Parametros_Analisis"]["Semantica"]["frases"]["Agrupacion"]["Maximo"] + 1)
68
+
69
+
70
+ #+++ Ortography +++
71
+
72
+ #If the ortographic level is activated
73
+ self.Ortografia = df["Parametros_Analisis"]["Ortografia"]["Activado"]
74
+ #Max number of permitted errors
75
+ self.NMaxErrores = df["Parametros_Rubrica"]["Ortografia"]["NMaxErrores"]
76
+ #Max number of permitted errors before beginning to substract
77
+ self.FaltasSalvaguarda= df["Parametros_Rubrica"]["Ortografia"]["FaltasSalvaguarda"]
78
+ #Level weight (rubrics)
79
+ self.PesoOrtografia = df["Parametros_Rubrica"]["Ortografia"]["Peso"]
80
+
81
+ #+++ Syntax +++
82
+ #if the syntactic level is activated
83
+ self.Sintaxis = df["Parametros_Analisis"]["Sintaxis"]["Activado"]
84
+ #max number of sentences and words permitted
85
+ self.NMaxFrases = df["Parametros_Rubrica"]["Sintaxis"]["NMaxFrases"]
86
+ self.NMaxPalabras= df["Parametros_Rubrica"]["Sintaxis"]["NMaxPalabras"]
87
+ #***weight of the level
88
+ self.PesoSintaxis = df["Parametros_Rubrica"]["Sintaxis"]["Peso"]
89
+
90
+
91
+ #+++ Semantics +++
92
+ #if the semantic level is activated
93
+ self.Semantica = df["Parametros_Analisis"]["Semantica"]["Activado"]
94
+ #***weight of the level
95
+ self.PesoSemantics = df["Parametros_Rubrica"]["Semantica"]["Peso"]
96
+
97
+ #--- Similarity ---
98
+ SpacyPackage = df["Parametros_Rubrica"]["Semantica"]["Similitud"]["Spacy"]["Package"]
99
+ self.spacy_package = df["Parametros_Rubrica"]["Semantica"]["Similitud"]["Spacy"][SpacyPackage]
100
+ print("spacy_package", self.spacy_package)
101
+ #the minimun value to select one line of response as similar (0.615 sm - 0.875 md and lg)
102
+ self.LofRespThreshold = df["Parametros_Rubrica"]["Semantica"]["LineaRespuesta"]["ThresholdToConsiderCeroValue"][SpacyPackage]
103
+ print("lofThreshold", self.LofRespThreshold)
104
+
105
+ #the different thresholds (min-max) to adapt the similarity score
106
+ self.UmbralesSimilitud= df["Parametros_Rubrica"]["Semantica"]["Similitud"]["UmbralesSimilitud"][SpacyPackage]
107
+ print("self.UmbralesSimilitud", self.UmbralesSimilitud)
108
+
109
+ #To configure only once the bert model parameters
110
+
111
+ model_name = df["Parametros_Rubrica"]["Semantica"]["Similitud"]["Bert"]["model_path"]
112
+ self.model_path = create_file_path('', doctype=4) + model_name
113
+ print("self.model_path", self.model_path)
114
+
115
+ self.modelr = df["Parametros_Rubrica"]["Semantica"]["Similitud"]["Bert"]["model"]
116
+ print("self.modelr", self.modelr)
117
+ self.epochr = df["Parametros_Rubrica"]["Semantica"]["Similitud"]["Bert"]["epoch"]
118
+ print("self.epochr", self.epochr)
119
+
120
+ self.BertModels_glbl = SentTransf_test([self.modelr], [self.epochr])
121
+
122
+ #Variables to store some values
123
+ self.studentID = ""
124
+ self.faltasOrto = 0
125
+ self.palabrasPorFrase = 0
126
+ self.minipreguntasMalSpacy = ""
127
+ self.minipreguntasMalBert = ""
128
+
129
+
130
+ def __getApiData(self, json_file):
131
+ """
132
+ This method is used to extract the data and format of the exam from the api (sub-question, sub-answers, etc)
133
+ """
134
+ self.answersDF = pd.DataFrame(json_file[0])
135
+ self.id_number = 0
136
+
137
+ self.minipreguntas = []
138
+ self.minirespuestas = []
139
+ self.indice_minipreguntas = []
140
+ self.respuesta_prof = ""
141
+
142
+ self.enunciado = json_file[1]['enunciado']
143
+ self.prof_keywords = json_file[1]['keywords']
144
+
145
+ try:
146
+ i=0
147
+ while True:
148
+ self.minirespuestas.append(json_file[1]['minipreguntas'][i]['minirespuesta'])
149
+ self.minipreguntas.append(json_file[1]['minipreguntas'][i]['minipregunta'])
150
+
151
+ self.indice_minipreguntas.append("minipregunta" + str(i))
152
+
153
+ if i == 0:
154
+ self.respuesta_prof = self.respuesta_prof + self.minirespuestas[i]
155
+ else:
156
+ self.respuesta_prof = self.respuesta_prof + ' ' + self.minirespuestas[i]
157
+
158
+ i+=1
159
+ except:
160
+ pass
161
+
162
+ info_profesor = []
163
+ for minipregunta, minirespuesta in zip(self.minipreguntas, self.minirespuestas):
164
+ info_profesor.append([minipregunta,minirespuesta])
165
+
166
+ save_json(create_file_path("MinirespuestasProfesor.json", 2), info_profesor)
167
+
168
+ def __getData(self, json_file):
169
+ """
170
+ This method is used to extract the data and format of the exam from the path that appears in the config json (sub-question, sub-answers, etc)
171
+ """
172
+
173
+ self.answersDF = pd.DataFrame(load_json(json_file))
174
+ #self.answersDF_json = copy.deepcopy(data)
175
+ #self.answersDF_json2 = dict()
176
+
177
+ self.id_number = 0
178
+
179
+ self.minipreguntas = []
180
+ self.minirespuestas = []
181
+ self.indice_minipreguntas = []
182
+ self.respuesta_prof = ""
183
+
184
+ self.enunciado = self.answersDF['metadata'][0]['enunciado']
185
+ self.prof_keywords = self.answersDF['metadata'][0]['keywords']
186
+
187
+
188
+ try:
189
+ i=0
190
+ while True:
191
+ #for i in range(4):
192
+ self.minirespuestas.append(self.answersDF['metadata'][0]['minipreguntas'][i]['minirespuesta'])
193
+ self.minipreguntas.append(self.answersDF['metadata'][0]['minipreguntas'][i]['minipregunta'])
194
+
195
+ self.indice_minipreguntas.append("minipregunta" + str(i))
196
+
197
+ if i == 0:
198
+ self.respuesta_prof = self.respuesta_prof + self.minirespuestas[i]
199
+ else:
200
+ self.respuesta_prof = self.respuesta_prof + ' ' + self.minirespuestas[i]
201
+
202
+ i+=1
203
+ except:
204
+ pass
205
+ #self.indice_minipreguntas.append("respuesta_completa")
206
+
207
+ #self.minirespuestas.append(self.respuesta_prof)
208
+
209
+ info_profesor = []
210
+ for minipregunta, minirespuesta in zip(self.minipreguntas, self.minirespuestas):
211
+ info_profesor.append([minipregunta,minirespuesta])
212
+
213
+ save_json(create_file_path("MinirespuestasProfesor.json", 2), info_profesor)
codeScripts/utils.py ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import numpy as np
3
+ import hunspell
4
+ import nltk
5
+ import nltk.corpus
6
+ from nltk.tokenize import sent_tokenize
7
+ from nltk.tokenize import word_tokenize
8
+ from nltk import ne_chunk
9
+ import re
10
+ import yake
11
+ import spacy
12
+ #dic = hunspell.Hunspell('/Users/miguel.r/Desktop/UNIR/PLenTaS/CORPUS/dict_es_ES/es_ES', '/Users/miguel.r/Desktop/es_ES/es_ES.dic')
13
+
14
+ nlp = spacy.load('es_core_news_sm') # Paquete spaCy en español (es)
15
+
16
+ # Clase creada para contar sílabas de una palabra (Source: https://github.com/amunozf/separasilabas/blob/master/separasilabas.py)
17
+
18
+ #class char():
19
+ #def __init__(self):
20
+ # pass
21
+
22
+ class char_line():
23
+ def __init__(self, word):
24
+ self.word = word
25
+ self.char_line = [(char, self.char_type(char)) for char in word]
26
+ self.type_line = ''.join(chartype for char, chartype in self.char_line)
27
+
28
+ def char_type(self, char):
29
+ if char in set(['a', 'á', 'e', 'é','o', 'ó', 'í', 'ú']):
30
+ return 'V' #strong vowel
31
+ if char in set(['i', 'u', 'ü']):
32
+ return 'v' #week vowel
33
+ if char=='x':
34
+ return 'x'
35
+ if char=='s':
36
+ return 's'
37
+ else:
38
+ return 'c'
39
+
40
+ def find(self, finder):
41
+ return self.type_line.find(finder)
42
+
43
+ def split(self, pos, where):
44
+ return char_line(self.word[0:pos+where]), char_line(self.word[pos+where:])
45
+
46
+ def split_by(self, finder, where):
47
+ split_point = self.find(finder)
48
+ if split_point!=-1:
49
+ chl1, chl2 = self.split(split_point, where)
50
+ return chl1, chl2
51
+ return self, False
52
+
53
+ def __str__(self):
54
+ return self.word
55
+
56
+ def __repr__(self):
57
+ return repr(self.word)
58
+
59
+ class silabizer():
60
+ def __init__(self):
61
+ self.grammar = []
62
+
63
+ def split(self, chars):
64
+ rules = [('VV',1), ('cccc',2), ('xcc',1), ('ccx',2), ('csc',2), ('xc',1), ('cc',1), ('vcc',2), ('Vcc',2), ('sc',1), ('cs',1),('Vc',1), ('vc',1), ('Vs',1), ('vs',1)]
65
+ for split_rule, where in rules:
66
+ first, second = chars.split_by(split_rule,where)
67
+ if second:
68
+ if first.type_line in set(['c','s','x','cs']) or second.type_line in set(['c','s','x','cs']):
69
+ #print 'skip1', first.word, second.word, split_rule, chars.type_line
70
+ continue
71
+ if first.type_line[-1]=='c' and second.word[0] in set(['l','r']):
72
+ continue
73
+ if first.word[-1]=='l' and second.word[-1]=='l':
74
+ continue
75
+ if first.word[-1]=='r' and second.word[-1]=='r':
76
+ continue
77
+ if first.word[-1]=='c' and second.word[-1]=='h':
78
+ continue
79
+ return self.split(first)+self.split(second)
80
+ return [chars]
81
+
82
+ def __call__(self, word):
83
+ return self.split(char_line(word))
84
+
85
+ # Contador número de frases y palabras empleadas en la respuesta
86
+ def check_senteces_words(student_answer):
87
+
88
+ # Tokenizing into sentences
89
+ sentences=[]
90
+ words=[]
91
+ letter_per_word=[]
92
+ syll=0 # syllables counter
93
+
94
+ TokenizeAnswer = sent_tokenize(student_answer)
95
+ for token in TokenizeAnswer:
96
+ regex = '\\.'
97
+ token = re.sub(regex , '', token)
98
+ sentences.append(token)
99
+ for i in range(len(sentences)):
100
+ word = sentences[i].split(' ')
101
+ for j in range(len(word)):
102
+ words.append(word[j])
103
+ syllables = silabizer()
104
+ syll=syll+len(syllables(word[j]))
105
+ letter_per_word.append(len(word[j]))
106
+
107
+ sentencesLenght = len(sentences)
108
+ wordsLenght = (len(words))
109
+ #print(f'Number of senteces used in the answer: {sentencesLenght}')
110
+ #print(f'Number of words used in the answer: {wordsLenght}')
111
+
112
+ return sentencesLenght, wordsLenght, syll, letter_per_word
113
+
114
+ # Contador faltas de ortografía
115
+ def spelling_corrector(student_answer, hunspell_aff = '/Users/javier.sanz/OneDrive - UNIR/Desktop/PLeNTas_V3/es_ES/es_ES' , hunspell_dic = '/Users/javier.sanz/OneDrive - UNIR/Desktop/PLeNTas_V3/es_ES/es_ES.dic' ):
116
+
117
+ dic = hunspell.Hunspell(hunspell_aff, hunspell_dic)
118
+ errors=0
119
+ words = student_answer.split(' ')
120
+ wrong_words = []
121
+ for word in words:
122
+ for element in clean_words(word):
123
+ if not dic.spell(element):
124
+ #print(f'Spelling mistake: {element}')
125
+ wrong_words.append(element)
126
+ errors+=1
127
+ #print(f'Spelling mistakes: {errors}')
128
+ return errors,wrong_words
129
+
130
+ # Legibilidad de la respuesta en función del índice Fernández-Huerta
131
+ def FHuertas_index(sentencesLenght, wordsLenght, syll):
132
+ FH = 206.84 - 0.60*(syll*100/wordsLenght) - 1.02*(sentencesLenght*100/wordsLenght)
133
+ FH = round(FH, 3)
134
+ legibilidad_fh = ""
135
+ #print(f'\nFernández-Huerta Index: {FH}')
136
+ if 0 < FH <= 30:
137
+ #print('Legibilidad FH: muy difícil.')
138
+ legibilidad_fh = 'muy díficil'
139
+ if 30 < FH <= 50:
140
+ #print('Legibilidad FH: difícil.')
141
+ legibilidad_fh = 'díficil'
142
+ if 50 < FH <= 60:
143
+ #print('Legibilidad FH: ligeramente difícil.')
144
+ legibilidad_fh = 'ligeramente díficil'
145
+ if 60 < FH <= 70:
146
+ #print('Legibilidad FH: adecuado.')
147
+ legibilidad_fh = 'adecuado'
148
+ if 70 < FH <= 80:
149
+ #print('Legibilidad FH: ligeramente fácil.')
150
+ legibilidad_fh = 'ligeramente fácil'
151
+ if 80 < FH <= 90:
152
+ #print('Legibilidad FH: fácil.')
153
+ legibilidad_fh = 'fácil'
154
+ if 90 < FH <= 100:
155
+ #print('Legibilidad FH: muy fácil.')
156
+ legibilidad_fh = 'muy fácil'
157
+
158
+ return FH, legibilidad_fh
159
+
160
+ # Legibilidad de la respuesta en función del índice mu
161
+ def mu_index(sentencesLenght, wordsLenght, letter_per_word):
162
+ med = np.mean(letter_per_word)
163
+ var = np.var(letter_per_word)
164
+ mu=(wordsLenght/(wordsLenght-1))*(med/var)*100
165
+ mu=round(mu, 3)
166
+
167
+ legibilidad_mu = ""
168
+ #print(f'\nMu index: {mu}')
169
+ if 0 < mu <= 30:
170
+ #print('Legibilidad Mu: muy difícil.')
171
+ legibilidad_mu = 'muy difícil'
172
+ if 30 < mu <= 50:
173
+ #print('Legibilidad Mu: difícil.')
174
+ legibilidad_mu = 'difícil'
175
+ if 50 < mu <= 60:
176
+ #print('Legibilidad Mu: ligeramente difícil.')
177
+ legibilidad_mu = 'ligeramente difícil'
178
+ if 60 < mu <= 70:
179
+ #print('Legibilidad Mu: adecuado.')
180
+ legibilidad_mu = 'adecuado'
181
+ if 70 < mu <= 80:
182
+ #print('Legibilidad Mu: ligeramente fácil.')
183
+ legibilidad_mu = 'ligeramente fácil'
184
+ if 80 < mu <= 90:
185
+ #print('Legibilidad Mu: fácil.')
186
+ legibilidad_mu = 'fácil'
187
+ if 90 < mu <= 100:
188
+ #print('Legibilidad Mu: muy fácil.')
189
+ legibilidad_mu = 'muy fácil'
190
+
191
+ return mu, legibilidad_mu
192
+
193
+ # Extractor de las kewords de un texto con librería yake
194
+ def keyword_extractor(text, numOfKeywords, language, max_ngram_size,deduplication_threshold = 0.9, features=None):
195
+ test_keywords=[]
196
+ # Deleting special characters and set text in lower case
197
+ regex = '\\\n'
198
+ text = re.sub(regex , ' ', text)
199
+ text = text.lower()
200
+ custom_kw_extractor = yake.KeywordExtractor(lan=language, n=max_ngram_size, dedupLim=deduplication_threshold, top=numOfKeywords, features= features )
201
+ keywords = custom_kw_extractor.extract_keywords(text)
202
+ for kw in keywords:
203
+ test_keywords.append(kw[0])
204
+ return test_keywords
205
+
206
+ # categorización de palabras
207
+ def word_categorization(student_answer):
208
+ fileDocument=[]
209
+ TokenizeAnswer = sent_tokenize(student_answer)
210
+ for token in TokenizeAnswer:
211
+ fileDocument.append(token)
212
+ sentencesLenght = len(fileDocument)
213
+ sentence=0
214
+ while sentence < sentencesLenght:
215
+ # Word Tokenize sentence and Tagging the grammer tag to words (verb, noun, adj, etc...)
216
+ word_tokens = word_tokenize(fileDocument[sentence])
217
+ doc = nlp(fileDocument[sentence])
218
+ pre_chunk = [(w.text, w.pos_) for w in doc]
219
+ #print(pre_chunk)
220
+ sentence += 1
221
+ #pre_chunk = nltk.pos_tag(word_tokens)
222
+ tree = ne_chunk(pre_chunk) # same tagging than before
223
+ #grammer_np = ("NP: {<DT>?<JJ>*<NN>}")
224
+
225
+ # Chunking rules to filter out:
226
+ grammer_np = ("NP: {<DET>?<ADJ>*<NOUN>*<VERB>}")
227
+ grammar = r"""
228
+ NP: {<DT|PP\$>?<JJ>*<NN>} # chunk determiner/possessive, adjectives and nouns
229
+ {<NNP>+} # chunk sequences of proper nouns
230
+ """
231
+ chunk_parser = nltk.RegexpParser(grammer_np)
232
+ chunk_result = chunk_parser.parse(tree)
233
+
234
+ #..................................................................................................
235
+ def char_split(word, character):
236
+ palabra1=""
237
+ palabra2=""
238
+ found = 0
239
+ for w in word:
240
+ if w == character and not found:
241
+ found = 1
242
+ else:
243
+ if not found:
244
+ palabra1 = palabra1 + w
245
+ else:
246
+ palabra2 = palabra2 + w
247
+
248
+ return [palabra1, palabra2]
249
+
250
+ def clean_words(string):
251
+ words_sentence = []
252
+ for w in string:
253
+ if not w.isalnum():
254
+ if char_split(string, w)[0] != "":
255
+ words_sentence.append(char_split(string, w)[0])
256
+ string = char_split(string, w)[len(char_split(string, w))-1]
257
+
258
+ if string != "":
259
+ words_sentence.append(string)
260
+ return words_sentence
261
+
262
+ def getNameFile(string):
263
+ directories = string.split("/")
264
+ return re.sub(".json","", directories[len(directories)-1])
265
+
266
+
267
+ def getIDrange(rango_ID, df):
268
+ if rango_ID == "All":
269
+ IDs = list(range(len(df['hashed_id'])))
270
+ else:
271
+ rango = []
272
+ r= rango_ID.split(",")
273
+ for i in r:
274
+ c_w= clean_words(i)
275
+ if len(c_w) == 2:
276
+ rango= rango + list(range(int(c_w[0]) -1 ,int(c_w[1])))
277
+ elif len(c_w) == 1:
278
+ rango.append(int(c_w[0]) -1)
279
+ IDs = rango
280
+
281
+ return IDs
282
+
283
+ def save_json(path, data, isIndent = True):
284
+ if isIndent:
285
+ json_object = json.dumps(data, indent = 11, ensure_ascii= False)
286
+ else:
287
+ json_object = json.dumps(data, ensure_ascii= False)
288
+ # Writing output to a json file
289
+ with open(path, "w") as outfile:
290
+ outfile.write(json_object)
291
+
292
+
293
+ def load_json(path):
294
+ with open(path, "r", encoding="utf8") as f:
295
+ data = json.loads("[" + f.read().replace("}\n{", "},\n{") + "]")
296
+
297
+ return data
298
+
299
+ def load_json_dtset(path):
300
+ with open(path, "r", encoding="latin-1") as f:
301
+ data = json.loads("[" + f.read().replace("}\n{", "},\n{") + "]")
302
+
303
+ return data
304
+
305
+
306
+ def splitResponse(respuesta_alumno_raw):
307
+ #pre-processing the student's response
308
+ regex = '\\\n'
309
+ respuesta_alumno = re.sub(regex , ' ', respuesta_alumno_raw)
310
+ respuesta_alumno = respuesta_alumno.lower()
311
+
312
+ #stacking each sentence of the student's response
313
+ sentences=[]
314
+ TokenizeAnswer = sent_tokenize(respuesta_alumno)
315
+ for token in TokenizeAnswer:
316
+ regex = '\\.'
317
+ token = re.sub(regex , '', token)
318
+ sentences.append(token)
319
+
320
+ return sentences
321
+
322
+ def create_file_path(file, doctype):
323
+ """
324
+ This function is to create relative paths to store data.
325
+ Inputs:
326
+ file: the file or subpath + file where the info is to be stored
327
+ doctype: 1- Info from the api, 2- Output documents, 3- Images, 4- Bert models/documents
328
+ Outputs:
329
+ path: the generated path
330
+ """
331
+ if doctype == 1:
332
+ path = "api/" + file
333
+ elif doctype == 2:
334
+ path = "archivos/OutputFiles2/" + file
335
+ elif doctype == 3:
336
+ path = "archivos/Images/" + file
337
+ else:
338
+ path = "codeScripts/Dependencies/BERT-models/Prueba3/" + file
339
+ return path
configV2.json ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ruta_fichero_entrada": "__appcache__/biConNotaAnon.json",
3
+ "ruta_fichero_salida": "archivos/OutputFiles/biConNotaAnon_out.json",
4
+ "ruta_hunspell": {
5
+ "aff": "C:/Users/javier.sanz/Espacio de trabajo - PLENTAS/plentas-backend-UPDATED/__appcache__/es_ES/es_ES",
6
+ "dic": "C:/Users/javier.sanz/Espacio de trabajo - PLENTAS/plentas-backend-UPDATED/__appcache__/es_ES/es_ES.dic"
7
+ },
8
+ "Parametros_Analisis": {
9
+ "estudiantes":{
10
+ "ID_rango": "{5-8, 15-20}",
11
+ "Todos": 0
12
+ },
13
+
14
+ "Ortografia":{
15
+ "Activado": 1
16
+ },
17
+ "Sintaxis":{
18
+ "Activado": 1
19
+
20
+ },
21
+
22
+ "Semantica":{
23
+ "Activado": 1,
24
+ "frases":{
25
+ "Agrupacion": {
26
+ "Minimo": 1,
27
+ "Maximo": 8
28
+ }
29
+ }
30
+
31
+ }
32
+ },
33
+ "Parametros_Rubrica": {
34
+
35
+ "Ortografia":{
36
+ "NMaxErrores": 5,
37
+ "FaltasSalvaguarda": 1,
38
+ "Peso": 0.1
39
+ },
40
+ "Sintaxis":{
41
+ "NMaxFrases": 0,
42
+ "NMaxPalabras":0,
43
+ "Peso": 0.1
44
+ },
45
+
46
+ "Semantica":{
47
+ "Peso": 0.8,
48
+ "Similitud":{
49
+ "UmbralesSimilitud": {
50
+ "SM": "{0.3-0.7}",
51
+ "MD": "{0.3-0.9}",
52
+ "LG": "{0.3-0.9}"
53
+ },
54
+
55
+ "Spacy":{
56
+ "SM": "es_core_news_sm",
57
+ "MD": "es_core_news_md",
58
+ "LG": "es_core_news_lg",
59
+ "Package": "SM"
60
+ },
61
+ "Bert":{
62
+ "model_path": "Prueba_anterior/Model_bert-base-multilingual-uncased/50_Epochs",
63
+ "model": "bert-base-multilingual-uncased",
64
+ "epoch": 50
65
+ }
66
+
67
+ },
68
+ "LineaRespuesta":{
69
+ "ThresholdToConsiderCeroValue": {
70
+ "SM": 0.615,
71
+ "MD": 0.875,
72
+ "LG": 0.875
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
plentas.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+
3
+ from codeScripts.methodologyPlentas import *
4
+ from codeScripts.rubrics import Ortografia2, Sintaxis2, GenerateFeedback
5
+ from codeScripts.settings import GetSettings
6
+ from codeScripts.utils import getIDrange, splitResponse
7
+
8
+
9
+ class Plentas():
10
+
11
+ def __init__(self, config, studentsData):
12
+
13
+ self.settings = GetSettings(config, studentsData)
14
+ #semantica
15
+ self.semantic_methodology = PlentasMethodology(self.settings)
16
+ #ortografia
17
+ self.ortografia = Ortografia2(self.settings)
18
+ #sintaxis
19
+ self.sintaxis = Sintaxis2(self.settings)
20
+
21
+ def __jsonToExcel__(self, jsonFile):
22
+ outputExcel = dict()
23
+ print(jsonFile)
24
+ for student in jsonFile:
25
+ for numb_id in student.keys():
26
+ for column in student[numb_id].keys():
27
+ if column == "SimilitudBert" or column == "SimilitudSpacy":
28
+ pass
29
+ else:
30
+ if column not in outputExcel.keys():
31
+ outputExcel[column] = []
32
+ outputExcel[column].append(student[numb_id][column])
33
+
34
+
35
+
36
+ df = pd.DataFrame(data=outputExcel)
37
+ #df = (df.T)
38
+ df.to_excel('archivos/OutputFiles2/backendExcel.xlsx')
39
+ return [jsonFile, outputExcel]
40
+
41
+
42
+
43
+
44
+
45
+
46
+ def setApiSettings(self, api_settings):
47
+ #lectura de parametros de la api
48
+ self.settings.setApiSettings(api_settings)
49
+
50
+ def processApiData(self):
51
+
52
+
53
+ if self.settings.PesoOrtografia == 0.0:
54
+ self.settings.Ortografia = 0
55
+ if self.settings.PesoSintaxis == 0.0:
56
+ self.settings.Sintaxis = 0
57
+ if self.settings.PesoSemantics == 0.0:
58
+ self.settings.Semantica = 0
59
+
60
+ AnalysisOfResponses = []
61
+ IDs = getIDrange(self.settings.rango_ID, self.settings.answersDF)
62
+ for id in IDs:
63
+ studentID = self.settings.answersDF['hashed_id'][id]
64
+ self.settings.studentID = studentID
65
+
66
+ nota_rubrica_spacy = 0
67
+ nota_rubrica_bert = 0
68
+
69
+ respuesta_alumno_raw = self.settings.answersDF['respuesta'][id].lower()
70
+
71
+
72
+
73
+ if self.settings.Sintaxis:
74
+ #ponderacion dentro de la función
75
+ nota_rubrica_sintaxis = self.sintaxis.Evaluation(respuesta_alumno_raw)
76
+ nota_rubrica_spacy = nota_rubrica_spacy + nota_rubrica_sintaxis
77
+ nota_rubrica_bert = nota_rubrica_bert + nota_rubrica_sintaxis
78
+ else:
79
+ nota_rubrica_sintaxis = 0
80
+
81
+
82
+ if self.settings.Ortografia:
83
+ #ponderacion dentro de la función
84
+ nota_rubrica_ortografia = self.ortografia.Evaluation(respuesta_alumno_raw)
85
+
86
+ nota_rubrica_spacy = nota_rubrica_spacy + nota_rubrica_ortografia
87
+ nota_rubrica_bert = nota_rubrica_bert + nota_rubrica_ortografia
88
+ else:
89
+ nota_rubrica_ortografia = 0
90
+
91
+
92
+ if self.settings.Semantica:
93
+ sentencesArr = splitResponse(respuesta_alumno_raw)
94
+
95
+ spacy_eval = self.semantic_methodology.getSimilarity(sentencesArr, "spacy")
96
+ bert_eval = self.semantic_methodology.getSimilarity(sentencesArr, "bert")
97
+
98
+ for sim1, sim2, nminip in zip(spacy_eval, bert_eval, range(len(spacy_eval))):
99
+ if sim1 < 0.5:
100
+ self.settings.minipreguntasMalSpacy = self.settings.minipreguntasMalSpacy + "Minipregunta " + str(nminip + 1)
101
+
102
+ if sim2 < 0.5:
103
+ if self.settings.minipreguntasMalBert != "" and nminip>0:
104
+ self.settings.minipreguntasMalBert = self.settings.minipreguntasMalBert + ", "
105
+
106
+ self.settings.minipreguntasMalBert = self.settings.minipreguntasMalBert + "Minipregunta " + str(nminip + 1)
107
+
108
+
109
+
110
+ spacy_eval_umbral = self.semantic_methodology.EvaluationMethod(studentID, "" if len(sentencesArr) == 1 and sentencesArr[0] == '' else sentencesArr, spacy_eval, "spacy")
111
+
112
+ bert_eval_umbral = self.semantic_methodology.EvaluationMethod(studentID, "" if len(sentencesArr) == 1 and sentencesArr[0] == '' else sentencesArr, bert_eval, "bert")
113
+
114
+ nota_rubrica_spacy = nota_rubrica_spacy + self.settings.PesoSemantics * spacy_eval_umbral
115
+ nota_rubrica_bert = nota_rubrica_bert + self.settings.PesoSemantics * bert_eval_umbral
116
+ else:
117
+ spacy_eval_umbral = 0
118
+ bert_eval_umbral = 0
119
+
120
+ feedback = GenerateFeedback(self.settings, respuesta_alumno_raw,nota_rubrica_ortografia, nota_rubrica_sintaxis, spacy_eval_umbral * self.settings.PesoSemantics, bert_eval_umbral * self.settings.PesoSemantics)
121
+
122
+ self.settings.minipreguntasMalSpacy = ""
123
+ self.settings.minipreguntasMalBert = ""
124
+
125
+
126
+ AnalysisOfResponses.append({ id : {
127
+ "ID": studentID,
128
+ "SimilitudSpacy": round(nota_rubrica_spacy,2),
129
+ "SimilitudBert": round(nota_rubrica_bert,2),
130
+ "NotaSemanticaSpacy": round(spacy_eval_umbral * self.settings.PesoSemantics,2),
131
+ "NotaSemanticaBert": round(bert_eval_umbral * self.settings.PesoSemantics,2),
132
+ "NotaSintaxis": round(nota_rubrica_sintaxis,2),
133
+ "NotaOrtografia": round(nota_rubrica_ortografia,2),
134
+ "NotaTotalSpacy": (round(nota_rubrica_ortografia,2) + round(nota_rubrica_sintaxis,2) + round(spacy_eval_umbral * self.settings.PesoSemantics,2))*10,
135
+ "NotaTotalBert": (round(nota_rubrica_ortografia,2) + round(nota_rubrica_sintaxis,2) + round(bert_eval_umbral * self.settings.PesoSemantics,2))*10,
136
+ "Feedback": feedback }
137
+ } )
138
+
139
+ AnalysisOfResponses = self.__jsonToExcel__(AnalysisOfResponses)
140
+ self.semantic_methodology.SemanticLevel.output.saveSimilarityResults(self.settings, "spacy")
141
+ self.semantic_methodology.SemanticLevel.output.saveSimilarityResults(self.settings, "bert")
142
+ if self.settings.Sintaxis:
143
+ self.sintaxis.saveResults()
144
+ if self.settings.Ortografia:
145
+ self.ortografia.SaveMistakes()
146
+
147
+ print(AnalysisOfResponses)
148
+ return AnalysisOfResponses
149
+
150
+
151
+
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ datasets==2.7.1
2
+ Flask==2.0.1
3
+ gradio==3.23.0
4
+ matplotlib==3.5.1
5
+ nltk==3.6.5
6
+ numpy==1.19.5
7
+ pandas==1.3.4
8
+ scikit_learn==1.2.2
9
+ seaborn==0.12.2
10
+ sentence_transformers==2.2.2
11
+ spacy==3.4.4
12
+ torch==1.13.1
13
+ yake==0.4.8
14
+ CyHunspell
15
+ https://github.com/explosion/spacy-models/releases/download/es_core_news_sm-3.4.0/es_core_news_sm-3.4.0.tar.gz