0

I am completely new to this so apologies if the question is a bit vague. I need to know where to start.

I am trying to make a simple setup with the following flow:

  1. User uploads a CSV file to HTML form, file is sent (see below) to the server.
  2. Server is running a python script that takes the CSV and turns it into an array.
  3. The python script runs functions on the array and makes a new array of information.
  4. This new array is sent back to the HTML webpage as an array that will be shown to the user through Javascript DOM.

I know how to do everything on the front-end and the processing in python. My question is, how would the file be sent to the server (would the form be submitted with GET or POST, etc.) and how can I make this like the diagram attached? Any guidance is appreciated.

Edit 5/10/20

My front-end code would look like this:

function sendfile(){
  var file = document.getElementById("uploadfile").value;
  $.post('localhost:22222', //this is where I am hosting the server
         {SendData: file},
          function(returnedArray) {
            var newData = //the response from python server, in array format;
            //set up different variables from the array
            //update the returned_info div with the new data
          })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
  Upload your file here:
  <input type="file" id="uploadfile">
  <button onclick="sendfile()">Submit!</button>
</form>

<div id="returned_info">
  
</div>

Is the above code correct, or is there anything I can improve?

How would I properly receive the request in the python server, so it can convert that CSV file to an array, like this? After the server is done getting data and making calculations from this original array, how can I have it send another array back to the HTML page to be used by jQuery callback (See code)?

I am using this reference.

enter image description here

atultw
  • 921
  • 7
  • 15
  • Could you provide more information on what kind of framework you are using for your python backend? For example, Django, Flask, etc. Also, could you provide th HTML for sending the file as well as the relevant backend code? – Moon Cheesez May 10 '20 at 03:43
  • 1
    `POST` can send more data. For rest you have to learn JavaScript or use `jQuery.post()`. For server AJAX requests is like normal request so there is no difference - only it doesn't have to send full HTML but only part of HTML or JSON data and then JavaScript in browser has to get it and replace part of HTML. – furas May 10 '20 at 09:03
  • @MoonCheesez I added my js and html code. I will be using flask, is this a good way to get started: https://flask.palletsprojects.com/en/1.1.x/quickstart/#accessing-request-data? Thank you! – atultw May 10 '20 at 19:51
  • 1
    first you should check [file upload](https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/) and build it without JavaScript and later add JavaScript. BTW: and you need `name=` in `` to access it in `Flask` – furas May 10 '20 at 20:56

1 Answers1

2

I assumed you don't use jQuery and build it only with pure JavaScript.

I use render_template_string instead of render_template only to make it simpler for tests - you can put all in one file and run it.

Url / display form with JavaScrip which use XHR/AJAX to send file to /upload and get result

Url /upload gets file from form and generate HTML with result which sends back to browser.

import os
from flask import Flask, request, render_template, render_template_string
from werkzeug.utils import secure_filename
import pandas as pd


app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = '.'


@app.route('/')
def index():
    return render_template_string('''<!DOCTYPE html>

<html>

<head>
    <title>Upload</title>
</head>

<body>

<form id="my_form" method="POST" action="/upload" enctype="multipart/form-data">
    <input type="file" name="my_file" />
    <input type="submit" id="my_button" value="Send" />
</form>

<div id="result"></div>

<script>
var my_form = document.getElementById("my_form");
var my_file = document.getElementById("my_file");
var my_button = document.getElementById("my_button");
var result = document.getElementById("result");

my_button.onclick = function(event){

    var formData = new FormData(my_form);
    formData.append('my_file', my_file);

    var xhr = new XMLHttpRequest();
    // Add any event handlers here...
    xhr.open('POST', '/upload', true);

    xhr.addEventListener('load', function(e) {
        // HTTP status message
        //console.log(xhr.status);
        // request.response will hold the response from the server
        //console.log(xhr.response);
        result.innerHTML = xhr.response;
    });

    xhr.send(formData);

    event.preventDefault(); 
};
</script>

</body>

</html>''')


@app.route('/upload', methods=['POST'])
def upload():

    #if request.method == 'POST': # no need if `methods=['POST']` 
    #if request.is_xhr():  # `is_xhr()` doesn't exist any more

    # get file     
    #print(request.files)
    temp_file = request.files.get('my_file')  #  `<input ... name="my_file">`
    print(temp_file.filename)

    # save it in 
    save_name = secure_filename(temp_file.filename)
    full_path = os.path.join(app.config['UPLOAD_FOLDER'], save_name)
    temp_file.save(full_path)

    #data = pd.read_csv(full_path)
    data = pd.DataFrame({
        'Text': ['abc', 'def', 'ghi'],
        'Number': [123, 456, 789],
    })
    print(data)

    # convert dataFrame to HTML 
    table = data.to_html() 
    #table = data.to_html(classes="my_table", header=True) 

    # send generated HTML
    return table

    # or

    # send template with embed HTML. It  needs `|safe` to display HTML correctly
    #return render_template_string('''DataFrame:</br>{{ table|safe }}</br>''', table=table)


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

EDIT: version with jQuery needs only changes in <script> in HTML.

@app.route('/')
def index():
    return render_template_string('''<!DOCTYPE html>

<html>

<head>
    <title>Upload</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>

<body>

<form id="my_form" method="POST" action="/upload" enctype="multipart/form-data">
    <input type="file" id="my_file" name="my_file" />
    <input type="submit" id="my_button" value="Send" />
</form>

<div id="result"></div>

<script>
//$("#my_form").submit(function(event){     
// or
$("#my_button").click(function(event){     
    $.post({
        url: '/upload',
        data: new FormData($("#my_form")[0]),
        processData: false,
        contentType: false, // Using FormData, no need to process data.
    }).done(function(data){
        $('#result').html(data);
    }).fail(function(){
        console.log("An error occurred, the files couldn't be sent!");
    });

    event.preventDefault();
});    

// warning: when I was using `alert(event)` or `alert($("#my_form")[0])` to see values in Firefox then code doesn't work
</script>

</body>

</html>''')

BTW: in Firefox JavaScript code didn't work when I try to use alert(event) or alert($("#my_form")[0])

furas
  • 134,197
  • 12
  • 106
  • 148
  • Thank you! I can see how this one-file setup is useful for testing. If I ever wanted to keep the HTML file separate, would that work too, if it is in the same directory? – atultw May 10 '20 at 21:53
  • 1
    if you what to keep HTML in separated files then you have to put them in subfolder `templates` because it is Flask's default folder for templates. And you use only filename without folder name `templates` - `return render_template('index.html')` or if you have `templates/other/index.html` then `render_template('other/index.html')` – furas May 10 '20 at 21:57
  • I have tried this and it works exactly how I wanted it to. Thank you for the guidance! – atultw May 10 '20 at 22:24