Stability fixes, antigrief, checkbox support
This commit is contained in:
parent
5568bc4925
commit
f99e85d3b6
2 changed files with 138 additions and 69 deletions
158
server.py
158
server.py
|
@ -9,6 +9,7 @@ import sys
|
|||
from threading import Thread
|
||||
from os import listdir
|
||||
from time import sleep, time
|
||||
from itertools import combinations
|
||||
from markdown import markdown
|
||||
import db_classes
|
||||
import timeout as tmo
|
||||
|
@ -18,7 +19,13 @@ CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|||
db = db_classes.PickleDB(".db")
|
||||
db.load()
|
||||
|
||||
def resolve_logs(tries = 3, backup = True):
|
||||
def comb(s):
|
||||
result = []
|
||||
for r in range(1, len(s) + 1):
|
||||
result.extend([''.join(p) for p in combinations(s, r)])
|
||||
return result
|
||||
|
||||
def resolve_logs(tries = 3, backup = True, backup_name = None):
|
||||
print("Resolving…")
|
||||
if tries < 1:
|
||||
try:
|
||||
|
@ -32,81 +39,108 @@ def resolve_logs(tries = 3, backup = True):
|
|||
print("Resolved")
|
||||
return
|
||||
if backup:
|
||||
db.save(f"backup{str(int(time()))}.db")
|
||||
for test in pending:
|
||||
logs = db.pop(f"tests.{test}.logs")
|
||||
if logs is None:
|
||||
continue
|
||||
correct = db.pop(f"tests.{test}.correct")
|
||||
if correct is None:
|
||||
correct = {}
|
||||
incorrect = db.pop(f"tests.{test}.incorrect")
|
||||
if incorrect is None:
|
||||
incorrect = {}
|
||||
for l in logs:
|
||||
for a in l['answers']:
|
||||
if a == "dummy":
|
||||
continue
|
||||
if a[0] in correct:
|
||||
if a[2] == correct[a[0]]:
|
||||
l['correct'] -= 1
|
||||
a[0] = '-'
|
||||
if a[0] in incorrect:
|
||||
if a[2] in incorrect[a[0]]:
|
||||
backup_name = f"backup{str(int(time()))}.db"
|
||||
db.save()
|
||||
try:
|
||||
for test in pending:
|
||||
logs = db.pop(f"tests.{test}.logs")
|
||||
if logs is None:
|
||||
continue
|
||||
correct = db.pop(f"tests.{test}.correct")
|
||||
if correct is None:
|
||||
correct = {}
|
||||
incorrect = db.pop(f"tests.{test}.incorrect")
|
||||
if incorrect is None:
|
||||
incorrect = {}
|
||||
for l in logs:
|
||||
if 'shadow' not in l:
|
||||
l['shadow'] = l['answers']
|
||||
for a in l['answers']:
|
||||
if a == "dummy":
|
||||
continue
|
||||
if a[0] in correct:
|
||||
if a[2] == correct[a[0]]:
|
||||
l['correct'] -= 1
|
||||
a[0] = '-'
|
||||
l['answers'] = list(filter(lambda a: a[0] != '-', l['answers']))
|
||||
if len(l['answers']) == l['correct']:
|
||||
for a in l['answers']:
|
||||
if a == "dummy":
|
||||
continue
|
||||
correct[a[0]] = a[2]
|
||||
if a[0] in incorrect:
|
||||
incorrect.pop(a[0])
|
||||
l['answers'] = []
|
||||
elif l['correct'] == 0:
|
||||
for a in l['answers']:
|
||||
if a == "dummy":
|
||||
continue
|
||||
if a[0] not in incorrect:
|
||||
incorrect[a[0]] = []
|
||||
incorrect[a[0]] = list(set(incorrect[a[0]] + [a[2]]))
|
||||
# TODO implement for checkboxes and text
|
||||
if a[2][0] != "[":
|
||||
if a[2][0] != "{":
|
||||
if len(incorrect[a[0]]) == (a[3] - 1):
|
||||
for c in CHARSET[:a[3]]:
|
||||
if c not in incorrect[a[0]]:
|
||||
correct[a[0]] = c
|
||||
break
|
||||
incorrect.pop(a[0])
|
||||
l['answers'] = []
|
||||
logs = list(filter(lambda l: (len(l['answers']) != l['answers'].count("dummy")), logs))
|
||||
new_logs = db.read(f"tests.{test}.logs")
|
||||
if new_logs is None:
|
||||
new_logs = []
|
||||
db.write(f"tests.{test}.logs", logs + new_logs)
|
||||
db.write(f"tests.{test}.correct", correct)
|
||||
db.write(f"tests.{test}.incorrect", incorrect)
|
||||
resolve_logs(tries - 1, False)
|
||||
if a[2] in incorrect[a[0]]:
|
||||
a[0] = '-'
|
||||
l['answers'] = list(filter(lambda a: a[0] != '-', l['answers']))
|
||||
if len(l['answers']) == l['correct']:
|
||||
for a in l['answers']:
|
||||
if a == "dummy":
|
||||
continue
|
||||
correct[a[0]] = a[2]
|
||||
if a[0] in incorrect:
|
||||
incorrect.pop(a[0])
|
||||
l['answers'] = []
|
||||
elif l['correct'] == 0:
|
||||
for a in l['answers']:
|
||||
if a == "dummy":
|
||||
continue
|
||||
if a[0] not in incorrect:
|
||||
incorrect[a[0]] = []
|
||||
incorrect[a[0]] = list(set(incorrect[a[0]] + [a[2]]))
|
||||
match a[2][0]:
|
||||
case "[":
|
||||
pass
|
||||
case "{":
|
||||
if len(incorrect[a[0]]) == (2**a[3] - 2): # Exclude empty and unknown
|
||||
for c in comb(CHARSET[:a[3]]):
|
||||
if c not in incorrect[a[0]]:
|
||||
correct[a[0]] = c
|
||||
break
|
||||
incorrect.pop(a[0])
|
||||
case _:
|
||||
if len(incorrect[a[0]]) == (a[3] - 1):
|
||||
for c in CHARSET[:a[3]]:
|
||||
if c not in incorrect[a[0]]:
|
||||
correct[a[0]] = c
|
||||
break
|
||||
incorrect.pop(a[0])
|
||||
l['answers'] = []
|
||||
elif l['correct'] < 0:
|
||||
for a in l['shadow']:
|
||||
if a[0] in incorrect:
|
||||
incorrect.pop(a[0])
|
||||
if a[0] in correct:
|
||||
correct.pop(a[0])
|
||||
l['answers'] = l['shadow']
|
||||
logs = list(filter(lambda l: (len(l['answers']) != l['answers'].count("dummy")), logs))
|
||||
new_logs = db.read(f"tests.{test}.logs")
|
||||
if new_logs is None:
|
||||
new_logs = []
|
||||
db.write(f"tests.{test}.logs", logs + new_logs)
|
||||
db.write(f"tests.{test}.correct", correct)
|
||||
db.write(f"tests.{test}.incorrect", incorrect)
|
||||
except:
|
||||
print("Something really bad happend. Recovering from backup")
|
||||
if backup_name is not None:
|
||||
db.load(backup_name)
|
||||
resolve_logs(tries - 1, False, backup_name)
|
||||
|
||||
def parse_request(r):
|
||||
try:
|
||||
results = json.loads(r)
|
||||
for field in ['type', 'id', 'uid', 'answers', 'correct', 'all']:
|
||||
if field not in results:
|
||||
return 400
|
||||
rtype = results['type']
|
||||
test_id = results['id']
|
||||
user_id = results['uid']
|
||||
blacklist = db.read('users.blacklist', set())
|
||||
vip = db.read('users.vip', set())
|
||||
if user_id in blacklist:
|
||||
return
|
||||
match (rtype):
|
||||
return 403
|
||||
match rtype:
|
||||
case "test_results":
|
||||
answers = results['answers']
|
||||
all_answ = int(results['all'])
|
||||
while (len(answers) != all_answ):
|
||||
while len(answers) != all_answ:
|
||||
answers.append("dummy")
|
||||
log = {
|
||||
'answers': answers,
|
||||
'shadow': answers,
|
||||
'correct': int(results['correct']),
|
||||
}
|
||||
logs = db.read(f'tests.{test_id}.logs', [])
|
||||
|
@ -115,6 +149,9 @@ def parse_request(r):
|
|||
pending = db.read('pending', set())
|
||||
pending = set(list(pending) + [test_id])
|
||||
db.write('pending', pending)
|
||||
contributors = db.read('contributors', set())
|
||||
contributors = set(list(contributors) + [user_id])
|
||||
db.write('contributors', contributors)
|
||||
return 202
|
||||
case "add_vip":
|
||||
if user_id not in vip:
|
||||
|
@ -141,6 +178,9 @@ def parse_request(r):
|
|||
except KeyError:
|
||||
print("Invalid request")
|
||||
return 400
|
||||
except json.decoder.JSONDecodeError:
|
||||
print("Bad request")
|
||||
return 400
|
||||
except:
|
||||
print("Something bad happend")
|
||||
return 400
|
||||
|
@ -150,7 +190,7 @@ class S(BaseHTTPRequestHandler):
|
|||
self.send_response(status)
|
||||
|
||||
def do_GET(self):
|
||||
match (self.path):
|
||||
match self.path:
|
||||
case "/":
|
||||
self._set_response(200)
|
||||
self.send_header('Content-type', 'text/html; charset=utf-8')
|
||||
|
|
49
user.js
49
user.js
|
@ -2,7 +2,7 @@
|
|||
// @name Sorryops
|
||||
// @name:ru Сориупс
|
||||
// @namespace https://git.disroot.org/electromagneticcyclone/sorryops
|
||||
// @version 20240429.2
|
||||
// @version 20240429.3
|
||||
// @description Collect and reuse ORIOKS test answers
|
||||
// @description:ru Скрипт для сбора и переиспользования ответов на тесты ОРИОКС
|
||||
// @icon https://orioks.miet.ru/favicon.ico
|
||||
|
@ -347,7 +347,7 @@ window.onkeydown = (e) => {
|
|||
|
||||
// const success = -1487162948;
|
||||
var answers = [];
|
||||
var variant, hash, type;
|
||||
var variant, hash, type, correct, incorrect;
|
||||
var testID = (() => {
|
||||
var url = document.URL;
|
||||
url = url.slice(url.indexOf("idKM=") + 5);
|
||||
|
@ -359,6 +359,23 @@ var testID = (() => {
|
|||
|
||||
/* Functions */
|
||||
|
||||
function comb(s, blacklist = []) {
|
||||
var result = [];
|
||||
for (var i = 1; i < (1 << s.length); i++) {
|
||||
var temp = '';
|
||||
for (var j = 0; j < s.length; j++) {
|
||||
if (i & Math.pow(2, j)) {
|
||||
temp += s[j];
|
||||
}
|
||||
}
|
||||
temp = "{" + temp + "}";
|
||||
if (!blacklist.includes(temp)) {
|
||||
result.push(temp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// https://github.com/ajayyy/maze-utils/blob/036086403f675b8fea0e22065f26ba534e351562/src/setup.ts
|
||||
function generate_user_id(length = 36) {
|
||||
var i;
|
||||
|
@ -456,6 +473,7 @@ function calculate_variant_hash() {
|
|||
|
||||
function update_variant() {
|
||||
var i, pbox;
|
||||
var status = "Неизвестен";
|
||||
var chosen_answer = "";
|
||||
switch (type) {
|
||||
case 'checkbox':
|
||||
|
@ -474,12 +492,21 @@ function update_variant() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (chosen_answer == correct) {
|
||||
status = "<span style='color: green;'>Верно</span>";
|
||||
} else if (incorrect.includes(chosen_answer)) {
|
||||
status = "<span style='color: red;'>Неверно</span>";
|
||||
}
|
||||
var pboxes = document.getElementsByTagName('p');
|
||||
const display_answer = config.get('display_answer');
|
||||
for (i = 0; i < pboxes.length; i++) {
|
||||
pbox = pboxes[i];
|
||||
if (pbox.textContent.includes("Вопрос:")) {
|
||||
pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i><br>Вопрос:";
|
||||
pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i>";
|
||||
if (config.get('server') != '') {
|
||||
pbox.innerHTML += "<br>Статус: " + status;
|
||||
}
|
||||
pbox.innerHTML += "<br>Вопрос:";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -506,7 +533,7 @@ function update_variant() {
|
|||
/* Handlers */
|
||||
|
||||
function test_form_handler(server_data) {
|
||||
var i, key, correct, incorrect, answer, sorry_val;
|
||||
var i, key, answer, sorry_val;
|
||||
var boxes = [];
|
||||
var sorted_objects;
|
||||
var objects_hash = new Object();
|
||||
|
@ -605,7 +632,7 @@ function test_form_handler(server_data) {
|
|||
if (answers[answer].sorry_value == correct) {
|
||||
var correct_element = answers[answer].parentNode;
|
||||
sorry_val = answers[answer].sorry_value;
|
||||
correct_element.innerHTML = "<div style='color: green;'>" + correct_element.innerHTML + "</div>";
|
||||
correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
|
||||
answers[answer] = correct_element.getElementsByTagName('input')[0];
|
||||
answers[answer].sorry_value = sorry_val;
|
||||
answers[answer].click();
|
||||
|
@ -613,7 +640,7 @@ function test_form_handler(server_data) {
|
|||
}
|
||||
}
|
||||
} else if (auto_answer == labels.auto_answer_random) {
|
||||
if (answers[0].type === 'radio') {
|
||||
if (type === 'radio') {
|
||||
var possible_answers = [];
|
||||
for (answer in answers) {
|
||||
if (incorrect.includes(answers[answer].sorry_value) == false) {
|
||||
|
@ -621,7 +648,7 @@ function test_form_handler(server_data) {
|
|||
} else {
|
||||
var incorrect_element = answers[answer].parentNode;
|
||||
sorry_val = answers[answer].sorry_value;
|
||||
incorrect_element.innerHTML = "<div style='color: red;'>" + incorrect_element.innerHTML + "</div>";
|
||||
incorrect_element.innerHTML = "<span style='color: red;'>" + incorrect_element.innerHTML + "</span>";
|
||||
answers[answer] = incorrect_element.getElementsByTagName('input')[0];
|
||||
answers[answer].sorry_value = sorry_val;
|
||||
answers[answer] = incorrect_element.getElementsByTagName('input')[0];
|
||||
|
@ -630,10 +657,12 @@ function test_form_handler(server_data) {
|
|||
var chosen_answer;
|
||||
chosen_answer = Math.floor(Math.random() * possible_answers.length);
|
||||
answers[possible_answers[chosen_answer]].click();
|
||||
} else {
|
||||
var pick = Math.floor(Math.random() * (Math.pow(2, answers.length) - 1)) + 1;
|
||||
} else if (type === 'checkbox') {
|
||||
var combs = comb(charset.slice(0, answers.length), incorrect);
|
||||
console.log(combs);
|
||||
var pick = combs[Math.floor(Math.random() * combs.length)];
|
||||
for (i = 0; i < answers.length; i++) {
|
||||
if(pick & Math.pow(2, i)) {
|
||||
if(pick.includes(answers[i].sorry_value)) {
|
||||
answers[i].click();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue