0

Preface: I've already looked at similar questions and tried to extrapolate code/concepts to help resolve with no luck.

-https://stackoverflow.com/questions/43645790/passing-javascript-variable-to-python-flask

-https://stackoverflow.com/questions/10313001/is-it-possible-to-make-post-request-in-flask

Please do not just close because it's a "similar" question.

I'm designing a simple flask app which can be viewed live here:

https://flaskapp.gullp.repl.co/

enter image description here

There is a chart using chart.js and underneath resides a Pandas dataframe.

Goal: When you click a bar on the chart, it filters the dataframe. Concretely, if I click the "Big Home" bar, the dataframe below will only show values where the label = "Big Home".

Attempts:

Trying to use Ajax/jQuery to pass the value back to Python and have it filter the dataframe accordingly.

I've been able to get the corresponding clicked value (i.e when you click the first bar it will output "big home") My thought is that i can take this value, pass it back to python, and have it filter the dataframe, and subsequently reload the dataframe using ajax on the site.

Current attempt = bar is clicked -> corresponding value is saved to javascript var -> the javascript var is loaded into json string -> json string is then loaded back to flask -> flask rerenders dataframe.

@app.route("/", methods=["GET", "POST"])
def home():
    labels = chart_df['label'].tolist()
    values = chart_df['value'].tolist()
    return render_template('index.html', values=values, labels=labels, column_names=df.columns.values, row_data=list(df.values.tolist()), zip=zip)

    if request.method == "POST":
        data = request.get_json()
        return jsonify(status="success", score=data)
        
    else:
        return render_template('index.html', score='', values=values, labels=labels, column_names=df.columns.values, row_data=list(df.values.tolist()), zip=zip)

the onclick logic i'm using to grab value from graph and pass it to variable:

             //below allows you to click the chart and get the respective value. you will pass this value to python
                ,onClick: function(c,i) {
      
                e = i[0];
                //console.log(e._index)
                var x_value = this.data.labels[e._index];
                var y_value = this.data.datasets[0].data[e._index];
               // console.log(x_value);
                //console.log(y_value);
                console.log('you clicked the graph, now the x value is = ' + x_value)
        
              } 
          }
      });

//below puts the clicked value into a json format so we can pass it back to python/flask to reload the dataframe table that resides below the chart/graph. I'm attempting to pass it via ajax.

//start off with default chart values
      var x_value = '444'
      console.log('when the page loaded we defaulted x-value = ' + x_value + ' now try clicking one of the bars below.')
      var ctx = document.getElementById('myChart').getContext('2d');



    var chart_clicked_data = { 'score' : x_value}
      console.log('this is the json chart clicked data that we want to pass back to flask = ' + JSON.stringify(chart_clicked_data) )

the ajax im attempting to implement

     $.ajax({
    type: "POST",
    url: "/",
    contentType: "application/json",
    data: JSON.stringify(chart_clicked_data),
    dataType: "json",
    success: function(response) {
        console.log(response);
    },
    error: function(err) {
        console.log(err);
    }
});

Problem:

I just learned flask, javascript, & jquery this week, so go easy on me, but I'm unable to get the entire process to work.

enter image description here

I'm suspecting that i have no way to trigger the post method? Not sure how to accomplish this.

My entire code is running here (can be edited here too) -> https://repl.it/join/rbkobiqi-gullp

app.py (application factory)

import os
import random
import pandas as pd
import datetime as dt
from flask import Flask, render_template, url_for, request, jsonify


'''dataframe for table/graph'''
df = pd.DataFrame({'label': ['Big Home','Big Home', 'Big Home',  'Medium Home', 'Medium Home', 'Small Home'], 
                   'value': [10, 9, 9, 7, 6, 2]})

'''dataframe to display graph'''
chart_df = df.copy()
chart_df = chart_df.groupby("label").count().reset_index()



'''Application Factory'''
app = Flask(  # Create a flask app
    __name__,
    template_folder='templates',  # Name of html file folder
    static_folder='static'  # Name of directory for static files
)

@app.route("/", methods=["GET", "POST"])
def home():
    labels = chart_df['label'].tolist()
    values = chart_df['value'].tolist()
    return render_template('index.html', values=values, labels=labels, column_names=df.columns.values, row_data=list(df.values.tolist()), zip=zip)

    if request.method == "POST":
        data = request.get_json()
        return jsonify(status="success", score=data)
        
    else:
        return render_template('index.html', score='', values=values, labels=labels, column_names=df.columns.values, row_data=list(df.values.tolist()), zip=zip)



