All puzzle list

This commit is contained in:
Stephani, John 2026-03-16 17:07:01 -05:00
parent 5a6bb5577b
commit 63f2ccebbf
No known key found for this signature in database
GPG Key ID: D1DF11026392A2DD
7 changed files with 114 additions and 11 deletions

Binary file not shown.

View File

@ -40,6 +40,9 @@ def get_puzzle_numbers(cursor):
rows = cursor.execute("SELECT number FROM puzzle ORDER BY number").fetchall() rows = cursor.execute("SELECT number FROM puzzle ORDER BY number").fetchall()
return [row[0] for row in rows] return [row[0] for row in rows]
def get_nav_numbers(puzzle_numbers, center):
return [number for number in puzzle_numbers if center - 2 <= number <= center + 2]
def get_puzzle_summaries(cursor): def get_puzzle_summaries(cursor):
rows = cursor.execute( rows = cursor.execute(
"SELECT number, author, creation_date FROM puzzle ORDER BY number" "SELECT number, author, creation_date FROM puzzle ORDER BY number"
@ -53,6 +56,12 @@ def get_puzzle_summaries(cursor):
for row in rows for row in rows
] ]
def get_all_puzzle_rows(cursor):
return [
{"number": 0, "author": DEFAULT_AUTHOR, "creation_date": DEFAULT_CREATION_DATE},
*get_puzzle_summaries(cursor),
]
def get_puzzle_record(cursor, puzzle_number): def get_puzzle_record(cursor, puzzle_number):
row = cursor.execute( row = cursor.execute(
"SELECT author, creation_date, data FROM puzzle WHERE number = ?", "SELECT author, creation_date, data FROM puzzle WHERE number = ?",
@ -82,6 +91,7 @@ def render_puzzle(puzzle_number):
if record is None: if record is None:
abort(404) abort(404)
nav_numbers = get_nav_numbers(puzzle_numbers, puzzle_number)
if puzzle_number == 0: if puzzle_number == 0:
categories = DEFAULT_CATEGORIES categories = DEFAULT_CATEGORIES
author = DEFAULT_AUTHOR author = DEFAULT_AUTHOR
@ -96,7 +106,7 @@ def render_puzzle(puzzle_number):
categories=categories, categories=categories,
author=author, author=author,
creation_date=creation_date, creation_date=creation_date,
puzzle_numbers=puzzle_numbers, nav_numbers=nav_numbers,
current_puzzle=puzzle_number, current_puzzle=puzzle_number,
) )
@ -108,15 +118,28 @@ def index():
def puzzle_by_number(puzzle_number): def puzzle_by_number(puzzle_number):
return render_puzzle(puzzle_number) return render_puzzle(puzzle_number)
@app.route('/all')
def all_puzzles():
with sqlite3.connect(config.db_file) as connection:
cursor = connection.cursor()
puzzle_numbers = get_puzzle_numbers(cursor)
nav_center = puzzle_numbers[-1] if puzzle_numbers else 0
nav_numbers = get_nav_numbers(puzzle_numbers, nav_center)
puzzles = get_all_puzzle_rows(cursor)
return render_template('all.html', nav_numbers=nav_numbers, puzzles=puzzles)
@app.route('/new', methods=['GET', 'POST']) @app.route('/new', methods=['GET', 'POST'])
def new_puzzle(): def new_puzzle():
with sqlite3.connect(config.db_file) as connection: with sqlite3.connect(config.db_file) as connection:
cursor = connection.cursor() cursor = connection.cursor()
puzzle_numbers = get_puzzle_numbers(cursor) puzzle_numbers = get_puzzle_numbers(cursor)
nav_center = puzzle_numbers[-1] if puzzle_numbers else 0
nav_numbers = get_nav_numbers(puzzle_numbers, nav_center)
default_creation_date = today_display_date() default_creation_date = today_display_date()
if request.method == 'GET': if request.method == 'GET':
return render_template('new.html', creation_date=default_creation_date, puzzle_numbers=puzzle_numbers) return render_template('new.html', creation_date=default_creation_date, nav_numbers=nav_numbers)
author = request.form.get("author", "").strip() author = request.form.get("author", "").strip()
raw_creation_date = request.form.get("creation_date", "").strip() or default_creation_date raw_creation_date = request.form.get("creation_date", "").strip() or default_creation_date
@ -136,7 +159,7 @@ def new_puzzle():
'new.html', 'new.html',
error="Author is required.", error="Author is required.",
creation_date=raw_creation_date, creation_date=raw_creation_date,
puzzle_numbers=puzzle_numbers, nav_numbers=nav_numbers,
form=request.form form=request.form
), 400 ), 400
@ -145,7 +168,7 @@ def new_puzzle():
'new.html', 'new.html',
error='Creation date must be in the format "Day Month Year" (example: 15 March 2026).', error='Creation date must be in the format "Day Month Year" (example: 15 March 2026).',
creation_date=raw_creation_date, creation_date=raw_creation_date,
puzzle_numbers=puzzle_numbers, nav_numbers=nav_numbers,
form=request.form form=request.form
), 400 ), 400
@ -154,7 +177,7 @@ def new_puzzle():
'new.html', 'new.html',
error="Each category needs a name and exactly 4 words.", error="Each category needs a name and exactly 4 words.",
creation_date=raw_creation_date, creation_date=raw_creation_date,
puzzle_numbers=puzzle_numbers, nav_numbers=nav_numbers,
form=request.form form=request.form
), 400 ), 400
@ -174,7 +197,7 @@ def new_puzzle():
'new.html', 'new.html',
success=f"Saved puzzle #{number}.", success=f"Saved puzzle #{number}.",
creation_date=creation_date, creation_date=creation_date,
puzzle_numbers=puzzle_numbers + [number], nav_numbers=get_nav_numbers(puzzle_numbers + [number], number),
) )
@app.route('/delete', methods=['GET', 'POST']) @app.route('/delete', methods=['GET', 'POST'])
@ -204,7 +227,7 @@ def delete_puzzle():
return render_template( return render_template(
'delete.html', 'delete.html',
puzzles=puzzles, puzzles=puzzles,
puzzle_numbers=[puzzle["number"] for puzzle in puzzles], nav_numbers=get_nav_numbers([puzzle["number"] for puzzle in puzzles], puzzles[-1]["number"] if puzzles else 0),
deleted=request.args.get("deleted"), deleted=request.args.get("deleted"),
error=request.args.get("error"), error=request.args.get("error"),
) )

