Add user interface elements supporting download
authorKarl O. Pinc <kop@karlpinc.com>
Wed, 18 Sep 2024 18:22:44 +0000 (13:22 -0500)
committerKarl O. Pinc <kop@karlpinc.com>
Wed, 18 Sep 2024 18:22:44 +0000 (13:22 -0500)
src/pgwui_sql/constants.py [new file with mode: 0644]
src/pgwui_sql/templates/sql_edit.mak
src/pgwui_sql/views/base.py
tests/templates/test_templates.py

diff --git a/src/pgwui_sql/constants.py b/src/pgwui_sql/constants.py
new file mode 100644 (file)
index 0000000..09efa60
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright (C) 2024 The Meme Factory, Inc.  http://www.karlpinc.com/
+
+# This file is part of PGWUI_SQL.
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public
+# License along with this program.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+# Karl O. Pinc <kop@karlpinc.com>
+#
+
+'''Constants used by the PGWUI_SQL program.
+'''
+
+# Form values
+
+ONE_FILE = 'one'
+MANY_FILES = 'many'
+
+# HTML radio button attributes
+# (That these exist is a sign that we're not really utilizing our toolset.)
+ONE_FILE_VALUE = ONE_FILE
+MANY_FILES_VALUE = MANY_FILES
index 9537cf8a61519f4ca3c52c0c41ac95a7885b59a6..1c977a80a9911e6668d967b792936b2c0eaaefc8 100644 (file)
   This template uses the following variables in it's context:
    
     sql  Text of the sql command(s)
+    download   Download the SQL results
+    csv_value  Value to supply in upload_fmt when csv is checked
+    tab_value  Value to supply in upload_fmt when tab is checked
+    csv_checked   HTML with the state of the csv checkbox
+    tab_checked   HTML with the sate of the tab checkbox
+    one_file_value  Value to supply in download_as when "one file" is checked
+    many_files_value  Value to supply in download_as when "many files"
+                      is checked
+    one_file_checked  HTML with the state of the "one file" checkbox
+    many_files_checked  HTML with the state of the "many files" checkbox
+    include_sql  Include SQL in the downloaded results
 </%doc>
 
 
         <%self.lib:td_label for_id="upload_sql_id">
           Execute SQL from a file
         </%self.lib:td_label>
-        <%self.lib:td_input tab_index="${tab_index}" clas="search_path_data">
+        <%self.lib:td_input tab_index="${tab_index}">
           <input name="upload_sql"
                  tabindex="${tab_index.val}"
                  id="upload_sql_id"
       </tr>
 </%def>
 
