diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000000000000000000000000000000000..dc46f1b625a526d4f3959ee5226b703aa6d23df5 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn main:app \ No newline at end of file diff --git a/README.md b/README.md index ca1dae20026b5a8d581fceaefe4b3edda21f158b..52497fc51eeb4a7021bb29492eafd7a14bd57497 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,7 @@ ---- title: OIST QUIZ APP emoji: 📊 colorFrom: indigo colorTo: indigo sdk: docker pinned: false -license: apache-2.0 ---- - -Check out the configuration reference at https://huggingface.co./docs/hub/spaces-config-reference +license: apache-2.0 \ No newline at end of file diff --git a/__pycache__/config.cpython-311.pyc b/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e2aefdb887485990808c5a25f96717d36fb7b6b Binary files /dev/null and b/__pycache__/config.cpython-311.pyc differ diff --git a/__pycache__/config.cpython-38.pyc b/__pycache__/config.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a76a69f33cc0e8b81040b1ee0790391be441a2ab Binary files /dev/null and b/__pycache__/config.cpython-38.pyc differ diff --git a/__pycache__/main.cpython-37.pyc b/__pycache__/main.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea796fcc11486894bead9cfae50fdc498120fac6 Binary files /dev/null and b/__pycache__/main.cpython-37.pyc differ diff --git a/app.db b/app.db new file mode 100644 index 0000000000000000000000000000000000000000..4da9fdfb6b47d0435aaca931b827cecfc537f8cb Binary files /dev/null and b/app.db differ diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ec98e4a549521db5e455c7d4f7ee97dc56f1a26e --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,11 @@ +from flask import Flask +from config import Config +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + +app = Flask(__name__) +app.config.from_object(Config) +db = SQLAlchemy(app) +migrate = Migrate(app, db) + +from app import routes diff --git a/app/__pycache__/__init__.cpython-311.pyc b/app/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5d48d1abf4bdedaf51ff25effd032d08737f208 Binary files /dev/null and b/app/__pycache__/__init__.cpython-311.pyc differ diff --git a/app/__pycache__/__init__.cpython-37.pyc b/app/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5317fc31f23b8c6baf3e63fff20c439036cd774 Binary files /dev/null and b/app/__pycache__/__init__.cpython-37.pyc differ diff --git a/app/__pycache__/__init__.cpython-38.pyc b/app/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b4246495fd88c9170a63a3378afc270b4515c7e Binary files /dev/null and b/app/__pycache__/__init__.cpython-38.pyc differ diff --git a/app/__pycache__/forms.cpython-311.pyc b/app/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1c37c1ca4c25b7ad0599609840fe5adcb83911c Binary files /dev/null and b/app/__pycache__/forms.cpython-311.pyc differ diff --git a/app/__pycache__/forms.cpython-37.pyc b/app/__pycache__/forms.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed13627e12504663918719ac2e0e8b27f725972e Binary files /dev/null and b/app/__pycache__/forms.cpython-37.pyc differ diff --git a/app/__pycache__/forms.cpython-38.pyc b/app/__pycache__/forms.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c25807f1b3690a0ce5c2a360be070e1f6266e23 Binary files /dev/null and b/app/__pycache__/forms.cpython-38.pyc differ diff --git a/app/__pycache__/models.cpython-311.pyc b/app/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cbbb7b4fbf5a4c3977b73cb4994e6dc88bccd2a Binary files /dev/null and b/app/__pycache__/models.cpython-311.pyc differ diff --git a/app/__pycache__/models.cpython-38.pyc b/app/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6776c9f57aa2879044a7f574f63d2243f29005ce Binary files /dev/null and b/app/__pycache__/models.cpython-38.pyc differ diff --git a/app/__pycache__/routes.cpython-311.pyc b/app/__pycache__/routes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1deeb3bb3e4188b91f13955d8b26b4ba3379c6a3 Binary files /dev/null and b/app/__pycache__/routes.cpython-311.pyc differ diff --git a/app/__pycache__/routes.cpython-37.pyc b/app/__pycache__/routes.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6488425ffcededbd84bc535396cf7672300311e3 Binary files /dev/null and b/app/__pycache__/routes.cpython-37.pyc differ diff --git a/app/__pycache__/routes.cpython-38.pyc b/app/__pycache__/routes.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97abd132743846e915add6851a1649533d846629 Binary files /dev/null and b/app/__pycache__/routes.cpython-38.pyc differ diff --git a/app/forms.py b/app/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..6ed8069d5730d4e40a17c2049e63af27c13f4aee --- /dev/null +++ b/app/forms.py @@ -0,0 +1,34 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, BooleanField, SubmitField, RadioField +from wtforms.validators import ValidationError, DataRequired, Email, EqualTo +from wtforms.ext.sqlalchemy.fields import QuerySelectField + +from app.models import User + +class LoginForm(FlaskForm): + username = StringField('Username', validators=[DataRequired()]) + password = PasswordField('Password', validators=[DataRequired()]) + remember_me = BooleanField('Remember Me') + submit = SubmitField('Login') + +class RegistrationForm(FlaskForm): + username = StringField('Username', validators=[DataRequired()]) + email = StringField('Email', validators=(DataRequired(), Email())) + password = PasswordField('Password', validators=[DataRequired()]) + password2 = PasswordField('Confirm Password', + validators=(DataRequired(), EqualTo('password'))) + submit = SubmitField('Register') + + def validate_username(self, username): + user = User.query.filter_by(username=username.data).first() + if user is not None: + raise ValidationError('Username already exists') + + def validate_email(self, email): + user = User.query.filter_by(email=email.data).first() + if user is not None: + raise ValidationError('Email already exists.') + +class QuestionForm(FlaskForm): + options = RadioField('Options: ', validators=[DataRequired()], default=1) + submit = SubmitField('Next') \ No newline at end of file diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000000000000000000000000000000000000..e3c15728b5e256f3e5582d59502be40eefecd23b --- /dev/null +++ b/app/models.py @@ -0,0 +1,30 @@ +from app import db +from werkzeug.security import generate_password_hash, check_password_hash + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(64), index=True, unique=True) + email = db.Column(db.String(120), index=True, unique=True) + password_hash = db.Column(db.String(128)) + marks = db.Column(db.Integer, index=True) + + def __repr__(self): + return ''.format(self.username) + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + +class Questions(db.Model): + q_id = db.Column(db.Integer, primary_key=True) + ques = db.Column(db.String(350), unique=True) + a = db.Column(db.String(100)) + b = db.Column(db.String(100)) + c = db.Column(db.String(100)) + d = db.Column(db.String(100)) + ans = db.Column(db.String(100)) + + def __repr__(self): + return ''.format(self.ques) \ No newline at end of file diff --git a/app/routes.py b/app/routes.py new file mode 100644 index 0000000000000000000000000000000000000000..9802c8a97bba36bd9d1ccc325023dc38cd1d403b --- /dev/null +++ b/app/routes.py @@ -0,0 +1,87 @@ +from app import app +from flask import render_template, request, redirect, url_for, session, g, flash +from werkzeug.urls import url_parse +from app.forms import LoginForm, RegistrationForm, QuestionForm +from app.models import User, Questions +from app import db + + +@app.before_request +def before_request(): + g.user = None + + if 'user_id' in session: + user = User.query.filter_by(id=session['user_id']).first() + g.user = user + +@app.route('/') +def home(): + return render_template('index.html', title='Home') + +@app.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + if form.validate_on_submit(): + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + return redirect(url_for('login')) + session['user_id'] = user.id + session['marks'] = 0 + next_page = request.args.get('next') + if not next_page or url_parse(next_page).netloc != '': + next_page = url_for('home') + return redirect(next_page) + return redirect(url_for('home')) + if g.user: + return redirect(url_for('home')) + return render_template('login.html', form=form, title='Login') + +@app.route('/register', methods=['GET', 'POST']) +def register(): + form = RegistrationForm() + if form.validate_on_submit(): + user = User(username=form.username.data, email=form.password.data) + user.set_password(form.password.data) + db.session.add(user) + db.session.commit() + session['user_id'] = user.id + session['marks'] = 0 + return redirect(url_for('home')) + if g.user: + return redirect(url_for('home')) + return render_template('register.html', title='Register', form=form) + + + +@app.route('/question/', methods=['GET', 'POST']) +def question(id): + form = QuestionForm() + q = Questions.query.filter_by(q_id=id).first() + if not q: + return redirect(url_for('score')) + if not g.user: + return redirect(url_for('login')) + if request.method == 'POST': + option = request.form['options'] + if option == q.ans: + session['marks'] += 10 + return redirect(url_for('question', id=(id+1))) + form.options.choices = [(q.a, q.a), (q.b, q.b), (q.c, q.c), (q.d, q.d)] + return render_template('question.html', form=form, q=q, title='Question {}'.format(id)) + + +@app.route('/score') +def score(): + if not g.user: + return redirect(url_for('login')) + g.user.marks = session['marks'] + # db.session.commit() + return render_template('score.html', title='Final Score') + +@app.route('/logout') +def logout(): + if not g.user: + return redirect(url_for('login')) + session.pop('user_id', None) + session.pop('marks', None) + return redirect(url_for('home')) \ No newline at end of file diff --git a/app/static/css/mob-styles.css b/app/static/css/mob-styles.css new file mode 100644 index 0000000000000000000000000000000000000000..aca7c6631d41f386a807571bfe79f8013e933221 --- /dev/null +++ b/app/static/css/mob-styles.css @@ -0,0 +1,178 @@ +@media all and (max-width: 600px) { + + nav { + /* height: 70px; */ + padding: 2%;; + } + + /* Home Page css */ + .home-img{ + width: auto; + height: 400px; + order: 1; + } + + .home-div { + flex-direction: column-reverse; + } + + .info-div{ + align-items: center; + } + + .info-div h1 { + font-weight: 700; + font-size: 25px; + } + + .info-div h4 { + color: #fff; + font-weight: 600; + font-size: 18px; + text-align: center; + /* width: 95%; */ + } + + .home-div a { + color: #fff; + order: 2; + font-size: 18px; + margin-top: 30px; + border-radius: 30px; + padding: 15px; + margin-bottom: 7%; + } + + + + /* Login page css */ + .login-img { + display: none; + } + + .login-form { + margin-top: 10%; + } + + /* Sign UP page css */ + + .register-img{ + display: none; + } + + .register-main{ + margin: 5%; + margin-top: 2%; + /* margin-top: 5%; */ + } + + + + /* Question Page CSS */ + + .ques-div{ + font-size: 20px; + margin: 7%; + margin-top: 2%; + } + + .qnum{ + font-size: 25px; + } + + .ques-heading{ + font-size: 25px; + } + + .options-div{ + margin-top: 13%; + } + + .options-div label{ + font-size: 17px; + } + .sub-btn{ + margin-left: auto; + margin-right: auto; + } + + + + /* Score Page csss */ + .score-main{ + margin-top: 30px; + } + + .highest-score{ + margin-top: 30px; + font-size: 18px; + width: 80%; + } + + + .score-head { + font-size: 35px; + margin-top: 40px; + } + + .score-circle { + border-width: 8px; + width: 120px; + height: 120px; + margin-top: 32px; + } + + .score-circle h1{ + font-size: 40px; + padding: 17px; + } + + .congrats-cls{ + margin-top: 35px; + font-size: 30px; + } + + .redirect-links{ + margin-top: 60px; + width: 70%; + } + + +} + +.container, +.container-fluid, +.container-sm, +.container-md, +.container-lg, +.container-xl { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px; + } +} diff --git a/app/static/css/styles.css b/app/static/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..0589dba8f9f7c121e5eb81e7e6459eea4604531a --- /dev/null +++ b/app/static/css/styles.css @@ -0,0 +1,358 @@ +body{ + height: 100%; + font-family: 'Inter', sans-serif; + font-weight: 600; + background-color: #fff; + /* background-color: #71baed; */ +} + +.home-bg{ + display: flex; + flex-direction: column; +} + +.home-bg .home-div { + flex: 1; +} + + +/* Nav CSS */ + + +nav { + display: flex; + justify-content: flex-end; + align-items: center; + background-color: #fff; + border-bottom: 1px solid #8b95bd; + /* height: 50px; */ + padding: 0.8%; +} + +.nav-links { + list-style: none; + margin-bottom: 0em; +} + +.nav-links li{ + display: inline-block; + padding: 0px 15px; + margin-left: 20px; +} + +.nav-links li a{ + transition: all 0.3s ease 0s; + text-decoration: none; + color: #3e4667; +} + + +/* Home page CSS */ + +.home-img{ + width: auto; + height: 525px; + border-radius: 50%; + margin-right: 20px; +} + +.home-div { + background-color: #71baed; + display: flex; + justify-content: space-around; + align-items: center; + flex-direction: row-reverse; + width: 100%; + height: auto; + margin-left: 0; + margin-right: 0; +} + + + +.info-div{ + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: flex-start; +} + +.info-div h1 { + color: #fff; + font-weight: 700; + font-size: 50px; +} + +.info-div h4 { + color: #fff; + font-weight: 700; +} + +.info-div a { + background-color: #fad500; + color: #fff; + font-size: 18px; + margin-top: 40px; + border-radius: 30px; + padding: 15px; + text-decoration: none; +} + + + +/* Login Page CSS */ + +.login-main { + display: flex; + justify-content: space-between; + background-color: #fff; +} + +.login-img { + width: 61%; + height: auto; + margin-top: 20px; +} + +.login-form { + display: flex; + justify-content: center; + align-items: center; + flex: 1; +} + +.login-intro{ + margin-bottom: 50px; + margin-left: 7px; + color: #8e8e8e; +} + +.login-intro h1{ + font-weight: 800; + color: #000; +} + + + +.form-para { + display: flex; + align-items: center; + flex-direction: row; + margin-bottom: 27px; +} + +.form-para svg { + border: none; + margin-right: 20px; + padding: 5px; + fill: #8e8e8e; +} + +.form-para input { + border: none; +} + +.form-para input ::placeholder{ + color: #8e8e8e; +} + +.form-para p { + color: #8e8e8e; + font-weight: 400; +} +.form-para span { + margin-left: 15px; + +} + +.form-para p input{ + margin-left: 100px; +} + + +/* Register Page css */ + + + +.register-main{ + display: flex; + justify-content: center; + background-color: #fff; + align-items: center; +} + +.register-img{ + width: 44.2%; + height: auto; +} + +.register-form { + margin-top: 7px; + display: flex; + justify-content: center; + align-items: center; + flex: 1; +} + +.register-form p{ + display: block; + margin-left: auto; +} + +.register-intro{ + margin-top: 28px; + margin-left: 7px; + color: #8e8e8e; + margin-bottom: 0px; +} + +.register-intro h1{ + font-weight: 800; + color: #000; +} + + + + + +/* Question Page CSS */ + +.ques-div { + background-color: #fff; + display: flex; + justify-content: center; + align-items: center; +} + +.qnum { + margin-top: 25px; + color: #989cad; +} + +form hr { + border: 1px dashed #525976; +} + + +.ques-heading { + font-weight: 800; + margin-bottom: 33px; + /* color: #fff; */ + color: #3e4667; + max-width: 100%; +} + + +.options-div li { + box-sizing: content-box; + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row-reverse; + border: 3px solid #989cad; + border-radius: 15px; + padding: 10px; + width: 95%; + height: 30px; + margin-bottom: 17px; + margin-top: 15px; +} + +.options-div label{ + font-size: 20px; + color: #3e4667; + margin-top: 7px; +} + +/* .options-div input{ + margin-top: 10px; +} */ + +.sub-btn { + display: block; + margin-left: auto; +} + +.check-circle{ + font-size: 1.4em; + color: #3e4667; +} + +/* Scores Page CSS */ + +.score-main { + display: flex; + justify-content: space-between; + flex-direction: column; + align-items: center; + background-color: #fff; +} + + + +.score-head { + font-weight: 600; + margin-top: 30px; + font-style: bold; + color: #49535f; + +} + +.score-circle { + display: flex; + align-items: center; + justify-content: center; + border: 10px solid #107eeb; + border-radius: 50%; + width: 140px; + height: 140px; + margin-top: 15px; +} + +.score-circle h1{ + font-size: 50px; + border-radius: 50%; + padding: 20px; + + color: #49535f; +} + +.congrats-cls{ + margin-top: 15px; + font-style: bold; + color: #49535f; +} + +.highest-score{ + display: flex; + flex-flow: row nowrap; + justify-content: center; + align-items: center; + margin-top: 30px; + border: 50px; + background-color: #107eeb; + color: #fff; + border: 2px solid #107eeb; + border-radius: 100px; + padding: 5px; + font-size: 26px; + width: 40%; +} + +.high-score-name { + background-color: #fff; + border: 2px solid #fff; + border-radius: 100px; + color: #49535f; + padding: 5px; + margin: 5px; + margin-left: 18px; +} + +.redirect-links{ + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 50px; + width: 60%; +} diff --git a/app/static/images/boy-with-glasses.jpg b/app/static/images/boy-with-glasses.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa91f6dbd5291336657073f0724c5f7b7119bf4b Binary files /dev/null and b/app/static/images/boy-with-glasses.jpg differ diff --git a/app/static/images/check-circle-solid.svg b/app/static/images/check-circle-solid.svg new file mode 100644 index 0000000000000000000000000000000000000000..6aaa9742e4e4c59179066755c7316b1a69fc93d3 --- /dev/null +++ b/app/static/images/check-circle-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/static/images/home.jpg b/app/static/images/home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a945b8dff6fcd3a1d095ebcc41f15e5f6b6d6875 Binary files /dev/null and b/app/static/images/home.jpg differ diff --git a/app/static/images/kids-studying-from-home.gif b/app/static/images/kids-studying-from-home.gif new file mode 100644 index 0000000000000000000000000000000000000000..a3595a25036c384da29292a3a985c742baafb257 Binary files /dev/null and b/app/static/images/kids-studying-from-home.gif differ diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000000000000000000000000000000000000..ff096aefcd5008d9828789a1d8f20ef558990542 --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,42 @@ + + + {% if title %} + {{ title }} + {% else %} + Default + {% endif %} + + + + + + + + + + + + + + + {% with messages = get_flashed_messages() %} + {% if messages %} +
    + {% for message in messages %} +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + {% block content %}{% endblock %} + + \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000000000000000000000000000000000000..d245023a78726ea662eb07a9c474b6078332b2e0 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,18 @@ +{% extends 'base.html' %} + +{% block content %} +
