Started the admin site
Can currently view all the questions and search through them
This commit is contained in:
parent
2d18b1b2bf
commit
197164d691
61
admin/admin.py
Normal file
61
admin/admin.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import json
|
||||||
|
from flask import render_template, Blueprint, request, jsonify
|
||||||
|
import database
|
||||||
|
from database import get_all_questions, get_question
|
||||||
|
|
||||||
|
|
||||||
|
Admin = Blueprint("admin", __name__, template_folder="templates")
|
||||||
|
|
||||||
|
|
||||||
|
@Admin.route("/admin/")
|
||||||
|
def index():
|
||||||
|
return render_template("admin/index.html", title="admin")
|
||||||
|
|
||||||
|
|
||||||
|
@Admin.route("/admin/questions/")
|
||||||
|
def questions():
|
||||||
|
return render_template("admin/question_list.html")
|
||||||
|
|
||||||
|
|
||||||
|
@Admin.route("/admin/questions/edit/<int:question_id>")
|
||||||
|
def edit_question(question_id):
|
||||||
|
question: database.AllQuestions = get_question(database.AllQuestions, question_id)
|
||||||
|
return render_template("admin/edit_question.html", question=question)
|
||||||
|
|
||||||
|
|
||||||
|
@Admin.route("/admin/question_query")
|
||||||
|
def query_questions():
|
||||||
|
offset = request.args.get("offset", type=int)
|
||||||
|
limit = request.args.get("limit", type=int)
|
||||||
|
sort = request.args.get("sort")
|
||||||
|
order = request.args.get("order")
|
||||||
|
query = parse_question_query(request.args.get("filter"))
|
||||||
|
result = database.query_all_questions(offset, limit, query, sort, order)
|
||||||
|
response_dict = {
|
||||||
|
"total": result[1],
|
||||||
|
"rows": [],
|
||||||
|
}
|
||||||
|
for question in result[0]:
|
||||||
|
response_dict["rows"].append({
|
||||||
|
"id": question.question_id,
|
||||||
|
"question_id": question.question_id,
|
||||||
|
"question": question.question,
|
||||||
|
"answer": question.answer,
|
||||||
|
"multiple_choice": getattr(question, "multiple_choice", None) is not None,
|
||||||
|
"hidden_answer": getattr(question, "hidden_answer", None) is not None,
|
||||||
|
})
|
||||||
|
# question1 = get_question(database.AllQuestions, 0)
|
||||||
|
# question2 = get_question(database.AllQuestions, 1)
|
||||||
|
# question3 = get_question(database.AllQuestions, 2)
|
||||||
|
return jsonify(response_dict)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_question_query(query):
|
||||||
|
if query:
|
||||||
|
query: dict = json.loads(query)
|
||||||
|
if "multiple_choice" in query.keys():
|
||||||
|
query["multiple_choice"] = True if query["multiple_choice"] == "true" else False
|
||||||
|
if "hidden_answer" in query.keys():
|
||||||
|
query["hidden_answer"] = True if query["hidden_answer"] == "true" else False
|
||||||
|
return query
|
||||||
|
return None
|
40
admin/templates/admin/base.html
Normal file
40
admin/templates/admin/base.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="h-100" lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Admin</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootswatch/4.5.2/cosmo/bootstrap.min.css">
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body class="d-flex flex-column h-100">
|
||||||
|
<nav class="navbar navbar-expand navbar-dark bg-primary">
|
||||||
|
<a class="navbar-brand" href="{{ url_for('admin.index') }}">Admin Site</a>
|
||||||
|
<button class="navbar-toggler" type="button">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('admin.index') }}">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('admin.questions') }}">Questions</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="container">
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
<footer class="mt-auto py-3">
|
||||||
|
<div class="container text-center">
|
||||||
|
<span class="">Created By ItIsGood.com</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
{% block scripts %}{% endblock %}
|
||||||
|
</html>
|
5
admin/templates/admin/index.html
Normal file
5
admin/templates/admin/index.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends "admin/base.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<p>This is the admin home page</p>
|
||||||
|
{% endblock %}
|
63
admin/templates/admin/question_list.html
Normal file
63
admin/templates/admin/question_list.html
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{% extends "admin/base.html" %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.18.2/dist/bootstrap-table.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.18.2/extensions/filter-control/bootstrap-table-filter-control.min.css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<table id="question-table" class="table"></table>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
<script src="https://unpkg.com/bootstrap-table@1.18.2/dist/bootstrap-table.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-table/1.18.2/extensions/filter-control/bootstrap-table-filter-control.min.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function query_params(params) {
|
||||||
|
console.log(params);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter_data = [true, false]
|
||||||
|
|
||||||
|
$("#question-table").bootstrapTable({
|
||||||
|
url: "/admin/question_query",
|
||||||
|
pagination: true,
|
||||||
|
sidePagination: "server",
|
||||||
|
filterControl: true,
|
||||||
|
queryParams: query_params,
|
||||||
|
showRefresh: true,
|
||||||
|
searchOnEnterKey: true,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
field: "question_id",
|
||||||
|
title: "Question ID",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "question",
|
||||||
|
title: "Question",
|
||||||
|
filterControl: "input",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "answer",
|
||||||
|
title: "Answer",
|
||||||
|
filterControl: "input",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "multiple_choice",
|
||||||
|
title: "Has Multiple Choice",
|
||||||
|
filterControl: "select",
|
||||||
|
filterData: "var:filter_data",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "hidden_answer",
|
||||||
|
title: "Has Hidden Answer",
|
||||||
|
filterControl: "select",
|
||||||
|
filterData: "var:filter_data",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -7,6 +7,7 @@ load_dotenv(".env")
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
SECRET_KEY = environ.get("SECRET_KEY")
|
SECRET_KEY = environ.get("SECRET_KEY")
|
||||||
|
SECURITY_PASSWORD_SALT = environ.get("SECURITY_PASSWORD_SALT")
|
||||||
DB_USER = environ.get("DB_USER")
|
DB_USER = environ.get("DB_USER")
|
||||||
DB_PASSWORD = environ.get("DB_PASSWORD")
|
DB_PASSWORD = environ.get("DB_PASSWORD")
|
||||||
DB_HOST = environ.get("DB_HOST")
|
DB_HOST = environ.get("DB_HOST")
|
||||||
|
30
database.py
30
database.py
@ -26,6 +26,9 @@ class AllQuestions(Base):
|
|||||||
answer = Column(String)
|
answer = Column(String)
|
||||||
addresses = Column(String)
|
addresses = Column(String)
|
||||||
|
|
||||||
|
multiple_choice = relationship("MultipleChoice", uselist=False, back_populates="all_question_relationship")
|
||||||
|
hidden_answer = relationship("HiddenAnswer", uselist=False, back_populates="all_question_relationship")
|
||||||
|
|
||||||
def __init__(self, question_id, question, answer, addresses):
|
def __init__(self, question_id, question, answer, addresses):
|
||||||
self.question_id = question_id
|
self.question_id = question_id
|
||||||
self.question = question
|
self.question = question
|
||||||
@ -43,7 +46,7 @@ class HiddenAnswer(Base):
|
|||||||
difficulty = Column(Integer)
|
difficulty = Column(Integer)
|
||||||
hint = Column(JSON)
|
hint = Column(JSON)
|
||||||
|
|
||||||
all_question_relationship = relationship("AllQuestions", foreign_keys=[question_id], lazy="joined", uselist=False)
|
all_question_relationship = relationship("AllQuestions", lazy="joined", back_populates="hidden_answer")
|
||||||
question = association_proxy("all_question_relationship", "question")
|
question = association_proxy("all_question_relationship", "question")
|
||||||
answer = association_proxy("all_question_relationship", "answer")
|
answer = association_proxy("all_question_relationship", "answer")
|
||||||
addresses = association_proxy("all_question_relationship", "addresses")
|
addresses = association_proxy("all_question_relationship", "addresses")
|
||||||
@ -66,7 +69,7 @@ class MultipleChoice(Base):
|
|||||||
hint = Column(JSON)
|
hint = Column(JSON)
|
||||||
wrong_answers = Column(ARRAY(String))
|
wrong_answers = Column(ARRAY(String))
|
||||||
|
|
||||||
all_question_relationship = relationship("AllQuestions", foreign_keys=[question_id], lazy="joined", uselist=False)
|
all_question_relationship = relationship("AllQuestions", lazy="joined", back_populates="multiple_choice")
|
||||||
question = association_proxy("all_question_relationship", "question")
|
question = association_proxy("all_question_relationship", "question")
|
||||||
answer = association_proxy("all_question_relationship", "answer")
|
answer = association_proxy("all_question_relationship", "answer")
|
||||||
addresses = association_proxy("all_question_relationship", "addresses")
|
addresses = association_proxy("all_question_relationship", "addresses")
|
||||||
@ -118,7 +121,7 @@ def get_category_count(category: Union[Type[MultipleChoice], Type[HiddenAnswer],
|
|||||||
return session.query(category).count()
|
return session.query(category).count()
|
||||||
|
|
||||||
|
|
||||||
def get_question(category: Union[Type[MultipleChoice], Type[HiddenAnswer], Type[AllQuestions]], question_id: int) -> Optional[Union[Type[MultipleChoice], Type[HiddenAnswer], Type[AllQuestions]]]:
|
def get_question(category: Union[Type[MultipleChoice], Type[HiddenAnswer], Type[AllQuestions]], question_id: int) -> Optional[Union[MultipleChoice, HiddenAnswer, AllQuestions]]:
|
||||||
session: sqlalchemy.orm.session.Session = Session()
|
session: sqlalchemy.orm.session.Session = Session()
|
||||||
return session.query(category).filter(category.question_id == question_id).one_or_none()
|
return session.query(category).filter(category.question_id == question_id).one_or_none()
|
||||||
|
|
||||||
@ -149,3 +152,24 @@ def check_answer(question_id: int, guess: str) -> Tuple[bool, str]:
|
|||||||
if question:
|
if question:
|
||||||
answer = question.answer
|
answer = question.answer
|
||||||
return answer == guess, answer
|
return answer == guess, answer
|
||||||
|
|
||||||
|
|
||||||
|
def query_all_questions(offset, limit, query: dict = None, sort=None, order=None) -> Tuple[List[AllQuestions], int]:
|
||||||
|
session: sqlalchemy.orm.session.Session = Session()
|
||||||
|
query_params = []
|
||||||
|
if query is not None:
|
||||||
|
for key in query.keys():
|
||||||
|
if key == "multiple_choice" or key == "hidden_answer":
|
||||||
|
if query[key]:
|
||||||
|
query_params.append(getattr(AllQuestions, key) != None)
|
||||||
|
else:
|
||||||
|
query_params.append(getattr(AllQuestions, key) == None)
|
||||||
|
else:
|
||||||
|
query_params.append(getattr(AllQuestions, key).ilike("%"+query[key]+"%"))
|
||||||
|
order_by = None
|
||||||
|
if sort and order:
|
||||||
|
order_by = getattr(getattr(AllQuestions, sort), order)()
|
||||||
|
q = session.query(AllQuestions).filter(*query_params).order_by(order_by)
|
||||||
|
questions = q.offset(offset).limit(limit).all()
|
||||||
|
count = q.count()
|
||||||
|
return questions, count
|
||||||
|
3
main.py
3
main.py
@ -1,8 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
from flask import Flask, render_template, request, jsonify
|
from flask import Flask, render_template, request, jsonify
|
||||||
|
from flask_security import Security
|
||||||
import database
|
import database
|
||||||
|
from admin import admin
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
app.register_blueprint(admin.Admin)
|
||||||
environment_configuration = os.environ['CONFIGURATION_SETUP']
|
environment_configuration = os.environ['CONFIGURATION_SETUP']
|
||||||
app.config.from_object(environment_configuration)
|
app.config.from_object(environment_configuration)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user