1
0
Fork 0
mirror of https://github.com/Findus23/se-simulator.git synced 2024-09-19 15:53:45 +02:00

some improvements and basic quiz

This commit is contained in:
Lukas Winkler 2018-03-27 20:41:57 +02:00
parent 8d89773539
commit 49f2ec65ba
8 changed files with 177 additions and 64 deletions

View file

@ -1,9 +1,10 @@
from datetime import datetime from datetime import datetime
import time
import sass import sass
from flask import render_template, send_from_directory, abort, session, jsonify, make_response from flask import render_template, send_from_directory, abort, session, jsonify, make_response
from flask_limiter import Limiter from flask_limiter import Limiter
from flask_limiter.util import get_remote_address from flask_limiter.util import get_remote_address
from flask_session import Session
from playhouse.flask_utils import PaginatedQuery, get_object_or_404 from playhouse.flask_utils import PaginatedQuery, get_object_or_404
from playhouse.shortcuts import model_to_dict from playhouse.shortcuts import model_to_dict
from sassutils.wsgi import SassMiddleware from sassutils.wsgi import SassMiddleware
@ -15,7 +16,13 @@ from models import *
app.jinja_env.globals.update(prettydate=utils.prettydate) app.jinja_env.globals.update(prettydate=utils.prettydate)
SESSION_TYPE = 'redis'
SESSION_COOKIE_SECURE = config.production
SESSION_USE_SIGNER = True
SESSION_KEY_PREFIX = "StackDataSessions:"
app.config.from_object(__name__)
app.secret_key = config.secret_key app.secret_key = config.secret_key
Session(app)
limiter = Limiter( limiter = Limiter(
app, app,
@ -46,7 +53,8 @@ def index(site=None):
num_pages=paginated_query.get_page_count(), num_pages=paginated_query.get_page_count(),
page=paginated_query.get_page(), page=paginated_query.get_page(),
questions=paginated_query.get_object_list(), questions=paginated_query.get_object_list(),
site=site_element site=site_element,
voted=session["voted"] if "voted" in session and not config.make_cacheable else None
) )
@ -63,7 +71,23 @@ def question(slug):
return render_template( return render_template(
"detail.html", "detail.html",
question=question, question=question,
answers=answers answers=answers,
voted=session["voted"] if "voted" in session and not config.make_cacheable else None
)
@app.route('/quiz')
def quiz():
time1 = time.time()
question = Question.select(Question, Title, User, Site) \
.join(Title).switch(Question) \
.join(User).switch(Question) \
.join(Site).where((Question.upvotes - Question.downvotes >= 0)).order_by(SQL("RAND()")).limit(1).get()
time2 = time.time()
print('{} ms'.format((time2 - time1) * 1000.0))
return render_template(
"quiz.html",
question=question
) )
@ -78,11 +102,6 @@ def sites():
@app.route('/test') @app.route('/test')
def sdfdsfds(): def sdfdsfds():
user = User.select().get()
for question in Question.select():
question.upvotes = 1
question.downvotes = 1
question.save()
return jsonify( return jsonify(
model_to_dict(Answer.select().where((Answer.question.is_null())).get())) model_to_dict(Answer.select().where((Answer.question.is_null())).get()))
@ -91,11 +110,9 @@ def sdfdsfds():
@limiter.limit("10 per minute") @limiter.limit("10 per minute")
def vote(type, id, vote): def vote(type, id, vote):
if "voted" not in session: if "voted" not in session:
voted = [] session["voted"] = {}
else: print(session["voted"])
voted = session["voted"] if (type, id) in session["voted"]:
print(voted)
if (type, id) in voted:
abort(403) abort(403)
if type == "question": if type == "question":
if vote == "up": if vote == "up":
@ -113,8 +130,7 @@ def vote(type, id, vote):
return abort(404) return abort(404)
else: else:
return abort(404) return abort(404)
voted.append((type, id)) session["voted"][(type, id)] = vote == "up"
session["voted"] = voted
query.execute() query.execute()
if type == "question": if type == "question":
query = Question.select(Question.upvotes, Question.downvotes).where(Question.id == id).get() query = Question.select(Question.upvotes, Question.downvotes).where(Question.id == id).get()

View file