@app.route('/tabletest')
def hello_world():
    return chart_df.to_html(header="true", table_id="table")


if __name__ == "__main__":  # Makes sure this is the main process
    app.run( # Starts the site
        host='0.0.0.0',  # EStablishes the host, required for repl to detect the site
        port=random.randint(2000, 9000),  # Randomly select the port the machine hosts on.
    debug=True
    )

index.html (under templates)

 <!doctype html>
 <html>
   <head>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
      <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
   </head>
  <body>

 <center><canvas id="myChart" width="600" height="200"></canvas>

    <script>
       var ctx = document.getElementById('myChart').getContext('2d');
       Chart.defaults.global.responsive = false;


      var myChart = new Chart(ctx, {
          type: 'bar',
          data: {
              labels: /* {{ labels }}*/ ['Big Home', 'Medium Home', 'Small Home'] ,
              datasets: [{
                  label: 'count per label',
                  data: /*{{ values }} */ [3,2,1]
              }]
          },
          options: {
              scales: {
                  yAxes: [{
                      ticks: {
                          beginAtZero: true
                      }
                  }]
              }
              //below allows you to click the chart and get the respective value. you will pass this value to python
                ,onClick: function(c,i) {
      
                e = i[0];
                //console.log(e._index)
                var x_value = this.data.labels[e._index];
                var y_value = this.data.datasets[0].data[e._index];
               // console.log(x_value);
                //console.log(y_value);
                console.log('you clicked the graph, now the x value is = ' + x_value)
        
              } 
          }
      });

//below puts the clicked value into a json format so we can pass it back to python/flask to reload the dataframe table that resides below the chart/graph. I'm attempting to pass it via ajax.

//start off with default chart values
      var x_value = '444'
      console.log('when the page loaded we defaulted x-value = ' + x_value + ' now try clicking one of the bars below.')
      var ctx = document.getElementById('myChart').getContext('2d');



    var chart_clicked_data = { 'score' : x_value}
      console.log('this is the json chart clicked data that we want to pass back to flask = ' + JSON.stringify(chart_clicked_data) )

      $.ajax({
    type: "POST",
    url: "/",
    contentType: "application/json",
    data: JSON.stringify(chart_clicked_data),
    dataType: "json",
    success: function(response) {
        console.log(response);
    },
    error: function(err) {
        console.log(err);
    }
});





      </script>


<!-- Table Logic Below -->

<br>
<br>
     <div> Your current score selected is = {{score}} </div>
<br>
<br>
Trying to make it so when you click "Big Home" for example, the data table will only show values for "big home"
<br>
<br>
      <table border='1'>
        <thead>

          <tr>
            {% for col in column_names %}
            <th>
            
              {{col}}
             
            </th>
            {% endfor %}
          </tr>

        </thead>
        <tbody>
          {% for row in row_data %}
          <tr>
            {% for col, row_ in zip(column_names, row) %}
            <td>{{row_}}</td>
            {% endfor %}
          </tr>
          {% endfor %}

         
        </tbody>
  
      </table>

</center>

</body>
</html>


mikelowry
  • 1,307
  • 4
  • 21
  • 43
  • What error are you getting on the server side? Have you verified that the server is receiving a POST request? What is is seeing when that happens? – Matt Morgan Jan 29 '21 at 21:50
  • An HTTP 500 error means that the problem is with your server-side code. – luek baja Jan 29 '21 at 21:58
  • @MattMorgan eems like on the repl.it version the 500 is not an issue -- im just missing something in regards to passing back and forth the javascript variable to python – mikelowry Jan 30 '21 at 00:56
  • @luek baja seems like on the repl.it version the 500 is not an issue -- im just missing something in regards to passing back and forth the javascript variable to python – mikelowry Jan 30 '21 at 00:56
  • You really need to add some server side logging...please re-read my first comment. – Matt Morgan Jan 30 '21 at 13:56
  • @MattMorgan thanks for gettitng back to me, i've updated the code to get rid of the 500 error. seems like tthe variable im trying to pass back to python is not getting updated. – mikelowry Jan 30 '21 at 19:37

1 Answers1

0

Not sure who added this logic, but it worked...

Notes: Solves the problem in a different way than initially wanted. This just hides the elements, rather than rerunning the pandas dataframe with a filter.

   document.querySelectorAll("tr").forEach((tr, i) => {
                  if(i === 0){
                    // skip column names 
                    return
                  }
                  if(tr.children[0].innerText != x_value){
                    tr.style.display = 'none';
                  }else {
                    tr.style.display = 'table-row';
                  }
                }
mikelowry
  • 1,307
  • 4
  • 21
  • 43