+ +
+ {% if g.user %} +

Welcome {{ g.user.username.capitalize() }},

+ {% else %} +

Sign In, Guest

+ {% endif %} +

Explore the Test Preparation + exclusively made for you! +

+ Take the test +
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/login.html b/app/templates/login.html new file mode 100644 index 0000000000000000000000000000000000000000..5ad219a183aa4d0833ce3b6ca3019887221d487e --- /dev/null +++ b/app/templates/login.html @@ -0,0 +1,42 @@ +{% extends 'base.html' %} + +{% block content %} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/question.html b/app/templates/question.html new file mode 100644 index 0000000000000000000000000000000000000000..651d21b2d1eb244ddee718c79d9bf5c0451a46de --- /dev/null +++ b/app/templates/question.html @@ -0,0 +1,22 @@ +{% extends 'base.html' %} + + +{% block content %} +
+
+

Question {{q.q_id}}

+
+
+

{{q.ques}}

+
+ {{ form.options(class="list-unstyled") }} +
+ {{ form.submit(class="btn btn-primary btn-lg sub-btn") }} +
+ +
+ +{% endblock %} + diff --git a/app/templates/register.html b/app/templates/register.html new file mode 100644 index 0000000000000000000000000000000000000000..1e30b27be3801a0eedfae49cf40b3544f03515b6 --- /dev/null +++ b/app/templates/register.html @@ -0,0 +1,81 @@ +{% extends 'base.html' %} + +{% block content %} +
+ kids-studying-from-home +
+
+ {{ form.hidden_tag() }} +
+

