releasing the app to the public
Browse files- .gitattributes +2 -9
- .gitignore +7 -0
- Dockerfile +11 -0
- README.md +62 -7
- app.py +83 -0
- cefr-vocab.csv +0 -0
- model.py +196 -0
- modules/dataset.py +19 -0
- modules/inference.py +11 -0
- requirements.txt +13 -0
- simple_trained_wsd_pipeline/config.json +52 -0
- simple_trained_wsd_pipeline/merges.txt +0 -0
- simple_trained_wsd_pipeline/pytorch_model.bin +3 -0
- simple_trained_wsd_pipeline/special_tokens_map.json +15 -0
- simple_trained_wsd_pipeline/tokenizer.json +0 -0
- simple_trained_wsd_pipeline/tokenizer_config.json +15 -0
- simple_trained_wsd_pipeline/vocab.json +0 -0
- static/index.js +126 -0
- static/style.css +79 -0
- templates/index.html +189 -0
- templates/result.html +210 -0
.gitattributes
CHANGED
@@ -1,35 +1,28 @@
|
|
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 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
-
*.
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
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 |
+
*.bin.* filter=lfs diff=lfs merge=lfs -text
|
5 |
*.bz2 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 |
*.model filter=lfs diff=lfs merge=lfs -text
|
12 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
13 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
14 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
15 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
16 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
17 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
18 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
19 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
|
|
20 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
21 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
22 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
23 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
24 |
*.wasm filter=lfs diff=lfs merge=lfs -text
|
25 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
26 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.zstandard filter=lfs diff=lfs merge=lfs -text
|
28 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
__pycache__/
|
2 |
+
modules/__pycache__/
|
3 |
+
192.168.184.49.txt
|
4 |
+
127.0.0.1.txt
|
5 |
+
.venv/
|
6 |
+
venv/
|
7 |
+
env/
|
Dockerfile
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.10
|
2 |
+
|
3 |
+
WORKDIR /code
|
4 |
+
|
5 |
+
COPY ./requirements.txt /code/requirements.txt
|
6 |
+
|
7 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
8 |
+
|
9 |
+
COPY . .
|
10 |
+
|
11 |
+
CMD ["waitress-serve", "--host", "0.0.0.0", "--port", "7860", "app:app"]
|
README.md
CHANGED
@@ -1,11 +1,66 @@
|
|
1 |
---
|
2 |
-
title: Vocabulary Exam Generator
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
-
sdk:
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
license: mit
|
|
|
9 |
---
|
10 |
|
11 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: T2E Vocabulary Exam Generator
|
3 |
+
emoji: ✏️
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: blue
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 4.40.0
|
8 |
+
python_version: 3.10.4
|
9 |
+
app_file: app.py
|
10 |
+
models:
|
11 |
+
- osanseviero/BigGAN-deep-128
|
12 |
+
- t5-small
|
13 |
+
datasets:
|
14 |
+
- emotion
|
15 |
license: mit
|
16 |
+
pinned: true
|
17 |
---
|
18 |
|
19 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces#reference
|
20 |
+
|
21 |
+
# T2E Vocabulary Exam Generator
|
22 |
+
T2E Vocabulary Exam Generator as the name suggest, is an open souce program that allows user, in this case being teachers, to generate English vocabulary exam right from their browser. We aim to help reduce the pain point of teacher having to spend his/her/their precious time and effort to make the entire vocabulary exam or quiz. To try it out, click the Demo link in the Quick Links section below.
|
23 |
+
|
24 |
+
This readme.md file is an explanation of this folder that it's in.
|
25 |
+
|
26 |
+
## Quick Links
|
27 |
+
- Demo: https://huggingface.co/spaces/nontGcob/T2E_Vocabulary_Exam_Generator
|
28 |
+
- Documentation: https://mario-world.medium.com/text-to-exam-generator-nlp-using-machine-learning-71da8dd93a4a
|
29 |
+
|
30 |
+
## Important File Explanation
|
31 |
+
- Exploratory_Data_Analysis.ipynb
|
32 |
+
- This code runs through the entire dataset that we have and analyse the part of speech of all the lexical vocabulary, the max/min/average vocabulary length, and the number of times a word is presented
|
33 |
+
- Test_&_Fine_tune_&_Retest.ipynb
|
34 |
+
- This code tests the accuracy of the pre-trained model which is our baseline. It fine-tunes the model. And then, it tests the fine-tuned model again to check whether the accuracy has increased or not.
|
35 |
+
- Error_Analysis.ipynb
|
36 |
+
- This code analyses the prediction by analysing the type number of correct and wrong vocabulary predictions, the types of the part of speech of the correct and wrong vocab predictions, and the average vocabulary length of the correct and wrong vocab predictions.
|
37 |
+
|
38 |
+
## Note
|
39 |
+
Please keep in mind that in order to run the .ipynb file in this folder, you must also download the "simple_trained_wsd_pipeline" and "semcor_samples_4-samples.json" as well which are not included in the folder due to the file size limitation of the code that can be uploaded to GitHub. For more information and the explanation of the entire process of this machine learning project, visit the Documentation link that is attached above in the Quick Links section.
|
40 |
+
|
41 |
+
## Contact me
|
42 |
+
- Name: Nutnornont Chamadol
|
43 |
+
- Email: nontc49@gmail.com
|
44 |
+
- Visit my personal website https://nontgcob.com/ to learn more about me.
|
45 |
+
|
46 |
+
## The MIT License (MIT)
|
47 |
+
|
48 |
+
Copyright (c) 2023 Nutnornont Chamadol
|
49 |
+
|
50 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
51 |
+
of this software and associated documentation files (the "Software"), to deal
|
52 |
+
in the Software without restriction, including without limitation the rights
|
53 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
54 |
+
copies of the Software, and to permit persons to whom the Software is
|
55 |
+
furnished to do so, subject to the following conditions:
|
56 |
+
|
57 |
+
The above copyright notice and this permission notice shall be included in all
|
58 |
+
copies or substantial portions of the Software.
|
59 |
+
|
60 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
61 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
62 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
63 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
64 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
65 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
66 |
+
SOFTWARE.
|
app.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, render_template, request, send_file
|
2 |
+
from urllib.parse import quote as url_quote
|
3 |
+
from model import model
|
4 |
+
|
5 |
+
app = Flask(__name__)
|
6 |
+
|
7 |
+
@app.route('/process', methods=['POST'])
|
8 |
+
def process():
|
9 |
+
T2E_exam = str(request.remote_addr) + ".txt"
|
10 |
+
text = request.form['text']
|
11 |
+
cefr_level = request.form['cefr_level']
|
12 |
+
|
13 |
+
# Call your Python function here to process the data
|
14 |
+
output = model(text, cefr_level)
|
15 |
+
|
16 |
+
user_data = {cefr_level: text}
|
17 |
+
with open("user_data_log.txt", "a") as file:
|
18 |
+
file.write(str(user_data) + "\n\n")
|
19 |
+
|
20 |
+
# Save the output to a file
|
21 |
+
count = 0
|
22 |
+
max_choice = 4
|
23 |
+
|
24 |
+
with open(T2E_exam, "w") as file:
|
25 |
+
file.write("__________ T2E Vocabulary Exam Generator __________\n")
|
26 |
+
file.write("| Welcome to T2E Vocabulary Exam Generator! |\n")
|
27 |
+
file.write("| We are glad that our service is useful to you. |\n")
|
28 |
+
file.write("| |\n")
|
29 |
+
file.write("| Copyrights 2023, Nutnornont Chamadol |\n")
|
30 |
+
file.write("| Email: nontc49@gmail.com |\n")
|
31 |
+
file.write("| Visit https://nontgcob.com to learn more |\n")
|
32 |
+
file.write("| |\n")
|
33 |
+
file.write("| Your exam is generated below. |\n")
|
34 |
+
file.write("| - Happy using T2E Vocabulary Exam Generator! - |\n")
|
35 |
+
file.write("|__________________________________________________|\n")
|
36 |
+
file.write("\n")
|
37 |
+
file.write("If you don't see any text on the Result page, try changing ")
|
38 |
+
file.write("the CEFR difficulty level selected or choose ALL CEFR level ")
|
39 |
+
file.write("to make sure you get all the questions that the AI can generate. ")
|
40 |
+
file.write("Another possible reason why nothing comes out of the program is ")
|
41 |
+
file.write("because there is no word that can be turned into an exam, try ")
|
42 |
+
file.write("putting a longer text passage as an input into the textbox instead.\n")
|
43 |
+
file.write("Visit https://scribehow.com/shared/How_to_use_T2E_Vocabulary_Exam_Generator__vyYu396JT_qZ0jKATVUqeQ#89cd5f52 for more information.\n")
|
44 |
+
file.write("\n")
|
45 |
+
file.write("Note: The first choice of each question is the correct answer, the rest are trick choices!\n")
|
46 |
+
file.write("\n")
|
47 |
+
file.write("\n")
|
48 |
+
|
49 |
+
for key, value in output.items():
|
50 |
+
vvocab, sentence = key.split(" = ")
|
51 |
+
# print(f'What does the word "{vvocab}" means in this sentence "{sentence}"?')
|
52 |
+
with open(T2E_exam, "a") as file:
|
53 |
+
file.write(f'What is the meaning of the word "{vvocab}" in this sentence "{sentence}"?\n')
|
54 |
+
|
55 |
+
for choice in value:
|
56 |
+
# print(f"- {choice}")
|
57 |
+
with open(T2E_exam, "a") as file:
|
58 |
+
file.write(f"- {choice}\n")
|
59 |
+
count += 1
|
60 |
+
# if count > (max_choice + 1):
|
61 |
+
# break
|
62 |
+
with open(T2E_exam, "a") as file:
|
63 |
+
file.write("\n")
|
64 |
+
|
65 |
+
# print("output:", output)
|
66 |
+
# print(type(output))
|
67 |
+
|
68 |
+
return render_template('result.html', output=output, file_path="T2E_exam.txt")
|
69 |
+
|
70 |
+
@app.route('/')
|
71 |
+
def index():
|
72 |
+
return render_template('index.html')
|
73 |
+
|
74 |
+
@app.route('/send')
|
75 |
+
def get_file():
|
76 |
+
T2E_exam = str(request.remote_addr) + ".txt"
|
77 |
+
return send_file(
|
78 |
+
str(request.remote_addr) + ".txt",
|
79 |
+
# download_name = "T2E_exam.txt"
|
80 |
+
)
|
81 |
+
|
82 |
+
if __name__ == "__main__":
|
83 |
+
app.run(host='0.0.0.0', port=7860)
|
cefr-vocab.csv
ADDED
The diff for this file is too large to render.
See raw diff
|
|
model.py
ADDED
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Importing libraries
|
2 |
+
from nltk.corpus import wordnet
|
3 |
+
import nltk
|
4 |
+
import transformers
|
5 |
+
import pandas as pd
|
6 |
+
import json
|
7 |
+
import random
|
8 |
+
import torch
|
9 |
+
|
10 |
+
device='cpu'
|
11 |
+
|
12 |
+
# Declare the (trained) model that will be used
|
13 |
+
classifier = transformers.pipeline("zero-shot-classification", model="simple_trained_wsd_pipeline", device=device)
|
14 |
+
|
15 |
+
import spacy
|
16 |
+
# Part Of Speech tagging (POS tagging)
|
17 |
+
nlp = spacy.load("en_core_web_sm")
|
18 |
+
|
19 |
+
print('successfully download model')
|
20 |
+
|
21 |
+
|
22 |
+
def model(passage, level):
|
23 |
+
# pip install spacy
|
24 |
+
# pip install transformers
|
25 |
+
# pip install torch
|
26 |
+
# pip install en_core_web_sm
|
27 |
+
# python -m spacy download en_core_web_sm
|
28 |
+
# pip install spacy-download
|
29 |
+
# pip install nltk
|
30 |
+
|
31 |
+
nltk.download('wordnet')
|
32 |
+
nltk.download('omw-1.4')
|
33 |
+
|
34 |
+
# Passing file directories into variables
|
35 |
+
# text_input = "./text_input.txt"
|
36 |
+
cefr_vocab = "cefr-vocab.csv"
|
37 |
+
|
38 |
+
# Create and open the text file
|
39 |
+
# with open(text_input, "a") as file:
|
40 |
+
# file.write(".") # Add a full stop at the end to make sure there is a full stop at the end of the text for the model to understand where to stop the sentence
|
41 |
+
|
42 |
+
|
43 |
+
# Ask the user for the CEFR level
|
44 |
+
# while True:
|
45 |
+
# cefr_level = input("Which CEFR level you want to test?: ").upper()
|
46 |
+
# if "A1" in cefr_level or "A2" in cefr_level or "B1" in cefr_level or "B2" in cefr_level or "C1" in cefr_level or "C2" in cefr_level:
|
47 |
+
# break
|
48 |
+
# else:
|
49 |
+
# continue
|
50 |
+
cefr_level = level
|
51 |
+
|
52 |
+
# Read from the input file
|
53 |
+
# with open(text_input, "r") as file:
|
54 |
+
# txt = str(file.readlines()).replace("[", "").replace("'", "").replace("]", "")
|
55 |
+
txt = passage + "."
|
56 |
+
|
57 |
+
if "." in txt:
|
58 |
+
txt = (txt.split("."))
|
59 |
+
else:
|
60 |
+
txt = txt
|
61 |
+
|
62 |
+
text_dict = {}
|
63 |
+
for n in txt:
|
64 |
+
n = n.strip()
|
65 |
+
ex1 = nlp(n)
|
66 |
+
|
67 |
+
for word in ex1:
|
68 |
+
sentence_question_tag = n.replace(word.text, f"[{word.text}]")
|
69 |
+
text_dict[f"{word.lemma_} = {sentence_question_tag}"] = word.pos_
|
70 |
+
|
71 |
+
# Collect the tagging results (filter in just NOUN, PROPN, VERB, ADJ, or ADV only)
|
72 |
+
collector = {}
|
73 |
+
for key, value in text_dict.items():
|
74 |
+
if "NOUN" in value or "VERB" in value or "ADJ" in value or "ADV" in value:
|
75 |
+
collector[key] = value
|
76 |
+
|
77 |
+
# Collect the CEFR level of the words collected before
|
78 |
+
reference = pd.read_csv(cefr_vocab)
|
79 |
+
|
80 |
+
matching = {}
|
81 |
+
for row_idx in range(reference.shape[0]):
|
82 |
+
row = reference.iloc[row_idx]
|
83 |
+
key = f"{row.headword}, {row.pos}"
|
84 |
+
matching[key] = row.CEFR
|
85 |
+
|
86 |
+
# Convert pos of the word into all lowercase to match another data set with CEFR level
|
87 |
+
for key1, value1 in collector.items():
|
88 |
+
if value1 == "NOUN":
|
89 |
+
collector[key1] = "noun"
|
90 |
+
if value1 == "VERB":
|
91 |
+
collector[key1] = "verb"
|
92 |
+
if value1 == "ADJ":
|
93 |
+
collector[key1] = "adjective"
|
94 |
+
if value1 == "ADV":
|
95 |
+
collector[key1] = "adverb"
|
96 |
+
|
97 |
+
# Matching 2 datasets together by the word and the pos
|
98 |
+
ready2filter = {}
|
99 |
+
for key, value in matching.items():
|
100 |
+
first_key, second_key = key.split(", ")
|
101 |
+
for key2, value2 in collector.items():
|
102 |
+
key2 = key2.split(" = ")
|
103 |
+
if first_key == key2[0].lower():
|
104 |
+
if second_key == value2:
|
105 |
+
ready2filter[f"{key} = {key2[1]}"] = value
|
106 |
+
|
107 |
+
# Filter in just the vocab that has the selected CEFR level that the user provided at the beginning
|
108 |
+
filtered0 = {}
|
109 |
+
for key, value in ready2filter.items():
|
110 |
+
if cefr_level == "ALL":
|
111 |
+
filtered0[key] = value
|
112 |
+
else:
|
113 |
+
if value == cefr_level:
|
114 |
+
filtered0[key] = value
|
115 |
+
|
116 |
+
# Rearrange the Python dictionary structure
|
117 |
+
filtered = {}
|
118 |
+
for key, value in filtered0.items():
|
119 |
+
key_parts = key.split(', ')
|
120 |
+
new_key = key_parts[0]
|
121 |
+
new_value = key_parts[1]
|
122 |
+
filtered[new_key] = new_value
|
123 |
+
|
124 |
+
# Grab the definition of each vocab from the NLTK wordnet English dictionary
|
125 |
+
def_filtered = {}
|
126 |
+
for key3, value3 in filtered.items():
|
127 |
+
syns = wordnet.synsets(key3)
|
128 |
+
partofspeech, context = value3.split(" = ")
|
129 |
+
def_filtered[f"{key3} = {context}"] = []
|
130 |
+
|
131 |
+
# pos conversion
|
132 |
+
if partofspeech == "noun":
|
133 |
+
partofspeech = "n"
|
134 |
+
if partofspeech == "verb":
|
135 |
+
partofspeech = "v"
|
136 |
+
if partofspeech == "adjective":
|
137 |
+
partofspeech = "s"
|
138 |
+
if partofspeech == "adverb":
|
139 |
+
partofspeech = "r"
|
140 |
+
|
141 |
+
# print("def_filtered 0:", def_filtered)
|
142 |
+
|
143 |
+
# Adding the definitions into the Python dictionary, def_filtered (syns variable does the job of finding the relevant word aka synonyms)
|
144 |
+
for s in syns:
|
145 |
+
# print('s:', s)
|
146 |
+
# print("syns:", syns)
|
147 |
+
def_filtered[f"{key3} = {context}"].append(s.definition())
|
148 |
+
# print("def_filtered 1:", def_filtered)
|
149 |
+
|
150 |
+
# Use Nvidia CUDA core if available
|
151 |
+
# if torch.cuda.is_available():
|
152 |
+
# device=0
|
153 |
+
# else:
|
154 |
+
|
155 |
+
|
156 |
+
# Process Python dictionary, def_filtereddic
|
157 |
+
correct_def = {}
|
158 |
+
for key4, value4 in def_filtered.items():
|
159 |
+
vocab, context = key4.split(" = ")
|
160 |
+
sequence_to_classify = context
|
161 |
+
candidate_labels = value4
|
162 |
+
# correct_def[key4] = []
|
163 |
+
correct_def_list = []
|
164 |
+
temp_def = []
|
165 |
+
hypothesis_template = 'The meaning of [' + vocab + '] is {}.'
|
166 |
+
|
167 |
+
output = classifier(sequence_to_classify, candidate_labels, hypothesis_template=hypothesis_template)
|
168 |
+
|
169 |
+
# Process the score of each definition and add it to the Python dictionary, correct_def
|
170 |
+
for label, score in zip(output['labels'], output['scores']):
|
171 |
+
temp_def.append(label)
|
172 |
+
# print(temp_def)
|
173 |
+
for first in range(len(temp_def)):
|
174 |
+
if first == 0:
|
175 |
+
val = f">> {temp_def[first]}"
|
176 |
+
else:
|
177 |
+
val = f"{temp_def[first]}"
|
178 |
+
|
179 |
+
correct_def_list.append(val)
|
180 |
+
|
181 |
+
print(type(key4), type(correct_def_list))
|
182 |
+
correct_def[key4] = correct_def_list
|
183 |
+
|
184 |
+
# correct_def[key4].append(f"{label}")
|
185 |
+
|
186 |
+
return correct_def
|
187 |
+
|
188 |
+
# with open(T2E_exam, "r") as file:
|
189 |
+
# exam = file.readlines()
|
190 |
+
# print(exam)
|
191 |
+
# return(exam)
|
192 |
+
|
193 |
+
|
194 |
+
# passage = "Computer is good"
|
195 |
+
# level = "A1"
|
196 |
+
# print(model(passage, level))
|
modules/dataset.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datasets import load_dataset
|
2 |
+
|
3 |
+
dataset = load_dataset("go_emotions", split="train")
|
4 |
+
|
5 |
+
emotions = dataset.info.features['labels'].feature.names
|
6 |
+
|
7 |
+
def query_emotion(start, end):
|
8 |
+
rows = dataset[start:end]
|
9 |
+
texts, labels = [rows[k] for k in rows.keys()]
|
10 |
+
|
11 |
+
observations = []
|
12 |
+
|
13 |
+
for i, text in enumerate(texts):
|
14 |
+
observations.append({
|
15 |
+
"text": text,
|
16 |
+
"emotion": emotions[labels[i]],
|
17 |
+
})
|
18 |
+
|
19 |
+
return observations
|
modules/inference.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import T5Tokenizer, T5ForConditionalGeneration
|
2 |
+
|
3 |
+
tokenizer = T5Tokenizer.from_pretrained("t5-small")
|
4 |
+
model = T5ForConditionalGeneration.from_pretrained("t5-small")
|
5 |
+
|
6 |
+
|
7 |
+
def infer_t5(input):
|
8 |
+
input_ids = tokenizer(input, return_tensors="pt").input_ids
|
9 |
+
outputs = model.generate(input_ids)
|
10 |
+
|
11 |
+
return tokenizer.decode(outputs[0], skip_special_tokens=True)
|
requirements.txt
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
datasets==2.*
|
2 |
+
flask==2.2.3
|
3 |
+
requests==2.27.*
|
4 |
+
sentencepiece==0.1.*
|
5 |
+
torch==2.*
|
6 |
+
transformers==4.*
|
7 |
+
gunicorn==20.1.*
|
8 |
+
waitress==2.0.*
|
9 |
+
nltk==3.8.1
|
10 |
+
spacy==3.5.3
|
11 |
+
numpy<2.0
|
12 |
+
Werkzeug==2.2.2
|
13 |
+
https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.5.0/en_core_web_sm-3.5.0-py3-none-any.whl
|
simple_trained_wsd_pipeline/config.json
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"_name_or_path": "facebook/bart-large-mnli",
|
3 |
+
"_num_labels": 3,
|
4 |
+
"activation_dropout": 0.0,
|
5 |
+
"activation_function": "gelu",
|
6 |
+
"add_final_layer_norm": false,
|
7 |
+
"architectures": [
|
8 |
+
"BartForSequenceClassification"
|
9 |
+
],
|
10 |
+
"attention_dropout": 0.0,
|
11 |
+
"bos_token_id": 0,
|
12 |
+
"classif_dropout": 0.0,
|
13 |
+
"classifier_dropout": 0.0,
|
14 |
+
"d_model": 1024,
|
15 |
+
"decoder_attention_heads": 16,
|
16 |
+
"decoder_ffn_dim": 4096,
|
17 |
+
"decoder_layerdrop": 0.0,
|
18 |
+
"decoder_layers": 12,
|
19 |
+
"decoder_start_token_id": 2,
|
20 |
+
"dropout": 0.1,
|
21 |
+
"encoder_attention_heads": 16,
|
22 |
+
"encoder_ffn_dim": 4096,
|
23 |
+
"encoder_layerdrop": 0.0,
|
24 |
+
"encoder_layers": 12,
|
25 |
+
"eos_token_id": 2,
|
26 |
+
"forced_eos_token_id": 2,
|
27 |
+
"gradient_checkpointing": false,
|
28 |
+
"id2label": {
|
29 |
+
"0": "contradiction",
|
30 |
+
"1": "neutral",
|
31 |
+
"2": "entailment"
|
32 |
+
},
|
33 |
+
"init_std": 0.02,
|
34 |
+
"is_encoder_decoder": true,
|
35 |
+
"label2id": {
|
36 |
+
"contradiction": 0,
|
37 |
+
"entailment": 2,
|
38 |
+
"neutral": 1
|
39 |
+
},
|
40 |
+
"max_position_embeddings": 1024,
|
41 |
+
"model_type": "bart",
|
42 |
+
"normalize_before": false,
|
43 |
+
"num_hidden_layers": 12,
|
44 |
+
"output_past": false,
|
45 |
+
"pad_token_id": 1,
|
46 |
+
"problem_type": "single_label_classification",
|
47 |
+
"scale_embedding": false,
|
48 |
+
"torch_dtype": "float32",
|
49 |
+
"transformers_version": "4.29.1",
|
50 |
+
"use_cache": true,
|
51 |
+
"vocab_size": 50265
|
52 |
+
}
|
simple_trained_wsd_pipeline/merges.txt
ADDED
The diff for this file is too large to render.
See raw diff
|
|
simple_trained_wsd_pipeline/pytorch_model.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ebe92cb602c9187d2ac775c5f0d98827ab9291293307c1a5090efae8ed94f251
|
3 |
+
size 1629551961
|
simple_trained_wsd_pipeline/special_tokens_map.json
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"bos_token": "<s>",
|
3 |
+
"cls_token": "<s>",
|
4 |
+
"eos_token": "</s>",
|
5 |
+
"mask_token": {
|
6 |
+
"content": "<mask>",
|
7 |
+
"lstrip": true,
|
8 |
+
"normalized": false,
|
9 |
+
"rstrip": false,
|
10 |
+
"single_word": false
|
11 |
+
},
|
12 |
+
"pad_token": "<pad>",
|
13 |
+
"sep_token": "</s>",
|
14 |
+
"unk_token": "<unk>"
|
15 |
+
}
|
simple_trained_wsd_pipeline/tokenizer.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
simple_trained_wsd_pipeline/tokenizer_config.json
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"add_prefix_space": false,
|
3 |
+
"bos_token": "<s>",
|
4 |
+
"clean_up_tokenization_spaces": true,
|
5 |
+
"cls_token": "<s>",
|
6 |
+
"eos_token": "</s>",
|
7 |
+
"errors": "replace",
|
8 |
+
"mask_token": "<mask>",
|
9 |
+
"model_max_length": 1024,
|
10 |
+
"pad_token": "<pad>",
|
11 |
+
"sep_token": "</s>",
|
12 |
+
"tokenizer_class": "BartTokenizer",
|
13 |
+
"trim_offsets": true,
|
14 |
+
"unk_token": "<unk>"
|
15 |
+
}
|
simple_trained_wsd_pipeline/vocab.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
static/index.js
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
if (document.location.search.includes('dark-theme=true')) {
|
2 |
+
document.body.classList.add('dark-theme');
|
3 |
+
}
|
4 |
+
|
5 |
+
let cursor = 0;
|
6 |
+
const RANGE = 5;
|
7 |
+
const LIMIT = 16_000;
|
8 |
+
|
9 |
+
const textToImage = async (text) => {
|
10 |
+
const inferenceResponse = await fetch(`infer_biggan?input=${text}`);
|
11 |
+
const inferenceBlob = await inferenceResponse.blob();
|
12 |
+
|
13 |
+
return URL.createObjectURL(inferenceBlob);
|
14 |
+
};
|
15 |
+
|
16 |
+
const translateText = async (text) => {
|
17 |
+
const inferResponse = await fetch(`infer_t5?input=${text}`);
|
18 |
+
const inferJson = await inferResponse.json();
|
19 |
+
|
20 |
+
return inferJson.output;
|
21 |
+
};
|
22 |
+
|
23 |
+
const queryDataset = async (start, end) => {
|
24 |
+
const queryResponse = await fetch(`query_emotion?start=${start}&end=${end}`);
|
25 |
+
const queryJson = await queryResponse.json();
|
26 |
+
|
27 |
+
return queryJson.output;
|
28 |
+
};
|
29 |
+
|
30 |
+
const updateTable = async (cursor, range = RANGE) => {
|
31 |
+
const table = document.querySelector('.dataset-output');
|
32 |
+
|
33 |
+
const fragment = new DocumentFragment();
|
34 |
+
|
35 |
+
const observations = await queryDataset(cursor, cursor + range);
|
36 |
+
|
37 |
+
for (const observation of observations) {
|
38 |
+
let row = document.createElement('tr');
|
39 |
+
let text = document.createElement('td');
|
40 |
+
let emotion = document.createElement('td');
|
41 |
+
|
42 |
+
text.textContent = observation.text;
|
43 |
+
emotion.textContent = observation.emotion;
|
44 |
+
|
45 |
+
row.appendChild(text);
|
46 |
+
row.appendChild(emotion);
|
47 |
+
fragment.appendChild(row);
|
48 |
+
}
|
49 |
+
|
50 |
+
table.innerHTML = '';
|
51 |
+
|
52 |
+
table.appendChild(fragment);
|
53 |
+
|
54 |
+
table.insertAdjacentHTML(
|
55 |
+
'afterbegin',
|
56 |
+
`<thead>
|
57 |
+
<tr>
|
58 |
+
<td>text</td>
|
59 |
+
<td>emotion</td>
|
60 |
+
</tr>
|
61 |
+
</thead>`
|
62 |
+
);
|
63 |
+
};
|
64 |
+
|
65 |
+
const imageGenSelect = document.getElementById('image-gen-input');
|
66 |
+
const imageGenImage = document.querySelector('.image-gen-output');
|
67 |
+
const textGenForm = document.querySelector('.text-gen-form');
|
68 |
+
const tableButtonPrev = document.querySelector('.table-previous');
|
69 |
+
const tableButtonNext = document.querySelector('.table-next');
|
70 |
+
|
71 |
+
imageGenSelect.addEventListener('change', async (event) => {
|
72 |
+
const value = event.target.value;
|
73 |
+
|
74 |
+
try {
|
75 |
+
imageGenImage.src = await textToImage(value);
|
76 |
+
imageGenImage.alt = value + ' generated from BigGAN AI model';
|
77 |
+
} catch (err) {
|
78 |
+
console.error(err);
|
79 |
+
}
|
80 |
+
});
|
81 |
+
|
82 |
+
textGenForm.addEventListener('submit', async (event) => {
|
83 |
+
event.preventDefault();
|
84 |
+
|
85 |
+
const textGenInput = document.getElementById('text-gen-input');
|
86 |
+
const textGenParagraph = document.querySelector('.text-gen-output');
|
87 |
+
|
88 |
+
try {
|
89 |
+
textGenParagraph.textContent = await translateText(textGenInput.value);
|
90 |
+
} catch (err) {
|
91 |
+
console.error(err);
|
92 |
+
}
|
93 |
+
});
|
94 |
+
|
95 |
+
tableButtonPrev.addEventListener('click', () => {
|
96 |
+
cursor = cursor > RANGE ? cursor - RANGE : 0;
|
97 |
+
|
98 |
+
if (cursor < RANGE) {
|
99 |
+
tableButtonPrev.classList.add('hidden');
|
100 |
+
}
|
101 |
+
if (cursor < LIMIT - RANGE) {
|
102 |
+
tableButtonNext.classList.remove('hidden');
|
103 |
+
}
|
104 |
+
|
105 |
+
updateTable(cursor);
|
106 |
+
});
|
107 |
+
|
108 |
+
tableButtonNext.addEventListener('click', () => {
|
109 |
+
cursor = cursor < LIMIT - RANGE ? cursor + RANGE : cursor;
|
110 |
+
|
111 |
+
if (cursor >= RANGE) {
|
112 |
+
tableButtonPrev.classList.remove('hidden');
|
113 |
+
}
|
114 |
+
if (cursor >= LIMIT - RANGE) {
|
115 |
+
tableButtonNext.classList.add('hidden');
|
116 |
+
}
|
117 |
+
|
118 |
+
updateTable(cursor);
|
119 |
+
});
|
120 |
+
|
121 |
+
textToImage(imageGenSelect.value)
|
122 |
+
.then((image) => (imageGenImage.src = image))
|
123 |
+
.catch(console.error);
|
124 |
+
|
125 |
+
updateTable(cursor)
|
126 |
+
.catch(console.error);
|
static/style.css
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
--text: hsl(0 0% 15%);
|
3 |
+
padding: 2.5rem;
|
4 |
+
font-family: sans-serif;
|
5 |
+
color: var(--text);
|
6 |
+
}
|
7 |
+
body.dark-theme {
|
8 |
+
--text: hsl(0 0% 90%);
|
9 |
+
background-color: hsl(223 39% 7%);
|
10 |
+
}
|
11 |
+
|
12 |
+
main {
|
13 |
+
max-width: 80rem;
|
14 |
+
text-align: center;
|
15 |
+
}
|
16 |
+
|
17 |
+
section {
|
18 |
+
display: flex;
|
19 |
+
flex-direction: column;
|
20 |
+
align-items: center;
|
21 |
+
}
|
22 |
+
|
23 |
+
a {
|
24 |
+
color: var(--text);
|
25 |
+
}
|
26 |
+
|
27 |
+
select, input, button, .text-gen-output {
|
28 |
+
padding: 0.5rem 1rem;
|
29 |
+
}
|
30 |
+
|
31 |
+
select, img, input {
|
32 |
+
margin: 0.5rem auto 1rem;
|
33 |
+
}
|
34 |
+
|
35 |
+
form {
|
36 |
+
width: 25rem;
|
37 |
+
margin: 0 auto;
|
38 |
+
}
|
39 |
+
|
40 |
+
input {
|
41 |
+
width: 70%;
|
42 |
+
}
|
43 |
+
|
44 |
+
button {
|
45 |
+
cursor: pointer;
|
46 |
+
}
|
47 |
+
|
48 |
+
.text-gen-output {
|
49 |
+
min-height: 1.2rem;
|
50 |
+
margin: 1rem;
|
51 |
+
border: 0.5px solid grey;
|
52 |
+
}
|
53 |
+
|
54 |
+
#dataset button {
|
55 |
+
width: 6rem;
|
56 |
+
margin: 0.5rem;
|
57 |
+
}
|
58 |
+
|
59 |
+
#dataset button.hidden {
|
60 |
+
visibility: hidden;
|
61 |
+
}
|
62 |
+
|
63 |
+
table {
|
64 |
+
max-width: 40rem;
|
65 |
+
text-align: left;
|
66 |
+
border-collapse: collapse;
|
67 |
+
}
|
68 |
+
|
69 |
+
thead {
|
70 |
+
font-weight: bold;
|
71 |
+
}
|
72 |
+
|
73 |
+
td {
|
74 |
+
padding: 0.5rem;
|
75 |
+
}
|
76 |
+
|
77 |
+
td:not(thead td) {
|
78 |
+
border: 0.5px solid grey;
|
79 |
+
}
|
templates/index.html
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>Input Form | Text to Exam (Vocabulary Exam Generator)</title>
|
5 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
6 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
7 |
+
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
8 |
+
<style>
|
9 |
+
* {
|
10 |
+
margin: 0;
|
11 |
+
padding: 0;
|
12 |
+
font-family: 'Rubik', sans-serif;
|
13 |
+
background-color: #1B1B1B;
|
14 |
+
color: #FFF7D0;
|
15 |
+
font-size: 62.5%;
|
16 |
+
box-sizing: border-box;
|
17 |
+
}
|
18 |
+
|
19 |
+
.container {
|
20 |
+
padding: 3rem 3.4rem;
|
21 |
+
background-color: #212529;
|
22 |
+
border-radius: 24px;
|
23 |
+
height: 50rem;
|
24 |
+
width: 38rem;
|
25 |
+
margin: 0 auto;
|
26 |
+
margin-top: 4%;
|
27 |
+
}
|
28 |
+
|
29 |
+
h1 {
|
30 |
+
font-size: 3.2rem;
|
31 |
+
text-align: center;
|
32 |
+
background-color: transparent;
|
33 |
+
margin-bottom: .6rem;
|
34 |
+
color: #FFE66C;
|
35 |
+
}
|
36 |
+
|
37 |
+
/* Custom Scrollbar */
|
38 |
+
/* width */
|
39 |
+
::-webkit-scrollbar {
|
40 |
+
width: .8rem;
|
41 |
+
}
|
42 |
+
/* Track */
|
43 |
+
::-webkit-scrollbar-track {
|
44 |
+
border-radius: 10px;
|
45 |
+
/* background: #2f3439; */
|
46 |
+
}
|
47 |
+
/* Scroller */
|
48 |
+
::-webkit-scrollbar-thumb {
|
49 |
+
background: #FFF7D0;
|
50 |
+
border-radius: 10px;
|
51 |
+
}
|
52 |
+
|
53 |
+
.subtitle {
|
54 |
+
background-color: transparent;
|
55 |
+
font-size: 1.6rem;
|
56 |
+
text-align: center;
|
57 |
+
margin-bottom: 1.5rem;
|
58 |
+
}
|
59 |
+
|
60 |
+
.docs-tutorial {
|
61 |
+
padding: .6rem 32%;
|
62 |
+
font-size: 1.6rem;
|
63 |
+
text-decoration: none;
|
64 |
+
border-radius: 10px;
|
65 |
+
color: #FFE66C;
|
66 |
+
background-color: #33363A;
|
67 |
+
transition: .3s ease-out;
|
68 |
+
}
|
69 |
+
|
70 |
+
.docs-tutorial:hover {
|
71 |
+
background-color: #FFE66C;
|
72 |
+
color: #212529;
|
73 |
+
}
|
74 |
+
|
75 |
+
.form {
|
76 |
+
margin-top: .8rem;
|
77 |
+
background-color: transparent;
|
78 |
+
width: 100%;
|
79 |
+
}
|
80 |
+
|
81 |
+
.textbox {
|
82 |
+
border: 2px solid #FFF7D0;
|
83 |
+
width: 100%;
|
84 |
+
height: 20rem;
|
85 |
+
background-color: transparent;
|
86 |
+
padding: 1.1rem;
|
87 |
+
font-size: 1.6rem;
|
88 |
+
border-radius: 13px;
|
89 |
+
margin: 1rem 0;
|
90 |
+
}
|
91 |
+
|
92 |
+
.level {
|
93 |
+
font-size: 1.6rem;
|
94 |
+
margin-right: .6rem;
|
95 |
+
background-color: transparent;
|
96 |
+
}
|
97 |
+
|
98 |
+
.cefr {
|
99 |
+
font-size: 1.6rem;
|
100 |
+
border: 2px solid #FFF7D0;
|
101 |
+
border-radius: 8px;
|
102 |
+
padding: .7rem;
|
103 |
+
width: 53.6%;
|
104 |
+
background-color: transparent;
|
105 |
+
}
|
106 |
+
|
107 |
+
.generate {
|
108 |
+
padding: .5rem;
|
109 |
+
font-size: 2rem;
|
110 |
+
border-radius: 20px;
|
111 |
+
background-color: #FFF7D0;
|
112 |
+
color: #212529;
|
113 |
+
font-weight: 600;
|
114 |
+
width: 100%;
|
115 |
+
margin-top: 1rem;
|
116 |
+
transition: .2s ease-out;
|
117 |
+
}
|
118 |
+
|
119 |
+
.generate:hover {
|
120 |
+
background-color: #FFE66C;
|
121 |
+
}
|
122 |
+
|
123 |
+
.dev {
|
124 |
+
background-color: transparent;
|
125 |
+
padding: 1rem 3rem;
|
126 |
+
width: 38rem;
|
127 |
+
margin: 0 auto;
|
128 |
+
margin-top: 10px;
|
129 |
+
margin-bottom: 1.2%;
|
130 |
+
border-radius: 40px;
|
131 |
+
border: 1px solid #33363A;
|
132 |
+
display: flex;
|
133 |
+
flex-direction: row;
|
134 |
+
font-size: 2.6rem;
|
135 |
+
display: flex;
|
136 |
+
justify-content: center;
|
137 |
+
background-color: #212529;
|
138 |
+
text-decoration: none;
|
139 |
+
transition: .2s;
|
140 |
+
}
|
141 |
+
|
142 |
+
.dev:hover {
|
143 |
+
/* background-color: #33363A; */
|
144 |
+
border: 1px solid #FFF7D0;
|
145 |
+
}
|
146 |
+
|
147 |
+
.credit {
|
148 |
+
width: fit-content;
|
149 |
+
margin-right: 3px;
|
150 |
+
background-color: transparent;
|
151 |
+
}
|
152 |
+
</style>
|
153 |
+
</head>
|
154 |
+
<body>
|
155 |
+
<div class="container">
|
156 |
+
<h1>T2E Vocabulary Exam Generator</h1>
|
157 |
+
<p class="subtitle">Generate Vocabulary Exam from context.</p>
|
158 |
+
<a class="docs-tutorial" target="_blank" href="https://scribehow.com/shared/How_to_use_T2E_Vocabulary_Exam_Generator__vyYu396JT_qZ0jKATVUqeQ">Docs & Tutorial</a>
|
159 |
+
<form action="/process" method="POST" class="form">
|
160 |
+
<textarea name="text" id="text" cols="90" rows="30" class="textbox" placeholder="Enter text here (i.e. Computer is good.)"></textarea>
|
161 |
+
<br>
|
162 |
+
<label for="cefr_level" class="level">Select CEFR level :</label>
|
163 |
+
<select name="cefr_level" id="cefr_level" class="cefr">
|
164 |
+
<option value="ALL">ALL</option>
|
165 |
+
<option value="A1">A1</option>
|
166 |
+
<option value="A2">A2</option>
|
167 |
+
<option value="B1">B1</option>
|
168 |
+
<option value="B2">B2</option>
|
169 |
+
<option value="C1">C1</option>
|
170 |
+
<option value="C2">C2</option>
|
171 |
+
</select>
|
172 |
+
<br>
|
173 |
+
<button id="changeText" class="generate">Generate</button>
|
174 |
+
</form>
|
175 |
+
</div>
|
176 |
+
<a href="https://nontgcob.com/" target="_blank" class="dev">
|
177 |
+
<p class="credit">Developed by Nutnornont Chamadol</p>
|
178 |
+
</a>
|
179 |
+
|
180 |
+
<script>
|
181 |
+
const btn = document.getElementById('changeText');
|
182 |
+
|
183 |
+
// Change button text on click
|
184 |
+
btn.addEventListener('click', function() {
|
185 |
+
btn.textContent = 'Generating exam...';
|
186 |
+
});
|
187 |
+
</script>
|
188 |
+
</body>
|
189 |
+
</html>
|
templates/result.html
ADDED
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>Result | Text to Exam (Vocabulary Exam Generator)</title>
|
5 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
6 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
7 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
8 |
+
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
9 |
+
<style>
|
10 |
+
* {
|
11 |
+
margin: 0;
|
12 |
+
padding: 0;
|
13 |
+
font-family: 'Rubik', sans-serif;
|
14 |
+
background-color: #1B1B1B;
|
15 |
+
color: #FFF7D0;
|
16 |
+
font-size: 62.5%;
|
17 |
+
box-sizing: border-box;
|
18 |
+
}
|
19 |
+
|
20 |
+
.container {
|
21 |
+
padding: 3rem 3.4rem;
|
22 |
+
background-color: #212529;
|
23 |
+
border-radius: 24px;
|
24 |
+
height: fit-content;
|
25 |
+
max-height: 53rem;
|
26 |
+
width: 80%;
|
27 |
+
margin: 0 auto;
|
28 |
+
margin-top: 5%;
|
29 |
+
}
|
30 |
+
|
31 |
+
.topbar {
|
32 |
+
display: flex;
|
33 |
+
flex-direction: row;
|
34 |
+
justify-content: space-between;
|
35 |
+
background-color: transparent;
|
36 |
+
}
|
37 |
+
|
38 |
+
h1 {
|
39 |
+
font-size: 3.5rem;
|
40 |
+
text-align: center;
|
41 |
+
background-color: transparent;
|
42 |
+
color: #FFE66C;
|
43 |
+
}
|
44 |
+
|
45 |
+
.exam {
|
46 |
+
background-color: transparent;
|
47 |
+
height: fit-content;
|
48 |
+
max-height: 34rem;
|
49 |
+
overflow-y: scroll;
|
50 |
+
margin-top: 2rem;
|
51 |
+
}
|
52 |
+
|
53 |
+
.generate_another {
|
54 |
+
background-color: #ffffff27;
|
55 |
+
width: fit-content;
|
56 |
+
height: fit-content;
|
57 |
+
padding: 1rem;
|
58 |
+
font-size: 1.8rem;
|
59 |
+
border-radius: 10px;
|
60 |
+
position: absolute;
|
61 |
+
right: 0;
|
62 |
+
margin-top: -7rem;
|
63 |
+
margin-right: 12%;
|
64 |
+
text-decoration: none;
|
65 |
+
border: 2px solid #FFE76C8D;
|
66 |
+
transition: .3s ease-out;
|
67 |
+
color: #FFE66C;
|
68 |
+
}
|
69 |
+
|
70 |
+
.generate_another:hover {
|
71 |
+
border: 2px solid #FFE66C;
|
72 |
+
background-color: #ffe76c22;
|
73 |
+
}
|
74 |
+
|
75 |
+
/* Custom Scrollbar */
|
76 |
+
/* width */
|
77 |
+
::-webkit-scrollbar {
|
78 |
+
width: .8rem;
|
79 |
+
}
|
80 |
+
/* Track */
|
81 |
+
::-webkit-scrollbar-track {
|
82 |
+
border-radius: 10px;
|
83 |
+
/* background: #2f3439; */
|
84 |
+
}
|
85 |
+
/* Scroller */
|
86 |
+
::-webkit-scrollbar-thumb {
|
87 |
+
background: #FFF7D0;
|
88 |
+
border-radius: 10px;
|
89 |
+
}
|
90 |
+
|
91 |
+
.note {
|
92 |
+
background-color: transparent;
|
93 |
+
margin: 1.5rem 0;
|
94 |
+
border: 1px solid #FF0000;
|
95 |
+
border-radius: 10px;
|
96 |
+
padding: 1rem;
|
97 |
+
display: flex;
|
98 |
+
flex-direction: row;
|
99 |
+
width: 100%;
|
100 |
+
background-color: #ff000010;
|
101 |
+
}
|
102 |
+
|
103 |
+
i {
|
104 |
+
margin-right: 1rem;
|
105 |
+
}
|
106 |
+
|
107 |
+
.alert {
|
108 |
+
font-size: 1.6rem;
|
109 |
+
color: #FF0000;
|
110 |
+
background-color: transparent;
|
111 |
+
}
|
112 |
+
|
113 |
+
.question {
|
114 |
+
font-size: 1.8rem;
|
115 |
+
font-weight: 400;
|
116 |
+
background-color: transparent;
|
117 |
+
}
|
118 |
+
|
119 |
+
ul {
|
120 |
+
background-color: transparent;
|
121 |
+
}
|
122 |
+
|
123 |
+
.choice {
|
124 |
+
font-size: 1.8rem;
|
125 |
+
margin-left: 2rem;
|
126 |
+
background-color: transparent;
|
127 |
+
}
|
128 |
+
|
129 |
+
.question_divider {
|
130 |
+
height: 2rem;
|
131 |
+
background-color: transparent;
|
132 |
+
}
|
133 |
+
|
134 |
+
.download {
|
135 |
+
font-size: 2rem;
|
136 |
+
border: 2px solid #FFF7D0;
|
137 |
+
border-radius: 8px;
|
138 |
+
background-color: #FFF7D0;
|
139 |
+
color: #212529;
|
140 |
+
font-weight: 500;
|
141 |
+
padding: .5rem 1.2rem;
|
142 |
+
width: fit-content;
|
143 |
+
height: fit-content;
|
144 |
+
text-decoration: none;
|
145 |
+
}
|
146 |
+
|
147 |
+
.dev {
|
148 |
+
background-color: transparent;
|
149 |
+
padding: 1rem 3rem;
|
150 |
+
width: 38rem;
|
151 |
+
margin: 0 auto;
|
152 |
+
margin-top: 10px;
|
153 |
+
margin-bottom: 1.2%;
|
154 |
+
border-radius: 40px;
|
155 |
+
border: 1px solid #33363A;
|
156 |
+
display: flex;
|
157 |
+
flex-direction: row;
|
158 |
+
font-size: 2.6rem;
|
159 |
+
display: flex;
|
160 |
+
justify-content: center;
|
161 |
+
background-color: #212529;
|
162 |
+
text-decoration: none;
|
163 |
+
transition: .2s;
|
164 |
+
}
|
165 |
+
|
166 |
+
.dev:hover {
|
167 |
+
/* background-color: #33363A; */
|
168 |
+
border: 1px solid #FFF7D0;
|
169 |
+
}
|
170 |
+
|
171 |
+
.credit {
|
172 |
+
width: fit-content;
|
173 |
+
margin-right: 3px;
|
174 |
+
background-color: transparent;
|
175 |
+
}
|
176 |
+
</style>
|
177 |
+
</head>
|
178 |
+
<body>
|
179 |
+
<div class="container">
|
180 |
+
<div class="topbar">
|
181 |
+
<h1>Results</h1>
|
182 |
+
<a href="/send" class="download" download>Download</a>
|
183 |
+
</div>
|
184 |
+
|
185 |
+
<div class="exam">
|
186 |
+
{% if not output %}
|
187 |
+
<div class="note">
|
188 |
+
<i class="alert fa-solid fa-triangle-exclamation"></i>
|
189 |
+
<p class="alert">Oops! Something went wrong. Try changing the CEFR difficulty level or choose ALL CEFR level. You might as well try putting a longer text passage next time.
|
190 |
+
<a class="alert" target="_blank" href="https://scribehow.com/shared/How_to_use_T2E_Vocabulary_Exam_Generator__vyYu396JT_qZ0jKATVUqeQ#89cd5f52">Read more</a>
|
191 |
+
</p>
|
192 |
+
</div>
|
193 |
+
{% endif %}
|
194 |
+
{% for key, values in output.items() %}
|
195 |
+
<h2 class="question">What is the meaning of the word "{{ key.split(' = ')[0] }}" in this sentence "{{ key.split(' = ')[1] }}"?</h2>
|
196 |
+
<ul>
|
197 |
+
{% for value in values %}
|
198 |
+
<li class="choice">{{ value }}</li>
|
199 |
+
{% endfor %}
|
200 |
+
</ul>
|
201 |
+
<div class="question_divider"></div>
|
202 |
+
{% endfor %}
|
203 |
+
</div>
|
204 |
+
</div>
|
205 |
+
<a class="generate_another" href="/">Generate another exam</a>
|
206 |
+
<a href="https://nontgcob.com/" target="_blank" class="dev">
|
207 |
+
<p class="credit">Developed by Nutnornont Chamadol</p>
|
208 |
+
</a>
|
209 |
+
</body>
|
210 |
+
</html>
|