Source code for src.rossmassey.fetch_leetcode_problem._problem_index
"""
Database for storing leetcode problem num, title, slug, and question id
Implements context manager protocol with sqlite3
"""
import os
import sqlite3
from ._util import absolute_path
# sqlite3 database file store location
DB_PATH = 'problems.db'
# path to SQL script that creates `problems` table
SCHEMA_PATH = 'schema.sql'
[docs]
class ProblemIndex:
"""
Manages a database connection to store and retrieve LeetCode problems info
GraphQL queries require a title-slug and question-id separate from the
frontend id/number. Leetcode has a public listing of all problems, but
fetching + scanning that is slow. Instead, cache the results so that
operation is not repeated.
"""
[docs]
def __init__(self):
"""
Initialize database connection and creates database/table if it
doesn't exist.
"""
self.db_path = absolute_path(DB_PATH)
self.schema_path = absolute_path(SCHEMA_PATH)
self.conn = None
if not os.path.exists(self.db_path):
self._init_db()
def __enter__(self):
"""
Opens database connection.
"""
self.conn = sqlite3.connect(self.db_path)
self.conn.row_factory = sqlite3.Row
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Closes database connection.
"""
if self.conn:
self.conn.close()
[docs]
def _init_db(self):
"""
Creates database and table.
"""
try:
with open(self.schema_path, 'r') as file:
schema = file.read()
except FileNotFoundError:
print(f'Could not find schema file: {self.schema_path}')
return
with sqlite3.connect(self.db_path) as conn:
conn.executescript(schema)
[docs]
def _query(self, stmt: str, params: tuple) -> sqlite3.Row:
"""
Executes a query.
Args:
stmt (str): SQL statement to execute
params (tuple): associated parameters
want_value (bool): if True, return query result
Returns:
sqlite3.Row: query result (if want_value)
"""
with self.conn as transaction:
cursor = transaction.cursor()
cursor.execute(stmt, params)
return cursor.fetchone()
[docs]
def update_problems(self, problems: list):
"""
Updates the problems table with the given problems.
Args:
problems: list of problems to insert
"""
for problem in problems:
self.insert_problem(*problem)
[docs]
def insert_problem(self, num: str, title: str, slug: str, question_id: str):
"""
Inserts a problem into the problems table.
Args:
num (str): problem number
title (str): problem title
slug (str): problem title slug
question_id (str): problem internal id
"""
stmt = 'INSERT OR REPLACE INTO problems (num, title, slug, question_id) VALUES (?, ?, ?, ?)'
self._query(stmt, (num, title, slug, question_id))
[docs]
def select_problem(self, num: int) -> sqlite3.Row:
"""
Selects a problem from the problems table.
Args:
num (int): problem number
Returns:
sqlite3.Row: row containing the problem's num, title, slug, and question id
"""
stmt = 'SELECT * FROM problems WHERE num = ?'
return self._query(stmt, (num,))
[docs]
def count_problems(self) -> sqlite3.Row:
"""
Counts the number of problems in the problems table.
Returns:
int: count of problems
"""
stmt = 'SELECT COUNT(*) FROM problems'
return self._query(stmt, ())