Created
February 24, 2025 14:27
-
-
Save dgwyer/cf3332e002c94edf96d359ead363d182 to your computer and use it in GitHub Desktop.
Audio Anki Cards
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
import sqlite3 | |
# Create the SQLite database | |
def create_sql_questions_database(): | |
# Connect to SQLite database (creates it if it doesn't exist) | |
conn = sqlite3.connect('sql_questions.db') | |
cursor = conn.cursor() | |
# Create table with auto-incrementing ID and question field | |
cursor.execute(''' | |
CREATE TABLE IF NOT EXISTS sql_questions ( | |
id INTEGER PRIMARY KEY AUTOINCREMENT, | |
question TEXT NOT NULL | |
) | |
''') | |
# Commit the changes and close the connection | |
conn.commit() | |
conn.close() | |
# Add question to the database | |
def add_question(question_text): | |
conn = sqlite3.connect('sql_questions.db') | |
cursor = conn.cursor() | |
cursor.execute('INSERT INTO sql_questions (question) VALUES (?)', (question_text,)) | |
conn.commit() | |
conn.close() |
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
import sqlite3 | |
from fasthtml.common import * | |
from monsterui.all import * | |
hdrs = ( | |
Theme.blue.headers(), | |
Link(rel='stylesheet', href='styles.css', type='text/css'), | |
Script(src="https://unpkg.com/[email protected]"), | |
) | |
app, rt = fast_app(live=True, hdrs=hdrs) | |
@rt | |
def index(): | |
socials = (('github','https://github.com/resolver101757'), | |
('twitter','https://x.com/alex_paul_kelly/'), | |
('linkedin','https://www.linkedin.com/in/alexpkelly/')) | |
return Titled("Audio Anki Cards", | |
Card( | |
H1("Welcome to the Audio Anki Cards", cls="mt-2"), | |
P("A simple app to add questions to a database and then use them to create audio anki cards", cls=TextPresets.muted_sm), | |
TableFromDicts( | |
header_data=['ID', 'Question', 'Actions'], | |
body_data=[ | |
{'ID': q['ID'], 'Question': Span(q['Question'], id=f"question-{q['ID']}"), 'Actions': Button("Edit", cls=ButtonT.secondary, hx_get=f'/modal/{q['ID']}', hx_target="body", hx_swap="beforeend")} | |
for q in fetch_questions() | |
], | |
cls=(TableT.responsive, TableT.sm, TableT.divider) | |
), | |
footer=DivLAligned(*[UkIconLink(icon,href=url) for icon,url in socials]), | |
id="main-card", | |
cls="mt-4" | |
) | |
) | |
@rt("/modal/{qid}") | |
def get(qid:int): | |
id, question = fetch_question(qid) | |
return Div( | |
Div(_="on click trigger closeModal", cls="modal-underlay"), | |
Div( | |
H1(f"Edit Question: {qid}", cls="text-xl font-bold mb-4"), | |
Form( | |
Input(type="text", | |
value=question, | |
name="question", | |
autocomplete="off", | |
cls="w-full p-2 border rounded"), | |
Hidden(name="qid", value=qid), | |
Div( | |
Button("Cancel", | |
type="button", | |
_="on click trigger closeModal", | |
cls="btn btn-sm close"), | |
Button("Save", | |
type="submit", | |
cls="btn btn-sm primary"), | |
cls="flex justify-between gap-2 mt-4" | |
), | |
hx_put="/save-question", | |
hx_target=f"#question-{qid}", | |
_="on htmx:afterOnLoad trigger closeModal", | |
id="edit-form" | |
), | |
cls="modal-content" | |
), | |
id="modal", tabindex="-1", | |
_="""on load call me.focus() | |
on closeModal add .closing then wait for animationend then remove me | |
on keyup[key=='Escape'] trigger closeModal""" | |
) | |
def fetch_questions(): | |
conn = sqlite3.connect('sql_questions.db') | |
cursor = conn.cursor() | |
cursor.execute('SELECT * FROM sql_questions') | |
questions = cursor.fetchall() | |
conn.close() | |
# Convert list of tuples to list of dictionaries | |
return [{'ID': q[0], 'Question': q[1]} for q in questions] | |
def fetch_question(id): | |
conn = sqlite3.connect('sql_questions.db') | |
cursor = conn.cursor() | |
cursor.execute('SELECT * FROM sql_questions WHERE id = ?', (id,)) | |
question = cursor.fetchone() | |
conn.close() | |
return question | |
# Save the edited question | |
@rt("/save-question") | |
def put(qid: int, question: str): | |
conn = sqlite3.connect('sql_questions.db') | |
cursor = conn.cursor() | |
cursor.execute('UPDATE sql_questions SET question = ? WHERE id = ?', (question, qid)) | |
conn.commit() | |
conn.close() | |
return question | |
serve() |
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
#modal { | |
/* Underlay covers entire screen. */ | |
position: fixed; | |
top: 0px; | |
bottom: 0px; | |
left: 0px; | |
right: 0px; | |
background-color: rgba(0, 0, 0, 0.5); | |
z-index: 1000; | |
/* Flexbox centers the .modal-content vertically and horizontally */ | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
/* Animate when opening */ | |
animation-name: fadeIn; | |
animation-duration: 150ms; | |
animation-timing-function: ease; | |
} | |
#modal>.modal-underlay { | |
/* underlay takes up the entire viewport. This is only | |
required if you want to click to dismiss the popup */ | |
position: absolute; | |
z-index: -1; | |
top: 0px; | |
bottom: 0px; | |
left: 0px; | |
right: 0px; | |
} | |
#modal>.modal-content { | |
/* Position visible dialog near the top of the window */ | |
margin-top: 10vh; | |
/* Sizing for visible dialog */ | |
width: 80%; | |
max-width: 600px; | |
/* Display properties for visible dialog*/ | |
border: solid 1px #999; | |
border-radius: 8px; | |
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.3); | |
background-color: white; | |
padding: 20px; | |
/* Animate when opening */ | |
animation-name: zoomIn; | |
animation-duration: 150ms; | |
animation-timing-function: ease; | |
} | |
#modal.closing { | |
/* Animate when closing */ | |
animation-name: fadeOut; | |
animation-duration: 150ms; | |
animation-timing-function: ease; | |
} | |
#modal.closing>.modal-content { | |
/* Animate when closing */ | |
animation-name: zoomOut; | |
animation-duration: 150ms; | |
animation-timing-function: ease; | |
} | |
@keyframes fadeIn { | |
0% { | |
opacity: 0; | |
} | |
100% { | |
opacity: 1; | |
} | |
} | |
@keyframes fadeOut { | |
0% { | |
opacity: 1; | |
} | |
100% { | |
opacity: 0; | |
} | |
} | |
@keyframes zoomIn { | |
0% { | |
transform: scale(0.9); | |
} | |
100% { | |
transform: scale(1); | |
} | |
} | |
@keyframes zoomOut { | |
0% { | |
transform: scale(1); | |
} | |
100% { | |
transform: scale(0.9); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment