How to create a webapp with Python?
Hello,
I want to build a webapp that calls a model i have trained and displays the result on the HTML fornt end. I have semi-built the HTML and Python components but I am unsure how to connect these two via JavaScript.
Any help would be much appreciate.
Answers
-
Hello Christina,
We have an introductory tutorial here on the topic here: https://academy.dataiku.com/latest/tutorial/webapps/standard.html#load-data-in-the-python-backend.
Do not hesitate to come back with additional questions if needed.
-
Hello
Thank you very much for your reply. I have been reading the tutorial but still my code does not workWhat I am trying to do is to have a simple input text where the user types some text and they press the button, the text is sent to the Python code which calls a model to get a prediction on that text. I know how to call the model using Dataiku API (I am ok with Python) and do the HTML but i struggle to link these two via JavaScript.Here is my HTML code:<link rel="stylesheet" href="/static/public/styles/1.0.0/dku-styles.css" />
<h1>Prediction demo</h1>
<div class="fetch-dataset-container">
<section class="fetch-dataset-form">
<form id="form-prediction" novalidate>
<div class="field">
<label for="description">Item's description</i></label>
<input type="text" name="description" placeholder="this is a test" id="description">
</div>
<button type="button" class="main-blue-button" id="predict-button">Predict</button>
</form>
</section>
</div>
<div>
<section class="fetch-prediction-results">
<span id="message">Results will appear here.</span>
</section>
</div>Here is the JavaScript code:let predictButton = document.getElementById('predict-button');
let descriptionText = document.getElementById('description');
let messageContainer = document.getElementById('message');
let selectedDescription = {};
function displayMessage(messageText, messageClassname) {
messageContainer.innerHTML = messageText;
messageContainer.className = '';
if (messageClassname && messageClassname.length > 0) {
messageContainer.className = messageClassname;
}
}
function clearMessage() {
displayMessage('');
}
function displayFailure() {
displayMessage('Error occured', 'error-message');
}
function displayPrediction(dataFrame) {
let columnsNames = dataFrame.getColumnNames();
let line = '------------------------------';
let text = selectedDescription + '\n'
+ line + '\n'
+ dataFrame.getNbRows() + ' Rows\n'
+ columnsNames.length + ' Columns\n'
+ '\n' + line + '\n'
+ 'Columns names: \n';
columnsNames.forEach(function(columnName) {
text += columnName + ', ';
});
displayMessage(text);
}
predictButton.addEventListener('click', function(event) {
$.getJSON(getWebAppBackendUrl('/first_api_call'), function(data) {
clearMessage();
selectedDescription.name = document.getElementById('description').value;
dataiku.fetch(selectedDescription.name, function(dataFrame) {
selectedDescription.dataFrame = dataFrame;
displayPrediction(dataFrame);
}, function() {
displayFailure();
});
const output = $('<pre />').text('Backend reply: ' + JSON.stringify(data));
$('body').append(output)
};
return false;
});And this is my Python code, it is only a test at the moment just trying to call a dataset which I have enabled from the Settings:@app.route('/First_api_call')
def first_api_call():
max_rows = request.args.get('max_rows') if 'max_rows' in request.args else 500
mydataset = dataiku.Dataset("test_dataset")
mydataset_df = mydataset.get_dataframe(sampling='head', limit=max_rows)
# Pandas dataFrames are not directly JSON serializable, use to_json()
data = mydataset_df.to_json()
return json.dumps({"status": "ok", "data": data})Please any help would be much appreicated.Many thanksChristina -
Hi Christina,
I quickly tested your code and I think I have spotted 2 small typos in your JS code :
- The parameter of the getWebBackendUrl function needs to match the parameter @app.route instruction in the python code
- You have a missing ")" in your addEventListener (before the return false)
Here is what I get :
predictButton.addEventListener('click', function(event) { $.getJSON(getWebAppBackendUrl('/First_api_call'), function(data) { clearMessage(); selectedDescription.name = document.getElementById('description').value; dataiku.fetch(selectedDescription.name, function(dataFrame) { selectedDescription.dataFrame = dataFrame; displayPrediction(dataFrame); }, function() { displayFailure(); }); const output = $('<pre />').text('Backend reply: ' + JSON.stringify(data)); $('body').append(output) }); return false; });
In order to debug your javascript tool, you can use the developer tools of your browser where you will be able to see the error message in the console and much more.
-
Hi lpkronekThank you very much for your reply. I made the 2 changes you mention but my code still does not workAny help would be very much appreciated.Many thanksChristina
-
Can you share more about the error that you see ?
-
So I changed my Python code to the dollowing:
@app.route('/First_api_call')
def first_api_call():
max_rows = request.args.get('max_rows') if 'max_rows' in request.args else 500data = {'First Column Name': ['First value', 'Second value'],
'Second Column Name': ['First value', 'Second value']
}
mydataset_df = pd.DataFrame (data, columns = ['First Column Name','Second Column Name'])
data = mydataset_df.to_json()
return json.dumps({"status": "ok", "data": data})The method creates and returns a simple dataframe which I would like to see it displayed on the front end when i click the Predict button. However when i click the button i get the following error:
405 (Request method 'POST' not supported)
please see error.png for more details
And when i click on this error i get what you see on error2.png
am i doing something wrong?
-
The error that you are seeing is coming from the JS code.
it contains a call to :
dataiku.fetch(selectedDescription.name, function(dataFrame) { selectedDescription.dataFrame = dataFrame; displayPrediction(dataFrame); }, function() { displayFailure(); });
This call is trying to get the data from the dataset that has been inputted in the text box.
You probably want to remove this call has your goal seems to render the data return by the python code.
Also, when using datasets in a webapp, you need to grant access to them. You will find a security button on the settings of the webapp.
-
Thank you lpkronek,
I have removed these lines as you suggested.
Now when i click on the Predict button, although i dont see any error on the Console, i dont see the dataframe displayed on the page. The python code is executed though as i see the following on the Console tab:
2020-02-21 15:08:26,863 INFO 127.0.0.1 - - [21/Feb/2020 15:08:26] "GET /First_api_call HTTP/1.1" 200 -
I attach my code again. Your help is much appreciated
HTML
<div class="fetch-dataset-container">
<section class="fetch-dataset-form">
<form id="form-dataset" novalidate>
<div class="field">
<label for="description">Item's description</i></label>
<input type="text" name="description" placeholder="i.e. Testing of new circuits as per specification" id="description">
</div>
<button type="button" class="main-blue-button" id="predict-button">Predict</button>
</form>
</section>
</div>
<div>
<section class="fetch-dataset-results">
<span id="message">Results will appear here.</span>
</section>
</div>JS
let predictButton = document.getElementById('predict-button');
let descriptionText = document.getElementById('description');
let messageContainer = document.getElementById('message');
let selectedDescription = {};function displayMessage(messageText, messageClassname) {
messageContainer.innerHTML = messageText;
messageContainer.className = '';
if (messageClassname && messageClassname.length > 0) {
messageContainer.className = messageClassname;
}
}function clearMessage() {
displayMessage('');
}function displayFailure() {
displayMessage('Error occured', 'error-message');
}function displayPrediction(dataFrame) {
let columnsNames = dataFrame.getColumnNames();
let line = '------------------------------';
let text = selectedDescription + '\n'
+ line + '\n'
+ dataFrame.getNbRows() + ' Rows\n'
+ columnsNames.length + ' Columns\n'
+ '\n' + line + '\n'
+ 'Columns names: \n';
columnsNames.forEach(function(columnName) {
text += columnName + ', ';
});
displayMessage(text);
}predictButton.addEventListener('click', function(event) {
$.getJSON(getWebAppBackendUrl('/First_api_call'), function(data) {
clearMessage();
displayPrediction(data);
});
return false;
});Python
@app.route('/First_api_call')
def first_api_call():
max_rows = request.args.get('max_rows') if 'max_rows' in request.args else 500data = {'First Column Name': ['First value', 'Second value'],
'Second Column Name': ['First value', 'Second value']
}
mydataset_df = pd.DataFrame (data, columns = ['First Column Name','Second Column Name'])
data = mydataset_df.to_json()
print (mydataset_df)
return json.dumps({"status": "ok", "data": data}) -
An update....
I am calling the displayPrediction() method from the predictButton.addEventListener() and the error i get says:
dataFrame.getColumnNames is not a function at displayPrediction
Is it something wrong with Dataiku API?
-
Hello Christina,
The data that you receives in your event listener is the json document that your python code return from first_api_call() and not a dataiku object. You need to adapt the displayPrediction function to process the json document.
-
Hello,
I managed to display what i get from Python
Do you know how can i pass a value from the front end to my Python code via Javascript?
Many thanks
Christina
-
Hello @lpkronek
Apologies for asking so many questions. I read the Dataiku literature and i have modified my code as per below
JS
let descriptionText = document.getElementById('description');
function displayMessage(messageText, messageClassname) {
messageContainer.innerHTML = messageText;
messageContainer.className = '';
if (messageClassname && messageClassname.length > 0) {
messageContainer.className = messageClassname;
}
}function clearMessage() {
displayMessage('');
}predictButton.addEventListener('click', function(event) {
$.getJSON(getWebAppBackendUrl('First_api_call'), {description:descriptionText})
.done(
function(data) {
clearMessage();
displayMessage(data.description);
}
);
return false;
});Python
from flask import request
@app.route('/First_api_call')
def first_api_call():
userdescription = str(request.args.get("description"))
max_rows = request.args.get('max_rows') if 'max_rows' in request.args else 500return json.dumps({"status":"ok", "description":userdescription})And in my HTML page i have got an input with id='description'
When i click the predict button i get the following error:
Uncaught RangeError: Maximum call stack size exceeded as per photo attached
Any help is much appreciated
Kind regards,
Christina
-
Hello,
It looks like a JS problem. Do not hesitate to look at dedicated resources on the topic to get more accurate support.
I'm under the impression that in your variable descriptionText you have the whole element and not the text value. In addition, you need to add quotes around "description". Your call to your python backend should look something like this :
$.getJSON(getWebAppBackendUrl('First_api_call'), {"description" : descriptionText.value})
$.getJSON(getWebAppBackendUrl('First_api_call'), {"description" : descriptionText.value})
-
Hi @lpkronek
,That was the probelm, thank you so much for your help.
I think i can take it from here.
again, thank you very much for your support!
Kind regards,
Christina