Create your
Account now!

+

Get unlimited typeforms, questions and responses.
Free Forever.

+
+ + + + +
+ + + + {{ form.username(placeholder="Username") }} + {% for error in form.username.errors %} + {{ error }} + {% endfor %} +
+ +
+ + + + {{ form.email(placeholder="Email") }} + {% for error in form.email.errors %} + {{ error }} + {% endfor %} +
+ +
+ + + + + {{ form.password(placeholder="Password") }} + {% for error in form.password.errors %} + {{ error }} + {% endfor %} +
+ +
+ + + + {{ form.password2(placeholder="Confirm Password") }} + {% for error in form.password2.errors %} + {{ error }} + {% endfor %} +
+ +
+

{{ form.submit(class="btn btn-primary") }}

+
+ +
+

Already have an account? Login

+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/score.html b/app/templates/score.html new file mode 100644 index 0000000000000000000000000000000000000000..1f9531ad127c0b6f6b1e63362ece9e4f23ba0017 --- /dev/null +++ b/app/templates/score.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
Highest Score: 69 by
John
+
+

Your Score

+
+

{{ g.user.marks }}

+
+

Congratulations!

+ +
+ +{% endblock %} \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000000000000000000000000000000000000..06b085018cdb0812fd2a09bf14184faeb0a207b8 --- /dev/null +++ b/config.py @@ -0,0 +1,10 @@ +import os + +basedir = os.path.abspath(os.path.dirname(__file__)) + +class Config(object): + SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess' + SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \ + 'sqlite:///' + os.path.join(basedir, 'app.db') + SQLALCHEMY_TRACK_MODIFICATIONS = False + QUES_PER_PAGE = 1 \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000000000000000000000000000000000000..f0e3ae3222658cfc1cf1dcb5b0ddeaded342cac0 --- /dev/null +++ b/main.py @@ -0,0 +1,5 @@ +from app import app + + +# if __name__ == "__main__": +# app.run(debug=True, host='192.168.42.38') \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 0000000000000000000000000000000000000000..98e4f9c44effe479ed38c66ba922e7bcc672916f --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/__pycache__/env.cpython-38.pyc b/migrations/__pycache__/env.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2805af3eb51cecec2e44ac4d06d6a20afc97e9b1 Binary files /dev/null and b/migrations/__pycache__/env.cpython-38.pyc differ diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000000000000000000000000000000000000..f8ed4801f78bcb83cc6acb589508c1b24eda297a --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/migrations/env.py b/migrations/env.py new file mode 100644 index 0000000000000000000000000000000000000000..945217923e89144bc0b84279caf82327762323c2 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from flask import current_app +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 0000000000000000000000000000000000000000..2c0156303a8df3ffdc9de87765bf801bf6bea4a5 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/4f3f4537bb29_users_table.py b/migrations/versions/4f3f4537bb29_users_table.py new file mode 100644 index 0000000000000000000000000000000000000000..dd4140ad027e6ce88398fe51cd6f5c86a258ed76 --- /dev/null +++ b/migrations/versions/4f3f4537bb29_users_table.py @@ -0,0 +1,38 @@ +"""users table + +Revision ID: 4f3f4537bb29 +Revises: a635661f4a03 +Create Date: 2020-09-22 13:41:27.540099 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '4f3f4537bb29' +down_revision = 'a635661f4a03' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=64), nullable=True), + sa.Column('email', sa.String(length=120), nullable=True), + sa.Column('password_hash', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_user_username'), table_name='user') + op.drop_index(op.f('ix_user_email'), table_name='user') + op.drop_table('user') + # ### end Alembic commands ### diff --git a/migrations/versions/59069a416ef2_question_table.py b/migrations/versions/59069a416ef2_question_table.py new file mode 100644 index 0000000000000000000000000000000000000000..bbbb2892dc28a4336a188a987fab271d111ff614 --- /dev/null +++ b/migrations/versions/59069a416ef2_question_table.py @@ -0,0 +1,41 @@ +"""question Table + +Revision ID: 59069a416ef2 +Revises: 9c77048e7767 +Create Date: 2020-09-22 19:15:22.752938 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '59069a416ef2' +down_revision = '9c77048e7767' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('questions', + sa.Column('q_id', sa.Integer(), nullable=False), + sa.Column('ques', sa.String(length=350), nullable=True), + sa.Column('a', sa.String(length=100), nullable=True), + sa.Column('b', sa.String(length=100), nullable=True), + sa.Column('c', sa.String(length=100), nullable=True), + sa.Column('d', sa.String(length=100), nullable=True), + sa.Column('ans', sa.String(length=100), nullable=True), + sa.Column('marks', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('q_id'), + sa.UniqueConstraint('ques') + ) + op.create_index(op.f('ix_questions_marks'), 'questions', ['marks'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_questions_marks'), table_name='questions') + op.drop_table('questions') + # ### end Alembic commands ### diff --git a/migrations/versions/9c77048e7767_questions_table.py b/migrations/versions/9c77048e7767_questions_table.py new file mode 100644 index 0000000000000000000000000000000000000000..47e0b8ad4949f8ca53ac5ebdbbad085998923090 --- /dev/null +++ b/migrations/versions/9c77048e7767_questions_table.py @@ -0,0 +1,43 @@ +"""questions table + +Revision ID: 9c77048e7767 +Revises: 4f3f4537bb29 +Create Date: 2020-09-22 18:51:37.951537 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '9c77048e7767' +down_revision = '4f3f4537bb29' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('questions', + sa.Column('q_id', sa.Integer(), nullable=False), + sa.Column('ques', sa.String(length=350), nullable=True), + sa.Column('a', sa.String(length=100), nullable=True), + sa.Column('b', sa.String(length=100), nullable=True), + sa.Column('c', sa.String(length=100), nullable=True), + sa.Column('d', sa.String(length=100), nullable=True), + sa.Column('ans', sa.String(length=1), nullable=True), + sa.Column('marks', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('q_id'), + sa.UniqueConstraint('ques') + ) + op.create_index(op.f('ix_questions_ans'), 'questions', ['ans'], unique=False) + op.create_index(op.f('ix_questions_marks'), 'questions', ['marks'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_questions_marks'), table_name='questions') + op.drop_index(op.f('ix_questions_ans'), table_name='questions') + op.drop_table('questions') + # ### end Alembic commands ### diff --git a/migrations/versions/__pycache__/4f3f4537bb29_users_table.cpython-38.pyc b/migrations/versions/__pycache__/4f3f4537bb29_users_table.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8bd08abdf2a4bdd82283864e685dc823e4487ace Binary files /dev/null and b/migrations/versions/__pycache__/4f3f4537bb29_users_table.cpython-38.pyc differ diff --git a/migrations/versions/__pycache__/59069a416ef2_question_table.cpython-38.pyc b/migrations/versions/__pycache__/59069a416ef2_question_table.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0766bf9d306e9dc8b14cf0cdf19d709e390c74c0 Binary files /dev/null and b/migrations/versions/__pycache__/59069a416ef2_question_table.cpython-38.pyc differ diff --git a/migrations/versions/__pycache__/9c77048e7767_questions_table.cpython-38.pyc b/migrations/versions/__pycache__/9c77048e7767_questions_table.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba9fb753a9d6b474a0e0456e1d86cd99268fe075 Binary files /dev/null and b/migrations/versions/__pycache__/9c77048e7767_questions_table.cpython-38.pyc differ diff --git a/migrations/versions/__pycache__/a635661f4a03_users_table.cpython-38.pyc b/migrations/versions/__pycache__/a635661f4a03_users_table.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ac7593aa7db8fae8246fba18f028b4338a1c632 Binary files /dev/null and b/migrations/versions/__pycache__/a635661f4a03_users_table.cpython-38.pyc differ diff --git a/migrations/versions/__pycache__/e96865e4fa62_users_added_marks.cpython-38.pyc b/migrations/versions/__pycache__/e96865e4fa62_users_added_marks.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b9208cb573ae44b18d8172b71e3b4b876ce7534 Binary files /dev/null and b/migrations/versions/__pycache__/e96865e4fa62_users_added_marks.cpython-38.pyc differ diff --git a/migrations/versions/a635661f4a03_users_table.py b/migrations/versions/a635661f4a03_users_table.py new file mode 100644 index 0000000000000000000000000000000000000000..29f2d737b96b472b8a168be4510a4908ff94fc3f --- /dev/null +++ b/migrations/versions/a635661f4a03_users_table.py @@ -0,0 +1,38 @@ +"""users table + +Revision ID: a635661f4a03 +Revises: +Create Date: 2020-09-21 23:00:27.622934 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'a635661f4a03' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=64), nullable=True), + sa.Column('email', sa.String(length=120), nullable=True), + sa.Column('password_hash', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_user_username'), table_name='user') + op.drop_index(op.f('ix_user_email'), table_name='user') + op.drop_table('user') + # ### end Alembic commands ### diff --git a/migrations/versions/e96865e4fa62_users_added_marks.py b/migrations/versions/e96865e4fa62_users_added_marks.py new file mode 100644 index 0000000000000000000000000000000000000000..41893adb5a6d80070ffa1272a104f71a8c20d80f --- /dev/null +++ b/migrations/versions/e96865e4fa62_users_added_marks.py @@ -0,0 +1,42 @@ +"""users added marks + +Revision ID: e96865e4fa62 +Revises: 59069a416ef2 +Create Date: 2020-09-23 14:29:28.764869 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'e96865e4fa62' +down_revision = '59069a416ef2' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('questions', + sa.Column('q_id', sa.Integer(), nullable=False), + sa.Column('ques', sa.String(length=350), nullable=True), + sa.Column('a', sa.String(length=100), nullable=True), + sa.Column('b', sa.String(length=100), nullable=True), + sa.Column('c', sa.String(length=100), nullable=True), + sa.Column('d', sa.String(length=100), nullable=True), + sa.Column('ans', sa.String(length=100), nullable=True), + sa.PrimaryKeyConstraint('q_id'), + sa.UniqueConstraint('ques') + ) + op.add_column('user', sa.Column('marks', sa.Integer(), nullable=True)) + op.create_index(op.f('ix_user_marks'), 'user', ['marks'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_user_marks'), table_name='user') + op.drop_column('user', 'marks') + op.drop_table('questions') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..55f62f487d7f5a2a31accd755d17d0bfa0936cd8 Binary files /dev/null and b/requirements.txt differ