expand local DB to store more data
This commit is contained in:
@@ -27,6 +27,7 @@ wordlinator = "wordlinator.app:sync_main"
|
|||||||
update = "wordlinator.app:sync_update"
|
update = "wordlinator.app:sync_update"
|
||||||
show-user = "wordlinator.app:sync_show_user"
|
show-user = "wordlinator.app:sync_show_user"
|
||||||
show-missing = "wordlinator.app:sync_show_missing"
|
show-missing = "wordlinator.app:sync_show_missing"
|
||||||
|
db-load = "wordlinator.app:load_db_scores"
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import asyncio
|
|||||||
import rich
|
import rich
|
||||||
import rich.table
|
import rich.table
|
||||||
|
|
||||||
|
import wordlinator.db
|
||||||
import wordlinator.sheets
|
import wordlinator.sheets
|
||||||
import wordlinator.twitter
|
import wordlinator.twitter
|
||||||
import wordlinator.utils
|
import wordlinator.utils
|
||||||
@@ -56,6 +57,19 @@ def print_score_table(wordle_day, scores):
|
|||||||
rich.print(table)
|
rich.print(table)
|
||||||
|
|
||||||
|
|
||||||
|
def _save_db_scores(wordle_day: wordlinator.utils.WordleDay, scores: dict):
|
||||||
|
db = wordlinator.db.WordleDb()
|
||||||
|
hole_data = wordle_day.golf_hole
|
||||||
|
if not hole_data:
|
||||||
|
return
|
||||||
|
game_no = hole_data.game_no
|
||||||
|
for user, score_list in scores.items():
|
||||||
|
if not db.get_user(user):
|
||||||
|
continue
|
||||||
|
for day, score_entry in enumerate(score_list, start=1):
|
||||||
|
db.add_score(user, game_no, day, score_entry)
|
||||||
|
|
||||||
|
|
||||||
async def main_update(
|
async def main_update(
|
||||||
wordle_day: wordlinator.utils.WordleDay = wordlinator.utils.WORDLE_TODAY,
|
wordle_day: wordlinator.utils.WordleDay = wordlinator.utils.WORDLE_TODAY,
|
||||||
):
|
):
|
||||||
@@ -65,7 +79,9 @@ async def main_update(
|
|||||||
if not any((s is not None for s in today_scores.values())):
|
if not any((s is not None for s in today_scores.values())):
|
||||||
raise ValueError("No scores pulled!")
|
raise ValueError("No scores pulled!")
|
||||||
|
|
||||||
sheets_client.update_scores(today_scores)
|
updated_scores = sheets_client.update_scores(today_scores)
|
||||||
|
|
||||||
|
_save_db_scores(wordle_day, updated_scores)
|
||||||
|
|
||||||
print_score_table(wordle_day, today_scores)
|
print_score_table(wordle_day, today_scores)
|
||||||
|
|
||||||
@@ -108,6 +124,13 @@ def _get_day():
|
|||||||
return wordle_day
|
return wordle_day
|
||||||
|
|
||||||
|
|
||||||
|
def load_db_scores():
|
||||||
|
wordle_day = _get_day()
|
||||||
|
client = wordlinator.sheets.SheetsClient(wordle_day=wordle_day)
|
||||||
|
scores = client.get_scores()
|
||||||
|
_save_db_scores(wordle_day, scores)
|
||||||
|
|
||||||
|
|
||||||
def sync_main():
|
def sync_main():
|
||||||
wordle_day = _get_day()
|
wordle_day = _get_day()
|
||||||
asyncio.run(main(wordle_day=wordle_day))
|
asyncio.run(main(wordle_day=wordle_day))
|
||||||
|
|||||||
126
wordlinator/db.py
Normal file
126
wordlinator/db.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
|
class WordleDb:
|
||||||
|
def __init__(self):
|
||||||
|
self.con = sqlite3.connect("wordle.db")
|
||||||
|
cur = self.con.cursor()
|
||||||
|
cur.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS user
|
||||||
|
(id INTEGER PRIMARY KEY,
|
||||||
|
username varchar(50) NOT NULL,
|
||||||
|
user_id varchar(20) NOT NULL)"""
|
||||||
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS game (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
game INTEGER NOT NULL)"""
|
||||||
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS hole
|
||||||
|
(id INTEGER PRIMARY KEY,
|
||||||
|
hole INTEGER NOT NULL,
|
||||||
|
game_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (game_id)
|
||||||
|
REFERENCES game (id)
|
||||||
|
)"""
|
||||||
|
)
|
||||||
|
cur.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS score
|
||||||
|
(id INTEGER PRIMARY KEY,
|
||||||
|
score INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
game_id INTEGER NOT NULL,
|
||||||
|
hole_id INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY (game_id)
|
||||||
|
REFERENCES game (id),
|
||||||
|
FOREIGN KEY (user_id)
|
||||||
|
REFERENCES user (id),
|
||||||
|
FOREIGN KEY (hole_id)
|
||||||
|
REFERENCES hole (id),
|
||||||
|
UNIQUE(user_id, game_id, hole_id)
|
||||||
|
)"""
|
||||||
|
)
|
||||||
|
self.con.commit()
|
||||||
|
|
||||||
|
def get_user(self, username):
|
||||||
|
cur = self.con.cursor()
|
||||||
|
res = list(cur.execute(f"SELECT * FROM user WHERE username = '{username}'"))
|
||||||
|
return res[0] if res else None
|
||||||
|
|
||||||
|
def get_user_id(self, username):
|
||||||
|
user = self.get_user(username)
|
||||||
|
if not user:
|
||||||
|
return None
|
||||||
|
return user[2]
|
||||||
|
|
||||||
|
def add_user(self, username, user_id):
|
||||||
|
cur = self.con.cursor()
|
||||||
|
cur.execute(
|
||||||
|
f"INSERT INTO user (username, user_id) VALUES ('{username}', '{user_id}')"
|
||||||
|
)
|
||||||
|
self.con.commit()
|
||||||
|
|
||||||
|
def get_or_create_round(self, round_no):
|
||||||
|
cur = self.con.cursor()
|
||||||
|
res = list(cur.execute(f"SELECT * FROM game WHERE game = {round_no}"))
|
||||||
|
if not res:
|
||||||
|
list(cur.execute(f"INSERT INTO game (game) VALUES ({round_no})"))
|
||||||
|
self.con.commit()
|
||||||
|
res = list(cur.execute(f"SELECT * FROM game WHERE game = {round_no}"))
|
||||||
|
return res[0]
|
||||||
|
|
||||||
|
def get_or_create_hole(self, round_no, hole_no):
|
||||||
|
round_id = self.get_or_create_round(round_no)[0]
|
||||||
|
cur = self.con.cursor()
|
||||||
|
res = list(
|
||||||
|
cur.execute(
|
||||||
|
f"SELECT * FROM hole WHERE hole = {hole_no} AND game_id = {round_id}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not res:
|
||||||
|
cur.execute(
|
||||||
|
f"INSERT INTO hole (hole, game_id) VALUES ({hole_no}, {round_id})"
|
||||||
|
)
|
||||||
|
res = list(
|
||||||
|
cur.execute(
|
||||||
|
"SELECT * FROM hole "
|
||||||
|
f"WHERE hole = {hole_no} AND game_id = {round_id}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.con.commit()
|
||||||
|
return res[0]
|
||||||
|
|
||||||
|
def create_round_holes(self, round_no):
|
||||||
|
for hole_no in range(1, 19):
|
||||||
|
self.get_or_create_hole(round_no, hole_no)
|
||||||
|
|
||||||
|
def add_score(self, username, game, hole, score):
|
||||||
|
if not score:
|
||||||
|
return
|
||||||
|
user = self.get_user(username)
|
||||||
|
if not user:
|
||||||
|
raise ValueError("No such user!")
|
||||||
|
user_id = user[0]
|
||||||
|
|
||||||
|
hole = self.get_or_create_hole(game, hole)
|
||||||
|
_, hole_id, game_id = hole
|
||||||
|
|
||||||
|
cur = self.con.cursor()
|
||||||
|
res = list(
|
||||||
|
cur.execute(
|
||||||
|
f"""SELECT score FROM score
|
||||||
|
WHERE user_id = {user_id} AND game_id = {game_id} AND hole_id = {hole_id}"""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if res:
|
||||||
|
cmd = f"""UPDATE score
|
||||||
|
SET score = {score}
|
||||||
|
WHERE user_id = {user_id}
|
||||||
|
AND game_id = {game_id}
|
||||||
|
AND hole_id = {hole_id}"""
|
||||||
|
else:
|
||||||
|
cmd = f"""INSERT INTO score (score, user_id, game_id, hole_id)
|
||||||
|
VALUES ({score}, {user_id}, {game_id}, {hole_id})"""
|
||||||
|
cur.execute(cmd)
|
||||||
|
self.con.commit()
|
||||||
@@ -145,6 +145,7 @@ class SheetsClient:
|
|||||||
current_row[day_idx] = score_val
|
current_row[day_idx] = score_val
|
||||||
current_scores[name] = current_row
|
current_scores[name] = current_row
|
||||||
self.write_scores(current_scores)
|
self.write_scores(current_scores)
|
||||||
|
return current_scores
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import datetime
|
|||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
import authlib.integrations.httpx_client
|
import authlib.integrations.httpx_client
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
import httpx
|
import httpx
|
||||||
import rich
|
import rich
|
||||||
|
|
||||||
|
import wordlinator.db
|
||||||
import wordlinator.utils
|
import wordlinator.utils
|
||||||
|
|
||||||
BASE_URL = "https://api.twitter.com/2"
|
BASE_URL = "https://api.twitter.com/2"
|
||||||
@@ -90,26 +90,6 @@ class WordleTweet:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserDb:
|
|
||||||
def __init__(self):
|
|
||||||
self.con = sqlite3.connect("users.db")
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute(
|
|
||||||
"""CREATE TABLE IF NOT EXISTS users (username text, user_id text)"""
|
|
||||||
)
|
|
||||||
self.con.commit()
|
|
||||||
|
|
||||||
def get_user(self, username):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
res = list(cur.execute(f"SELECT * from users WHERE username = '{username}'"))
|
|
||||||
return res[0] if res else None
|
|
||||||
|
|
||||||
def add_user(self, username, user_id):
|
|
||||||
cur = self.con.cursor()
|
|
||||||
cur.execute(f"INSERT INTO users VALUES ('{username}', '{user_id}')")
|
|
||||||
self.con.commit()
|
|
||||||
|
|
||||||
|
|
||||||
class TwitterClient(httpx.AsyncClient):
|
class TwitterClient(httpx.AsyncClient):
|
||||||
SEARCH_PATH = "tweets/search/recent"
|
SEARCH_PATH = "tweets/search/recent"
|
||||||
USER_PATH = "users/by/username/{username}"
|
USER_PATH = "users/by/username/{username}"
|
||||||
@@ -125,7 +105,7 @@ class TwitterClient(httpx.AsyncClient):
|
|||||||
auth = authlib.integrations.httpx_client.OAuth1Auth(**oauth_creds)
|
auth = authlib.integrations.httpx_client.OAuth1Auth(**oauth_creds)
|
||||||
kwargs["auth"] = auth
|
kwargs["auth"] = auth
|
||||||
super().__init__(base_url=BASE_URL, **kwargs)
|
super().__init__(base_url=BASE_URL, **kwargs)
|
||||||
self.db = UserDb()
|
self.db = wordlinator.db.WordleDb()
|
||||||
self.wordle_day = wordle_day
|
self.wordle_day = wordle_day
|
||||||
if not oauth_creds:
|
if not oauth_creds:
|
||||||
self.headers["Authorization"] = f"Bearer {TOKEN}"
|
self.headers["Authorization"] = f"Bearer {TOKEN}"
|
||||||
@@ -145,9 +125,9 @@ class TwitterClient(httpx.AsyncClient):
|
|||||||
return await self.get(self.USER_PATH.format(username=username))
|
return await self.get(self.USER_PATH.format(username=username))
|
||||||
|
|
||||||
async def get_user_id(self, username: str):
|
async def get_user_id(self, username: str):
|
||||||
db_user = self.db.get_user(username)
|
db_user_id = self.db.get_user_id(username)
|
||||||
if db_user:
|
if db_user_id:
|
||||||
return db_user[1]
|
return db_user_id
|
||||||
else:
|
else:
|
||||||
twitter_user = await self.get_user_by(username)
|
twitter_user = await self.get_user_by(username)
|
||||||
user_id = None
|
user_id = None
|
||||||
|
|||||||
Reference in New Issue
Block a user