0

I have a python flask app and I'm wondering how to have the html page to show the selected parameters from dropdown list, text box, multi-selection etc.

A toy example of my current-working setup looks like this: app.py

from flask import Flask, render_template, request, jsonify

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html",
                           selection1=["1","2"],
                           selection2=["1","2"],
                            )

@app.route("/get-values")
def get_values():
    dummy = [
        ["123", "123", "123"],
        ["456", "456", "456"],
        ["789", "789", "789"],
    ]

    result = {
        'sessions': dummy,
        'total_sessions': 3,
        'total_pages': 1,
        'current_page': 1
    }
    return jsonify(result)


if __name__ == '__main__':
    app.run(debug=True)

The step is basically:

  1. index.html contains the page where user can update parameters to have the table showing different information

  2. when you open the page, it shows the result from default selections

  3. whenever you update any of the field, the result will be updated automatically (done by using $('#selection1-field').on('change', getFirstPageValues);

  4. the update is done by calling /get-values in app.py and sending the new paramters.

What I want to do now is to be able to save (bookmark in browser) the url with updated parameters. Currently it is only saving http://localhost:5000/ which is the home page as well as where the result is shown, but I want it to be able to save url with updated parameters so that next time when I open the bookmarked page, it has the parameters applied already, something like: http://localhost:5000/get-values?c1=v1&c2=v2&c3=v3.

I think I will need to have something like getUrlParam (How to get the value from the GET parameters?) but I'm very new to js and I don't know how to do it. Also probably I need to make changes on the python flask end as well?

Another problem is that since the main url is the index (/) but I'm calling a different endpoint (/get-values) to get data for table, if I were to use something like http://localhost:5000/get-values?c1=v1&c2=v2&c3=v3, then according to the current setup, I'm basically just saving the json output rather than the page with table display of the json output result. Ideally it should be http://localhost:5000/c1=v1&c2=v2&c3=v3 but I don't know how to make this work and I was not able to find any references.


The corresponding index.html is below. You should be able to put it under /templates and make the app work. It has function displayResults(result) which is used to display the table.

<!doctype html>
<html>
<head>

  <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>
  <link rel='stylesheet' href='//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css' />
  <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/css/bootstrap-select.min.css'>
  <style type='text/css'>

            .CSSTableGenerator {
                margin: 50px;
                padding: 0px;
                width: 95%;
                border: 1px solid #000000;
                -moz-border-radius-bottomleft: 0px;
                -webkit-border-bottom-left-radius: 0px;
                border-bottom-left-radius: 0px;
                -moz-border-radius-bottomright: 0px;
                -webkit-border-bottom-right-radius: 0px;
                border-bottom-right-radius: 0px;
                -moz-border-radius-topright: 0px;
                -webkit-border-top-right-radius: 0px;
                border-top-right-radius: 0px;
                -moz-border-radius-topleft: 0px;
                -webkit-border-top-left-radius: 0px;
                border-top-left-radius: 0px;
            }

            .CSSTableGenerator table {
                width: 100%;
                height: 100%;
                margin: 0px;
                padding: 0px;
            }

            .CSSTableGenerator tr:last-child td:last-child {
                -moz-border-radius-bottomright: 0px;
                -webkit-border-bottom-right-radius: 0px;
                border-bottom-right-radius: 0px;
            }

            .CSSTableGenerator table tr td {
                -moz-border-radius-topleft: 0px;
                -webkit-border-top-left-radius: 0px;
                border-top-left-radius: 0px;
            }

            .CSSTableGenerator table tr:first-child td:last-child {
                -moz-border-radius-topright: 0px;
                -webkit-border-top-right-radius: 0px;
                border-top-right-radius: 0px;
            }

            .CSSTableGenerator tr:last-child td:first-child {
                -moz-border-radius-bottomleft: 0px;
                -webkit-border-bottom-left-radius: 0px;
                border-bottom-left-radius: 0px;
            }

            .CSSTableGenerator tr:hover td {

            }

            /*.CSSTableGenerator tr:nth-child(odd) {
                background-color: #aad4ff
            }

            .CSSTableGenerator tr:nth-child(even) {
                background-color: #ffffff
            }*/

            .CSSTableGenerator td {
                vertical-align: middle;
                border: 1px solid #000000;
                border-width: 0px 1px 0px 0px;
                text-align: left;
                padding: 7px;
                font-size: 13px;
                font-family: Arial;
                font-weight: normal;
                color: #000000;
            }

            .CSSTableGenerator tr:last-child td {
                border-width: 0px 1px 0px 0px
            }

            .CSSTableGenerator tr:last-child td:last-child {
                border-width: 0px 0px 0px 0px
            }

            .CSSTableGenerator tr:first-child th {
                background: -o-linear-gradient(bottom, #005fbf 5%, #003f7f 100%);
                background: -webkit-gradient( linear, left top, left bottom, color-stop(0.05, #005fbf), color-stop(1, #003f7f) );
                background: -moz-linear-gradient( center top, #005fbf 5%, #003f7f 100% );
                filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#005fbf", endColorstr="#003f7f");
                background: -o-linear-gradient(top,#005fbf,003f7f);
                background-color: #005fbf;
                border: 0px solid #000000;
                text-align: center;
                border-width: 0px 0px 1px 1px;
                font-size: 14px;
                font-family: Arial;
                font-weight: bold;
                color: #ffffff;
            }

            .CSSTableGenerator tr:first-child td:first-child {
                border-width: 0px 1px 1px 0px
            }

            .CSSTableGenerator tr:first-child td:last-child {
                border-width: 0px 0px 1px 1px
            }

            #radial-center {
                /* fallback */
                background-color: #2F2727;
                background-position: center center;
                background-repeat: no-repeat;
                /* Safari 4-5, Chrome 1-9 */
                /* Can't specify a percentage size? Laaaaaame. */
                background: -webkit-gradient(radial, center center, 0, center center, 460, from(#1a82f7), to(#2F2727));
                /* Safari 5.1+, Chrome 10+ */
                background: -webkit-radial-gradient(circle, #1a82f7, #2F2727);
                /* Firefox 3.6+ */
                background: -moz-radial-gradient(circle, #1a82f7, #2F2727);
                /* IE 10 */
                background: -ms-radial-gradient(circle, #1a82f7, #2F2727);
                /* Opera couldn't do radial gradients, then at some point they started supporting the -webkit- syntax, how it kinda does but it's kinda broken (doesn't do sizing) */
            }

            td {
                vertical-align: top;
            }

            .content {
                width: 650px;
            }
            .sidebar {
                width: 300px;
            }

            .leftNavButton {
                width: 190px;
                line-height: 1;
            }

            /* Start by setting display:none to make this hidden.
             Then we position it in relation to the viewport window
             with position:fixed. Width, height, top and left speak
             speak for themselves. Background we set to 80% white with
             our animation centered, and no-repeating */

            /* When the body has the loading class, we turn
             the scrollbar off with overflow:hidden */
            body.isloading {
                overflow: hidden;
            }

            /* Anytime the body has the loading class, our
             modal element will be visible */
            body.isloading .mymodal {
                display: block;
            }

            .pace .pace-progress {
                background: red;
                position: fixed;
                z-index: 2000;
                top: 0;
                left: 0;
                height: 5px;
                -webkit-transition: width 1s;
                -moz-transition: width 1s;
                -o-transition: width 1s;
                transition: width 1s;
            }

            .pace-inactive {
                display: none;
            }

            .ignoreDetailsRow {
                cursor: pointer
            }

            .mistagDetailsRow {
                cursor: pointer
            }

            .fixDetailsRow {
                cursor: pointer
            }

            .ignoreSummaryRow {
                cursor: pointer
            }

            .fixSummaryRow {
                cursor: pointer
            }

            .mistagSummaryRow {
                cursor: pointer
            }

            pre {
                outline: 1px solid #ccc;
                padding: 5px;
                margin: 5px;
                background-color: #000;
            }

            .string {
                color: white;
            }
            .number {
                color: darkorange;
            }
            .boolean {
                color: blue;
            }
            .null {
                color: magenta;
            }
            .key {
                color: gold;
            }
            /* shirokov additions */
            * {
                -webkit-border-radius: 3 !important;
                -moz-border-radius: 3 !important;
                border-radius: 3 !important;
            }

            .container {
                width: 95%;
            }

            .bootstrap-select > .dropdown-toggle {
                width: 100%;
                padding-right: 25px;
            }

  </style>

  <title>Test</title>

</head>
<body>

  <div id='homepage-container' class='container body-container' style='width: 1500px;'>
    <div id='title-div' class='row' style='margin-bottom: 30px;'>
      <h1 class='title' style='text-align: center; margin-top: 30px;'>Test</h1>
    </div><!-- title-div -->

    <div id='form-div' class='row' style='margin-bottom: 30px;'>
      <div class='form-holder'>
        <form id='query-form'>
          <div class='col-lg-1 col-md-1'></div>
          <div class='col-lg-2 col-md-2'>
            <label id='date-range'>Date</label>
              <div id='reportrange' class='pull-right' style='background: #fff; cursor: pointer; padding: 5px 8px; border: 1px solid #ccc; width: 100%'>
                <span id='date-field'></span> <b class='caret'></b>
              </div>
          </div>
          <div class='col-lg-1 col-md-1'>
            <label>Selection1</label>
              <select class='form-control' id='selection1-field'>
                {% for d in selection1 %}
                <option value="{{ d[0] }}">{{ d[1] }}</option>
                {% endfor %}
              </select>
          </div>
          <div class='col-lg-2 col-md-2'>
            <label>Selection2</label>
              <select id='selection2-field' class='selectpicker form-control' name='selection2' title='All' multiple data-live-search='true' style='width: 100%;'>
                {% for d in selection2 %}
                <option value='{{ d }}'> {{ d }}</option>
                {% endfor %}
              </select>
          </div>
          <div class='col-lg-2 col-md-2'>
            <label>Query Search</label>
            <input type="text" class="form-control" id="query-search-field">
          </div>
        </form>
      </div><!-- form-holder -->
    </div><!-- form-div -->

    <div id='result-div' class='row' style='margin-bottom: 30px;'>
      <p class='result-num' id='fetching' style='margin-left: 50px;'></p>
      <table border="0" cellpadding="0" width="100%" id='result-table'> </table>
      <p class='result-num' style='margin-left: 50px;'></p>
    </div><!-- result-div -->
  </div><!-- homepage-container -->

  <script src='https://code.jquery.com/jquery.js'></script>
  <script src='//cdn.jsdelivr.net/momentjs/latest/moment.min.js'></script>
  <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>
  <script src='//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js'></script>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.4/js/bootstrap-select.min.js'></script>
  <script type='text/javascript'>

    $(function() {
        var start = moment().year(2019).month(10).day(1);
        var end = moment().year(2019).month(10).day(5);
        function cb(start, end) {
            $('#reportrange span').html(start.format('MMMM DD, YYYY') + ' - ' + end.format('MMMM DD, YYYY'));
            getFirstPageValues();
        }
        $('#reportrange').daterangepicker({
            startDate: start,
            endDate: end,
            alwaysShowCalendars: true,
            opens: 'right',
            ranges: {
               'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
               'Last 7 Days': [moment().subtract(6, 'days'), moment()],
               'Last 30 Days': [moment().subtract(29, 'days'), moment()],
               'This Month': [moment().startOf('month'), moment().endOf('month')],
               'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
            }
        }, cb);
        cb(start, end);
    });

    function displayResults(result) {
        $('.result-num').empty();
        $('#result-table').empty();
        if ((result == 'ERROR: memory exceeded') || (result == 'ERROR: no results found for given parameters') || (result == 'ERROR: invalid date range')) {
            $('#result-table').append(result);
        } else {
            var sessions = result['sessions'];
            var totalSessions = result['total_sessions'];
            var totalPages = result['total_pages'];
            var currentPage = result['current_page'];

            console.log(sessions[0]);

            // setup table
            var table = $("<table id=\'result-table\' />").addClass('CSSTableGenerator');
            var row1 = $("<tr/>");
            row1.append($("<td/>").text("C1"));
            row1.append($("<td/>").text("C2"));
            row1.append($("<td/>").text("C3"));
            table.append(row1);

            for (var i=0, len=sessions.length; i<len; i++) {
                for (var j=0, len2=sessions[i].length; j<len2; j++) {

                    var c1 = sessions[i][j][0];
                    var c2 = sessions[i][j][1];
                    var c3 = sessions[i][j][2];

                    row = $("<tr/>").css("border-top", "1px solid #d6d6d6");
                    row.append($("<td/>").text(c1));
                    row.append($("<td/>").text(c2));
                    row.append($("<td/>").text(c3));

                    table.append(row);
                }
            }

            $('.result-num').append('Total sessions: ' + totalSessions.toString() + '<br/>');
            $('.result-num').append('Showing page ' + currentPage.toString() + ' out of ' + totalPages.toString() + '<br/>');
            if (currentPage == 1) {
                $('.result-num').append('<a href="" id="nextpage-btn" class="clickable" data-page="' + currentPage + '" data-action="next">>> Next Page</a>');
            } else if (currentPage == totalPages) {
                $('.result-num').append('<a href="" id="prevpage-btn" class="clickable" data-page="' + currentPage + '" data-action="prev"><< Prev Page</a>');
            } else {
                $('.result-num').append('<a href="" id="prevpage-btn" class="clickable" data-page="' + currentPage + '" data-action="prev">Prev Page <<</a> <a href="" id="nextpage-btn" class="clickable" data-page="' + currentPage + '" data-action="next">>> Next Page</a>');
            }
            $('#result-table').append(table);

            var nextPageBtn = document.querySelectorAll('#nextpage-btn');
            for (var i = 0; i < nextPageBtn.length; i++) {
                nextPageBtn[i].addEventListener('click', function(evt) {
                    goToNextPage(evt);
                });
            }

            var prevPageBtn = document.querySelectorAll('#prevpage-btn');
            for (var i = 0; i < prevPageBtn.length; i++) {
                prevPageBtn[i].addEventListener('click', function(evt) {
                    goToPrevPage(evt);
                });
            }
        }
    }

    function getFirstPageValues() {
        $('.result-num').empty();
        $('#result-table').empty();
        $('#fetching').append('Fetching results...');
        var formInputs = {
            'date': $('#date-field').html(),
            'selection1': $('#selection1-field').val(),
            'selection2': JSON.stringify($('#selection2-field').val()),
            'querypattern': $('#query-search-field').val(),
            'page_num': 1
        };
        $.get('/get-values',
               formInputs,
               displayResults
               );
    }

    $('#selection1-field').on('change', getFirstPageValues);
    $('#selection2-field').on('change', getFirstPageValues);
    $('#query-search-field').on('change', getFirstPageValues);

    function goToNextPage(evt) {
        evt.preventDefault();
        var formInputs = {
            'date': $('#date-field').html(),
            'selection1': $('#selection1-field').val(),
            'selection2': JSON.stringify($('#selection2-field').val()),
            'querypattern': $('#query-search-field').val(),
            'page_num': parseInt(document.getElementById('nextpage-btn').dataset.page) + 1
        };
        $.get('/get-values-ns',
               formInputs,
               displayResults
               );
    }

    function goToPrevPage(evt) {
        evt.preventDefault();
        var formInputs = {
            'date': $('#date-field').html(),
            'selection1': $('#selection1-field').val(),
            'selection2': JSON.stringify($('#selection2-field').val()),
            'querypattern': $('#query-search-field').val(),
            'page_num': parseInt(document.getElementById('prevpage-btn').dataset.page) - 1
        };
        $.get('/get-values',
               formInputs,
               displayResults
               );
    }

  </script>

</body>
</html>

TYZ
  • 8,466
  • 5
  • 29
  • 60

1 Answers1

0

What I want to do now is to be able to save (bookmark in browser) the url with updated parameters. Currently it is only saving http://localhost:5000/ which is the home page as well as where the result is shown, but I want it to be able to save url with updated parameters so that next time when I open the bookmarked page, it has the parameters applied already, something like: http://localhost:5000/get-values?c1=v1&c2=v2&c3=v3.

You'll need JavaScript. When you modify the value in the select box, you'll need to update the query string so that when you bookmark it, you're bookmarking the correct url.

Modifying a query string without reloading the page

Then in the /get-values route, you need to use request.args.get('c1'), etc. to get the values.

Seth Killian
  • 908
  • 9
  • 20
  • However, my main page ("/") and the endpoint for getting the value for the table ("/get-values") are different, even if I were able to do this, isn't the URL going to be "/get-values?c1=v1", and this will return a json object of data rather than showing the page. – TYZ Jan 03 '20 at 15:46