Skip to content

Commit

Permalink
urbanstats persistent changes for storing infinite (#897)
Browse files Browse the repository at this point in the history
  • Loading branch information
kavigupta authored Feb 1, 2025
1 parent 5f06f36 commit 9b30fe2
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import time
from typing import List, Tuple

from .utils import corrects_to_bytes

table_for_quiz_kind = {
"juxtastat": "JuxtaStatIndividualStats",
"retrostat": "JuxtaStatIndividualStatsRetrostat",
Expand All @@ -25,6 +27,12 @@ def table():
"""CREATE TABLE IF NOT EXISTS JuxtaStatIndividualStatsRetrostat
(user integer, week integer, corrects integer, time integer, PRIMARY KEY (user, week))"""
)
# juxtastat infinite
c.execute(
"""CREATE TABLE IF NOT EXISTS JuxtaStatInfiniteStats
(user integer, seed string, version integer, corrects varbinary(128), score integer, num_answers integer, time integer, PRIMARY KEY (user, seed, version))"""

This comment has been minimized.

Copy link
@lukebrody

lukebrody Feb 1, 2025

Collaborator

FYI, varbinary is not a real SQLite type. You should probably use blob here.

This comment has been minimized.

Copy link
@lukebrody
)

# user to domain name
c.execute(
"""
Expand Down Expand Up @@ -158,6 +166,38 @@ def store_user_stats_retrostat(user, week_stats: List[Tuple[int, List[bool]]]):
store_user_stats_into_table(user, week_stats, "JuxtaStatIndividualStatsRetrostat")


def has_infinite_stats(user, seeds_versions):
user = int(user, 16)
_, c = table()
c.execute(
"SELECT seed, version FROM JuxtaStatInfiniteStats WHERE user=?",
(user,),
)
results = c.fetchall()
results = set(results)
return [(seed, version) in results for seed, version in seeds_versions]


def store_user_stats_infinite(user, seed, version, corrects: List[bool]):
user = int(user, 16)
conn, c = table()
correctBytes = corrects_to_bytes(corrects)
time_unix_millis = round(time.time() * 1000)
c.execute(
"INSERT OR REPLACE INTO JuxtaStatInfiniteStats VALUES (?, ?, ?, ?, ?, ?, ?)",
(
user,
seed,
version,
correctBytes,
sum(corrects),
len(corrects),
time_unix_millis,
),
)
conn.commit()


def get_per_question_stats_from_table(day, table_name, column):
day = int(day)
_, c = table()
Expand Down Expand Up @@ -245,6 +285,14 @@ def todays_score_for(requestee, requesters, date, quiz_kind):
)


def infinite_results(requestee, requesters, seed, version):
"""
For each `requseter` returns the pattern of correct answers if `(requester, requestee)` is a friend pair.
"""

return _compute_friend_results(requestee, requesters, compute_fn=lambda c, for_user: _infinite_results(c, for_user, seed, version))


def _compute_friend_results(requestee, requesters, compute_fn):
requestee = int(requestee, 16)

Expand Down Expand Up @@ -287,3 +335,32 @@ def _compute_daily_score(date, quiz_kind, c, for_user):
return dict(corrects=None)
else:
return dict(corrects=bitvector_to_corrects(res[0]))

def _infinite_results(c, for_user, seed, version):
"""
Returns the result for the given user for the given seed and version, as well
as the maximum score and corresponding seed and version.
"""

c.execute(
"SELECT score FROM JuxtaStatInfiniteStats WHERE user=? AND seed=? AND version=?",
(for_user, seed, version),
)
res = c.fetchone()
for_this_seed = None if res is None else res[0]

c.execute(
"SELECT seed, version, score FROM JuxtaStatInfiniteStats WHERE user=? AND score=(SELECT MAX(score) FROM JuxtaStatInfiniteStats WHERE user=?)",
(for_user, for_user),
)
res = c.fetchone()
max_score_seed = None if res is None else res[0]
max_score_version = None if res is None else res[1]
max_score = None if res is None else res[2]

return dict(
forThisSeed=for_this_seed,
maxScore=max_score,
maxScoreSeed=max_score_seed,
maxScoreVersion=max_score_version
)
42 changes: 42 additions & 0 deletions urbanstats-persistent-data/urbanstats_persistent_data/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
friend_request,
get_per_question_stats,
get_per_question_stats_retrostat,
has_infinite_stats,
infinite_results,
latest_day,
latest_week_retrostat,
register_user,
store_user_stats,
get_full_database,
store_user_stats_infinite,
store_user_stats_retrostat,
todays_score_for,
unfriend,
Expand Down Expand Up @@ -80,6 +83,7 @@ def get_authenticated_user(additional_required_params=()):
required_params = ["user", "secureID"] + list(additional_required_params)

if not all([param in form for param in required_params]):
print("NEEDS PARAMS", required_params, "GOT", form.keys())
return False, (
flask.jsonify(
{
Expand All @@ -101,10 +105,13 @@ def authenticate(fields):
def decorator(fn):
@functools.wraps(fn)
def wrapper():
print("AUTHENTICATE", flask_form())
success, error = get_authenticated_user(fields)
if not success:
print("AUTHENTICATE ERROR", error)
return error
return fn()

return wrapper

return decorator
Expand Down Expand Up @@ -145,6 +152,28 @@ def juxtastat_store_user_stats_request():
return flask.jsonify(dict())


@app.route("/juxtastat_infinite/has_infinite_stats", methods=["POST"])
@authenticate(["seedVersions"])
def juxtastat_infinite_has_infinite_stats_request():
form = flask_form()
print("HAS INFINITE STATS", form)
res = dict(has=has_infinite_stats(form["user"], form["seedVersions"]))
print("HAS INFINITE STATS", res)
return flask.jsonify(
res
)


@app.route("/juxtastat_infinite/store_user_stats", methods=["POST"])
@authenticate(["seed", "version", "corrects"])
def juxtastat_infinite_store_user_stats_request():
form = flask_form()
store_user_stats_infinite(
form["user"], form["seed"], form["version"], form["corrects"]
)
return flask.jsonify(dict())


@app.route("/retrostat/store_user_stats", methods=["POST"])
@authenticate(["day_stats"])
def retrostat_store_user_stats_request():
Expand Down Expand Up @@ -216,6 +245,19 @@ def juxtastat_todays_score_for():
return flask.jsonify(res)


@app.route("/juxtastat/infinite_results", methods=["POST"])
@authenticate(["requesters", "seed", "version"])
def juxtastat_infinite_results():
form = flask_form()
res = dict(
results=infinite_results(
form["user"], form["requesters"], form["seed"], form["version"]
)
)
print("INFINITE RESULTS FOR", res)
return flask.jsonify(res)


import logging

logging.getLogger("flask_cors").level = logging.DEBUG
Expand Down
12 changes: 12 additions & 0 deletions urbanstats-persistent-data/urbanstats_persistent_data/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from typing import List


def corrects_to_bytes(corrects: List[bool]) -> bytes:
result = []
for i in range(0, len(corrects), 8):
byte = 0
for j in range(8):
if i + j < len(corrects) and corrects[i + j]:
byte |= 1 << j
result.append(byte)
return bytes(result)

0 comments on commit 9b30fe2

Please sign in to comment.