Last active
April 6, 2021 13:55
-
-
Save abhiyerra/7a7c31d7e695010e0bd0669c7cf18885 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from airtable import Airtable | |
from flask import Flask, redirect, request, url_for | |
import json | |
import markdown | |
from datetime import datetime, timedelta, timezone | |
# https://gist.github.com/doctorpangloss/13ab29abd087dc1927475e560f876797 | |
def sm2(x: [int], a=6.0, b=-0.8, c=0.28, d=0.02, theta=0.2) -> float: | |
""" | |
Returns the number of days to delay the next review of an item by, fractionally, based on the history of answers x to | |
a given question, where | |
x == 0: Incorrect, Hardest | |
x == 1: Incorrect, Hard | |
x == 2: Incorrect, Medium | |
x == 3: Correct, Medium | |
x == 4: Correct, Easy | |
x == 5: Correct, Easiest | |
@param x The history of answers in the above scoring. | |
@param theta When larger, the delays for correct answers will increase. | |
""" | |
assert all(0 <= x_i <= 5 for x_i in x) | |
correct_x = [x_i >= 3 for x_i in x] | |
# If you got the last question incorrect, just return 1 | |
if not correct_x[-1]: | |
return 1.0 | |
# Calculate the latest consecutive answer streak | |
num_consecutively_correct = 0 | |
for correct in reversed(correct_x): | |
if correct: | |
num_consecutively_correct += 1 | |
else: | |
break | |
return a*(max(1.3, 2.5 + sum(b+c*x_i+d*x_i*x_i for x_i in x)))**(theta*num_consecutively_correct) | |
app = Flask(__name__) | |
@app.route("/") | |
def show(): | |
recalls = Airtable('', 'Recalls', api_key='').get_all(view="Review", maxRecords=1) | |
recall = None | |
if len(recalls) > 0: | |
recall = recalls[0] | |
else: | |
return "Nothing to Review" | |
script = ''' | |
$(document).ready(function() { | |
$("#show").click(function(){ | |
$("#back").removeClass("d-none"); | |
$("#show").addClass("d-none"); | |
}); | |
}); | |
''' | |
return f''' | |
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
<title>TotalRecall</title> | |
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" | |
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> | |
</link> | |
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script> | |
<script> | |
{script} | |
</script> | |
</head> | |
<body> | |
<div class="container mt-5"> | |
<div class="row"> | |
<div class="col text-center" id="front"> | |
{recall['fields']['Front']} | |
</div> | |
</div> | |
<div class="row mt-5"> | |
<div class="col text-center"> | |
<button id="show" class="btn btn-primary text-white text-center">Show</button> | |
</div> | |
</div> | |
<div class="row mt-5"> | |
<div class="col text-center d-none" id="back"> | |
{markdown.markdown(recall['fields']['Back'])} | |
<br/> | |
</div> | |
</div> | |
<form action="update/{recall['id']}" method="POST"> | |
<div class="row mt-5"> | |
<div class="col"> | |
<input type="submit" class="btn btn-primary text-white text-center" name="recall_score" value="1"> | |
Incorrect, Hardest | |
</div> | |
<div class="col"> | |
<input type="submit" class="btn btn-primary text-white text-center" name="recall_score" value="2"> | |
Incorrect, Hard | |
</div> | |
<div class="col"> | |
<input type="submit" class="btn btn-primary text-white text-center" name="recall_score" value="3"> | |
Incorrect, Medium | |
</div> | |
</div> | |
<div class="row mt-5"> | |
<div class="col"> | |
<input type="submit" class="btn btn-primary text-white text-center" name="recall_score" value="4"> | |
Correct, Medium | |
</div> | |
<div class="col"> | |
<input type="submit" class="btn btn-primary text-white text-center" name="recall_score" value="5"> | |
Correct, Easy | |
</div> | |
<div class="col"> | |
<input type="submit" class="btn btn-primary text-white text-center" name="recall_score" value="6"> | |
Correct, Easiest | |
</div> | |
</div> | |
</form> | |
</div> | |
</body> | |
</html> | |
''' | |
@app.route("/update/<airtableId>", methods=['POST']) | |
def update(airtableId=None): | |
recalls = Airtable('', 'Recalls', api_key='') | |
recall = recalls.get(airtableId) | |
scores = [] | |
if 'Scores' in recall['fields']: | |
scores = [int(i) for i in recall['fields']['Scores'].split(",")] | |
current_score = request.form['recall_score'] | |
scores += [current_score] | |
next_review_at = datetime.now(timezone.utc) + timedelta(days=sm2([int(i) for i in scores])) | |
recalls.update(airtableId, { | |
'NextReviewAt': next_review_at.isoformat(), | |
'Scores': ','.join([str(i) for i in scores]) | |
}) | |
return redirect(url_for('show')) | |
def sync_handler(event, context): | |
recalls = Airtable('', 'Recalls', api_key='') | |
records = recalls.get_all(view="Grid") | |
for record in records: | |
if 'NextReviewAt' not in record['fields']: | |
recalls.update(record['id'], { | |
'NextReviewAt': datetime.now(timezone.utc).isoformat() | |
}) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment