0% found this document useful (0 votes)
10 views

quizlet.py

Uploaded by

dingklefard
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

quizlet.py

Uploaded by

dingklefard
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 13

import random

import os
import time
import shutil
from InputHandler import getch_handler
import threading
import platform
import traceback
import sys

reverseDict = {"0" : False, "1": True, "True": True, "False": False}


skip = False
file = None
reverse = False
subura = False
learn = True
threshold = 2

for index, arg in enumerate(sys.argv[1:]):


if skip:
skip = not skip
continue
if arg == "-f" or arg == "-file":
file = sys.argv[index + 2]
skip = True
if arg == "-r":
reverse = reverseDict[sys.argv[index + 2]]
skip = True
elif arg == "-s":
subura = True
learn = False
elif arg == "-l":
learn = True
elif arg == "-t":
threshold = int(sys.argv[index + 2])
skip = True

if file == None:
raise Exception("Add file (-f) argument, such as -f file.txt")

if platform.system() == "Windows":
def clear_screen():
os.system("cls")
else:
def clear_screen():
os.system("clear")

lines = open(file).readlines()

for index in range(len(lines)):


lines[index] = lines[index].strip().split("-#-")

lines.sort()
#print(lines)

def move_terminal_cursor(x, y):


print(f'\033[{y};{x}H')

gradient = [[236, 14, 14], [237, 45, 0], [237, 64, 0], [236, 80, 0], [234, 94, 0],
[231, 107, 0], [227, 120, 0], [222, 132, 0], [216, 143, 0], [210, 154, 0], [204,
164, 0], [197, 174, 0], [189, 184, 0], [180, 193, 0], [170, 202, 0], [159, 211, 0],
[146, 220, 0], [130, 229, 0], [108, 238, 0], [82, 247, 0], [36, 255, 0]]
lengths = [36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 35, 35]

def get_code(i):
div = int(i//5)

if i - div*5 >= 2.5:


return gradient[div + 1]
return gradient[div]

def get_length(i):
if i == "N/A":
return 36
div = int(i//5)
if i - div*5 >= 2.5:
return lengths[div + 1]
return lengths[div]

def format_colour(string, integer):


red, green, blue = get_code(integer)
return f"\033[38;2;{red};{green};{blue}m" + string + f"\033[38;2;255;255;255m"

class Card:
def __init__(self, front, back):
self.front = front.strip()
self.back = [back.strip()]
self.answered_back = []
self.values = [int(threshold)]
self.availableValues = [0]
self.answered_correct = 0
self.answered = 0
self.uindex = 0

def checkAnswer(self, answer, subCard, hasFailed = False):


if answer.strip() == self.back[subCard]:
self.values[subCard] -= (1 * int(not hasFailed)) * (-1 if subura else
0)
if self.values[subCard] == 0:
self.availableValues.pop(self.availableValues.index(subCard))
TopBar.cardsCompleted += (1 * int(not hasFailed))
TopBar.sectionCorrect += (1 * int(not hasFailed))
TopBar.cardsCorrect += (1 * int(not hasFailed))
TopBar.cardAnswered += (1 * int(not hasFailed))
self.answered_correct += (1 * int(not hasFailed))
self.answered += (1 * int(not hasFailed))
if not hasFailed and subura:
item_index = 0
for index, _ in enumerate(validCardSegment.listReference):
if _.card == self:
item_index = index
break
validCardSegment.correct(item_index)
return True
elif not hasFailed:
if self.values[subCard] + 1 > threshold:
self.values[subCard] = threshold
else:
self.values[subCard] += (1 * int(hasFailed))
TopBar.cardsCompleted -= (1 * int(hasFailed))
self.answered += (1 * int(not hasFailed))
TopBar.cardAnswered += (1 * int(not hasFailed))
return False
#print(self.values)

def addAnswer(self, back):


self.back.append(back.strip())
self.values.append(int(threshold))
self.availableValues.append(len(self.availableValues))

def registerScore(self, score):


#print("r", self.uindex)
self.values[self.uindex] = score
self.uindex += 1
if self.uindex == len(self.values):
return True
return False

def __repr__(self):
return f"\n{self.front}\n{self.back}\n"

class CardPointer:
def __init__(self, card):
self.card = card

class PointerSlice:
def __init__(self, listReference, index, endIndex):
self.index = index
self.endIndex = endIndex
self.listReference = listReference
self.currentIndex = None
self.previousIndex = None
self.search = []

def getRandom(self):
#print(self.endIndex)
self.currentIndex = random.randint(0, self.endIndex-1) if self.endIndex > 1
else 0
#print(self.currentIndex, self.listReference)
return self.listReference[self.index: self.endIndex][self.currentIndex]

def getMinimum(self):
self.previousIndex = self.currentIndex
if self.previousIndex != None and len(self.search) > 1:
(da := min(self.search[:self.previousIndex] +
self.search[self.previousIndex+1:]))
#print(da, self.search[:self.previousIndex] +
self.search[self.previousIndex+1:])
#self.currentIndex = random.choice([index for index, _ in
enumerate(self.search[:self.previousIndex] + self.search[self.previousIndex+1:]) if
_ == da])
self.currentIndex = random.choice([index for index, _ in
enumerate(self.search[:self.previousIndex]) if _ == da] + [index +
len(self.search[:self.previousIndex]) + 1 for index, _ in
enumerate(self.search[self.previousIndex+1:]) if _ == da])
else:
return self.getRandom()
return self.listReference[self.currentIndex]