+<%def name="download_row(tab_index)">
+      <tr>
+        <%self.lib:td_label for_id="download_id">
+          Download
+        </%self.lib:td_label>
+        <%self.lib:td_input tab_index="${tab_index}">
+          <input name="download"
+                 tabindex="${tab_index.val}"
+                 id="download_id"
+                 type="checkbox"
+                 ${download}
+                 />
+        </%self.lib:td_input>
+
+        <%self.lib:td_label>Download Format</%self.lib:td_label>
+        <%self.lib:td_input tab_index="${tab_index}" tab_inc="${2}">
+          <input name="download_fmt"
+                 id="download_fmt_csv_id"
+                 tabindex="${tab_index.val}"
+                 type="radio"
+                 value="${csv_value}"
+                 ${csv_checked | n}
+                 />
+          <label class="label" for="download_fmt_csv_id">CSV</label>
+          <br />
+          <input name="download_fmt"
+                 id="download_fmt_tab_id"
+                 tabindex="${tab_index.val + 1}"
+                 type="radio"
+                 value="${tab_value}"
+                 ${tab_checked | n}
+                 />
+          <label class="label" for="download_fmt_tab_id">Tab delimited</label>
+        </%self.lib:td_input>
+      </tr>
+</%def>
+
+<%def name="download_as_row(tab_index)">
+      <tr>
+        <%self.lib:td_label>Download as</%self.lib:td_label>
+        <%self.lib:td_input tab_index="${tab_index}" tab_inc="${2}">
+          <input name="download_as"
+                 id="download_as_one_file_id"
+                 tabindex="${tab_index.val}"
+                 type="radio"
+                 value="${one_file_value}"
+                 ${one_file_checked | n}
+                 />
+          <label class="label" for="download_as_one_file_id">A single file
+            with all results</label>
+          <br />
+          <input name="download_as"
+                 id="download_as_many_files_id"
+                 tabindex="${tab_index.val + 1}"
+                 type="radio"
+                 value="${many_files_value}"
+                 ${many_files_checked | n}
+                 />
+          <label class="label" for="download_fmt_tab_id">A zip file with one
+          file for each<br>statement's results</label>
+        </%self.lib:td_input>
+      </tr>
+</%def>
+
+
+<%def name="include_sql_row(tab_index)">
+      <tr>
+        <%self.lib:td_label for_id="include_sql_id">
+          Include SQL in the download
+        </%self.lib:td_label>
+        <%self.lib:td_input tab_index="${tab_index}">
+          <input name="include_sql"
+                 tabindex="${tab_index.val}"
+                 id="include_sql_id"
+                 type="checkbox"
+                 ${include_sql}
+                 />
+        </%self.lib:td_input>
+      </tr>
+</%def>
+
 <%def name="table_rows(tab_index)">
   <%parent:table_rows tab_index="${tab_index}" args="tab_index">
     ## A blank table row for spacing
     <tr class="verticalgap"><td></td><td></td></tr>
     ${self.sql_row(tab_index)}
-    ${self.file_input_row(tab_index)}
   </%parent:table_rows>
 </%def>
 
   <% tab_index.inc() %>
 </%def>
 
