commit 9d917beac687387db32e78cda89cbf53690857e3
parent 1afb54fae7b798e23032b33974b141ecd375a8f4
Author: William Lindholm <william_lindholm@outlook.com>
Date: Tue, 7 Nov 2023 19:51:26 +0100
API endpoint messages can now send JSON or HTML with X headers.
Diffstat:
7 files changed, 108 insertions(+), 67 deletions(-)
diff --git a/WebAPI/namespaces/messages.py b/WebAPI/namespaces/messages.py
@@ -1,4 +1,4 @@
-from flask import request
+from flask import request, render_template, make_response
from flask_restx import Namespace, Resource, fields
from database.models.messages import MessageModel
@@ -36,12 +36,14 @@ paginated_message_model = messages_ns.model('MessageList', {
@messages_ns.route('/')
class MessageList(Resource):
+ @messages_ns.doc('get_messages')
@messages_ns.param('sort_by', 'Field to sort the messages by')
@messages_ns.param('sort_order', 'Order to sort the messages (ASC or DESC)')
@messages_ns.param('page', 'The page to retrieve')
@messages_ns.param('page_size', 'The number of messages to retrieve per page')
- @messages_ns.marshal_list_with(paginated_message_model)
+ @messages_ns.produces(['application/json', 'text/html'])
def get(self):
+ """Get (paginated) messages (HTML or JSON)"""
args = request.args
page = args.get('page', 1, type=int)
page_size = args.get('page_size', 10, type=int)
@@ -57,12 +59,22 @@ class MessageList(Resource):
messages, total_pages = message_model.get_page(page, page_size, sort=sort_by, sort_order=sort_order)
- return {
- 'messages': messages,
- 'total_pages': total_pages,
- 'current_page': page,
- 'page_size': page_size
- }
+ accept_header = request.headers.get('Accept', '')
+ if 'text/html' in accept_header:
+ response = make_response(render_template('messages_template.html', messages=messages))
+ response.headers['X-Total-Pages'] = total_pages
+ response.headers['X-Current-Page'] = page
+ return response
+
+ return render_template('messages_template.html', messages=messages)
+ else:
+ # Default to JSON response
+ return {
+ 'messages': messages,
+ 'total_pages': total_pages,
+ 'current_page': page,
+ 'page_size': page_size
+ }
# Create message
@messages_ns.expect(message_create_model)
diff --git a/WebInterface/routes.py b/WebInterface/routes.py
@@ -1,10 +1,9 @@
import json
from pathlib import Path
-from flask import Flask, request, redirect, url_for, render_template, session
+from flask import request, redirect, url_for, render_template, session
from dbrepository import DatabaseRepository
from . import web_interface
-from .tableBuilder import TableBuilder
from .tableFactory import TableFactory
db = DatabaseRepository()
diff --git a/WebInterface/tableFactory.py b/WebInterface/tableFactory.py
@@ -1,11 +1,10 @@
from WebInterface.tableBuilder import TableBuilder
-from dbrepository import DatabaseRepository
-
+from database.models.messages import MessageModel
class TableFactory:
def __init__(self):
- self.db = DatabaseRepository()
+ self.db_model = MessageModel()
@staticmethod
def delete_link(row_data):
@@ -15,7 +14,7 @@ class TableFactory:
def flag_link(row_data):
return f'<a href="/message/flag/{row_data[0]}" class="flag-icon" title="Flag Message"><i class="material-icons">flag</i></a>'
- def get_message_table(self, page=0, page_size=10):
+ def get_message_table(self, page=0, page_size=10, sort='id', sort_order='ASC', search=''):
headers = [
("ID", "70px"),
("Sender", "100px"),
@@ -25,7 +24,7 @@ class TableFactory:
("", "50px")
]
- messages, total_pages = self.db.get_messages(page, page_size)
+ messages, total_pages = self.db_model.get_page(page, page_size, sort, sort_order, search)
table_data = (TableBuilder()
.set_headers(headers)
@@ -35,8 +34,8 @@ class TableFactory:
.set_current_page(page)
.set_sortable(["ID"])
.set_on_click("/message/{0}")
- .add_callback_column(self.flag_link)
- .add_callback_column(self.delete_link)
+ #.add_callback_column(self.flag_link)
+ #.add_callback_column(self.delete_link)
.get_table_data())
return table_data
diff --git a/database/database.db b/database/database.db
Binary files differ.
diff --git a/database/models/messages.py b/database/models/messages.py
@@ -7,23 +7,29 @@ class MessageModel:
def __init__(self):
self.db_repository = DbRepository()
- def get_page(self, page, page_size, sort="id", sort_order="ASC"):
+ def get_page(self, page, page_size, sort="id", sort_order="ASC", search=''):
+ search_term = f"%{search}%"
total_messages_query = 'SELECT COUNT(*) FROM messages'
total_messages = self.db_repository.execute_query(total_messages_query, expect_result=True)[0][0]
+ common_sql_query_base = f"""
+ FROM messages
+ WHERE (name LIKE ? OR email LIKE ? OR subject LIKE ? OR content LIKE ?)
+ """
+
offset = (page - 1) * page_size
total_pages = math.ceil(total_messages / page_size)
paginated_messages_query = f"""
- SELECT id, name, email, subject, content, timestamp, relevance
- FROM messages
- ORDER BY {sort} {sort_order}
- LIMIT ? OFFSET ?
- """
+ SELECT id, name, email, subject, content, timestamp, relevance
+ {common_sql_query_base}
+ ORDER BY {sort} {sort_order}
+ LIMIT ? OFFSET ?
+ """
paginated_messages = self.db_repository.execute_query(
paginated_messages_query,
- (page_size, offset),
+ (search_term, search_term, search_term, search_term, page_size, offset),
expect_result=True
)
diff --git a/static/html/tabletest.html b/static/html/tabletest.html
@@ -2,55 +2,78 @@
<html lang="en">
<head>
<meta charset="UTF-8">
- <title>Messages Table with HTMX</title>
+ <title>Messages</title>
+ <!-- Materialize CSS -->
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
+ <!-- HTMX -->
<script src="https://unpkg.com/htmx.org"></script>
- <style>
- table {
- width: 100%;
- border-collapse: collapse;
- }
- table, th, td {
- border: 1px solid black;
- }
- th, td {
- padding: 10px;
- text-align: left;
- }
- .pagination {
- padding: 10px;
- text-align: center;
- }
- .pagination a {
- padding: 5px 10px;
- margin: 5px;
- border: 1px solid #ddd;
- text-decoration: none;
- }
- .pagination a.active {
- background-color: #007bff;
- color: white;
- border: 1px solid #007bff;
- }
- .pagination a:hover:not(.active) {
- background-color: #ddd;
- }
- </style>
</head>
<body>
+ <div class="container">
+ <h2>Messages</h2>
+
+ <!-- Search Bar -->
+ <div class="input-field">
+ <input type="text" id="search-bar" placeholder="Search Messages..." hx-get="/api/messages" hx-trigger="keyup changed delay:500ms" hx-target="#table-body" hx-headers='{"Accept":"text/html"}' hx-indicator="#loading-indicator">
+ <div id="loading-indicator" style="display: none;">Loading...</div>
+ </div>
+
+ <!-- Messages Table -->
+ <table class="highlight responsive-table">
+ <thead>
+ <tr>
+ <th>ID</th>
+ <th>Name</th>
+ <th>Email</th>
+ <th>Subject</th>
+ <th>Timestamp</th>
+ <th>Relevance</th>
+ </tr>
+ </thead>
+ <tbody id="table-body" hx-get="/api/messages" hx-trigger="load" hx-headers='{"Accept":"text/html"}'>
+ <!-- Table rows will be populated by HTMX -->
+ </tbody>
+ </table>
-<div id="messages-table">
- <!-- This is where our messages table will be loaded -->
- <div hx-get="/api/messages?page=1&page_size=10" hx-trigger="load" hx-target="#messages-table">
- Loading messages...
+ <!-- Pagination -->
+ <ul class="pagination" id="pagination-container">
+ <!-- Pagination links will be dynamically inserted here -->
+ </ul>
</div>
-</div>
-<script>
- // Function to handle the pagination click
- function loadPage(page) {
- htmx.ajax('GET', '/api/messages?page=' + page + '&page_size=10', {target: '#messages-table'});
- }
-</script>
+ <!-- Materialize JS -->
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
+
+ <script>
+ // JavaScript for dynamically updating pagination
+ function updatePagination(currentPage, totalPages) {
+ const container = document.getElementById('pagination-container');
+ container.innerHTML = ''; // Clear existing pagination
+
+ for (let i = 1; i <= totalPages; i++) {
+ const li = document.createElement('li');
+ li.className = i === currentPage ? 'active' : 'waves-effect';
+ const a = document.createElement('a');
+ a.href = '#!';
+ a.textContent = i;
+ a.setAttribute('hx-get', '/api/messages?page=' + i);
+ a.setAttribute('hx-target', '#table-body');
+ a.setAttribute('hx-trigger', 'click');
+ a.setAttribute('hx-headers', '{"Accept":"text/html"}');
+ li.appendChild(a);
+ container.appendChild(li);
+ }
+ }
+ // Initial pagination setup
+ document.body.addEventListener('htmx:load', function(event) {
+ if (event.detail.path === '/api/messages') {
+ // Assuming the server sends back total pages and current page as data attributes
+ const totalPages = event.detail.xhr.getResponseHeader('X-Total-Pages');
+ const currentPage = event.detail.xhr.getResponseHeader('X-Current-Page');
+ updatePagination(parseInt(currentPage, 10), parseInt(totalPages, 10));
+ }
+ });
+ </script>
</body>
</html>
diff --git a/templates/messages_template.html b/templates/messages_template.html
@@ -0,0 +1 @@
+<tbody>{% for message in messages %}<tr><td>{{ message.id }}</td><td>{{ message.name }}</td><td>{{ message.email }}</td><td>{{ message.subject }}</td><td>{{ message.timestamp }}</td><td>{{ message.relevance }}</td></tr>{% endfor %}</tbody>
+\ No newline at end of file