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


The following commit(s) were added to refs/heads/trunk by this push:
     new 91d3a48  Continued page development (voter/admin). Add vote-on.
91d3a48 is described below

commit 91d3a48aa845cc922f209a39e53bd8de69bf8923
Author: Greg Stein <[email protected]>
AuthorDate: Mon Oct 6 01:52:43 2025 -0500

    Continued page development (voter/admin). Add vote-on.
    
    * admin.ezt: provide cards for each election, lifted from voter.ezt
    * voter.ezt: new link target for voting on an election
    * vote-on.ezt: new page to vote on an election
    * e_bad_eid.ezt: unknown or invalid election for the user
    
    * pages.py:
      - add TEMPLATES global, and use throughout
      - T_BAD_EID is a 404 page
      - voter_page(): use the signed-in uid rather than dev/test gstein.
        Only pass election data, not owned/admin data. Provide lengths for
        both of those.
      - vote_on_page(): new page to display a page for a Person to vote on
        an Election. may use the new T_BAD_EID.
      - admin_page(): fetch elections for this user, and the owned
        elections. pass into template, along with lengths.
      - raise_404(): new func to raise a 404 with custom content.
---
 v3/server/pages.py                               | 85 +++++++++++++++++-------
 v3/server/templates/admin.ezt                    | 58 +++++++++++++++-
 v3/server/templates/{admin.ezt => e_bad_eid.ezt} |  3 +-
 v3/server/templates/{admin.ezt => vote-on.ezt}   |  5 +-
 v3/server/templates/voter.ezt                    |  2 +-
 5 files changed, 126 insertions(+), 27 deletions(-)

diff --git a/v3/server/pages.py b/v3/server/pages.py
index 3ab5340..eecf61a 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -37,6 +37,7 @@ APP = asfquart.APP
 
 THIS_DIR = pathlib.Path(__file__).resolve().parent
 DB_FNAME = THIS_DIR / APP.cfg.db
+TEMPLATES = THIS_DIR / 'templates'
 
 sys.path.insert(0, str(THIS_DIR.parent))
 import steve.election
@@ -48,6 +49,8 @@ FMT_DATE_FULL = '%Y-%m-%d %H:%M'
 SOON_1HOUR = 60 * 60
 SOON_CUTOFF = 48 * SOON_1HOUR  # 48 hours, in seconds
 
+T_BAD_EID = APP.load_template(TEMPLATES / 'e_bad_eid.ezt')
+
 
 async def signin_info():
     "Return EZT template data for the Sign-In, in the upper right."
@@ -60,7 +63,7 @@ async def signin_info():
 
 
 @APP.get('/')
[email protected]_template('templates/home.ezt')
[email protected]_template(TEMPLATES / 'home.ezt')
 async def home_page():
     result = await signin_info()
     result.title = 'Home'
@@ -70,49 +73,80 @@ async def home_page():
 
 @APP.get('/voter')
 @asfquart.auth.require({R.committer})  ### need general solution
[email protected]_template('templates/voter.ezt')
[email protected]_template(TEMPLATES / 'voter.ezt')
 async def voter_page():
-    pid = 'gstein'  ### get from session
+    result = await signin_info()
+    result.title = 'Voting'
 
     with asfpy.stopwatch.Stopwatch():
         # These are lists of EasyDict instances for each Election.
-        election = steve.election.Election.open_to_pid(DB_FNAME, pid)
-        owned = steve.election.Election.owned_elections(DB_FNAME, pid)
+        election = steve.election.Election.open_to_pid(DB_FNAME, result.uid)
+        owned = steve.election.Election.owned_elections(DB_FNAME, result.uid)
 
-    ### should change q_owned to return owner_pid even though it is
-    ### known from the query param. (ie. solve in sql, not python)
-    for e in owned:
-        e.owner_pid = pid
+    result.election = [ postprocess_election(e) for e in election ]
+
+    result.len_election = len(election)
+    result.len_owned = len(owned)
 
