This is an automated email from the ASF dual-hosted git repository.

gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git

commit 1af3a006f9c197f294c420cac94163fb2cc96986
Author: Greg Stein <[email protected]>
AuthorDate: Thu Oct 2 03:31:56 2025 -0500

    Format and display more Election data.
    
    * pages.py: format dates. better test/random data.
    * voter.ezt: present cards with all Election data. Draft.
---
 v3/server/pages.py            | 70 +++++++++++++++++++++++++++++++++++++++----
 v3/server/templates/voter.ezt | 44 ++++++++++++++++++++++++---
 2 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/v3/server/pages.py b/v3/server/pages.py
index c394e0e..4938350 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -24,12 +24,14 @@
 
 import sys
 import pathlib
+import datetime
 
 from easydict import EasyDict as edict
 import asfpy.stopwatch
 import quart
 import asfquart.session
 from asfquart.auth import Requirements as R
+import ezt
 
 APP = asfquart.APP
 
@@ -40,6 +42,12 @@ sys.path.insert(0, str(THIS_DIR.parent))
 import steve.election
 import steve.crypto
 
+# Formatted values to inject into templates.
+FMT_DATE = '%b %d'
+FMT_DATE_FULL = '%Y-%m-%d %H:%M'
+SOON_1HOUR = 60 * 60
+SOON_CUTOFF = 48 * SOON_1HOUR  # 48 hours, in seconds
+
 
 async def signin_info():
     "Return EZT template data for the Sign-In, in the upper right."
@@ -70,6 +78,23 @@ async def voter_page():
         owned = steve.election.Election.owned_elections(DB_FNAME, 'gstein')
 
     ### for now
+    def some_future():
+        import random
+
+        # 50% no time
+        if random.randrange(2):
+            return None
+        # 66% days, then: 50% hours or minutes each
+        if random.randrange(3):
+            delta = random.randint(10, 50) * 24 * 60 * 60  # days
+        elif random.randrange(2):
+            delta = random.randint(5, 40) * 60 * 60  # hours
+        else:
+            delta = random.randint(10, 50) * 60  # minutes
+
+        return (datetime.datetime.now() + datetime.timedelta(seconds=delta)
+                ).timestamp()
+
     def new_test_election():
         return edict(
             eid=steve.crypto.create_id(),
@@ -77,17 +102,17 @@ async def voter_page():
             owner_pid='alice',
             authz=None,
             closed=None,
-            open_at=None,
-            close_at=None,
+            open_at=some_future(),
+            close_at=some_future(),
             )
-    election = [ new_test_election() ]
-    owned = [ new_test_election() ]
+    election = [ new_test_election() for i in range(10) ]
+    owned = [ new_test_election() for i in range(10) ]
 
     result = await signin_info()
     result.title = 'Voting'
 
-    result.election = election
-    result.owned = owned
+    result.election = [ postprocess_election(e) for e in election ]
+    result.owned = [ postprocess_election(e) for e in owned ]
 
     return result
 
@@ -148,3 +173,36 @@ async def about_page():
 @APP.route('/static/<path:filename>')
 async def serve_static(filename):
     return await quart.send_from_directory('static', filename)
+
+
+def format_datetime(dt):
+    "Format a datetime as absolute or relative."
+
+    # Carry through an absent datetime.
+    if not dt:
+        return None
+
+    delta = dt.timestamp() - datetime.datetime.now().timestamp()
+    if 0 < delta < SOON_CUTOFF:
+        if delta < SOON_1HOUR:
+            return f'about {int(delta / 60)} minutes'
+        return f'about {int(delta / 60 / 60)} hours'
+
+    return dt.strftime(FMT_DATE)  # short format
+
+
+def postprocess_election(e):
+    "Post-process attributes in an Election, as an EasyDict."
+
+    # Anything but 1 means the Election is Open.
+    e.closed = ezt.boolean(e.closed == 1)
+
+    # Format dates, if present.
+    dt_open = e.open_at and datetime.datetime.fromtimestamp(e.open_at)
+    e.fmt_open_at = format_datetime(dt_open)
+    e.fmt_open_at_full = dt_open and dt_open.strftime(FMT_DATE_FULL)
+    dt_close = e.close_at and datetime.datetime.fromtimestamp(e.close_at)
+    e.fmt_close_at = format_datetime(dt_close)
+    e.fmt_close_at_full = dt_close and dt_close.strftime(FMT_DATE_FULL)
+
+    return e
diff --git a/v3/server/templates/voter.ezt b/v3/server/templates/voter.ezt
index 2fadefe..24a4b1e 100644
--- a/v3/server/templates/voter.ezt
+++ b/v3/server/templates/voter.ezt
@@ -1,14 +1,50 @@
 [include "header.ezt"]
     <div class="container">
         <h1>[title]</h1>
-        <p>
+
           [for election]
-            [election.eid] [election.title]
+            <div class="col-md-5 mb-4">
+                <a href="#" class="text-decoration-none">
+                    <div class="card h-100">
+                        <div class="card-body">
+                            <h5 class="card-title">[election.title]</h5>
+                            <p class="card-text">
+                                something about the election?
+                                <br/>
+                                eid: [election.eid]
+                                <br/>
+                                created by: [election.owner_pid]
+                                <br/>
+                                authz: [election.authz]
+                                <br/>
+                                closed: [election.closed]
+                            </p>
+                        </div>
+                        <div class="card-footer text-muted">
+                            <div>
+                            OPEN:
+                                [election.open_at]
+                                [election.fmt_open_at]
+                                [election.fmt_open_at_full]
+                            </div>
+                            <div>
+                            CLOSE:
+                                [election.close_at]
+                                [election.fmt_close_at]
+                                [election.fmt_close_at_full]
+                            </div>
+                        </div>
+                    </div>
+                </a>
+            </div>
           [end]
-        </p>
+
         <p>
           [for owned]
-            [owned.eid] [owned.title] [owned.authz] [owned.closed]
+            [owned.eid] [owned.title] [owned.owner_pid]
+            [owned.authz] [owned.closed]
+            [owned.open_at] [owned.fmt_open_at] [owned.fmt_open_at_full]
+            [owned.close_at] [owned.fmt_close_at] [owned.fmt_close_at_full]
           [end]
         </p>
     </div>

Reply via email to