Version 1.0 ready (#1)
* FEAT: Basic app completed & file uploading added * Added pdf/txt support and quiz template * FEAT: PDF/TEXT uploading and getting file contents done * Updated .gitignore * Modified quiz template * Fix UI - Added CSS to upload page * Feat Quiz UI - Added CSS to Quiz Page * Feat: Question Extraction - Extract questions from raw text * Feat: added incorrect answer generation * Feat: Tied together question extraction and incorrect anwer generation * Fix: handle word not in vocabulary error * Fix: added document as argument to get_questions_dict * Feat: Integrated uploading file and question generation; needs fixes * Feat Quiz UI - added correct answer UI * Feat: Clean text for better processing * Fix UI - added gradient and spacing * Feat UI - Added uploading animation * Fixed options issue on quiz screen * Fix UI - added fonts, favicon etc * Fix - Formatted code * Feat UI - added fork me on github ribbon * Feat: Added quiz result functionality * Fix UI - added text to landing page * Fix UI - added CSS to submit button * Feat UI - added CSS to results page Co-authored-by: user86 <kshitij.kotasthane@gmail.com> Co-authored-by: telescopic <vigneshsuresh1775@gmail.com>
This commit is contained in:
parent
812a92bbea
commit
9d2adf338d
141
.gitignore
vendored
Normal file
141
.gitignore
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
# Do not upload folder, auto created at each run
|
||||
pdf/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
@ -1,4 +1,4 @@
|
||||
# Fantastic-Falcons-1.0
|
||||
# MLH Quizzet
|
||||
|
||||
This is a smart Quiz Generator that generates a dynamic quiz from any uploaded text/PDF document using NLP. This can be used for self-analysis, question paper generation, and evaluation, thus reducing human effort.
|
||||
|
||||
@ -21,6 +21,7 @@ This is a smart Quiz Generator that generates a dynamic quiz from any uploaded t
|
||||
|
||||
## Technology Stack:
|
||||
|
||||
|
||||
<img src="https://img.shields.io/badge/html5%20-%23E34F26.svg?&style=for-the-badge&logo=html5&logoColor=white"/> <img src="https://img.shields.io/badge/css3%20-%231572B6.svg?&style=for-the-badge&logo=css3&logoColor=white"/> <img src="https://img.shields.io/badge/javascript%20-%23323330.svg?&style=for-the-badge&logo=javascript&logoColor=%23F7DF1E"/> <img src="https://img.shields.io/badge/python%20-%2314354C.svg?&style=for-the-badge&logo=python&logoColor=white"/> <img src="https://img.shields.io/badge/flask%20-%23000.svg?&style=for-the-badge&logo=flask&logoColor=white"/> <img src="https://img.shields.io/badge/bootstrap%20-%23563D7C.svg?&style=for-the-badge&logo=bootstrap&logoColor=white"/> <img src="https://img.shields.io/badge/github%20-%23121011.svg?&style=for-the-badge&logo=github&logoColor=white"/> <img src ="https://img.shields.io/badge/sqlite-%2307405e.svg?&style=for-the-badge&logo=sqlite&logoColor=white"/>
|
||||
|
||||
- **Frontend**: HTML, CSS, Vanilla JS
|
||||
@ -107,6 +108,12 @@ $ python app.py
|
||||
| 2. | Kshitij Kotasthane | Backend Developer | [@kshitij86](https://github.com/kshitij86) |
|
||||
| 3. | Vignesh S | ML | [@telescopic](https://github.com/telescopic) |
|
||||
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||

|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
72
app.py
Normal file
72
app.py
Normal file
@ -0,0 +1,72 @@
|
||||
import os
|
||||
from flask import Flask, render_template, redirect, url_for
|
||||
from flask.globals import request
|
||||
from werkzeug.utils import secure_filename
|
||||
from workers import pdf2text, txt2questions
|
||||
|
||||
# Constants
|
||||
UPLOAD_FOLDER = './pdf/'
|
||||
|
||||
|
||||
# Init an app object
|
||||
app = Flask(__name__)
|
||||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
|
||||
# Global quiz object
|
||||
questions = dict()
|
||||
|
||||
|
||||
@ app.route('/')
|
||||
def index():
|
||||
""" The landing page for the app """
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@ app.route('/quiz', methods=['GET', 'POST'])
|
||||
def quiz():
|
||||
""" Handle upload and conversion of file + other stuff """
|
||||
|
||||
UPLOAD_STATUS = False
|
||||
|
||||
# Make directory to store uploaded files, if not exists
|
||||
if not os.path.isdir('./pdf'):
|
||||
os.mkdir('./pdf')
|
||||
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
# Retrieve file from request
|
||||
uploaded_file = request.files['file']
|
||||
file_path = os.path.join(
|
||||
app.config['UPLOAD_FOLDER'],
|
||||
secure_filename(
|
||||
uploaded_file.filename))
|
||||
file_exten = uploaded_file.filename.rsplit('.', 1)[1].lower()
|
||||
|
||||
# Save uploaded file
|
||||
uploaded_file.save(file_path)
|
||||
# Get contents of file
|
||||
uploaded_content = pdf2text(file_path, file_exten)
|
||||
questions = txt2questions(uploaded_content)
|
||||
|
||||
# File upload + convert success
|
||||
if uploaded_content is not None:
|
||||
UPLOAD_STATUS = True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return render_template(
|
||||
'quiz.html',
|
||||
uploaded=UPLOAD_STATUS,
|
||||
questions=questions,
|
||||
size=len(questions))
|
||||
|
||||
|
||||
@app.route('/result', methods=['POST', 'GET'])
|
||||
def result():
|
||||
correct_q = 0
|
||||
for k, v in request.form.items():
|
||||
correct_q += 1
|
||||
return render_template('result.html', total=5, correct=correct_q)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True)
|
||||
61
incorrect_answer_generation.py
Normal file
61
incorrect_answer_generation.py
Normal file
@ -0,0 +1,61 @@
|
||||
''' This module contains the class
|
||||
for generating incorrect alternative
|
||||
answers for a given answer
|
||||
'''
|
||||
import gensim
|
||||
import gensim.downloader as api
|
||||
from gensim.models import Word2Vec
|
||||
from nltk.tokenize import sent_tokenize, word_tokenize
|
||||
import random
|
||||
import numpy as np
|
||||
|
||||
|
||||
class IncorrectAnswerGenerator:
|
||||
''' This class contains the methods
|
||||
for generating the incorrect answers
|
||||
given an answer
|
||||
'''
|
||||
|
||||
def __init__(self, document):
|
||||
# model required to fetch similar words
|
||||
self.model = api.load("glove-wiki-gigaword-100")
|
||||
self.all_words = []
|
||||
for sent in sent_tokenize(document):
|
||||
self.all_words.extend(word_tokenize(sent))
|
||||
self.all_words = list(set(self.all_words))
|
||||
|
||||
def get_all_options_dict(self, answer, num_options):
|
||||
''' This method returns a dict
|
||||
of 'num_options' options out of
|
||||
which one is correct and is the answer
|
||||
'''
|
||||
options_dict = dict()
|
||||
try:
|
||||
similar_words = self.model.similar_by_word(answer, topn=15)[::-1]
|
||||
|
||||
for i in range(1, num_options + 1):
|
||||
options_dict[i] = similar_words[i - 1][0]
|
||||
|
||||
except BaseException:
|
||||
self.all_sim = []
|
||||
for word in self.all_words:
|
||||
if word not in answer:
|
||||
try:
|
||||
self.all_sim.append(
|
||||
(self.model.similarity(answer, word), word))
|
||||
except BaseException:
|
||||
self.all_sim.append(
|
||||
(0.0, word))
|
||||
else:
|
||||
self.all_sim.append((-1.0, word))
|
||||
|
||||
self.all_sim.sort(reverse=True)
|
||||
|
||||
for i in range(1, num_options + 1):
|
||||
options_dict[i] = self.all_sim[i - 1][1]
|
||||
|
||||
replacement_idx = random.randint(1, num_options)
|
||||
|
||||
options_dict[replacement_idx] = answer
|
||||
|
||||
return options_dict
|
||||
199
question_extraction.py
Normal file
199
question_extraction.py
Normal file
@ -0,0 +1,199 @@
|
||||
'''This file contains the module for generating
|
||||
'''
|
||||
import nltk
|
||||
import spacy
|
||||
from nltk.corpus import stopwords
|
||||
from nltk.tokenize import sent_tokenize, word_tokenize
|
||||
from sklearn.feature_extraction.text import TfidfVectorizer
|
||||
|
||||
|
||||
class QuestionExtractor:
|
||||
''' This class contains all the methods
|
||||
required for extracting questions from
|
||||
a given document
|
||||
'''
|
||||
|
||||
def __init__(self, num_questions):
|
||||
|
||||
self.num_questions = num_questions
|
||||
|
||||
# hash set for fast lookup
|
||||
self.stop_words = set(stopwords.words('english'))
|
||||
|
||||
# named entity recognition tagger
|
||||
self.ner_tagger = spacy.load('en_core_web_md')
|
||||
|
||||
self.vectorizer = TfidfVectorizer()
|
||||
|
||||
self.questions_dict = dict()
|
||||
|
||||
def get_questions_dict(self, document):
|
||||
'''
|
||||
Returns a dict of questions in the format:
|
||||
question_number: {
|
||||
question: str
|
||||
answer: str
|
||||
}
|
||||
|
||||
Params:
|
||||
* document : string
|
||||
Returns:
|
||||
* dict
|
||||
'''
|
||||
# find candidate keywords
|
||||
self.candidate_keywords = self.get_candidate_entities(document)
|
||||
|
||||
# set word scores before ranking candidate keywords
|
||||
self.set_tfidf_scores(document)
|
||||
|
||||
# rank the keywords using calculated tf idf scores
|
||||
self.rank_keywords()
|
||||
|
||||
# form the questions
|
||||
self.form_questions()
|
||||
|
||||
return self.questions_dict
|
||||
|
||||
def get_filtered_sentences(self, document):
|
||||
''' Returns a list of sentences - each of
|
||||
which has been cleaned of stopwords.
|
||||
Params:
|
||||
* document: a paragraph of sentences
|
||||
Returns:
|
||||
* list<str> : list of string
|
||||
'''
|
||||
sentences = sent_tokenize(document) # split documents into sentences
|
||||
|
||||
return [self.filter_sentence(sentence) for sentence in sentences]
|
||||
|
||||
def filter_sentence(self, sentence):
|
||||
'''Returns the sentence without stopwords
|
||||
Params:
|
||||
* sentence: A string
|
||||
Returns:
|
||||
* string
|
||||
'''
|
||||
words = word_tokenize(sentence)
|
||||
return ' '.join(w for w in words if w not in self.stop_words)
|
||||
|
||||
def get_candidate_entities(self, document):
|
||||
''' Returns a list of entities according to
|
||||
spacy's ner tagger. These entities are candidates
|
||||
for the questions
|
||||
|
||||
Params:
|
||||
* document : string
|
||||
Returns:
|
||||
* list<str>
|
||||
'''
|
||||
entities = self.ner_tagger(document)
|
||||
entity_list = []
|
||||
|
||||
for ent in entities.ents:
|
||||
entity_list.append(ent.text)
|
||||
|
||||
return list(set(entity_list)) # remove duplicates
|
||||
|
||||
def set_tfidf_scores(self, document):
|
||||
''' Sets the tf-idf scores for each word'''
|
||||
self.unfiltered_sentences = sent_tokenize(document)
|
||||
self.filtered_sentences = self.get_filtered_sentences(document)
|
||||
|
||||
self.word_score = dict() # (word, score)
|
||||
|
||||
# (word, sentence where word score is max)
|
||||
self.sentence_for_max_word_score = dict()
|
||||
|
||||
tf_idf_vector = self.vectorizer.fit_transform(self.filtered_sentences)
|
||||
feature_names = self.vectorizer.get_feature_names()
|
||||
tf_idf_matrix = tf_idf_vector.todense().tolist()
|
||||
|
||||
num_sentences = len(self.unfiltered_sentences)
|
||||
num_features = len(feature_names)
|
||||
|
||||
for i in range(num_features):
|
||||
word = feature_names[i]
|
||||
self.sentence_for_max_word_score[word] = ""
|
||||
tot = 0.0
|
||||
cur_max = 0.0
|
||||
|
||||
for j in range(num_sentences):
|
||||
tot += tf_idf_matrix[j][i]
|
||||
|
||||
if tf_idf_matrix[j][i] > cur_max:
|
||||
cur_max = tf_idf_matrix[j][i]
|
||||
self.sentence_for_max_word_score[word] = self.unfiltered_sentences[j]
|
||||
|
||||
# average score for each word
|
||||
self.word_score[word] = tot / num_sentences
|
||||
|
||||
def get_keyword_score(self, keyword):
|
||||
''' Returns the score for a keyword
|
||||
Params:
|
||||
* keyword : string of possible several words
|
||||
Returns:
|
||||
* float : score
|
||||
'''
|
||||
score = 0.0
|
||||
for word in word_tokenize(keyword):
|
||||
if word in self.word_score:
|
||||
score += self.word_score[word]
|
||||
return score
|
||||
|
||||
def get_corresponding_sentence_for_keyword(self, keyword):
|
||||
''' Finds and returns a sentence containing
|
||||
the keywords
|
||||
'''
|
||||
words = word_tokenize(keyword)
|
||||
for word in words:
|
||||
|
||||
if word not in self.sentence_for_max_word_score:
|
||||
continue
|
||||
|
||||
sentence = self.sentence_for_max_word_score[word]
|
||||
|
||||
all_present = True
|
||||
for w in words:
|
||||
if w not in sentence:
|
||||
all_present = False
|
||||
|
||||
if all_present:
|
||||
return sentence
|
||||
return ""
|
||||
|
||||
def rank_keywords(self):
|
||||
'''Rank keywords according to their score'''
|
||||
self.candidate_triples = [] # (score, keyword, corresponding sentence)
|
||||
|
||||
for candidate_keyword in self.candidate_keywords:
|
||||
self.candidate_triples.append([
|
||||
self.get_keyword_score(candidate_keyword),
|
||||
candidate_keyword,
|
||||
self.get_corresponding_sentence_for_keyword(candidate_keyword)
|
||||
])
|
||||
|
||||
self.candidate_triples.sort(reverse=True)
|
||||
|
||||
def form_questions(self):
|
||||
''' Forms the question and populates
|
||||
the question dict
|
||||
'''
|
||||
used_sentences = list()
|
||||
idx = 0
|
||||
cntr = 1
|
||||
num_candidates = len(self.candidate_triples)
|
||||
while cntr <= self.num_questions and idx < num_candidates:
|
||||
candidate_triple = self.candidate_triples[idx]
|
||||
|
||||
if candidate_triple[2] not in used_sentences:
|
||||
used_sentences.append(candidate_triple[2])
|
||||
|
||||
self.questions_dict[cntr] = {
|
||||
"question": candidate_triple[2].replace(
|
||||
candidate_triple[1],
|
||||
'_' * len(candidate_triple[1])),
|
||||
"answer": candidate_triple[1]
|
||||
}
|
||||
|
||||
cntr += 1
|
||||
idx += 1
|
||||
56
question_generation_main.py
Normal file
56
question_generation_main.py
Normal file
@ -0,0 +1,56 @@
|
||||
'''This module ties together the
|
||||
questions generation and incorrect answer
|
||||
generation modules
|
||||
'''
|
||||
from question_extraction import QuestionExtractor
|
||||
from incorrect_answer_generation import IncorrectAnswerGenerator
|
||||
import re
|
||||
from nltk import sent_tokenize
|
||||
|
||||
|
||||
class QuestionGeneration:
|
||||
'''This class contains the method
|
||||
to generate questions
|
||||
'''
|
||||
|
||||
def __init__(self, num_questions, num_options):
|
||||
self.num_questions = num_questions
|
||||
self.num_options = num_options
|
||||
self.question_extractor = QuestionExtractor(num_questions)
|
||||
|
||||
def clean_text(self, text):
|
||||
text = text.replace('\n', ' ') # remove newline chars
|
||||
sentences = sent_tokenize(text)
|
||||
cleaned_text = ""
|
||||
for sentence in sentences:
|
||||
# remove non alphanumeric chars
|
||||
cleaned_sentence = re.sub(r'([^\s\w]|_)+', '', sentence)
|
||||
|
||||
# substitute multiple spaces with single space
|
||||
cleaned_sentence = re.sub(' +', ' ', cleaned_sentence)
|
||||
cleaned_text += cleaned_sentence
|
||||
|
||||
if cleaned_text[-1] == ' ':
|
||||
cleaned_text[-1] = '.'
|
||||
else:
|
||||
cleaned_text += '.'
|
||||
|
||||
cleaned_text += ' ' # pad with space at end
|
||||
return cleaned_text
|
||||
|
||||
def generate_questions_dict(self, document):
|
||||
document = self.clean_text(document)
|
||||
self.questions_dict = self.question_extractor.get_questions_dict(
|
||||
document)
|
||||
self.incorrect_answer_generator = IncorrectAnswerGenerator(document)
|
||||
|
||||
for i in range(1, self.num_questions + 1):
|
||||
if i not in self.questions_dict:
|
||||
continue
|
||||
self.questions_dict[i]["options"] \
|
||||
= self.incorrect_answer_generator.get_all_options_dict(
|
||||
self.questions_dict[i]["answer"],
|
||||
self.num_options
|
||||
)
|
||||
|
||||
return self.questions_dict
|
||||
39
requirements.txt
Normal file
39
requirements.txt
Normal file
@ -0,0 +1,39 @@
|
||||
blis==0.4.1
|
||||
catalogue==1.0.0
|
||||
certifi==2020.6.20
|
||||
chardet==3.0.4
|
||||
click==7.1.2
|
||||
cymem==2.0.3
|
||||
en-core-web-md @ https://github.com/explosion/spacy-models/releases/download/en_core_web_md-2.3.1/en_core_web_md-2.3.1.tar.gz
|
||||
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.3.1/en_core_web_sm-2.3.1.tar.gz
|
||||
Flask==1.1.2
|
||||
gensim==3.8.3
|
||||
idna==2.10
|
||||
importlib-metadata==2.0.0
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.2
|
||||
joblib==0.17.0
|
||||
MarkupSafe==1.1.1
|
||||
murmurhash==1.0.2
|
||||
nltk==3.5
|
||||
numpy==1.19.2
|
||||
pkg-resources==0.0.0
|
||||
plac==1.1.3
|
||||
preshed==3.0.2
|
||||
PyPDF2==1.26.0
|
||||
regex==2020.9.27
|
||||
requests==2.24.0
|
||||
scikit-learn==0.23.2
|
||||
scipy==1.5.2
|
||||
six==1.15.0
|
||||
sklearn==0.0
|
||||
smart-open==3.0.0
|
||||
spacy==2.3.2
|
||||
srsly==1.0.2
|
||||
thinc==7.4.1
|
||||
threadpoolctl==2.1.0
|
||||
tqdm==4.50.2
|
||||
urllib3==1.25.10
|
||||
wasabi==0.8.0
|
||||
Werkzeug==1.0.1
|
||||
zipp==3.3.0
|
||||
105
static/css/quiz.css
Normal file
105
static/css/quiz.css
Normal file
@ -0,0 +1,105 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
position: relative;
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
body {
|
||||
user-select: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
counter-reset: points;
|
||||
background-color: #77aa77;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 2 1'%3E%3Cdefs%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='0' x2='0' y1='0' y2='1'%3E%3Cstop offset='0' stop-color='%2377aa77'/%3E%3Cstop offset='1' stop-color='%234fd'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='2' y2='2'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect x='0' y='0' fill='url(%23a)' width='2' height='1'/%3E%3Cg fill-opacity='0.5'%3E%3Cpolygon fill='url(%23b)' points='0 1 0 0 2 0'/%3E%3Cpolygon fill='url(%23c)' points='2 1 2 0 0 0'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
font-family: "helvetica", sans-serif !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
section {
|
||||
padding-top: 150px;
|
||||
}
|
||||
|
||||
main {
|
||||
-webkit-box-shadow: 0 10px 6px -6px #777;
|
||||
-moz-box-shadow: 0 10px 6px -6px #777;
|
||||
box-shadow: 0 10px 6px -6px #777;
|
||||
color: #000000;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 50px 40px 50px;
|
||||
width: 95%;
|
||||
max-width: 590px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.text-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="radio"] + label {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="radio"] + label:hover {
|
||||
border: 1px solid #000000;
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
background-image: none;
|
||||
background-color: #0c0;
|
||||
color: #fff;
|
||||
border: 1px solid #0c0 !important;
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
-moz-transition: all 0.2s ease-in-out;
|
||||
-o-transition: all 0.2s ease-in-out;
|
||||
-ms-transition: all 0.2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.worngans {
|
||||
background-color: #f36;
|
||||
color: #fff;
|
||||
border: 1px solid #f36 !important;
|
||||
-webkit-transition: all 0.2s ease-in-out;
|
||||
-moz-transition: all 0.2s ease-in-out;
|
||||
-o-transition: all 0.2s ease-in-out;
|
||||
-ms-transition: all 0.2s ease-in-out;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.end {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.button.is-fullwidth {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
margin-top: 30px;
|
||||
}
|
||||
57
static/css/results.css
Normal file
57
static/css/results.css
Normal file
@ -0,0 +1,57 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
position: relative;
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
body {
|
||||
user-select: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
counter-reset: points;
|
||||
background-color: #77aa77;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 2 1'%3E%3Cdefs%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='0' x2='0' y1='0' y2='1'%3E%3Cstop offset='0' stop-color='%2377aa77'/%3E%3Cstop offset='1' stop-color='%234fd'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='2' y2='2'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect x='0' y='0' fill='url(%23a)' width='2' height='1'/%3E%3Cg fill-opacity='0.5'%3E%3Cpolygon fill='url(%23b)' points='0 1 0 0 2 0'/%3E%3Cpolygon fill='url(%23c)' points='2 1 2 0 0 0'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
font-family: "helvetica", sans-serif !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
section {
|
||||
padding-top: 150px;
|
||||
}
|
||||
|
||||
main {
|
||||
-webkit-box-shadow: 0 10px 6px -6px #777;
|
||||
-moz-box-shadow: 0 10px 6px -6px #777;
|
||||
box-shadow: 0 10px 6px -6px #777;
|
||||
color: #000000;
|
||||
background: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 30px 40px 30px;
|
||||
width: 95%;
|
||||
max-width: 590px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.button.is-fullwidth {
|
||||
display: flex;
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
margin-top: 30px;
|
||||
}
|
||||
173
static/css/style.css
Normal file
173
static/css/style.css
Normal file
@ -0,0 +1,173 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap");
|
||||
html,
|
||||
body {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #77aa77;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 2 1'%3E%3Cdefs%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='0' x2='0' y1='0' y2='1'%3E%3Cstop offset='0' stop-color='%2377aa77'/%3E%3Cstop offset='1' stop-color='%234fd'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3ClinearGradient id='c' gradientUnits='userSpaceOnUse' x1='0' y1='0' x2='2' y2='2'%3E%3Cstop offset='0' stop-color='%23cf8' stop-opacity='0'/%3E%3Cstop offset='1' stop-color='%23cf8' stop-opacity='1'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect x='0' y='0' fill='url(%23a)' width='2' height='1'/%3E%3Cg fill-opacity='0.5'%3E%3Cpolygon fill='url(%23b)' points='0 1 0 0 2 0'/%3E%3Cpolygon fill='url(%23c)' points='2 1 2 0 0 0'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
font-family: "helvetica", sans-serif !important;
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 10px;
|
||||
margin-top: 50px !important;
|
||||
height: 350px !important;
|
||||
width: 250px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.card.has-text-centered .card-header,
|
||||
.card.has-text-centered .card-content,
|
||||
.card.has-text-centered .card-footer {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card.has-text-centered h1 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 12.5rem;
|
||||
margin: 0;
|
||||
padding: 1.5rem 3.125rem;
|
||||
background-color: #3498db;
|
||||
border: none;
|
||||
border-radius: 0.3125rem;
|
||||
box-shadow: 0 12px 24px 0 rgba(0, 0, 0, 0.2);
|
||||
color: white;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
font-family: "Open Sans", sans-serif !important;
|
||||
}
|
||||
|
||||
.button:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background-color: #54d98c;
|
||||
}
|
||||
|
||||
.button span {
|
||||
position: absolute;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.button span i {
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.button span:nth-of-type(1) {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.button span:nth-of-type(2) {
|
||||
top: 100%;
|
||||
transform: translateY(0%);
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.button span:nth-of-type(3) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #2ecc71;
|
||||
}
|
||||
|
||||
.active:before {
|
||||
width: 100%;
|
||||
transition: width 30s linear !important;
|
||||
}
|
||||
|
||||
.active span:nth-of-type(1) {
|
||||
top: -100%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.active span:nth-of-type(2) {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.active span:nth-of-type(2) i {
|
||||
animation: loading 10000ms linear infinite !important;
|
||||
}
|
||||
|
||||
.active span:nth-of-type(3) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.finished {
|
||||
background-color: #54d98c;
|
||||
}
|
||||
|
||||
.finished .submit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.finished .loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.finished .check {
|
||||
display: block !important;
|
||||
font-size: 24px;
|
||||
animation: scale 0.5s linear;
|
||||
}
|
||||
|
||||
.finished .check i {
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale {
|
||||
0% {
|
||||
transform: scale(10);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.2);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
90% {
|
||||
transform: scale(0.7);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-top: 70px !important;
|
||||
}
|
||||
BIN
static/fonts/helvetica.ttf
Normal file
BIN
static/fonts/helvetica.ttf
Normal file
Binary file not shown.
97
templates/index.html
Normal file
97
templates/index.html
Normal file
@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/style.css') }}" />
|
||||
<link rel="shortcut icon" href="https://avatars0.githubusercontent.com/u/65834464?s=200&v=4" type="image/x-icon">
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css" />
|
||||
<script src="https://kit.fontawesome.com/22be60108b.js" crossorigin="anonymous"></script>
|
||||
<title>MLH Quizzet</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: helvetica;
|
||||
src: "{{ url_for('static', filename='fonts/helvetica.ttf')}}"
|
||||
}
|
||||
</style>
|
||||
<nav class="navbar is-dark is-fixed-top has-text-centered" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<img src="https://avatars0.githubusercontent.com/u/65834464?s=200&v=4" height="32" width="64"
|
||||
style="margin: 10px">
|
||||
<a class="navbar-item has-text-centered" href="{{ url_for('index') }}"><strong
|
||||
class="is-size-3 has-text-centered">MLH Quizzet</strong></a>
|
||||
<a class="github-fork-ribbon" href="https://github.com/PragatiVerma18/Fantastic-Falcons-1.0"
|
||||
data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a>
|
||||
<div class="navbar-burger" data-target="navMenu">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</nav>
|
||||
<main role="main">
|
||||
<div class="hero">
|
||||
<p class="is-size-3 has-text-black has-text-weight-bold has-text-centered">Upload any Document to get an instant Quiz</p>
|
||||
<p class="is-size-4 has-text-weight-bold has-text-centered">You Know 📖, You Grow 🚀</p>
|
||||
<p class="is-size-5 has-text-black has-text-weight-medium has-text-centered"> Practice More • Learn More </p>
|
||||
</div>
|
||||
<div class="card has-text-centered">
|
||||
<img src="https://cdn4.iconfinder.com/data/icons/files-and-folders-thinline-icons-set/144/File_PDF-512.png"
|
||||
alt="upload"/>
|
||||
<form action="http://localhost:5000/quiz" method="POST" enctype="multipart/form-data">
|
||||
<div id="file-js-example" class="file has-name is-fullwidth">
|
||||
<label class="file-label">
|
||||
<input class="file-input" type="file" name="file" accept=".txt, application/pdf" />
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">
|
||||
<i class="fas fa-upload"></i>
|
||||
</span>
|
||||
<span class="file-label"> Choose a file… </span>
|
||||
</span>
|
||||
<span class="file-name"> No file uploaded </span>
|
||||
</label>
|
||||
</div>
|
||||
<script>
|
||||
const fileInput = document.querySelector(
|
||||
"#file-js-example input[type=file]"
|
||||
);
|
||||
fileInput.onchange = () => {
|
||||
if (fileInput.files.length > 0) {
|
||||
const fileName = document.querySelector(
|
||||
"#file-js-example .file-name"
|
||||
);
|
||||
fileName.textContent = fileInput.files[0].name;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<button class="button has-text-centered is-large is-fullwidth is-dark">
|
||||
<span class="submit has-text-centered" type="submit" name="upload file">Submit</span>
|
||||
<span class="loading"><i class="fa fa-refresh"></i></span>
|
||||
<span class="check"><i class="fa fa-check"></i></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
<script>const button = document.querySelector('.button');
|
||||
const submit = document.querySelector('.submit');
|
||||
|
||||
function toggleClass() {
|
||||
this.classList.toggle('active');
|
||||
}
|
||||
|
||||
function addClass() {
|
||||
this.classList.add('finished');
|
||||
}
|
||||
|
||||
button.addEventListener('click', toggleClass);
|
||||
button.addEventListener('transitionend', toggleClass);
|
||||
button.addEventListener('transitionend', addClass);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
87
templates/quiz.html
Normal file
87
templates/quiz.html
Normal file
@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/quiz.css') }}" />
|
||||
<link rel="shortcut icon" href="https://avatars0.githubusercontent.com/u/65834464?s=200&v=4"
|
||||
type="image/x-icon">
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css" />
|
||||
<title>MLH Quizzet</title>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" type='text/javascript'></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('label').click(function () {
|
||||
$('label').removeClass('worngans');
|
||||
$(this).addClass('worngans');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: helvetica;
|
||||
src: "{{ url_for('static', filename='fonts/helvetica.ttf')}}"
|
||||
}
|
||||
</style>
|
||||
<nav class="navbar is-dark is-fixed-top has-text-centered" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<img src="https://avatars0.githubusercontent.com/u/65834464?s=200&v=4" height="32" width="64"
|
||||
style="margin: 7px">
|
||||
<a class="navbar-item has-text-centered" href="{{ url_for('index') }}"><strong
|
||||
class="is-size-3 has-text-centered">MLH
|
||||
Quizzet</strong></a>
|
||||
<a class="github-fork-ribbon" href="https://github.com/PragatiVerma18/Fantastic-Falcons-1.0"
|
||||
data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a>
|
||||
<div class="navbar-burger" data-target="navMenu">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% if uploaded == true %}
|
||||
<form action="http://localhost:5000/result" method="POST">
|
||||
{% for i in range(size) %}
|
||||
<section class="section-1" id="section-1">
|
||||
<main>
|
||||
<div class="scp-quizzes-main">
|
||||
<div class="scp-quizzes-data">
|
||||
<h3 class="is-size-6 has-text-weight-bold">{{ i+1 }}. {{ questions[i+1]['question'] }}</h3>
|
||||
<br />
|
||||
{% for op in questions[i+1]['options'] %}
|
||||
{% if op == questions[i+1]['answer'] %}
|
||||
<input type="radio" id="{{ questions[i+1]['answer'] }}" name="question{{ i+1 }}">
|
||||
<label for="{{ questions[i+1]['answer'] }}">
|
||||
{{ op }}</label><br />
|
||||
{% else %}
|
||||
<input type="radio" name="question{{ i+1 }}">
|
||||
<label> {{ op }}</label><br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
{% endfor %}
|
||||
<div class="end">
|
||||
<button type="submit" class="button is-dark has-text-weight-bold has-text-centered is-fullwidth is-rounded">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<section class="section-1" id="section-1">
|
||||
<h1>Could not upload file</h1>
|
||||
</section>
|
||||
{% endif %}
|
||||
<div class="has-text-white has-text-centered" style="margin-top: 50px; background-color: #363636; padding: 10px;">
|
||||
MIT License © Copyright 2020 Fantastic Falcons
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
54
templates/result.html
Normal file
54
templates/result.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/results.css') }}" />
|
||||
<link rel="shortcut icon" href="https://avatars0.githubusercontent.com/u/65834464?s=200&v=4"
|
||||
type="image/x-icon">
|
||||
<link rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css" />
|
||||
<script src="https://kit.fontawesome.com/22be60108b.js" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
|
||||
<title>MLH Quizzet</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: helvetica;
|
||||
src: "{{ url_for('static', filename='fonts/helvetica.ttf')}}"
|
||||
}
|
||||
</style>
|
||||
<nav class="navbar is-dark is-fixed-top has-text-centered" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<img src="https://avatars0.githubusercontent.com/u/65834464?s=200&v=4" height="32" width="64"
|
||||
style="margin: 10px">
|
||||
<a class="navbar-item has-text-centered" href="{{ url_for('index') }}"><strong
|
||||
class="is-size-3 has-text-centered">MLH Quizzet</strong></a>
|
||||
<a class="github-fork-ribbon" href="https://github.com/PragatiVerma18/Fantastic-Falcons-1.0"
|
||||
data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a>
|
||||
<div class="navbar-burger" data-target="navMenu">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</nav>
|
||||
<section class="section-1" id="section-1">
|
||||
<main>
|
||||
<lottie-player src="https://assets3.lottiefiles.com/packages/lf20_0ge4xP.json" background="transparent" speed="1"
|
||||
style="width: 240px; height: 240px; margin: auto !important;" loop autoplay></lottie-player>
|
||||
<h1 class="has-text-centered is-size-3 has-text-weight-bold"> You got {{ correct }}/{{ total }} right!</h1>
|
||||
<a class="button is-dark has-text-centered has-text-weight-bold is-rounded is-fullwidth" href="{{ url_for('index') }}"> Upload another document</a>
|
||||
</main>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="has-text-white has-text-centered" style="margin-top: 40px; background-color: #363636; padding: 10px;">
|
||||
MIT License © Copyright 2020 Fantastic Falcons
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
38
workers.py
Normal file
38
workers.py
Normal file
@ -0,0 +1,38 @@
|
||||
from PyPDF2 import PdfFileReader
|
||||
from question_generation_main import QuestionGeneration
|
||||
|
||||
|
||||
def pdf2text(file_path: str, file_exten: str) -> str:
|
||||
""" Converts a given file to text content """
|
||||
|
||||
_content = ''
|
||||
|
||||
# Identify file type and get its contents
|
||||
if file_exten == 'pdf':
|
||||
with open(file_path, 'rb') as pdf_file:
|
||||
_pdf_reader = PdfFileReader(pdf_file)
|
||||
for p in range(_pdf_reader.numPages):
|
||||
_content += _pdf_reader.getPage(p).extractText()
|
||||
# _content = _pdf_reader.getPage(0).extractText()
|
||||
print('PDF operation done!')
|
||||
|
||||
elif file_exten == 'txt':
|
||||
with open(file_path, 'r') as txt_file:
|
||||
_content = txt_file.read()
|
||||
print('TXT operation done!')
|
||||
|
||||
return _content
|
||||
|
||||
|
||||
def txt2questions(doc: str, n=5, o=4) -> dict:
|
||||
""" Get all questions and options """
|
||||
|
||||
qGen = QuestionGeneration(n, o)
|
||||
q = qGen.generate_questions_dict(doc)
|
||||
for i in range(len(q)):
|
||||
temp = []
|
||||
for j in range(len(q[i + 1]['options'])):
|
||||
temp.append(q[i + 1]['options'][j + 1])
|
||||
# print(temp)
|
||||
q[i + 1]['options'] = temp
|
||||
return q
|
||||
Loading…
x
Reference in New Issue
Block a user