+    return result
+
+
[email protected]('/vote-on/<eid>')
[email protected]({R.committer})  ### need general solution
[email protected]_template(TEMPLATES / 'vote-on.ezt')
+async def vote_on_page(eid):
     result = await signin_info()
-    result.title = 'Voting'
+    result.title = 'Vote On Election'
 
-    result.election = [ postprocess_election(e) for e in election ]
-    result.owned = [ postprocess_election(e) for e in owned ]
+    e = steve.election.Election(DB_FNAME, eid)
 
-    result.len_elections = len(result.election)
-    result.len_owned = len(result.owned)
+    try:
+        md = e.get_metadata()
+    except AttributeError:
+        # If the EID is wrong, the fetch fails trying to access metadata.
+        ### YES, very poor way to signal a bad EID. fix this.
+        result.title = 'Unknown Election'
+        result.eid = eid
+        # Note: result.uid (and friends) are needed for the navbar.
+        raise_404(T_BAD_EID, result)
+        # NOTREACHED
+
+    ### check authz
+
+    ### rando for now. fetch the issues, and put in a count.
+    result.issue_count = 7
 
     return result
 
 
-### NOTE: this is for ASF committers only. Obviously, this is not a
-### general purpose solution. Something for the future, to figure out
-### how we'd like to do configuration authorization for various install
-### scenarios and authn systems.
 @APP.get('/admin')
 @asfquart.auth.require({R.committer})  ### need general solution
[email protected]_template('templates/admin.ezt')
[email protected]_template(TEMPLATES / 'admin.ezt')
 async def admin_page():
     result = await signin_info()
     result.title = 'Administration'
 
+    with asfpy.stopwatch.Stopwatch():
+        # These are lists of EasyDict instances for each Election.
+        election = steve.election.Election.open_to_pid(DB_FNAME, result.uid)
+        owned = steve.election.Election.owned_elections(DB_FNAME, result.uid)
+
+    ### for now. future: adjust query
+    for e in owned:
+        e.issue_count = 5
+        e.owner_pid = result.uid
+
+    result.owned = [ postprocess_election(e) for e in owned ]
+
+    result.len_election = len(election)
+    result.len_owned = len(owned)
+
     return result
 
 
 @APP.get('/profile')
 @asfquart.auth.require  # Bare decorator means just require a valid session
[email protected]_template('templates/profile.ezt')
[email protected]_template(TEMPLATES / 'profile.ezt')
 async def profile_page():
     result = await signin_info()
     result.title = 'Profile'
@@ -122,7 +156,7 @@ async def profile_page():
 
 @APP.get('/settings')
 @asfquart.auth.require  # Bare decorator means just require a valid session
[email protected]_template('templates/settings.ezt')
[email protected]_template(TEMPLATES / 'settings.ezt')
 async def settings_page():
     result = await signin_info()
     result.title = 'Settings'
@@ -131,7 +165,7 @@ async def settings_page():
 
 
 @APP.get('/privacy')
[email protected]_template('templates/privacy.ezt')
[email protected]_template(TEMPLATES / 'privacy.ezt')
 async def privacy_page():
     result = await signin_info()
     result.title = 'Privacy'
@@ -140,7 +174,7 @@ async def privacy_page():
 
 
 @APP.get('/about')
[email protected]_template('templates/about.ezt')
[email protected]_template(TEMPLATES / 'about.ezt')
 async def about_page():
     result = await signin_info()
     result.title = 'About'
@@ -190,3 +224,8 @@ def postprocess_election(e):
     e.fmt_close_at_full = dt_close and dt_close.strftime(FMT_DATE_FULL)
 
     return e
+
+
+def raise_404(template, data):
+    content = asfquart.utils.render(template, data)
+    quart.abort(quart.Response(content, status=404, mimetype='text/html'))
diff --git a/v3/server/templates/admin.ezt b/v3/server/templates/admin.ezt
index 4d95432..a4dada5 100644
--- a/v3/server/templates/admin.ezt
+++ b/v3/server/templates/admin.ezt
@@ -1,8 +1,64 @@
 [include "header.ezt"]
     <div class="container">
         <h1>[title]</h1>