def correct(self, i):


#print(i, self.search)
self.search[i] += 1

def isCardValid(self, finished):


for index, card in enumerate(self.listReference):
if len(card.card.availableValues) == 0:
finished.append(self.listReference.pop(index))
self.endIndex -= 1

def len(self):
return self.endIndex - self.index

global input_list
input_list = []

class Input:
def __init__(self, string):
self.string = string

def assign_index(self, input_index):


self.input_index = input_index

def display(self):
if self.string.strip() == "" and self.input_index == len(input_list) - 1:
self.string = "Enter Answer"
res = self.display()
self.string = ""
return res
elif self.string.strip() == "":
self.string = "Did Not Answer"
res = self.display()
return res
return "\033[38;2;129;133;137;m" + self.string + "\033[38;2;255;255;255;m"

def centre(string, sizeOfLine, fix = "", padding = " ", idk = 0):
sizeOfLine -= len(string) - idk
return fix + (padding * (sizeOfLine - int(sizeOfLine//2))) + string + (padding
* int(sizeOfLine//2)) + fix

def centrep(string, sizeOfLine, rfix = "", lfix = "", padding=" "):


sizeOfLine -= len(string)
return lfix + (padding * (sizeOfLine - int(sizeOfLine//2))) + string + (padding
* int(sizeOfLine//2)) + rfix

def centrei(string, sizeOfLine, fix = "", padding = " "):


sizeOfLine -= len(string.string)
return fix + (padding * (sizeOfLine - int(sizeOfLine//2))) + string.display() +
(padding * int(sizeOfLine//2)) + fix
class TopBar:
y = shutil.get_terminal_size()[1]
startTime = time.time()
cardAccuracy = 0
cardsCompleted = 0
segment = PointerSlice(0, 0 ,0)
sectionSize = 0
cardType = 0
cardsCorrect = 0
cardAnswered = 0
repeats_left = 0
sizeOfLine = 0
result = ""
isResult = False
sectionCorrect = 0
accuracyLength = 0
newTime = 0
avg = 0
leniency = 0
elapsed_prev_speed = ["N/A", 0]
topBarString = ""
isVariant = False

@staticmethod
def calc_time(s):
navg = ((TopBar.avg * (TopBar.cardAnswered - 1)) + s)/(TopBar.cardAnswered
+ TopBar.leniency)

if s > max(60, TopBar.avg*2.5):


TopBar.leniency += 1
navg = (TopBar.avg * TopBar.cardAnswered)/(TopBar.cardAnswered +
TopBar.leniency)
return 0
#0% (1.9x slower than avg)
# 25% (1.6)x
# 50 1.3x
#75% avg
#75-100% faster (100% 1.5x than avg)
r = s/( 1 if TopBar.avg == 0 else TopBar.avg)

if r >= 1:
a = r - 1
x = (max(int(a*(-83.3) + 75), 0))
else:
x = (min(int(16.6*(1/r)) + 76,100))
TopBar.avg = navg
return x

@staticmethod
def update_with_segment(segment):
TopBar.segment = segment
TopBar.sectionSize = segment.endIndex - segment.index
TopBar.sectionCorrect = 0

@staticmethod
def update_with_card_info(card, subCard):
if card.card.answered == 0:
TopBar.cardAccuracy = format_colour("N/A", 0)
TopBar.accuracyLength = get_length(0)
else:
TopBar.cardAccuracy = int(card.card.answered_correct/card.card.answered
* 100)
TopBar.accuracyLength = get_length(TopBar.cardAccuracy)
TopBar.cardAccuracy = format_colour(str(TopBar.cardAccuracy) + "%",
TopBar.cardAccuracy)
TopBar.repeats_left = card.card.values[subCard]
TopBar.isVariant = bool(len(card.card.values)-1)
TopBar.cardType = subCard
TopBar.newTime = time.time()

@staticmethod
def render_handler():
if TopBar.isResult:
return TopBar.render_result()
TopBar.isResult = False
else:
return TopBar.render()

@staticmethod
def get_percentage(upper, bottom):
if bottom == 0:
return "N/A"
else:
return int(upper/bottom * 100)

@staticmethod
def format_percentage(percentage):
if percentage == "N/A":
return format_colour("N/A", 0)
return format_colour(str(percentage) + "%", percentage)

@staticmethod
def get_result():
if TopBar.result == "Correct":
return 100
elif TopBar.result == "Incorrect":
return 0
else:
return 50

@staticmethod
def format_result():
return format_colour(TopBar.result, TopBar.get_result())

@staticmethod
def get_time():
return f"\033[38;2;{32};{218};{218}m" + f"{TopBar.avg:.2f}" + f"\
033[38;2;255;255;255m"

@staticmethod
def format_time():
return f"{"{0:0=2d}".format(int((t := time.time() -
TopBar.startTime)//60))}:{"{0:0=2d}".format(int(t%60))}"

@staticmethod
def get_elapsed():
TopBar.elapsed_prev_speed = [f"{(t := time.time() - TopBar.newTime):.2f}",
TopBar.calc_time(t)]
@staticmethod
def render():
deck_completion =
TopBar.get_percentage(TopBar.cardsCompleted,len(lines)*threshold)
accuracy = TopBar.get_percentage(TopBar.cardsCorrect, TopBar.cardAnswered)
left = f"Deck Completion: {TopBar.format_percentage(deck_completion)} Card
Accuracy: {TopBar.cardAccuracy}"
#left = f"Deck Completion: {TopBar.format_completion()} Current Section:
{TopBar.sectionCorrect}/{TopBar.sectionSize*2} Accuracy:
{TopBar.format_accuracy()}"
#right = f"{TopBar.cardAccuracy}%: Card Accuracy {TopBar.repeats_left}:
Card Repeats Left {TopBar.cardType}: Card Variant"
#right = f"Card Accuracy: {TopBar.cardAccuracy} Card Repeats Left:
{TopBar.repeats_left} Card Variant : {TopBar.cardType}"
right = f"Accuracy: {TopBar.format_percentage(accuracy)} Avg Speed:
{TopBar.get_time()} Time: {format_colour(TopBar.elapsed_prev_speed[0],
TopBar.elapsed_prev_speed[1])}"
length = TopBar.sizeOfLine - len(left) - len(right) - 4 +
get_length(deck_completion) + get_length(accuracy) + TopBar.accuracyLength + 37 +
get_length(TopBar.elapsed_prev_speed[1])
return " " + left + " " * (length) + right + " "

@staticmethod
def render_result():
deck_completion =
TopBar.get_percentage(TopBar.cardsCompleted,len(lines)*threshold)
accuracy = TopBar.get_percentage(TopBar.cardsCorrect, TopBar.cardAnswered)
left = f"Deck Completion: {TopBar.format_percentage(deck_completion)} Card
Accuracy: {TopBar.cardAccuracy}"
#left = f"Deck Completion: {TopBar.format_completion()} Current Section:
{TopBar.sectionCorrect}/{TopBar.sectionSize*2} Accuracy:
{TopBar.format_accuracy()}"
#right = f"{TopBar.cardAccuracy}%: Card Accuracy {TopBar.repeats_left}:
Card Repeats Left {TopBar.cardType}: Card Variant"
#right = f"Card Accuracy: {TopBar.cardAccuracy} Card Repeats Left:
{TopBar.repeats_left} Card Variant : {TopBar.cardType}"
right = f"Accuracy: {TopBar.format_percentage(accuracy)} Avg Speed:
{TopBar.get_time()} Time: {format_colour(TopBar.elapsed_prev_speed[0],
TopBar.elapsed_prev_speed[1])}"
length = TopBar.sizeOfLine - len(left) - len(right) - 4 +
get_length(deck_completion) + get_length(accuracy) + TopBar.accuracyLength + 37 +
get_length(TopBar.elapsed_prev_speed[1]) + get_length(TopBar.get_result())
return " " + centrep(TopBar.format_result(), length, right, left) + " "

@staticmethod
def render_botombar():
repeats = f" {"Repeats Left" if not subura else "Score"}:
{TopBar.repeats_left}"
section = f"Section:
{TopBar.sectionCorrect}/{TopBar.sectionSize*threshold}"
variant = f"{("Card Variant: " + str(TopBar.cardType)) if not
TopBar.isVariant else (f"\033[38;2;{32};{218};{218}m" + "Card Variant: " +
str(TopBar.cardType) + f"\033[38;2;255;255;255m")}"
return " " + section + repeats + " " * (TopBar.sizeOfLine - len(section)
- len(variant) - len(repeats) - 4 + (37 * TopBar.isVariant)) + variant + " "

@staticmethod
def press_to_continue():
key = threading.Thread(name="getch", target=getch_handler)
key.start()
k = 1
#move_terminal_cursor(0, TopBar.y-1)
while key.is_alive():
move_terminal_cursor(0, TopBar.y-1)
print((st := " " + "Press a key to continue" + "." * k) + " " *
(TopBar.sizeOfLine-len(st)), end="\033[F")
time.sleep(0.1)
k = (k + 1 if k != 5 else 0)

# @staticmethod
# def render_result():
# #left = f"Deck Completion: {TopBar.format_completion()} Current Section:
{TopBar.sectionCorrect}/{TopBar.sectionSize*2} Accuracy:
{TopBar.format_accuracy()}"
# #right = f"Card Accuracy: {TopBar.cardAccuracy} Card Repeats Left:
{TopBar.repeats_left} Card Variant : {TopBar.cardType}"
# length = TopBar.sizeOfLine - len(left) - len(right) - 5 +
get_length(TopBar.get_accuracy()) + get_length(TopBar.get_completion()) +
get_length(TopBar.get_result()) + TopBar.accuracyLength
# return " " + left + centre(TopBar.format_result(), length) + right + "
"

def enter(cardDisplay, input_string, cardReference, subCard, input_field_size,


failed = False):
if cardReference.card.checkAnswer(input_string, subCard, failed):
TopBar.get_elapsed()
TopBar.isResult = True
TopBar.result = "Correct"
return [True]
elif failed:
return [False]
else:
TopBar.result = "Incorrect"
TopBar.isResult = True
cardDisplay[-1] = f"The answer was {cardReference.card.back[subCard]}"
cardDisplay.append(Input(" "))
cardDisplay.append(Lower())
return format(cardDisplay, cardReference, subCard, input_field_size, True)

TopBar.sizeOfLine = list(shutil.get_terminal_size())[0]

def print_lines(lines):
clear_screen()
lines[0]= TopBar.render_handler()
TopBar.isResult = False
for line in lines[:-1]:
print(line)
print(lines[-1], end="")

class Upper:
string = ""
@staticmethod
def create(n, d):
return centre("╭" + "─" * (n) + "╮", d, padding=".")

class Lower:
string = ""
@staticmethod
def create(n, d):
return centre("╰" + "─" * n + "╯", d, padding=".")

def format(cardDisplay, cardReference, subCard, input_field_size = 0, failed =


False):# an array of all the strings to print, "USER INPUT" will indicate to
replace the line a space to print
cursor_pos = [0, 0]
clear_screen()
screenDimensions = list(shutil.get_terminal_size())
lines = ["." *(screenDimensions[0]) for _ in range(screenDimensions[1])]
starting_line = (len(lines)-len(cardDisplay)-2)//2
maximum = max(max([len(_.string) if not isinstance(_, str) else len(_) for _ in
cardDisplay]), input_field_size)
const = 100
TopBar.update_with_card_info(cardReference, subCard)
lines[0] = TopBar.render_handler()
lines[-1] = TopBar.render_botombar()
TopBar.isResult = False
#lines[starting_line] = centre("╭" + "─" * (maximum+const) + "╮",
screenDimensions[0])
inputLine = -1
c = 40
global input_list
input_list = []
i = 0

for index, line in enumerate(cardDisplay):


if isinstance(line, Upper):
lines[starting_line + index] = Upper.create(maximum + const,
screenDimensions[0])
elif isinstance(line, Lower):
lines[starting_line + index] = Lower.create(maximum + const,
screenDimensions[0])
elif isinstance(line, Input):
line.assign_index(i)
i += 1
input_list.append(starting_line + index)
cursor_pos = [0, starting_line + index]
inputLine = index
lines[starting_line + index] = "Here"
else:
lines[starting_line + index] = centre("│" + centre(line, maximum+const)
+ "│", screenDimensions[0], padding=".")
#lines[starting_line + len(cardDisplay) + 1] = centre("╰" + "─" *
(maximum+const) + "╯", screenDimensions[0])

for i in input_list:
print("was")
lines[i] = centre("│" + centre(cardDisplay[i - starting_line].display(),
maximum+const, idk=c) + "│", screenDimensions[0], padding=".", idk=c)

for line in lines[:-1]:


print(line)
print(lines[-1], end = "")

def addDataToInput(input, data):


print(data)
if len(d := input.string + data) > maximum + const:
pass
else:
input.string = d

while True:
move_terminal_cursor(*cursor_pos)
data = getch_handler()
def render():
lines[starting_line + inputLine] = centre("│" +
centre(cardDisplay[inputLine].display(), maximum+const, idk=c) + "│",
screenDimensions[0], padding=".", idk=c)
print("\033[F" + lines[starting_line + inputLine], end="")

def renderDel():
lines[starting_line + inputLine] = centre("│" +
centre(cardDisplay[inputLine].display(), maximum+const, idk=c) + "│",
screenDimensions[0], padding=".", idk=c)
print(lines[starting_line + inputLine], end="")

if data.signal == 0:
addDataToInput(cardDisplay[inputLine], data.data)
render()
elif data.signal == 2:
cardDisplay[inputLine].string = cardDisplay[inputLine].string[:-1]
renderDel()
elif data.signal == 1:
addDataToInput(cardDisplay[inputLine], data.data())
render()
elif data.signal == 5:
ret = enter(cardDisplay, cardDisplay[inputLine].string, cardReference,
subCard, input_field_size, failed)
if not ret[0]:
continue
if failed:
return ret + [lines]
elif len(ret) == 2:
print_lines(ret[1])
else:
print_lines(lines)
TopBar.isResult = False
return
elif data.signal == 6:
clear_screen()
print("Do you want to exit?")
if input().lower() == "yes":
exit()

cardList = []
validCards = []
finishedCards = []

if reverse:
for index in range(len(lines)):
lines[index][0], lines[index][1] = lines[index][1], lines[index][0]
if len(lines) == 0:
raise Exception("No cards")
else:
cardList.append(Card(lines[0][0], lines[0][1]))
validCards.append(CardPointer(cardList[-1]))

#print(cardList)

for index in range(1, len(lines)):


if cardList[-1].front == lines[index][0]:
cardList[-1].addAnswer(lines[index][1])
else:
cardList.append(Card(lines[index][0], lines[index][1]))
validCards.append(CardPointer(cardList[-1]))

#for card in cardList:


# print(card)

for card in validCards:


rhs = random.randint(0, len(validCards)-1)
lhs = random.randint(0, len(validCards)-1)
validCards[rhs], validCards[lhs] = validCards[lhs], validCards[rhs]

if learn:
try:
while len(validCards) != 0:
validCardSegment = PointerSlice(validCards, 0, min(10,
len(validCards)))
TopBar.update_with_segment(validCardSegment)

while validCardSegment.len() != 0:
TopBar.press_to_continue()
clear_screen()
time.sleep(0.05)
currentCard = validCardSegment.getRandom()
#print(currentCard)
subCard = currentCard.card.availableValues[random.randint(0,
len(currentCard.card.availableValues)-1)] if len(currentCard.card.availableValues)
> 1 else currentCard.card.availableValues[0]
format([Upper(), currentCard.card.front, Lower(), Upper(), Input("
"), Lower()],currentCard, subCard, len(currentCard.card.back[subCard]))
validCardSegment.isCardValid(finishedCards)
except Exception as e:
#clear_screen()
print(f"Unexpected runtime error")
traceback.print_exc()
print("Saving data")
elif subura:
if os.path.isfile(file + ".score"):
f = open(file + ".score", "r+").readlines()
card_index = 0
search = []
#print([i.front for i in cardList])
for line in f:
if cardList[card_index].registerScore(int(line.strip())):
card_index += 1
search.append(int(line.strip()))
else:
open(file + ".score", "w").close()
search = [0] * len(lines)
card_index = 0
for i in range(len(lines)):
if cardList[card_index].registerScore(0):
card_index += 1
validCardSegment = PointerSlice(validCards, 0, len(validCards))
validCardSegment.search = search
TopBar.update_with_segment(validCardSegment)
try:
while TopBar.cardsCorrect != (len(lines) * threshold):
TopBar.press_to_continue()
clear_screen()
#time.sleep(0.1)
currentCard = validCardSegment.getMinimum()
#print(currentCard)
subCard = currentCard.card.availableValues[random.randint(0,
len(currentCard.card.availableValues)-1)] if len(currentCard.card.availableValues)
> 1 else currentCard.card.availableValues[0]
format([Upper(), currentCard.card.front, Lower(), Upper(), Input(""),
Lower()],currentCard, subCard, len(currentCard.card.back[subCard]))
#validCardSegment.isCardValid(finishedCards)

except Exception as e:
traceback.print_exc()
pass
search = [str(i)+"\n" for i in validCardSegment.search]
file = open(file + ".score", "w")
file.writelines(search)
file.close()
print("exited")

You might also like