View File

@ -114,7 +114,7 @@ button:disabled {
color: #2b7d32; color: #2b7d32;
} }
.subtitle { .subtitle {
margin-top: -8px; margin-top: 0;
margin-bottom: 18px; margin-bottom: 18px;
font-weight: bold; font-weight: bold;
letter-spacing: 1px; letter-spacing: 1px;
@ -237,6 +237,34 @@ button:disabled {
margin: 0; margin: 0;
font-weight: bold; font-weight: bold;
} }
.all-table-wrap {
width: 100%;
max-width: 900px;
overflow-x: auto;
}
.all-table {
width: 100%;
border-collapse: collapse;
background: #fff;
}
.all-table th,
.all-table td {
border-bottom: 1px solid #ddd;
padding: 10px 12px;
text-align: left;
}
.all-table th {
text-transform: uppercase;
letter-spacing: 0.7px;
font-size: 12px;
}
.table-link-btn {
display: inline-block;
padding: 6px 12px;
border-radius: 999px;
font-size: 12px;
line-height: 1;
}
@media (max-width: 640px) { @media (max-width: 640px) {
.meta-row { .meta-row {
grid-template-columns: 1fr; grid-template-columns: 1fr;

49
app/templates/all.html Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>All Connections Puzzles</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav class="header-links">
<a href="/all" class="active">all</a>
<a href="/0">0</a>
{% for number in nav_numbers %}
<a href="/{{ number }}">{{ number }}</a>
{% endfor %}
<a href="/new">new</a>
<a href="/delete">delete</a>
</nav>
<h1>Connections</h1>
<p class="subtitle">All Puzzles</p>
<div class="all-table-wrap">
<table class="all-table">
<thead>
<tr>
<th>Puzzle Number</th>
<th>Author</th>
<th>Creation Date</th>
<th>Link</th>
</tr>
</thead>
<tbody>
{% for puzzle in puzzles %}
<tr>
<td>{{ puzzle.number }}</td>
<td>{{ puzzle.author }}</td>
<td>{{ puzzle.creation_date }}</td>
<td><a class="ghost-link table-link-btn" href="/{{ puzzle.number }}">Open</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>

View File

@ -10,8 +10,9 @@
<body> <body>
<nav class="header-links"> <nav class="header-links">
<a href="/all">all</a>
<a href="/0">0</a> <a href="/0">0</a>
{% for number in puzzle_numbers %} {% for number in nav_numbers %}
<a href="/{{ number }}">{{ number }}</a> <a href="/{{ number }}">{{ number }}</a>
{% endfor %} {% endfor %}
<a href="/new">new</a> <a href="/new">new</a>

View File

@ -10,8 +10,9 @@
<body> <body>
<nav class="header-links"> <nav class="header-links">
<a href="/all">all</a>
<a href="/0" class="{{ 'active' if current_puzzle == 0 else '' }}">0</a> <a href="/0" class="{{ 'active' if current_puzzle == 0 else '' }}">0</a>
{% for number in puzzle_numbers %} {% for number in nav_numbers %}
<a href="/{{ number }}" class="{{ 'active' if current_puzzle == number else '' }}">{{ number }}</a> <a href="/{{ number }}" class="{{ 'active' if current_puzzle == number else '' }}">{{ number }}</a>
{% endfor %} {% endfor %}
<a href="/new">new</a> <a href="/new">new</a>

View File

@ -10,8 +10,9 @@
<body> <body>
<nav class="header-links"> <nav class="header-links">
<a href="/all">all</a>
<a href="/0">0</a> <a href="/0">0</a>
{% for number in puzzle_numbers %} {% for number in nav_numbers %}
<a href="/{{ number }}">{{ number }}</a> <a href="/{{ number }}">{{ number }}</a>
{% endfor %} {% endfor %}
<a href="/new" class="active">new</a> <a href="/new" class="active">new</a>