+
+
+          [for owned]
+            <div class="col-md-5 mb-4">
+                <a href="/vote-on/[owned.eid]" class="text-decoration-none">
+                    <div class="card h-100">
+                        <div class="card-body">
+                            <h5 class="card-title">[owned.title][#
+                              ][if-any owned.closed] (closed)[else][#
+                              ][if-any owned.is_opened] (open)[end][end]</h5>
+                            <p class="card-text">
+                                This election currently has 
[owned.issue_count] issues.
+                                <br/>
+                                eid: [owned.eid]
+                                <br/>
+                                created by: [owned.owner_pid]
+                                <br/>
+                                authz: [owned.authz]
+                                <br/>
+                                closed: [owned.closed]
+                            </p>
+                        </div>
+                        <div class="card-footer text-muted">
+                            [if-any owned.closed]
+                                Closed
+                                <span title="[owned.fmt_close_at_full]"
+                                        style="border-bottom: 1px dotted 
#007bff;"
+                                        >[owned.fmt_close_at]</span>
+                            [else]
+                                [if-any owned.open_at]
+                                  <div>
+                                    [if-any owned.is_opened]
+                                        Opened
+                                    [else]
+                                        Opening
+                                    [end]
+                                    <span title="[owned.fmt_open_at_full]"
+                                        style="border-bottom: 1px dotted 
#007bff;"
+                                        >[owned.fmt_open_at]</span>
+                                  </div>
+                                [end]
+                                [if-any owned.close_at]
+                                  <div>
+                                    Closing
+                                    <span title="[owned.fmt_close_at_full]"
+                                        style="border-bottom: 1px dotted 
#007bff;"
+                                        >[owned.fmt_close_at]</span>
+                                  </div>
+                                [end]
+                            [end]
+                        </div>
+                    </div>
+                </a>
+            </div>
+          [end]
+
         <p>
-            TBD
+          You have [len_election] election(s) <a href="/voter">to vote 
upon</a>.
         </p>
     </div>
 [include "footer.ezt"]
diff --git a/v3/server/templates/admin.ezt b/v3/server/templates/e_bad_eid.ezt
similarity index 54%
copy from v3/server/templates/admin.ezt
copy to v3/server/templates/e_bad_eid.ezt
index 4d95432..5beb3b8 100644
--- a/v3/server/templates/admin.ezt
+++ b/v3/server/templates/e_bad_eid.ezt
@@ -2,7 +2,8 @@
     <div class="container">
         <h1>[title]</h1>
         <p>
-            TBD
+            The Election ID ([eid]) does not exist,
+            or you have no issues to vote in that Election.
         </p>
     </div>
 [include "footer.ezt"]
diff --git a/v3/server/templates/admin.ezt b/v3/server/templates/vote-on.ezt
similarity index 52%
copy from v3/server/templates/admin.ezt
copy to v3/server/templates/vote-on.ezt
index 4d95432..f027df9 100644
--- a/v3/server/templates/admin.ezt
+++ b/v3/server/templates/vote-on.ezt
@@ -2,7 +2,10 @@
     <div class="container">
         <h1>[title]</h1>
         <p>
-            TBD
+            You have [issue_count] issues to vote upon, in this election.
+        </p>
+        <p>
+            TBD: list
         </p>
     </div>
 [include "footer.ezt"]
diff --git a/v3/server/templates/voter.ezt b/v3/server/templates/voter.ezt
index 544d7fe..a19db62 100644
--- a/v3/server/templates/voter.ezt
+++ b/v3/server/templates/voter.ezt
@@ -4,7 +4,7 @@
 
           [for election]
             <div class="col-md-5 mb-4">
-                <a href="#" class="text-decoration-none">
+                <a href="/vote-on/[election.eid]" class="text-decoration-none">
                     <div class="card h-100">
                         <div class="card-body">
                             <h5 class="card-title">[election.title][#

Reply via email to