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 628a264  Split add/edit of issues.
628a264 is described below

commit 628a2648dd81ab70890fe568d9a3477f3c5aa9d2
Author: Greg Stein <[email protected]>
AuthorDate: Sat Dec 20 04:37:45 2025 -0600

    Split add/edit of issues.
    
    We should not have conflated the method to add issues with that of
    editing issues. These are now separate methods, and separate cursor
    objects for updating the database.
    
    In pages.py, use the two distinct endpoints. ... well, actually have
    add_issue get called(!)
    
    Also, fix up the coverage testing for the API change.
---
 v3/queries.yaml            | 12 +++++-------
 v3/server/pages.py         | 16 ++++++++--------
 v3/steve/election.py       | 36 ++++++++++++++++++++++++++++++------
 v3/tests/check_coverage.py | 15 ++++-----------
 4 files changed, 47 insertions(+), 32 deletions(-)

diff --git a/v3/queries.yaml b/v3/queries.yaml
index bf7ab41..1f0663f 100644
--- a/v3/queries.yaml
+++ b/v3/queries.yaml
@@ -37,13 +37,11 @@ election:
         UPDATE election
         SET closed = 1, close_at = unixepoch('now')
         WHERE eid = ?
-    c_add_issue: |
-        INSERT INTO issue VALUES (?, ?, ?, ?, ?, ?)
-        ON CONFLICT DO UPDATE SET
-            title=excluded.title,
-            description=excluded.description,
-            type=excluded.type,
-            kv=excluded.kv
+    c_add_issue: INSERT INTO issue VALUES (?, ?, ?, ?, ?, ?)
+    c_edit_issue: |
+        UPDATE issue
+        SET title = ?, description = ?, type = ?, kv = ?
+        WHERE iid = ?
     c_delete_issue: DELETE FROM issue WHERE iid = ?
     c_add_vote: |
         INSERT INTO vote (vote_token, ciphertext)
diff --git a/v3/server/pages.py b/v3/server/pages.py
index 48ad695..f698bbd 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -354,17 +354,17 @@ async def do_add_issue_endpoint(election):
     form = edict(await quart.request.form)
     print('FORM:', form)
 
-    ### do stuff
-    ### add_issue(iid, title, description, vtype, kv)
-    ### the IID should be created by add_issue. Do this for now.
-    ### does add_issue() return an edict for the added issue?
-    issue = edict(iid=steve.crypto.create_id(), title=form.title)
+    ### do better with these
+    vtype = 'yna'
+    kv = None
+
+    iid = election.add_issue(form.title, form.description, vtype, kv)
 
     _LOGGER.info(
-        f'User[U:{result.uid}] added issue[I:{issue.iid}] to 
election[E:{election.eid}]'
+        f'User[U:{result.uid}] added issue[I:{iid}] to 
election[E:{election.eid}]'
     )
 
-    await flash_success(f'Issue "{issue.title}" has been added.')
+    await flash_success(f'Issue "{form.title}" has been added.')
 
     # Return to the management page for this Election.
     return quart.redirect(f'/manage/{election.eid}', code=303)
@@ -383,7 +383,7 @@ async def do_edit_issue_endpoint(election, issue):
 
     # Update the title/description.
     ### for now, no way to update the vtype or KV pairs.
-    election.add_issue(issue.iid, form.title, form.description, issue.vtype, 
issue.kv)
+    election.edit_issue(issue.iid, form.title, form.description, issue.vtype, 
issue.kv)
 
     _LOGGER.info(
         f'User[U:{result.uid}] edited issue[I:{issue.iid}]'
diff --git a/v3/steve/election.py b/v3/steve/election.py
index 0b705b4..c2e6d88 100644
--- a/v3/steve/election.py
+++ b/v3/steve/election.py
@@ -200,17 +200,41 @@ class Election:
         # NEVER return issue.salt
         return (issue.title, issue.description, issue.type, 
self.json2kv(issue.kv))
 
-    def add_issue(self, iid, title, description, vtype, kv):
-        "Add or update an issue designated by IID."
+    def add_issue(self, title, description, vtype, kv):
+        "Add a new issue with a generated unique IID."
         assert self.is_editable()
         assert vtype in vtypes.TYPES
 
-        # If we ADD, then SALT will be NULL. If we UPDATE, then it will not
-        # be touched (it should be NULL).
-        self.c_add_issue.perform(
-            iid, self.eid, title, description, vtype, self.kv2json(kv)
+        while True:
+            iid = crypto.create_id()
+            try:
+                # Pure INSERT - SALT will be NULL until election opens
+                self.c_add_issue.perform(
+                    iid, self.eid, title, description, vtype, self.kv2json(kv)
+                )
+                break
+            except sqlite3.IntegrityError:
+                _LOGGER.debug('IID conflict(!!) ... trying again.')
+
+        _LOGGER.info(f'Created issue[I:{iid}] in election[E:{self.eid}]')
+
+        return iid
+
+    def edit_issue(self, iid, title, description, vtype, kv):
+        "Update an existing issue designated by IID."
+        assert self.is_editable()
+        assert vtype in vtypes.TYPES
+
+        self.c_edit_issue.perform(
+            title, description, vtype, self.kv2json(kv), iid
         )
 
+        # If the issue didn't exist, we updated nothing.
+        if self.c_edit_issue.rowcount == 0:
+            raise IssueNotFound(iid)
+
+        _LOGGER.info(f'Updated issue[I:{iid}] in election[E:{self.eid}]')
+
     def delete_issue(self, iid):
         "Delete the Issue designated by IID."
 
diff --git a/v3/tests/check_coverage.py b/v3/tests/check_coverage.py
index 0b22c6c..766dbd5 100755
--- a/v3/tests/check_coverage.py
+++ b/v3/tests/check_coverage.py
@@ -38,7 +38,6 @@ def touch_every_line():
 
     # Do the imports *WITHIN* the coverage test.
     import steve.election
-    import steve.crypto
     import steve.persondb
 
     # Start the election, and open it.
@@ -64,13 +63,8 @@ def touch_every_line():
     pdb.delete_person('david')
     _ = pdb.get_person('alice')
 
-    i1 = steve.crypto.create_id()
-    i2 = steve.crypto.create_id()
-    i3 = steve.crypto.create_id()
-
-    e.add_issue(i1, 'issue A', None, 'yna', None)
-    e.add_issue(
-        i2,
+    i1 = e.add_issue('issue A', None, 'yna', None)
+    i2 = e.add_issue(
         'issue B',
         None,
         'stv',
@@ -86,7 +80,7 @@ def touch_every_line():
         },
     )
     _ = e.list_issues()
-    e.add_issue(i3, 'issue C', None, 'yna', None)
+    i3 = e.add_issue('issue C', None, 'yna', None)
     e.delete_issue(i3)
     _ = e.get_issue(i1)
 
@@ -114,8 +108,7 @@ def touch_every_line():
     e2 = steve.election.Election.create(TESTING_DB, 'E2', 'alice')
     # Provide some data that should get deleted.
     ### note: the referential integrity should to into a test suite.
-    e2i1 = steve.crypto.create_id()
-    e2.add_issue(e2i1, 'issue E2.A', None, 'yna', None)
+    _ = e2.add_issue('issue E2.A', None, 'yna', None)
     e2.add_voter('alice')
     e2.delete()
 

Reply via email to