@ -3,12 +3,12 @@
{% block body %} {% block body %}
{{ siteheader(question.site) }} {{ siteheader(question.site) }}
<h1>{{ question.title.text }}</h1> <h1>{{ question.title.text }}</h1>
{% set vote=voted[("question", question.id)] %}
<div class="content question"> <div class="content question">
<div class="vote" data-id="{{ question.id }}" data-type="question"> <div class="vote" data-id="{{ question.id }}" data-type="question">
<a class="up"></a> <a class="up {{ "active" if vote == True }}"></a>
<div>{{ question.upvotes - question.downvotes }}</div> <div>{{ question.upvotes - question.downvotes }}</div>
<a class="down"></a> <a class="down {{ "active" if vote == False }}"></a>
</div> </div>
<div class="contentbox"> <div class="contentbox">
@ -26,11 +26,12 @@
</div> </div>
<h2 class="answerheader">{{ answers|length }} Answers</h2> <h2 class="answerheader">{{ answers|length }} Answers</h2>
{% for answer in answers %} {% for answer in answers %}
{% set vote=voted[("answer", answer.id)] %}
<div class="content answer"> <div class="content answer">
<div class="vote" data-id="{{ answer.id }}" data-type="answer" data-ranking="{{ answer.ci_lower_bound }}"> <div class="vote" data-id="{{ answer.id }}" data-type="answer" data-ranking="{{ answer.ci_lower_bound }}">
<a class="up"></a> <a class="up {{ "active" if vote == True }}"></a>
<div>{{ answer.upvotes - answer.downvotes }}</div> <div>{{ answer.upvotes - answer.downvotes }}</div>
<a class="down"></a> <a class="down {{ "active" if vote == False }}"></a>
</div> </div>
<div class="contentbox"> <div class="contentbox">
{% for paragraph in answer.text.split("\n") %} {% for paragraph in answer.text.split("\n") %}

View file

@ -3,20 +3,21 @@
{% block body %} {% block body %}
{{ siteheader(site) }} {{ siteheader(site) }}
<label for="siteselector">Seite</label> <label class="label-inline" for="siteselector">Seite</label>
<input id="siteselector" class="awesomplete" value="{{ site.url if not site.fallback }}"/> <input id="siteselector" class="awesomplete" value="{{ site.url if not site.fallback }}" data-mode="filter"/>
{% if not site.fallback %} {% if not site.fallback %}
<a href="{{ url_for("index") }}">Clear filter</a> <a href="{{ url_for("index") }}">Clear filter</a>
{% endif %} {% endif %}
{{ pagination(pagearray, num_pages, page, True) }} {{ pagination(pagearray, num_pages, page, True) }}
{% for question in questions %} {% for question in questions %}
{% set vote=voted[("question", question.id)] %}
<div class="content question" <div class="content question"
style="border-right-color:{{ question.site.tag_foreground_color }};background-color:{{ question.site.tag_background_color }}"> style="border-right-color:{{ question.site.tag_foreground_color }};background-color:{{ question.site.tag_background_color }}">
<div class="vote" data-id="{{ question.id }}" data-type="question"> <div class="vote" data-id="{{ question.id }}" data-type="question">
<a class="up"></a> <a class="up {{ "active" if vote == True }}"></a>
<div>{{ question.upvotes - question.downvotes }}</div> <div>{{ question.upvotes - question.downvotes }}</div>
<a class="down"></a> <a class="down {{ "active" if vote == False }}"></a>
</div> </div>
<div class="contentbox"> <div class="contentbox">
<a href="https://{{ question.site.url }}" class="sitename" target="_blank" rel="noopener"> <a href="https://{{ question.site.url }}" class="sitename" target="_blank" rel="noopener">