+<%def name="controls(tab_index)">
+  <table>
+    ${self.file_input_row(tab_index)}
+    ${self.download_row(tab_index)}
+    ${self.download_as_row(tab_index)}
+    ${self.include_sql_row(tab_index)}
+  </table>
+</%def>
+
 % if havecreds:
     <% tab_index = self.attr.TabIndex() %>
     ${self.main_form(tab_index, target=self.sql_results_target(),
index ccf2f1f77fff2879f5d3406da54ba911da8d96fe..bb02a2c27224a3f5d112f9160525bc7fc9b3f7fd 100644 (file)
@@ -22,12 +22,31 @@ import pgwui_core.core
 import pgwui_core.forms
 import wtforms.fields
 
+from pgwui_core.constants import (
+    CHECKED,
+    UNCHECKED,
+    CSV,
+    TAB,
+    CSV_VALUE,
+    TAB_VALUE,
+)
+from pgwui_sql.constants import (
+    ONE_FILE,
+    MANY_FILES,
+    ONE_FILE_VALUE,
+    MANY_FILES_VALUE,
+)
+
 
 @attrs.define(slots=False)
 class SQLInitialPost(pgwui_core.forms.UserInitialPost):
     sql = attrs.field(default='')
     upload_sql = attrs.field(default=False)
     sql_file = attrs.field(default=None)
+    download = attrs.field(default=False)
+    download_fmt = attrs.field(default=CSV)
+    download_as = attrs.field(default=ONE_FILE)
+    include_sql = attrs.field(default=True)
 
 
 class SQLWTForm(pgwui_core.forms.AuthWTForm):
@@ -40,6 +59,17 @@ class SQLWTForm(pgwui_core.forms.AuthWTForm):
         'Execute SQL from a file:', id='upload_sql_id')
     sql_file = wtforms.fields.FileField(
         'File of SQL statements')
+    download = wtforms.fields.BooleanField(
+        'Download', id='download_id')
+    download_fmt = wtforms.fields.RadioField(
+        'Download Format:', choices=[('CSV', CSV),
+                                     ('Tab delimited', TAB)])
+    download_as = wtforms.fields.RadioField(
+        'Download as:',
+        choices=[('A single file with all results', ONE_FILE),
+                 ("One file for each statement's results", MANY_FILES)])
+    include_sql = wtforms.fields.BooleanField(
+        'Include SQL in the download', id='include_sql_id')
 
 
 @attrs.define(slots=False)
@@ -56,6 +86,10 @@ class SQLForm(pgwui_core.forms.UploadFormBaseMixin,
     sql_file = attrs.field(default=None)
     sql_file_read = attrs.field(default=False)
     filename = attrs.field(default=None)
+    download = attrs.field(default=False)
+    download_fmt = attrs.field(default=None)
+    download_as = attrs.field(default=None)
+    include_sql = attrs.field(default=True)
 
     def read(self):
         '''
@@ -86,10 +120,50 @@ class SQLForm(pgwui_core.forms.UploadFormBaseMixin,
         else:
             self['sql'] = self._form.sql.data
 
+        self['download'] = self._form.download.data
+        self['download_fmt'] = self._form.download_fmt.data
+        self['download_as'] = self._form.download_as.data
+        self['include_sql'] = self._form.include_sql.data
+
     def write(self, result, errors):
         '''
         Produces the dict pyramid will use to render the form.
         '''
         response = super().write(result, errors)
         response['sql'] = self['sql']
+
+        if self['download']:
+            download_checked = CHECKED
+        else:
+            download_checked = UNCHECKED
+        response['download'] = download_checked
+
+        if self['download_fmt'] == CSV:
+            csv_checked = CHECKED
+            tab_checked = UNCHECKED
+        else:
+            tab_checked = CHECKED
+            csv_checked = UNCHECKED
+        response['csv_checked'] = csv_checked
+        response['tab_checked'] = tab_checked
+        response['csv_value'] = CSV_VALUE
+        response['tab_value'] = TAB_VALUE
+
+        response['download_as'] = self['download_as']
+        if self['download_as'] == ONE_FILE:
+            one_file_checked = CHECKED
+            many_files_checked = UNCHECKED
+        else:
+            many_files_checked = CHECKED
+            one_file_checked = UNCHECKED
+        response['one_file_checked'] = one_file_checked
+        response['many_files_checked'] = many_files_checked
+        response['one_file_value'] = ONE_FILE_VALUE
+        response['many_files_value'] = MANY_FILES_VALUE
+
+        if self['include_sql']:
+            include_sql_checked = CHECKED
+        else:
+            include_sql_checked = UNCHECKED
+        response['include_sql'] = include_sql_checked
         return response
index 68ac7e7ef387cc604a73979b755153ef758f2333..60f8ed0d65182d583aba0236805f68d67126330f 100644 (file)
@@ -25,6 +25,16 @@ import pytest
 import pgwui_server.pgwui_server
 import pgwui_develop.testing
 import pyramid.testing
+from pgwui_core.constants import (
+    CHECKED,
+    UNCHECKED,
+    CSV_VALUE,
+    TAB_VALUE,
+)
+from pgwui_sql.constants import (
+    ONE_FILE_VALUE,
+    MANY_FILES_VALUE,
+)
 
 # A dict of "standard" settings
 pgwui_settings = {'dry_run': False,
@@ -46,7 +56,17 @@ stock_template_args = {
     'csrf_token': 'somecsrftoken',
     'sql': 'select 1;',
     'search_path': '"$user", somedb',
-    'upload_sql': True
+    'upload_sql': True,
+    'download': False,
+    'csv_value': CSV_VALUE,
+    'tab_value': TAB_VALUE,
+    'csv_checked': CHECKED,
+    'tab_checked': UNCHECKED,
+    'one_file_value': ONE_FILE_VALUE,
+    'many_files_value': MANY_FILES_VALUE,
+    'one_file_checked': CHECKED,
+    'many_files_checked': UNCHECKED,
+    'include_sql': True,
 }
 
 # The templates to test