ContactBridge

Log | Files | Refs | README

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:
MWebAPI/namespaces/messages.py | 28++++++++++++++++++++--------
MWebInterface/routes.py | 3+--
MWebInterface/tableFactory.py | 13++++++-------
Mdatabase/database.db | 0
Mdatabase/models/messages.py | 20+++++++++++++-------
Mstatic/html/tabletest.html | 109++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Atemplates/messages_template.html | 2++
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