29
templates/quiz.html Normal file
View file

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block body %}
<input id="siteselector" class="awesomplete" data-mode="quiz"/>
<button id="check">Check</button>
<h1>{{ question.title.text }}</h1>
<div class="content question">
<div class="vote">
<a class="up"></a>
<div>{{ question.upvotes - question.downvotes }}</div>
<a class="down"></a>
</div>
<div class="contentbox">
{% for paragraph in question.text.split("\n") %}
<p>{{ paragraph }}</p>
{% endfor %}
<div class="contentfooter">
<div class="authorbox">
asked {{ prettydate(question.datetime) }}
<br>
{{ question.user.username }}
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,21 +1,17 @@
document.addEventListener("DOMContentLoaded", function (event) { document.addEventListener("DOMContentLoaded", function (event) {
var vote = document.getElementsByClassName("vote"); var vote = document.getElementsByClassName("vote");
console.warn(vote);
Array.prototype.forEach.call(vote, function (elvote) { Array.prototype.forEach.call(vote, function (elvote) {
var id = elvote.dataset.id; var id = elvote.dataset.id;
var type = elvote.dataset.type; var type = elvote.dataset.type;
Array.prototype.forEach.call(elvote.querySelectorAll("a"), function (el) { Array.prototype.forEach.call(elvote.querySelectorAll("a"), function (el) {
el.addEventListener("click", function (event) { el.addEventListener("click", function (event) {
console.info(elvote);
var vote = el.classList[0]; var vote = el.classList[0];
console.info(type, id, vote);
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open("POST", "/api/vote/" + type + "/" + id + "/" + vote, true); request.open("POST", "/api/vote/" + type + "/" + id + "/" + vote, true);
request.onload = function () { request.onload = function () {
if (this.status >= 200 && this.status < 400) { if (this.status >= 200 && this.status < 400) {
var resp = JSON.parse(this.response); var resp = JSON.parse(this.response);
console.info(resp);
el.classList.add("active"); el.classList.add("active");
elvote.querySelector("div").textContent = resp.upvotes - resp.downvotes elvote.querySelector("div").textContent = resp.upvotes - resp.downvotes
} else { } else {
@ -31,43 +27,54 @@ document.addEventListener("DOMContentLoaded", function (event) {
}); });
}); });
var input = document.getElementById("siteselector"); var input = document.getElementById("siteselector");
var request = new XMLHttpRequest(); if (input) {
request.open("GET", "/api/sites", true); var mode = input.dataset.mode;
var request = new XMLHttpRequest();
request.open("GET", "/api/sites", true);
request.onload = function () { request.onload = function () {
if (this.status >= 200 && this.status < 400) { if (this.status >= 200 && this.status < 400) {
var resp = JSON.parse(this.response); var resp = JSON.parse(this.response);
var list = []; var list = [];
for (var key in resp) { for (var key in resp) {
if (resp.hasOwnProperty(key)) { if (resp.hasOwnProperty(key)) {
var site, shortname; var site, shortname;
site = resp[key]; site = resp[key];
shortname = site.url.replace(".stackexchange.com", ".SE"); shortname = site.url.replace(".stackexchange.com", ".SE");
list.push({ list.push({
label: site.name + " (" + shortname + ")", label: site.name + " (" + shortname + ")",
value: site.url value: site.url
}); });
}
} }
new Awesomplete(input, {
list: list,
minChars: 0,
autoFirst: true
});
input.addEventListener("awesomplete-select", function (event) {
if (!(event.text.value in resp)) { // shouldn't happen
return false
}
var selectedSite = resp[event.text.value];
if (mode === "filter") {
window.location.href = "/s/" + selectedSite.url
} else if (mode === "quiz") {
console.log(selectedSite);
}
});
} else {
// We reached our target server, but it returned an error
} }
new Awesomplete(input, { };
list: list request.onerror = function () {
}); // There was a connection error of some sort
input.addEventListener("awesomplete-select", function (event) { };
if (!(event.text.value in resp)) { // shouldn't happen request.send();
return false
}
var selectedSite=resp[event.text.value];
window.location.href="/s/"+selectedSite.url
}); }
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function () {
// There was a connection error of some sort
};
request.send();
}); });

View file

@ -1,3 +1,8 @@
// Based on https://github.com/LeaVerou/awesomplete/blob/gh-pages/awesomplete.base.css
// Copyright (c) 2015 Lea Verou
// MIT License
.awesomplete [hidden] { .awesomplete [hidden] {
display: none; display: none;
} }

View file

@ -0,0 +1,55 @@
// Based on https://github.com/LeaVerou/awesomplete/blob/gh-pages/awesomplete.theme.css
// Copyright (c) 2015 Lea Verou
// MIT License
@import "awesomplete.base";
.awesomplete {
> ul {
padding: 1rem 1rem 0;
margin: .2em 0 0;
border: 1px solid rgba(0, 0, 0, .3);
box-shadow: .05em .2em .6em rgba(0, 0, 0, .2);
text-shadow: none;
> li {
position: relative;
padding: .2em .5em;
cursor: pointer;
&:hover {
background: hsl(200, 40%, 80%);
color: black;
mark {
background: hsl(68, 100%, 41%);
}
}
&[aria-selected="true"] {
background: hsl(205, 40%, 40%);
color: white;
mark {
background: hsl(86, 100%, 21%);
color: inherit;
}
}
}
&:before { // pointer
content: "";
position: absolute;
top: -.43em;
left: 1em;
width: 0;
height: 0;
padding: .4em;
background: white;
border: inherit;
border-right: 0;
border-bottom: 0;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
}
mark {
background: hsl(65, 100%, 50%);
}
}

View file

@ -2,11 +2,10 @@ $link-color: #07C;
$link-hover-color: #3af; $link-hover-color: #3af;
@import "../../milligram/src/milligram"; @import "../../milligram/src/milligram";
@import "awesomplete.base"; @import "awesomplete.custom";
@import "pagination"; @import "pagination";
body { body {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
font-size: 15px; font-size: 15px;
color: #111; color: #111;
} }