contentmatcher/sr_session.py

243 lines
7.7 KiB
Python

from random import choice, choices
from datetime import date, timedelta
from flask import flash, session, redirect, url_for, request, render_template
from sqlalchemy import desc
from share import share
from create_db import Card, Rating, get_session
from settings import get_settings
"""
testing
"""
def autofill_ratings():
dbsession = get_session()
for i in range(100):
card = Rating(user_id="1", card_id=choice([10, 13, 14, 75]), rating_value=choice(["Maybe", "No"]), rating_time=date.today()-timedelta(choice([i for i in range(100)])))
dbsession.add(card)
dbsession.commit()
dbsession.close()
return None
"""
Config
"""
maybe_factor = 1.2
no_factor = 2.1
"""
Main fuctions
"""
def calculate_interval(sorted_rates, maybe_factor, no_factor):
if sorted_rates == []:
return 1
elif sorted_rates[0].rating_value == "Yes":
return 1
elif sorted_rates[0].rating_value == "Maybe":
sorted_rates.pop(0)
return calculate_interval(sorted_rates, maybe_factor, no_factor) * maybe_factor
elif sorted_rates[0].rating_value == "No":
sorted_rates.pop(0)
return calculate_interval(sorted_rates, maybe_factor, no_factor) * no_factor
elif sorted_rates[0].rating_value == "Delete":
return -1 #"Deleted"
def get_interval(card_id):
"""
takes card_id looks at the history of rates and gives the current interval
"""
dbsession = get_session()
sorted_rates_by_card = dbsession.query(Rating).filter(Rating.card_id == card_id).order_by(desc(Rating.rating_time)).all()
dbsession.close()
if sorted_rates_by_card == None:
print("ni kart... kaj zdaj") #@TODO
return None
else:
interval = calculate_interval(sorted_rates_by_card, maybe_factor, no_factor)
return round(interval)
#to vključuje tudi new...
def is_due(card_id):
dbsession = get_session()
interval = get_interval(card_id)
if interval < 0:
return False
last_rating_date = dbsession.query(Rating).filter(Rating.card_id == card_id).order_by(desc(Rating.rating_time)).first()
if last_rating_date == None:
dbsession.close()
return True
due_date = last_rating_date.rating_time + timedelta(interval)
dbsession.close()
return date.today() >= due_date
def list_of_due_cards_by_ids(user_id):
dbsession = get_session()
cards = dbsession.query(Card).filter(Card.owner_id == user_id).all()
l = []
for card in cards:
if is_due(card.id):
l.append(card.id)
dbsession.close()
return l
def list_of_new_cards_by_ids(user_id):
dbsession = get_session()
cards = dbsession.query(Card).filter(Card.owner_id == user_id).all()
l = []
for card in cards:
rating = dbsession.query(Rating).filter(Rating.card_id == card.id).first()
if rating == None:
l.append(card.id)
dbsession.close()
return l
#TODO sus ker ni user id?
def rated_today_by_staus (card_status, user_id):
"""
Returns number of cards rated today by user by status (new or due)
"""
dbsession = get_session()
today = date.today()
ratings_today = dbsession.query(Rating).filter(
Rating.rating_time == today,
Rating.user_id == user_id
)
n = 0
d = 0
for rating in ratings_today:
count_all_rates_of_card = dbsession.query(Card).filter(Card.id == rating.card_id).count()
if count_all_rates_of_card == 1:
n+=1
elif count_all_rates_of_card > 1:
d+=1
else:
print("wtf")
raise Exception("Count be wrong! There should be at least one rating")
dbsession.close()
if card_status == "new":
return n
elif card_status == "due":
return d
else:
raise Exception("must input either 'new' or 'due' as parameter")
def get_a_card_by_status(user_id, card_status):
if card_status == "new":
l = list_of_new_cards_by_ids(user_id)
card_id = l[0]
if card_status == "due":
l = list_of_due_cards_by_ids(user_id)
card_id = l[0]
return card_id
"""
Export functions
"""
def schedule_status(user_id):
"""
Returns information needed for start/continue/done menu ui
"""
user_settings = get_settings(user_id)
max_new = int(user_settings["max_new"])
max_due = int(user_settings["max_due"])
rated_new = rated_today_by_staus("new", user_id)
rated_due = rated_today_by_staus("due", user_id)
scheduled_new = len(list_of_new_cards_by_ids(user_id))
scheduled_due = len(list_of_due_cards_by_ids(user_id))-scheduled_new #TODO ta problem je mogoče še kje...
max_today_new = min(max_new, scheduled_new+rated_new)
max_today_due = min(max_due, scheduled_due+rated_due)
print("max_new_today", max_today_new)
print("max_due_today", max_today_due)
print("rated_due", rated_due)
print("rated_new", rated_new)
if max_today_due+max_today_new == rated_due+rated_new:
return "done"
elif rated_due+rated_new == 0:
return "start"
elif max_today_due+max_today_new > rated_due+rated_new:
return "continue"
else:
raise Exception("Arithmetic problem with collection // rated too much?")
def remaining_by_status(user_id, status):
if status == "new":
l = len(list_of_new_cards_by_ids(user_id))
elif status == "due":
l = len(list_of_due_cards_by_ids(user_id))
return l
"""
Engine
"""
def sr_session():
dbsession = get_session()
#check if user in session
if not 'user_id' in session:
redirect(url_for('login'))
user_id = session['user_id']
username = session['username']
#form sent?
if request.method == 'POST':
#get card_id from the card, rendered from the template, data actually from the template... why tho
card_id = request.form.get('card_id', None)
if not card_id:
raise Exception("card_id necesarry!")
#@TODO check if this card is part of the user's deck to prevent possibile hack?
submit_card = dbsession.query(Card).get(card_id)
# Share
#@@TODO, this should not change the card
share_request = request.form.get("share", None)
if share_request:
share(submit_card, user_id)
#Rate
else:
rate = request.form.get('rate', None)
if not rate:
raise Exception("Need rate info!")
r = Rating(user_id=user_id, card_id=submit_card.id, rating_value=rate, rating_time=date.today())
dbsession.add(r)
dbsession.commit()
dbsession.close()
user_settings = get_settings(user_id)
max_new = user_settings["max_new"]
max_due = user_settings["max_due"]
all_new = len(list_of_new_cards_by_ids(user_id))
all_due = len(list_of_due_cards_by_ids(user_id))
rated_today_new = rated_today_by_staus("new", user_id)
rated_today_due = rated_today_by_staus("due", user_id)
#checks if there are any new/due cards left for today and gets the next one. max can be more than all scheduled, min decides which is the limt.
if min(int(max_new), all_new) - int(rated_today_new) > 0:
new_card_id=get_a_card_by_status(user_id, "new")
elif min(int(max_due), all_due) - int(rated_today_due) > 0:
new_card_id=get_a_card_by_status(user_id, "due")
else:
dbsession.close()
flash("no more cards today")
return redirect("/menu")
show_card = dbsession.query(Card).get(new_card_id)
interval = get_interval(new_card_id)
#these factors should be better packaged
return render_template("deck.html", username=username, card=show_card, maybe_in=round(interval*maybe_factor), no_in=round(no_factor*interval))