From ec9210c48f26e609e8701e646a8fbfbf22c78f55 Mon Sep 17 00:00:00 2001 From: Artem Baranovskyi <artem.baranovsky1980@gmail.com> Date: Wed, 26 Jun 2024 23:32:48 +0300 Subject: [PATCH] Fixed Supervisor & Flask infrastructure issues. Added Plugin DB Migration. ----------------------------------------------------------- TODO: Create an API to use script run_LR_SBERT.py on http://127.0.0.1:5000/api/data or like that. --- .env | 4 ++- Dockerfile | 7 ++--- api.py | 21 ++++++++++--- asyst/Source/Skript/german/run_LR_SBERT.py | 28 ++++++++++-------- docker-compose.yml | 9 ++---- install_moodle.sh | 4 +-- readme.md | 9 ++++-- supervisord.conf | 8 +++-- yourplugin/db/install.xml | 22 ++++++++++++++ yourplugin/lang/en/yourplugin.php | 34 ++++++++++++---------- yourplugin/lib.php | 27 +++++++++++++++++ yourplugin/version.php | 6 ++-- 12 files changed, 127 insertions(+), 52 deletions(-) create mode 100644 yourplugin/db/install.xml create mode 100644 yourplugin/lib.php diff --git a/.env b/.env index 98be37a..80ed354 100755 --- a/.env +++ b/.env @@ -13,4 +13,6 @@ MOODLE_BASE_DIR_DATA=/var/www/html/moodledata MOODLE_WWWROOT=https://www.moodle.loc MOODLE_FULLNAME="Moodle LMS Site" -MOODLE_SHORTNAME="Moodle" \ No newline at end of file +MOODLE_SHORTNAME="Moodle" + +FLASK_APP=/var/www/html/moodle/api.py \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ab9cf7d..f9d3e65 100755 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,8 @@ RUN python3 -m venv /opt/myenv RUN /opt/myenv/bin/python3 -m pip install --upgrade pip #RUN /opt/myenv/bin/python3 -m pip install matplotlib Flask torch sklearn-learn COPY requirements.txt /opt/myenv/ +COPY sentence-transformers-paraphrase-multilingual-MiniLM-L12-v2 /var/www/html/moodle/sentence-transformers-paraphrase-multilingual-MiniLM-L12-v2 +WORKDIR /var/www/html/moodle RUN /opt/myenv/bin/python3 -m pip install -r /opt/myenv/requirements.txt RUN /opt/myenv/bin/python3 -m pip install --upgrade setuptools wheel @@ -149,11 +151,6 @@ RUN a2enmod ssl #Opening ports EXPOSE 80 443 5000 -#RUN /var/www/html/moodle/install_moodle.sh -# Run the Flask app (modify entrypoint or use a custom script if needed) -#CMD ["bash", "-c", "service apache2 start && source /opt/myenv/bin/activate && python3 /var/www/html/moodle/install_moodle.sh /var/www/html/moodle/api.py "] -#CMD ["bash", "-c", "service apache2 start && source /opt/myenv/bin/activate && python3 /var/www/html/moodle/api.py && /var/www/html/moodle/install_moodle.sh"] - # Setting up supervisord COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/api.py b/api.py index 65c8f17..43e1490 100755 --- a/api.py +++ b/api.py @@ -2,20 +2,33 @@ from flask import Flask, jsonify +# TODO: Adding correct path to ASYST script run_LR_SBERT.py +# from run_LR_SBERT import main + +# sys.path.append(os.path.join(os.path.dirname(__file__), 'asyst/Source/Skript/german')) + app = Flask(__name__) -# Example of an endpoint returning JSON data @app.route('/api/data', methods=['GET']) def get_data(): -# TODO: use run_LR_SBERT.py ASYST script instead - data = { + # Using path to data and model + data_path = '/var/www/html/moodle/asyst/Source/Skript/outputs/test.tsv' + model_dir = '/var/www/html/moodle/asyst/Source/Skript/german/models' + + # Obtaining results from run_asyst function +# results => run_asyst(data_path, model_dir) + + # Demo dummy API output + results = { 'message': 'Hello from Python API!', 'data': { 'key1': 'value1', 'key2': 'value2' } } - return jsonify(data) + + # Returning result in JSON format + return jsonify(results) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/asyst/Source/Skript/german/run_LR_SBERT.py b/asyst/Source/Skript/german/run_LR_SBERT.py index e4f169e..1f01ac6 100755 --- a/asyst/Source/Skript/german/run_LR_SBERT.py +++ b/asyst/Source/Skript/german/run_LR_SBERT.py @@ -36,7 +36,6 @@ def main(): #default=None, default="/var/www/html/moodle/asyst/Source/Skript/outputs/test.tsv", type=str, - # required=True, required=False, help="The input data file for the task.", ) @@ -45,7 +44,6 @@ def main(): # default=None, default="/var/www/html/moodle/asyst/Source/Skript/outputs", type=str, - # required=True, required=False, help="The output directory where predictions will be written.", ) @@ -54,32 +52,38 @@ def main(): # default=None, default=location+"/Skript/german/models", type=str, - # required=True, required=False, help="The directory where the ML models are stored.", ) + parser.add_argument( + "--transformer_model_dir", + default="/var/www/html/moodle/sentence-transformers-paraphrase-multilingual-MiniLM-L12-v2", + type=str, + required=False, + help="The directory where the SentenceTransformer model is stored.", + ) args = parser.parse_args() # open a log file next to the executable with line buffering # out = open("log.txt", "a",buffering=1); - # print("Started German processing in",location,file=out); + # print("Started German processing in", location, file=out); # import SentenceTransformer-model start_time = time.time() - # print("Reading from",args.data, file=out); + # print("Reading from", args.data, file=out); - with open(os.path.join(location,args.data)) as ft: + with open(args.data) as ft: dft = pd.read_csv(ft, delimiter='\t') # Sentences we want sentence embeddings for sentences1_test = dft['referenceAnswer'].values.tolist() sentences2_test = dft['studentAnswer'].values.tolist() - # print("Input read:",sentences2_test, file=out); + # print("Input read:", sentences2_test, file=out); # Use BERT for mapping tokens to embeddings - word_embedding_model = models.Transformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2') + word_embedding_model = models.Transformer(args.transformer_model_dir) # pooling operation can choose by setting true (Apply mean pooling to get one fixed sized sentence vector) pooling_model = models.Pooling(word_embedding_model.get_word_embedding_dimension(), pooling_mode_mean_tokens=True, @@ -91,7 +95,7 @@ def main(): # print("Model loaded", file=out); sentence_embeddings1_test = model.encode(sentences1_test, convert_to_tensor=True, show_progress_bar=False) - # print("Embeddings RefA:",sentence_embeddings1_test,file=out); + # print("Embeddings RefA:", sentence_embeddings1_test, file=out); sentence_embeddings2_test = model.encode(sentences2_test, convert_to_tensor=True, show_progress_bar=False) # print("Embeddings found", file=out); @@ -107,7 +111,7 @@ def main(): # calls the similarity function and get the concatenated values between the sentence embeddings computed_simis_test = similarity(sentence_embeddings1_test, sentence_embeddings2_test) - # get the sentence embeddings and the labels fpr train and test + # get the sentence embeddings and the labels for train and test X_test = computed_simis_test # Y_test = np.array(dft['label']) @@ -115,12 +119,12 @@ def main(): # UP: read pre-trained LR model clf_log = pickle.load(open("/var/www/html/moodle/asyst/Source/Skript/german/models/clf_BERT.pickle", "rb")) - # print('--------Evaluate on Testset------- ', file=out) predictions = clf_log.predict(X_test) # UP print results with open(args.output_dir + "/predictions.txt", "w") as writer: + # TODO: write results to plugins DB Table writer.write("question\treferenceAnswer\tstudentAnswer\tsuggested grade\tobserved grade\n") for i in range(len(dft)): hrpred = "incorrect" @@ -139,7 +143,7 @@ def main(): + "\n" ) - # print('\nExecution time:', time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time)),file=out) + # print('\nExecution time:', time.strftime("%H:%M:%S", time.gmtime(time.time() - start_time)), file=out) if __name__ == "__main__": diff --git a/docker-compose.yml b/docker-compose.yml index 0932d69..0f8c399 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,6 +42,9 @@ services: - 80:80 - 443:443 - 5000:5000 + dns: + - 8.8.8.8 + - 8.8.4.4 depends_on: # Dependency from MariaDB service - mariadb volumes: @@ -51,12 +54,6 @@ services: - moodledata:${MOODLE_BASE_DIR_DATA} # Том Ð´Ð»Ñ Ð´Ð°Ð½Ð½Ñ‹Ñ… Moodle networks: - network # Добавление к Ñети - healthcheck: - test: /bin/bash -c 'while ! nc -z mariadb 3306; do echo "Ждем Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº базе данных..."; sleep 5; done' - interval: 10s - timeout: 5s - retries: 5 - command: [ "bash", "-c", "apache2ctl -D FOREGROUND" ] volumes: moodledata: driver: local # Локальный драйвер томов diff --git a/install_moodle.sh b/install_moodle.sh index c261025..49744e2 100755 --- a/install_moodle.sh +++ b/install_moodle.sh @@ -24,8 +24,8 @@ docker-compose exec moodle php ${MOODLE_BASE_DIR}/admin/cli/install.php \ docker-compose exec moodle chmod -R 755 /var/www/html/moodle # Set correct access rules for the plugin -#docker-compose exec moodle chown -R www-data:www-data /var/www/html/moodle/mod/yourplugin -#docker-compose exec moodle chmod -R 775 /var/www/html/moodle/mod/yourplugin +docker-compose exec moodle chown -R www-data:www-data /var/www/html/moodle/mod/yourplugin +docker-compose exec moodle chmod -R 775 /var/www/html/moodle/mod/yourplugin # Create the run_sag script file docker-compose exec moodle bash -c 'echo "#!/bin/bash" > /usr/local/bin/run_sag' diff --git a/readme.md b/readme.md index f3c8a29..46c67b3 100755 --- a/readme.md +++ b/readme.md @@ -4,11 +4,16 @@ Run these commands at CLI to use ASYST with universal BERT model based on German ~~~bash docker-compose up -d --build -install_moodle +/bin/bash install_moodle.sh ~~~ +Use these creds to access Moodle admin page +admin:rootpassword + +Database connection URL: jdbc:mariadb://localhost:3306/moodle + It is suggested to use our moodle plugin to communicate with ASYST script using such a -route http://127.0.0.1:5000/api/data +route http://127.0.0.1:5000 Now the preinstalled MOODLE LMS is available at https://www.moodle.loc diff --git a/supervisord.conf b/supervisord.conf index 8cc191d..a5d4e26 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -5,9 +5,13 @@ nodaemon=true command=/usr/sbin/apache2ctl -D FOREGROUND autostart=true autorestart=true +stdout_logfile=/var/log/supervisor/flask.log +stderr_logfile=/var/log/supervisor/flask_err.log [program:flask] -command=/opt/myenv/bin/python /var/www/html/moodle/api.py +command=/bin/bash -c 'source /opt/myenv/bin/activate && FLASK_APP=/var/www/html/moodle/api.py /opt/myenv/bin/flask run --host=0.0.0.0' directory=/var/www/html/moodle autostart=true -autorestart=true \ No newline at end of file +autorestart=true +stdout_logfile=/var/log/supervisor/moodle.log +stderr_logfile=/var/log/supervisor/moodle_err.log \ No newline at end of file diff --git a/yourplugin/db/install.xml b/yourplugin/db/install.xml new file mode 100644 index 0000000..5c484d0 --- /dev/null +++ b/yourplugin/db/install.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<XMLDB PATH="mod/yourplugin/db" VERSION="2024032201" COMMENT="XMLDB file for mod yourplugin plugin" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd" +> + <TABLES> + <TABLE NAME="mod_yourplugin" COMMENT="Table for storing yourplugin data"> + <FIELDS> + <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true"/> + <FIELD NAME="question" TYPE="text" NOTNULL="true"/> + <FIELD NAME="referenceanswer" TYPE="text" NOTNULL="true"/> + <FIELD NAME="studentanswer" TYPE="text" NOTNULL="true"/> + <FIELD NAME="observed_grade" TYPE="int" LENGTH="3" NOTNULL="true"/> + <FIELD NAME="studentid" TYPE="int" LENGTH="10" NOTNULL="false"/> + <FIELD NAME="recordid" TYPE="int" LENGTH="10" NOTNULL="false"/> + </FIELDS> + <KEYS> + <KEY NAME="primary" TYPE="primary" FIELDS="id"/> + </KEYS> + </TABLE> + </TABLES> +</XMLDB> diff --git a/yourplugin/lang/en/yourplugin.php b/yourplugin/lang/en/yourplugin.php index 28c38a6..9968e15 100755 --- a/yourplugin/lang/en/yourplugin.php +++ b/yourplugin/lang/en/yourplugin.php @@ -10,32 +10,36 @@ $string['pluginname'] = 'New local plugin'; // Path to the Python 3 executable -$python_executable = '/usr/bin/python3'; +$python_executable = '/opt/myenv/bin/python3'; // Path to the moodlemlbackend script to be executed. First we use test API api.py, then run_LR_SBERT.py $python_script = '/var/www/html/moodle/api.py'; //$python_script = '/var/www/html/moodle/asyst/Source/Skript/german/run_LR_SBERT.py'; // Python command you want to execute -$python_command = 'print("Hello, world!!!")'; +//$python_command = 'print("Hello, world!!!")'; +// Execution the command and getting the result +//shell_exec($python_command); // Formation of a command to execute //$full_command = $python_executable . ' -c \'' . $python_command . '\''; -$full_command = $python_executable . ' ' . $python_script; +//$full_command = $python_executable . ' ' . $python_script; -// Execution the command and getting the result -$result = shell_exec($full_command); +// Now call the API +$api_url = 'http://127.0.0.1:5000/api/data'; +$response = file_get_contents($api_url); -// Output the result -echo $result; -// Output the result (assuming moodlemlbackend returns JSON) -$data = json_decode($result, true); -if ($data !== null) { +// Output the result (assuming backend returns JSON) +if ($response !== false) { + $data = json_decode($response, true); + if ($data !== null) { // Data processing -// Example: output results - echo "<pre>"; - print_r($data); - echo "</pre>"; +// Example: output results echo "<pre>"; + print_r($data); + echo "</pre>"; + } else { + echo "Error on data processing from moodlemlbackend!"; + } } else { - echo "Error on data processing from moodlemlbackend!"; + echo "Execution error: result is not a string."; } diff --git a/yourplugin/lib.php b/yourplugin/lib.php new file mode 100644 index 0000000..d495ebd --- /dev/null +++ b/yourplugin/lib.php @@ -0,0 +1,27 @@ +<?php +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +/** + * @package mod_yourplugin + * @author Artem Baranovskyi + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +function mod_yourplugin_before_footer() +{ + if (!get_config('mod_yourplugin', 'enabled')) { + return; + } +} \ No newline at end of file diff --git a/yourplugin/version.php b/yourplugin/version.php index 452923b..26404c9 100755 --- a/yourplugin/version.php +++ b/yourplugin/version.php @@ -2,14 +2,14 @@ /** * Version details. * - * @package local_yourplugin + * @package mod_yourplugin * @copyright 2024 Artem Baranovskyi * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022051101; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2024032201; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2014050800; // Requires this Moodle version. -$plugin->component = 'yourplugin';// Full name of the plugin (used for diagnostics). +$plugin->component = 'mod_yourplugin'; // Full name of the plugin (used for diagnostics). $plugin->cron = 0; -- GitLab