readme.md 9.32 KB
Newer Older
1
# ASYSTGRADE Moodle Plugin
2

3
4
5
6
7
This plugin, along with the relevant Docker infrastructure, is designed to facilitate teachers’ activities in evaluating students’ short answers. 
The plugin uses the ASYST grading [script](https://transfer.hft-stuttgart.de/gitlab/ulrike.pado/ASYST) modified to function as a web endpoint.
The plugin requires the ASYST ML Backend to be isolated in a standalone Docker container accessible via the local network or the Internet.
ASYSTGRADE Moodle Plugin runs each time the teacher reaches some manual grading page (a part of [Essay auto-grade plugin](https://moodle.org/plugins/qtype_essayautograde) maintained by Gordon Bateson)!

8
9
10
The first time the plugin makes a request to the ASYST ML backend, there is a delay due to the loading of the BERT model in the Flask container.

This solution is part of the Master’s Thesis titled “Integration of a Machine Learning Backend into Assessment Processes in a PHP-based Educational Environment” at the University of Applied Sciences Stuttgart, within the Software Technology course, 2024, by Artem Baranovskyi.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

## Plugin and ASYST ML Backend Interaction Concept
```mermaid
flowchart TD
    A[Moodle LMS] --> B[PHP Plugin]
    B -->|HTTP Request| C[Flask API Python Backend]
    C --> D[Pre-trained BERT Model]
    C --> E[Logistic Regression Model]
    C --> F[Sentence Transformers]
    D --> C
    E --> C
    F --> C
    C -->|Response| B
    B -->|Processed Results| A

subgraph Backend-Services
    C
    D
    E
    F
end

subgraph Docker Containers
    Backend-Services
end

B --> G{cURL}
```

## Description of Use Case processes at Sequence Diagram
```mermaid
sequenceDiagram
    participant Student
    participant Teacher
    participant Moodle
    participant PHP Plugin
    participant Flask API
    participant BERT Model

    Student->>Moodle: Submits Answer
    Teacher->>PHP Plugin: Visit Manual Grading Page
    Moodle->>PHP Plugin: Answers GET Params ($qid, $slot)
    PHP Plugin->>Flask API: Sends HTTP POST Request
    Flask API->>BERT Model: Processes Data (Embeddings)
    BERT Model-->>Flask API: Returns Processed Result
    Flask API-->>PHP Plugin: Sends Grading Response
    PHP Plugin-->>Moodle: Displays Predicted Grade
    Moodle-->>Teacher: Displays Predicted Grade
    Teacher->>Moodle: Grade (Regrade) Answer
    Moodle-->>Student: Displays Final Result
```

## Plugin components' Diagram
```mermaid
classDiagram
class local_asystgrade_lib {
+void local_asystgrade_before_footer()
+void pasteGradedMarks(array grades, array inputNames, float maxmark) // DOM Manipulation
+array prepare_api_data(quizquery database, question_attempts, referenceAnswer)
+string generate_script(array grades, array inputNames, float maxmark)
}

class quizquery {
+get_question_attempts($qid, $slot)
+get_reference_answer($qid)
+get_attempt_steps($question_attempt_id)
+get_student_answer($attemptstepid)
+bool gradesExist(int quizid, int userid)
}

class client {
+send_data(array $data): bool|string
+getInstance(string $endpoint, http_client_interface $httpClient): client
-string endpoint
-http_client_interface httpClient
-static ?client instance
}

class http_client {
+post(string $url, array $data): bool|string
}

class http_client_interface {
+post(string $url, array $data): bool|string
}

class provider {
+string get_reason()
}

class DB {
    
}

local_asystgrade_lib <--> quizquery : interacts with
local_asystgrade_lib --> client : sends data to
local_asystgrade_lib <-- client : get response from
client --> http_client_interface : implements
client --> http_client : uses
http_client_interface <|-- http_client : implements
quizquery --> quizquery_interface : implements
quizquery --> DB : uses
provider --> core_privacy_local_metadata_null_provider : implements

quiz_api_test --> client : sends data to
quiz_api_test <-- client : get response from
class quiz_api_test {
+setUp()
+test_quiz_api()
+create_question_category()
+create_quiz_attempt()
}
```

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
## ML Backend Components' Diagram
```mermaid
classDiagram
class FlaskApp {
+Flask app
+get_data()
}
class run_LR_SBERT {
+process_data(data)
+similarity(sentence_embeddings1, sentence_embeddings2)
}
class SentenceTransformer {
+encode(sentences, convert_to_tensor, show_progress_bar)
}
class LogisticRegression {
+predict(X_test)
}

class models {
+Transformer(model_name)
+Pooling(word_embedding_dimension, pooling_mode_mean_tokens, pooling_mode_cls_token, pooling_mode_max_tokens)
}

FlaskApp --> run_LR_SBERT : Calls process_data()
FlaskApp <-- run_LR_SBERT : Receives process_data()
run_LR_SBERT --> SentenceTransformer : Uses for sentence encoding
run_LR_SBERT --> LogisticRegression : Uses for prediction
run_LR_SBERT --> models : Uses Transformer and Pooling modules

class DiagramInteractions {
FlaskApp receives POST /api/autograde
FlaskApp extracts JSON data from request
FlaskApp calls process_data() in run_LR_SBERT
run_LR_SBERT encodes reference and student answers using SentenceTransformer
run_LR_SBERT calculates similarity between embeddings
run_LR_SBERT uses LogisticRegression model to predict correctness
FlaskApp returns predictions as JSON response
}
```

165
166
167
168
## How to wrap up solution
###  Full Solution with Moodle Server (demo with all Moodle+MariaDb+Flask):
To use ASYST with a universal BERT model based on the German language, run these commands in the CLI. 
They will build and run all 3 containers (Moodle+MariaDb+Flask) for demo, development, and testing purposes along with the Moodle environment installation.
169
170

~~~bash
Artem Baranovskyi's avatar
Artem Baranovskyi committed
171
docker-compose up -d --build && ./install_moodle.sh
172
~~~
173

174
175
176
177
178
179
180
181
182
This will set up a brand new Moodle instance with ready to go PHPUnit test environment.
If you already have a Moodle LMS, you can use its database backup in this project. 
Just place it in the /moodle folder and rename it to moodle_backup.sql.

#### Use these credentials to access the Moodle admin page:

**admin**:*rootpassword*

These credentials can be easily changed along with other environment variables in the [.env](https://github.com/ArtemBaranovsky/moodle-asyst-sync/blob/master/.env) file.
Artem Baranovskyi's avatar
Artem Baranovskyi committed
183

184
After installation, the database will have all the necessary entities to check the plugin’s functionality (Courses, Tests, Students, Quiz Attempts, etc.).
185

186
187
188
189
For a demo, simply visit the link: https://www.moodle.loc/mod/quiz/report.php?id=2&mode=grading&slot=1&qid=1&grade=needsgrading and wait for the auto answer evaluation.
Current plugin implementation starts automatically for a batch of answers right at **Essay (auto-grade)**  page.
Teacher can accept propose evaluated mark or regrade it.
ASYSTGRADE Plugin didn't save anything to DB on his own! 
Artem Baranovskyi's avatar
Artem Baranovskyi committed
190

191
192
193
194
195
196
197
198
199
## Using only ASYST API on Flask
It is not necessary to build full solution if you want just use the plugin at your existing Moodle LMS. 

To build only the Flask ASYST microservice, run:

~~~bash
docker-compose up flask -d
~~~

200
If a standalone Flask image is ready, it can be run with:
201
202
203
204
205

~~~bash
docker run -p 5000:5000 asyst-flask
~~~

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
If ASYST ML Backend is being used alone, ASYSTGRADE plugin should be copied from /asystgrade with the folder to ../moodle/local/ folder for local plugins.

The stucture of request to ASYST ML Backend: 
~~~JSON
{
  "referenceAnswer": "The reference answer",
  "studentAnswers": [
    "First Student's Answer",
    "Second Student's Answer",
    ...
  ]
}
~~~
**Explanation:**

**referenceAnswer**: This is the model answer provided by the teacher. It includes detailed explanations and grading criteria.

**studentAnswers**: This array contains the answers submitted by students. Each answer is evaluated against the reference answer.

The stucture of responce from ASYST ML Backend:
~~~JSON
[
  {
    "predicted_grade": "incorrect"
  },
  {
    "predicted_grade": "correct"
  },
  ...
]
~~~
**Explanation:**

**predicted_grade**: The response includes a predicted grade for each student answer, indicating whether it is “correct” or “incorrect”.

Similarity of any text now could be checked with a curl request:
```curl
curl -X POST http://127.0.0.1:5000/api/autograde -H "Content-Type: application/json" -d '{
    "referenceAnswer": "Multithreading improves the performance of a program because the processor can switch between different tasks, utilizing waiting times in one thread to process other threads. This allows for more efficient use of CPU resources.",
    "studentAnswers": [
        "Multithreading enhances a program’s performance by allowing the CPU to handle multiple tasks simultaneously. This means that while one thread is waiting for data, the CPU can process other threads, leading to more efficient use of processing power.",
        "Multithreading slows down the performance of a program because the processor gets overloaded with too many tasks at once."
    ]
}'
```
Artem Baranovskyi's avatar
Artem Baranovskyi committed
251

Artem Baranovskyi's avatar
Artem Baranovskyi committed
252
## Development tips
253
254
To facilitate DB monitoring in your IDE, set the following database connection URL:

Artem Baranovskyi's avatar
Artem Baranovskyi committed
255
256
257
~~~bash
jdbc:mariadb://localhost:3306/moodle
~~~
258

259
It is suggested to use our Moodle plugin to communicate with the Flask-based ASYST script using this route: http://127.0.0.1:5000/api/autograde
260

261
Now the preinstalled Moodle LMS is available at https://www.moodle.loc
262

263
264
**Note**: Bind https://www.moodle.loc to your localhost at **hosts** file depending on your OS.

265
266
## Running Integration Test
To run only the plugin’s test, execute in the project’s CLI (inside the container):
267
268
269
~~~bash
vendor/bin/phpunit --testsuite local_asystgrade_testsuite
~~~
270
271
272
273
or run outside it:
~~~bash
docker-compose exec moodle vendor/bin/phpunit --testsuite local_asystgrade_testsuite
~~~