Stability fixes, antigrief, checkbox support

This commit is contained in:
Egor Guslyancev 2024-04-29 15:40:49 -03:00
parent 5568bc4925
commit f99e85d3b6
GPG Key ID: D7E709AA465A55F9
2 changed files with 138 additions and 69 deletions

158
server.py
View File

@ -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
View File

@ -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();
}
}