Trigger onchange via Selenium from Python - python

I have a Django webapp displaying a form. One of the fields is a FileField, defined via the Django model of the form:
From models.py:
class Document(models.Model):
...
description = models.CharField(max_length=100, default="")
document = models.FileField(upload_to="documents/", max_length=500)
The document file_field has an onchange ajax function attached that will parse the uploaded filename, check some database stuff depending on it, and populate other fields on the html-page with the results.
From forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ("document",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["customer"] = forms.CharField(initial="", required=True)
self.fields["output_profile"] = forms.CharField(initial="", required=True)
self.fields["document"].widget.attrs[
"onchange"
] = "checkFileFunction(this.value, '/ajax/check_file/')"
From urls.py:
urlpatterns = [
#...
path("ajax/check_file/", views.check_file, name="ajax_check_file")
]
From views.py:
def check_file(request):
full_data = {"my_errors": []}
my_path = pathlib.Path(request.GET.get("file_path").replace("\\", os.sep))
# parse customer ID from file_path
# get data of customer from db
# assemble everything into full_data
return JsonResponse(full_data)
This is the full html page as displayed (copied from Chrome => show source and cleaned up the indentation & whitespaces some):
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="/static/css/main.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/dataTables.jqueryui.css"/>
<script src="/static/js/jquery.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/dataTables.jqueryui.js"></script>
<title>
Convert RES FILE
</title>
</head>
<body>
<header id="header">
<section class="top_menu_left">
Login
<a>&nbsp|&nbsp</a>
Logout
<a>&nbsp|&nbsp</a>
Edit User
<a>&nbsp|&nbsp</a>
Register
</section>
<section class="top_menu_right">
About Us
<a>&nbsp|&nbsp</a>
Contact Us
<a>&nbsp|&nbsp</a>
Submit an issue
<a>&nbsp|&nbsp</a>
Documentation
<a>&nbsp|&nbsp</a>
Home
</section>
<div id="topbanner" >
<img src="/static/banner_small.png" alt="" width="100%" height="150"/>
</div>
</header>
<aside id="leftsidebar">
<section class="nav_account">
<h4>Submit a New Request</h3>
<ul>
<li>Get Typing Results</li>
<li>Compare Typing Results</li>
<li>Convert Typing Format</li>
</ul>
</section>
<section class="nav_tools">
<h4>View Your Requests</h3>
<ul>
<li>View My Submissions</li>
</ul>
</section>
</aside>
<section id="main">
<p> </p>
<h2>Convert Typing Results to Format of Choice</h2>
<p> </p>
<h3>Upload a file to our database</h3>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrfmiddlewaretoken" value="qqIKcsAynuE35MQ37dvjF5XeIyfcEbHb3wjtgygGZaigQReNxHLQewoDKcEb8Roj">
<div id="div_id_document" class="form-group">
<label for="id_document" class=" requiredField">
Document<span class="asteriskField">*</span>
</label>
<div class="">
<input type="file" name="document" onchange="checkFileFunction(this.value, '/ajax/check_file/')" class="clearablefileinput form-control-file" required id="id_document">
</div>
</div>
<input type="hidden" id="id_description" name="description" value="">
<p> </p>
<div id="div_id_customer" class="form-group">
<label for="id_customer" class=" requiredField">
Customer<span class="asteriskField">*</span>
</label>
<div class="">
<input type="text" name="customer" readonly class="textinput textInput form-control" required id="id_customer">
</div>
</div>
<div id="div_id_output_profile" class="form-group">
<label for="id_output_profile" class=" requiredField">
Output profile<span class="asteriskField">*</span>
</label>
<div class="">
<input type="text" name="output_profile" readonly class="textinput textInput form-control" required id="id_output_profile">
</div>
</div>
<div class="form-group">
<div id="div_id_notify_me" class="form-check">
<input type="checkbox" name="notify_me" style="width:15px;height:15px;" class="checkboxinput form-check-input" id="id_notify_me">
<label for="id_notify_me" class="form-check-label">
Notify me
</label>
</div>
</div>
<p>
<button class="linkbutton" type="submit" id="submit_btn">Convert</button>
<button id="create-book" class="linkbutton" type="button" name="button" style="float: right;">Create an Output Profile</button>
</p>
</form>
<div class="modal fade" tabindex="-1" role="dialog" id="modal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content" id="form-modal-content">
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="modal-check-res-file">
<div class="modal-dialog" role="document">
<div class="modal-content" id="form-modal-content-check-res-file">
</div>
</div>
</div>
<script>
var formAjaxSubmit = function(form, modal) {
$(form).on('submit', function (e) {
e.preventDefault();
console.log("submitting...");
var my_val = $("#id_profile_name").val();
var this_val = $("#confirm_save").val();
var res = this_val.split(",");
var this_val_contains_my_val = (res.indexOf(my_val) > -1);
if (this_val_contains_my_val === true) {
var conf = confirm("Are you sure want to overwrite an exsisting profile?");
}else {var conf = true;};
if (conf === true) {
$.ajax({
type: $(this).attr('method'),
url: "/new_customer_profile/",
data: $(this).serialize(),
success: function (xhr, ajaxOptions, thrownError) {
if ( $(xhr).find('.invalid-feedback').length > 0 ) {
$(modal).find('.modal-content').html(xhr);
formAjaxSubmit(form, modal);
} else {
$(modal).find('.modal-content').html(xhr);
}
},
error: function (xhr, ajaxOptions, thrownError) {
}
});
};
});
};
$('#create-book').click(function() {
console.log("hhallo");
$('#form-modal-content').load('/new_customer_profile/', function () {
var iam_alive = document.getElementById("modal");
// check if iam_alive is defined, this is required if a session expired -> in that case the modal is lost and it would redirect to an almost empty page.
if (iam_alive) {
$('#modal').modal('toggle');
formAjaxSubmit('#form-modal-body form', '#modal');
}
// if not iam_alive: redirect to login page
else {
window.location.replace('/accounts/login/');
}
});
});
$('#check-res-file').click(function() {
console.log("hhallo hier unten jetzt");
$('#form-modal-content-check-res-file').load('/check_res_file/', function () {
$('#modal-check-res-file').modal('toggle');
//formAjaxSubmit('#form-modal-body form', '#modal');
});
});
</script>
<script type="text/javascript">
$(document).ready(function() {
var mycell = document.getElementById("create-book");
mycell.style.display = "none";
});
</script>
<script>
function checkFileFunction(myfile, url) {
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/hr/ajax/load-cities/)
data: {"file_path": myfile},
dataType: 'json',
success: function (x) {
if (x.my_errors.length == 0) {
$('#id_customer').val(x.customer_name);
$('#id_output_profile').val(x.customer_profile);
$('#id_description').val(x.customer_file);
}else{
$('#id_customer').val("");
$('#id_customer').val("");
$('#id_output_profile').val("");
alert(x.my_errors);
var showme = function myFunction() {
var mycell = document.getElementById("create-book");
mycell.style.display = "block";
};
showme();
}
},
});
}
</script>
</section>
</body>
</html>
Now, I'm trying to test this with pytest via Selenium.
I can send the file path to the field via send_keys(). However, the onchange event seems not to be triggered. (It does work fine when I select the file manually.)
file_field = self.driver.find_element(By.NAME, "document")
file_field.clear()
file_field.send_keys(str(path/to/myfile))
This will register the file fine and it will be uploaded, but the onchange function never happens.
I have searched and it seems others also have encountered the problem of send_keys not triggering the onchange event. But I have not been able to implement any of the suggested solutions in my Python code. (I have not written the Django code for this app, I'm just the tester and my grasp on Django and javascript is not very good, yet. My native programming language is Python.)
The only solution I understood how to implement was sending a TAB or ENTER afterwards (file_field.send_keys(Keys.TAB)) to change the focus, but that triggers an
selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: File not found
(The file I enterted does exist, the path is fine. I can successfully call .exists() on it.)
Simply selecting a different element after send_keys to shift the focus (i.e., customer_field.click()) does not trigger the onchange function of file_field, either.
How can I trigger an onchange event via Selenium from Python? Or otherwise make sure it is triggered?

First of all, your onchange specification is a bit kludgy and would be preferably specified as:
<input type="file" name="document" onchange="checkFileFunction(this.value, '/ajax/check_file/');">
I am using Selenium with the latest version of Chrome and its ChromeDriver under Windows 10 and have no problems with the onchange event being taken. This can be demonstrated with the following HTML document. If the onchange event is taken, then it should create a new div element with id 'result' that will contain the path of the filename selected:
File test.html
<!doctype html>
<html>
<head>
<title>Test</title>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<script>
function checkFileFunction(value)
{
const div = document.createElement('div');
div.setAttribute('id', 'result');
const content = document.createTextNode(value);
div.appendChild(content);
document.body.appendChild(div);
}
</script>
</head>
<body>
<input type="file" name="document" onchange="checkFileFunction(this.value);">
</body>
</html>
Next we have this simple Selenium program that sends a file path to the file input element and then waits for up to 3 seconds (with the call to driver.implicitly_wait(3)) for an element with an id value of 'result' to be found on the current page and then prints out the text value. This element will only exist if the onchange event occurs:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("headless")
options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(options=options)
try:
# Wait up to 3 seconds for an element to appear
driver.implicitly_wait(3)
driver.get('http://localhost/test.html')
file_field = driver.find_element_by_name("document")
file_field.clear()
file_field.send_keys(r'C:\Util\chromedriver_win32.zip')
result = driver.find_element_by_id('result')
print(result.text)
finally:
driver.quit()
Prints:
C:\Util\chromedriver_win32.zip
Now if your driver is different and that is the reason why the onchange event is not occurring and you do not wish to or cannot switch to the lastest ChromeDriver, then you can manually execute the function specified by the onchange argument. In this version of the HTML file, I have not specified the onchange argument to simulate the situation where specifying it has no effect:
File test.html Version 2
<!doctype html>
<html>
<head>
<title>Test</title>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<script>
function checkFileFunction(value)
{
const div = document.createElement('div');
div.setAttribute('id', 'result');
const content = document.createTextNode(value);
div.appendChild(content);
document.body.appendChild(div);
}
</script>
</head>
<body>
<input type="file" name="document">
</body>
</html>
And the new Selenium code:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("headless")
options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(options=options)
try:
# Wait up to 3 seconds for an element to appear
driver.implicitly_wait(3)
driver.get('http://localhost/test.html')
file_field = driver.find_element_by_name("document")
file_field.clear()
file_path = r'C:\Util\chromedriver_win32.zip'
file_field.send_keys(file_path)
# Force execution of the onchange event function:
driver.execute_script(f"checkFileFunction('{file_path}');")
result = driver.find_element_by_id('result')
print(result.text)
finally:
driver.quit()
Update
I guess you missed <script src="/static/js/jquery.js"> in the <head> section, which appears to be jQuery. But I would have thought that with this script tag being in the <head> section that jQuery would have to be loaded by time Selenium found the file element. So I confess that your getting a javascript error: $ is not defined is somewhat surprising. I can only suggest now that you try loading it manually as follows as detailed in the code below.
I have re-iterated the 3 things you should try in order in the code below moving on to the next approach if the previous one does not work:
Give jQuery time to load before sending keystrokes to eliminate the $ not defined error.
Manually loading jQuery before sending keystrokes.
Manually executing the checkFileFunction.
# 1. Give jQuery time to load before sending keystrokes:
import time
time.sleep(3)
# 2. if the above sleeping does not work,
# remove the above call to sleep and manually load jQuery.
# Specify where the home page would be loaded from:
document_path = '/home/usr/account/public_html' # for example
jQuery_path = document_path = '/static/js/jQuery.js'
with open(jQuery_path, 'r') as f:
jQuery_js = f.read()
self.driver.execute_script(jQuery_js)
# Send keystrokes:
file_path = str(path/to/myfile)
file_field.send_keys(file_path)
# 3. Execute the following if the onchange doesn't fire by itself:
self.driver.execute_script(f"checkFileFunction('{file_path}', '/ajax/check_file/');")

As it turns out, the actual problem was that my manual test were done on the Django app served via python manage.py runserver. This calls some hidden Django magic, including collecting the statics files (css, jQuery.js etc.) under the hood.
I now learned that, in order to serve a Django app on a proper server, one needs to first call python manage.py collectstatic. This will generate a static folder in the parent directory, which contains all the static files and also an explicit jQuery.js.
Then, when Selenium is run, it will find that static folder and the jQuery.js file therein. And then, everything works as expected, including onchange.
So the problem was, that this parent static folder was missing, which I never saw because serving the website via python manage.py runserver doesn't need it.

Related

data given in the Ajax is not reaching in the views.py file and hence alert function is not working

I tried to reset password using Ajax in Django.For that, firstly I took email id from input box using a function and send it to views.py file using Ajax.In views.py file,there is a code for receiving email-id using GET method and check whether the email-id is available or not in the table.If available send the email-id back to html file using HttpResponse and display it using alert function.But it does not work properly.Can anyone suggest a solution for this.
HTML file :
<!DOCTYPE html>
<html lang="en">
{% load static %}
<head>
<meta charset="UTF-8">
<title>Index Page</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link href="{% static 'styles/style.css' %}" rel="stylesheet"/>
<script>
function getpass(){
let username=$("#uname").val()
$.ajax({
url : "{% url 'passwordreset' %}",
type : "GET",
data : {username : username},
success :function(data){
alert(data);
}
})
}
</script>
</head>
<body>
<section class="sflog" id="sflog">
<div class="container-fluid">
<div class="row">
<div class="col-12" id="std">
<form method="GET" action="">
{%csrf_token%}
<center>
<h3>Password <span>Reset</span></h3><br><br>
</center>
<div id="result"></div>
<label style="color:white;padding-left:13%;">Enter Your Username</label>
<center>
<input type="text" id="uname" name="username" placeholder="Username" required><br>
</center>
<button type="submit" style="margin-left:12%;" onclick="getpass()" name="login">Submit</button><br><br><br><br>
</form>
</div>
</div>
</div>
</section>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
urls.py
from django.urls import path
from . import views
urlpatterns=[
path('User/Forgot_Password',views.user_forgot_password,name="ufpassword"),
path('User/Forgot_Password/Reset',views.user_forgot_password_reset,name="passwordreset"),
]
views.py
from django.shortcuts import render,redirect,get_object_or_404
from user.models import clientlogin
from django.http import HttpResponse
def user_forgot_password(request):
return render(request,"forgot_password.html")
def user_forgot_password_reset(request):
if request.method == 'GET':
un = request.GET.get('username')
print(un)
try:
user = get_object_or_404(clientlogin, Username=un)
return HttpResponse(user.Username)
except:
return HttpResponse("No user registered with this Email ID")
else:
return render(request, "forgot_password.html")
First change the order in urls
urlpatterns=[
path('User/Forgot_Password',views.user_forgot_password,name="ufpassword"),
path('User/Forgot_Password/Reset',views.user_forgot_password_reset,name="passwordreset")
]
Because when request arrives, it will match the first always so either edit it to be
re_path('User/Forgot_Password$',views.user_forgot_password,name="ufpassword")
This way your reset URL won't match the forget password.
As some said put JQuery before $ sign.

Cisco WebEx: Problem generating a new code for a new set of tokens using Python 2.7

Questions at bottom.
Explanation:
We're trying to automate different aspects of the Cisco WebEx API. For instance, getting all devices and device data from the API and then presenting it. The devices could show connectivity status, locations and much more. This is just an example.
In order to get access to the WebEx API, you will need to do the following:
Login to your business account (https://developer.webex.com).
Then enter your password:
After this, all logins are redirected through:
When you are logged in, you can use the documentation and browse through the API reference. Here you can also try out all the functions and this is using a personal token, which, as far as i know, cannot be exported or seen anywhere on the site. You can copy it and use it, but it expires after 24 hours. I thought maybe you could use Python to log into your account and continously refresh your personal token, but this does not seem like a good solution.
And therefore, this brings me to:
Create an integration, that will eventually give you the tokens needed for the API access.
So here I will show how you can create an integration and thereby get the codes and tokens needed for the API access.
Create a new app and then select integration.
Fill out all information and then click add integration.
Generate a code for requesting a token by accepting the terms and conditions.
After adding, you will be taken to the app integration page and here you can see all the information needed to generate a code, which will be used to generate a token set.
As you can see in the black box, this is the URL you need to copy/paste into a browser and then accept the terms and conditions, before you can generate a code. This is the result of that:
Checkbox says: "only ask when new permissions are requested"
After clicking accept, you are redirected to the URL specified when creating the app integration. This website does not exist, but it will still show the code in the URL:
Copying the code you can now generate a token set using the following code and information from the app:
Code:
import requests, json
clientID = "C7aa20b0a57cae4127d1e08b15e3a94efacd5c7bb3e1a70ceed4308903a5b807b"
secretID = "b52db3ec6ad8622d0e0e01a0572bac982b214076308ea2e73a566fcf848c9369"
redirectURI = "https://www.your-oauth-website-here.dk/oauth"
def get_tokens(code):
"""Gets access token and refresh token"""
print("code:", code)
url = "https://api.ciscospark.com/v1/access_token"
headers = {'accept':'application/json','content-type':'application/x-www-form-urlencoded'}
payload = ("grant_type=authorization_code&client_id={0}&client_secret={1}&"
"code={2}&redirect_uri={3}").format(clientID, secretID, code, redirectURI)
req = requests.post(url=url, data=payload, headers=headers)
results = json.loads(req.text)
print(results)
access_token = results["access_token"]
refresh_token = results["refresh_token"]
return access_token, refresh_token
test = get_tokens("MWU4YTJmYTgtNjllMy00YjAzLTg0NTQtMTRkYTRiMmIxMzZkNWEzNzRkZDQtYTNk_PF84_33672567-1029-48fb-ba77-7fe2001ee897") # CODE
Result:
code: MWU4YTJmYTgtNjllMy00YjAzLTg0NTQtMTRkYTRiMmIxMzZkNWEzNzRkZDQtYTNk_PF84_33672567-1029-48fb-ba77-7fe2001ee897
{'access_token': 'OTU5MWNhYTItOWNiZC00MWU1LThlZDktNjRlYjI5OGIyYjNmNjI2M2U2MzgtNjAz_PF84_33672567-1029-48fb-ba77-7fe2001ee897', 'expires_in': 1209599, 'refresh_token': 'MGQ4MWRmMzAtYjQyNi00Mzk1LWI0MzAtMmRkMGIzMWQ3ZDVjNzQwZDM3N2YtMWIw_PF84_33672567-1029-48fb-ba77-7fe2001ee897', 'refresh_token_expires_in': 7775999}
If you try to generate a new token pair using the same code, then it displays the following error:
{'message': "POST failed: HTTP/1.1 400 Bad Request (url = https://idbroker.webex.com/idb/oauth2/v1/access_token, request/response TrackingId = ROUTER_5FD9B9DA-FCB3-01BB-03C9-503F01C403C9, error = '(invalid_grant) Authorization code has been used.')", 'errors': [{'description': "POST failed: HTTP/1.1 400 Bad Request (url = https://idbroker.webex.com/idb/oauth2/v1/access_token, request/response TrackingId = ROUTER_5FD9B9DA-FCB3-01BB-03C9-503F01C403C9, error = '(invalid_grant) Authorization code has been used.')"}], 'trackingId': 'ROUTER_5FD9B9DA-FCB3-01BB-03C9-503F01C403C9'}
Traceback (most recent call last):
File "c:/Python/test_shit.py", line 117, in <module>
test = get_tokens("MWU4YTJmYTgtNjllMy00YjAzLTg0NTQtMTRkYTRiMmIxMzZkNWEzNzRkZDQtYTNk_PF84_33672567-1029-48fb-ba77-7fe2001ee897") # CODE
File "c:/Python/test_shit.py", line 113, in get_tokens
access_token = results["access_token"]
KeyError: 'access_token'
Problem:
The tokens validity is set as followed:
Token: 14 days expiration.
Refresh token: 90 days expiration.
After this period you will have to go through the process of creating a new code and then request a new set of tokens.
I'm struggling to see how I can automate the process of getting a new code, using only Python 2.7.5 and CLI interface on a Red Hat Linux system.
I have tried using the request library to open and click accept, but i've not had any success.
Here's an example of what happends when i try to open the code URL in the black box via requests session:
>>> url = "https://webexapis.com/v1/authorize?client_id=C7aa20b0a57cae4127d1e08b15e3a94efacd5c7bb3e1a70ceed4308903a5b807b&response_type=code&redirect_uri=https%3A%2F%2Fwww.your-oauth-website-here.dk%2Foauth&scope=spark%3Aall%20spark%3Akms&state=set_state_here"
>>> with requests.Session() as s:
... response = s.get(url)
...
>>> print(response.text)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>
Sign In - Webex
</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script>
window.jQuery || document.write("<script src='/idb/js/jquery-3.5.1.min.js'><\/script>");
</script>
<link rel="stylesheet" href="/idb/css/momentum-ui_fbdee616043ab213856f370993c83f01.min.css"/>
<link rel="stylesheet" href="/idb/css/idbstyle_service_default.css" type="text/css" />
<script src="/idb/js/auth_symphony_997017a549f50f21347311e1488607ab.js"></script>
<link rel="stylesheet" href="/idb/css/idbstyle_symphony_a2736f93e46b8c14f389ccac12436dcb.css" type="text/css" />
<link rel="apple-touch-icon" sizes="180x180" href="/idb/favicons/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/idb/favicons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/idb/favicons/favicon-16x16.png" />
<link rel="manifest" href="/idb/favicons/manifest.json" />
<link rel="mask-icon" href="/idb/favicons/safari-pinned-tab.svg" color="#07C1E4" />
<link rel="shortcut icon" href="/idb/favicons/favicon.ico" />
<meta name="msapplication-config" content="/idb/favicons/browserconfig.xml" />
<meta name="theme-color" content="#ffffff" />
<meta name="robots" content="noindex, nofollow" />
<!-- Ref: http://seclab.stanford.edu/websec/framebusting/framebust.pdf -->
<style id="antiClickjack">body{display:none !important;}</style>
<noscript><style>body{display:block !important;}</style></noscript>
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
<script>
var nameValidated = false;
var redirectUrl = "https://idbroker.webex.com/idb/IdBGlobalLogin?type=login&goto=https%3A%2F%2Fidbroker.webex.com%2Fidb%2Foauth2%2Fv1%2Fauthorize%3Fclient_id%3DC7aa20b0a57cae4127d1e08b15e3a94efacd5c7bb3e1a70ceed4308903a5b807b%26response_type%3Dcode%26redirect_uri%3Dhttps%253A%252F%252Fwww.your-oauth-website-here.dk%252Foauth%26scope%3Dspark%253Aall%2520spark%253Akms%26state%3Dset_state_here";
$(document).ready(function() {
$("#md-form").submit(function(e){
e.preventDefault();
if(nameValidated) {
processForm();
}
});
var cookieEmail = "";
if(cookieEmail.length > 0) {
$('#GlobalEmailLookupForm').find('input[id="email"]').val(cookieEmail);
$('#GlobalEmailLookupForm').find('input[id="isCookie"]').val("true");
$('#GlobalEmailLookupForm').submit();
}
});
function validateName(name, nameId) {
nameId = nameId.replace('IDToken' ,'');
var divId = 'DivToken' + nameId;
if(name.length > 0) {
$.ajax({
type: "POST",
url: "/idb/validateEmail",
data: { user: name, action: 'login', validate: "true" }
})
.done(function( responseData ) {
if(responseData.status == 'invalid') {
document.getElementById('nameContextualError'+nameId).innerHTML = "Enter a valid email address. Example: name#email.com"
highlightErrorInputTextbox(divId);
} else {
nameValidated = true;
}
});
}
}
function processForm() {
var email = $.trim(document.getElementById('IDToken1').value);
if(nameValidated) {
$('#GlobalEmailLookupForm').find('input[id="email"]').val(email);
$('#GlobalEmailLookupForm').submit();
} else {
if(email.length > 0) {
$.ajax({
type: "POST",
url: "/idb/validateEmail",
data: { user: email, action: 'login', validate: "true" }
})
.done(function( responseData ) {
if(responseData.status == 'invalid') {
document.getElementById('nameContextualError1').innerHTML = "Enter a valid email address. Example: name#email.com"
highlightErrorInputTextbox('DivToken1');
} else {
$('#GlobalEmailLookupForm').find('input[id="email"]').val(email);
$('#GlobalEmailLookupForm').submit();
}
});
}
}
return false;
}
</script>
<style>
#media (forced-colors: active) {
.md-button--blue {
border: 1px solid;
}
}
</style>
</head>
<body id="login" class="md md--sites">
<div id="globalInfo" style="display:none;">
<div>
It appears that cookies are not enabled on your computer, so some functions will not work. To enable cookies, change the privacy settings in your browser, and then refresh the page.
</div>
<a id="close_crossplatform_message" href="javascript:">
Close
</a>
</div>
<noscript>
<div>
It appears that JavaScript is not enabled on your computer, so some functions will not work. To enable JavaScript, change the privacy settings in your browser, and then refresh the page.
</div>
</noscript>
<div class="md-panel md-panel--form md-panel--full">
<div class="md-panel__main">
<div class="md-panel__image ci__logo"></div>
<div class="md-panel__title">
Enter your email address
</div>
<form class="md-panel__form" id="md-form" novalidate="">
<div class="md-input-container md-input--filled" id="DivToken1">
<div class="md-input__wrapper">
<input class="md-input" id="IDToken1" data-monitor-id="IDToken1"
name="IDToken1" value="" autocomplete="email"
placeholder="Email address"
alt="Email address"
onblur="validateName($.trim(this.value), this.id);" maxlength="512"
type="email" autofocus>
</div>
<div class="md-input__messages" id="DivErrorToken1">
<div class="message" id="nameContextualError1">
Enter the email address for your Webex account.
</div>
</div>
</div>
<div class="md-panel__cta">
<button name="btnOK" type="submit" id="IDButton2"
class="md-button md-button--blue" onClick="processForm();">
Next
</button>
</div>
</form>
</div>
<form name="GlobalEmailLookup" id="GlobalEmailLookupForm" method="post" action="/idb/globalLogin">
<input type="hidden" id="email" name="email" value="" />
<input type="hidden" id="isCookie" name="isCookie" value="false" />
<input type="hidden" id="ForceAuth" name="ForceAuth" value="false" />
<input type="hidden" id="cisService" name="cisService" value="common" />
<input type="hidden" name="gotoUrl" value="aHR0cHM6Ly9pZGJyb2tlci53ZWJleC5jb20vaWRiL29hdXRoMi92MS9hdXRob3JpemU/Y2xpZW50X2lkPUM3YWEyMGIwYTU3Y2FlNDEyN2QxZTA4YjE1ZTNhOTRlZmFjZDVjN2JiM2UxYTcwY2VlZDQzMDg5MDNhNWI4MDdiJnJlc3BvbnNlX3R5cGU9Y29kZSZyZWRpcmVjdF91cmk9aHR0cHMlM0ElMkYlMkZ3d3cueW91ci1vYXV0aC13ZWJzaXRlLWhlcmUuZGslMkZvYXV0aCZzY29wZT1zcGFyayUzQWFsbCUyMHNwYXJrJTNBa21zJnN0YXRlPXNldF9zdGF0ZV9oZXJl" />
<input type="hidden" id="encodedParamsString" name="encodedParamsString" value="dHlwZT1sb2dpbiY=" />
</form>
<div id="footer" class="md-panel__footer">
<img class="footer__logo" src="/idb/images/cisco-webex/lockup/cisco-webex-lockup-blue.svg" alt="Cisco Webex logo"/>
<div id="footer-links" class="footer__copyright">
By using Webex you accept the
<a href="https://www.cisco.com/c/en/us/about/legal/cloud-and-software/cloud-terms.html" target="_blank" rel="noopener">
Terms of Service
</a> &
<a href="https://www.cisco.com/web/siteassets/legal/privacy.html" target="_blank" rel="noopener">
Privacy Statement
</a>.
<a href="https://www.webex.com" target="_blank" rel="noopener">
Learn more about
</a> Webex
</div>
</div>
</div>
</body>
</html>
I'm just getting the login screen.
Alternatively: Is there a way to sign into the WebEx website using Python CLI?
If I could just log into the Cisco WebEx developer site and then use this session to get a new code, that might work out.
Update:
I have managed to get past the "enter email address", but I'm now stuck at enter password, but i continue investigating how I can log into the WebEx website.
with requests.Session() as s:
login_url = "https://developer.webex.com/login"
result = s.get(login_url)
payload = { "email": "xx#xx.xx" }
print(result.url)
result = s.post(result.url, data=payload)
print(result.text)
Any thoughts on the matter will be highly appreciated.
Thank you.
Turns out I didn't read the documentation properly, sorry for anyone who spent time reading/etc.
The refresh token expiration will also reset, when refreshing the normal token. I didn't know this was the case, but after contacting Cisco Dev support today, they showed me.
The official reply was:
Hi XXX,
Thank you for contacting Webex Developer Support. There is no
way to fully automate the OAuth Grant Flow, as the first manual step,
where the user authorises the integration to act on their behalf is
always mandatory. However once the access token expires or even before
that, you can use the refresh token to extend the validity of the
access token. With that, the validity of the refresh token will also
be renewed, so you can practically keep refreshing your access token
indefinitely. The refresh token expiry time is reset at most once a
day for performance reason, so the counter will seem to be counting
down within the span of one day, but if you were to refresh it the
following day, you should see that time/integer go back up.
The code i use to refresh the token:
def refreshCiscoWebExToken(refresh_token):
"""Refresh access token"""
payload = ( "grant_type=refresh_token&client_id={0}&client_secret={1}&"
"refresh_token={2}").format(clientID, secretID, refresh_token)
response = requests.post(url=TokenUrl, data=payload, headers=TokenHeaders)
if response.status_code == 200:
results = json.loads(response.text)
access_token = results["access_token"]
expires_in = results["expires_in"]
return(access_token, expires_in)
I hope this can/will help others in search of an answer.

Take variable from a Python file and display in Flask

OK so I am still struggling on this one, here is my complete code after using #DrAgon suggestion. I have 3 files, the first one is my Python file that merely get the local time.
import datetime
import time
now = datetime.datetime.now()
print(now)
If I set this file up as:
import datetime
import time
while True:
now = datetime.datetime.now()
print(now)
time.sleep(2)
It refreshes and gets me the date every 2 seconds but when it's imported in to my flask project it takes over and just will not load the web page.
so I use the first version of the file that gets the local time. Next I have my flask Python page:
from flask import Flask, render_template, request, jsonify
from flask_bootstrap import Bootstrap
import Timereppper
import datetime
now = datetime.datetime.now()
app = Flask(__name__)
Bootstrap(app)
#app.route('/')
def hello():
return render_template('hello.html', myvar= now.second)
#app.route('/currentstatus')
def currentstatus():
return render_template('hello.html', myvar= now.second)
#app.route('/historic')
def historic():
return render_template('historic.html')
#app.route('/data')
def get_data():
mbdata = Timereppper.now() # Call your code that gets the data here
return jsonify(mbdata) # And return it as JSON
if __name__ == '__main__':
app.run(host= 'localhost', port=5000, debug=False, threaded=True)
and then i have my main hello.html file with the code suggested by #DrAgon at the bottom. This returns the text on the bottom of the main webpage "New Data Goes Here" and if I look at lcalhost:5000/data I get the date that was read at the when the flask server was first started. I want this date to update continuously or every few seconds and display on the main home page of the website. Can anyne show me where I am going wrong. I apologise I am new to flask.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> Vibration Monitor</title>
<meta name="viewport" content="width=device-wi dth, initial-scale=1">
<link href="{{url_for('static', filename='css/bootstrap.min.css')}}"
rel="stylesheet">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico')
}}">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">
</script>
<script>window.jQuery || document.write('<script src="{{
url_for('static', filename='jquery.js') }}">\x3C/script>')</script>
</head>
<nav class="navbar navbar-expand-lg navbar-light bg-#808080">
<a class="navbar-brand" href= https://www.iotindustries.co.uk>
<img src="/static/img/IOT Industries Logo 1THIN.png" width="300" height="90"
class="d-inline-block" alt="">
</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link font-weight-bold" href="/currentstatus">Current
Status</a>
</li>
<li class="nav-item active">
<a class="nav-link font-weight-bold" href="/historic">Historic</a>
</li>
</ul>
</nav>
<div class="alert alert-secondary" role="alert"></div>
<div class="card-deck" style="width: 42rem;">
<div class="card text-white bg-dark mb-3" style="width: 16rem;">
<img class="card-img-top" src="/static/img/vibes1.png" alt="Vibration
Image">
<div class="card-body">
<h5 class="card-title">Current vibration level:</h5>
<h1 class="card-text font-weight-bold">15 mA</h1>
<a class="btn btn-success">Acknowledge</a>
</div>
</div>
<div class="card text-white bg-dark mb-3" style="width: 16rem;">
<img class="card-img-top" src="/static/img/timer.svg" alt="Timer Image">
<div class="card-body">
<h5 class="card-title">Estimated days until failure:</h5>
<h1 class="card-text font-weight-bold"> 3 Days {{myvar}} </h1>
<a class="btn btn-info" href="/historic">View Historic</a>
</div>
</div>
</div>
<body>
<div id="myDataDiv">New Data Goes Here</div>
</body>
<script>
var goGetNewData = function() {
$.ajax({
url: '/data',
dataType: 'json',
type: 'get',
success: function(e) {
$('#myDataDiv').html('New Data ' + e);
},
error: function(e) {
$('#myDataDiv').html('Error');
},
});
}
var waitTime = 10; // 10 seconds
var fetchInterval = window.setInterval(goGetNewData, waitTime*1000);
</script>
</html>
I have a Python file which is constantly reading a Modbus register every couple of seconds, using the Python sleep.() function. This value is getting logged to a google FireBase database.
What I would like to do is display this value read from the ModBus register on my Flask website. I just don't know how to do it. Am I able to either:
Get the updated ModBus value from the Python file, pass that in to "MyFlaskApp.py" continuously or at the click of a button in my HTML file.
Query the Firebase database and get this to display the latest written value either continuously or at the click of a button.
Which, if any of these, can be done and what would be the best method. Can anyone post an example of how I would be able to do this?
Thanks
Well If you want to do it on a click of a button, you can make the button part of an html form and have it post to your flask application:
#app.route('/update')
def val():
return render_template("index.html",data=data)
This would work, considering the value you want to pass to the html is called data.
To display the data passed through, your html should look like this:
<p>{{data}}</p>
Instead of updating the modbus value every two seconds, you could do it only when the button is clicked like so:
#app.route('/update')
def val():
#code to get the value
return render_template("index.html",data=data)
This way everytime you click the form button to get a new value, then the data is read from the database. Instead of using another file as an import and using datetime, this would make sure that your program not only saves memory but still returns the desired value.
In the now method, you should write to a txt file as such:
#get the data
f = open("mod.txt", w+)
f.write(data)
f.close
Then when you recive the button click in the flask app:
#app.route('/update')
def val():
f = open("mod.txt",r)
data = f.read()
f.close()
return render_template("index.html",data=data)
To make sure that the other program is running you can do this:
if __name__ == '__main__':
execfile('modbusreginfo.py')
app.run(host= 'localhost', port=5000, debug=False, threaded=True)
If you want to make the page reload every 2 seconds so there is no button click you can add this to your html:
<meta http-equiv="Refresh" content="2">
Here's one way.
You will need to create 2 routes in your MyFlaskApp.py
/home - This route will render an HTML template
/data - This route will return JSON (containing the data from your ModBus file OR from querying Firebase)
(you can name the routes whatever you want)
In the HTML template returned by the first route you will need a div (with an id) to render the data and some JavaScript to continuously fetch new data.
Then just point the Ajax GET at your /data endpoint and your on your way.
<body>
<div id="myDataDiv">New Data Goes Here</div>
</body>
<script>
var goGetNewData = function() {
$.ajax({
url: '/data',
dataType: 'json',
type: 'get',
success: function(e) {
$('#myDataDiv').html('New Data ' + e);
},
error: function(e) {
$('#myDataDiv').html('Error');
},
});
}
var waitTime = 10; // 10 seconds
var fetchInterval = window.setInterval(goGetNewData, waitTime*1000);
</script>
The /data route could look something like this.
from flask import jsonify
import Modbusreginfo # Import your other python code
#app.route('/data')
def get_data():
mbdata = Modbusreginfo.getModBusData() # Call your code that gets the data here
return jsonify(mbdata) # And return it as JSON

Adding two numbers in flask

<html>
<head>
<title>Addition</title>
<script>
function display(id_name,result_name){
document.getElementById(result_name).innerHTML = document.getElementById(id_name).value;
}
function calculate(id1,id2,result_id) {
document.getElementById(result_id).innerHTML = parseInt(document.getElementById(id1).value)+parseInt(document.getElementById(id2).value)
}
</script>
</head>
<body>
<div class="input">
Enter 1st Number:
<input type="text" id="input1_text">
<button type="button" onclick="display('input1_text','input1')">Enter 1st Number</button>
<span id="input1"></span>
</div>
<div class="input">
Enter 2nd Number:
<input type="text" id="input2_text">
<button type="button" onclick="display('input2_text','input2')">Enter 2nd Number</button>
<span id="input2"></span>
</div>
<div class="result">
<button type="button" onclick="calculate('input1_text','input2_text','result_value')">Calculate</button>
<span id="result_value"></span>
</div>
</body>
</html>
so in the above code i am not only adding 2 nubmers but also displaying the numbers after pressing the button.so now what i am looking for is to make this using flask framework on pressing buttons their respective functions should be fired up and data should be updated.I have tried using forms so the problem is on clicking button of 1st number to display is whole page is refreshing and i am losing whole data.so now how do i write those functions in python and also making sure that the page should not reload.Also is it possible to reuse the display function based on the paramters rather than hard coding and writing 2 display functions for 2 numbers seperately
Unless you need to use the user inputted values in the backend of your application, you can simply perform the calculations in the front-end:
<html>
<body>
<p>Input first number:</p>
<input type='text' class='first_val'>
<p>Input second number:</p>
<input type='text' class='second_val'>
<div class='results'></div>
<button type='button' class='calculate'>Calculate</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('.calculate').click(function() {
var val1 = parseInt($('.first_val').val());
var val2 = parseInt($('.second_val').val());
var val3 = val1+val2;
$('.results').html('The result is '+val3);
});
});
</script>
</html>
Edit: using Python in the backend, ajax can be utilized:
index.html:
<html>
<body>
<p>Input first number:</p>
<input type='text' class='first_val'>
<p>Input second number:</p>
<input type='text' class='second_val'>
<div class='results'></div>
<button type='button' class='calculate'>Calculate</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js">
</script>
<script>
$(document).ready(function() {
$('.calculate').click(function() {
var val1 = $('.first_val').val();
var val2 = $('.second_val').val();
$.ajax({
url: "/get_numbers",
type: "get",
data: {val1: val1, val2:val2},
success: function(response) {
$(".results").html(response.packet);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
Then, in the Python app:
#app.route('/', methods=['GET', 'POST'])
def home():
return flask.render_template('index.html')
#app.route('/get_numbers')
def get_values():
value1 = flask.request.args.get('val1')
value2 = flask.request.args.get('val2')
return flask.jsonify({'data':f'<p>The result is: {value1+value2}</p>'})
You should build an flask API function: a GET route which accepts the argument from your form and returns a JSON response object.
Then in your HTML when you click the button that should perform an AJAX GET quesry accessing your API route and return the values to that page. This avoids having Flask re-render your page and returns the data directly to your existing client web browser.
To make it easy to manipulate data dynamically in the webpage I suggest a JS framework like angular, react or vue. Personally I prefer vue since it can be self contained, loaded as script into the page and the setup is often minimal. The docs are also very easy and you can easily see how to link form input controls.

Using asynchronous POST requests with Python's Bottle?

I'm trying to create a turn-based strategy game (think Dominion) in Python. The core game objects and methods are Python classes with methods in them (just typical OO stuff). The UI is an HTML client using Bottle. I'm aiming for an entirely asynchronous approach. So the sole page's content is generated from the Python objects, and I want submits from the page to update those objects without ever leaving the page by going back through the bottle webserver (using jQuery AJAX for this).
At the moment I'm working on a basic chat system that retrieves player-written messages and stores them as Chat objects (containing player and text data, nothing else). These objects are then written to a chat window using AJAX that updates the windows once every second. The HTML format of the chat lines is <div class="chatLine"><p>Player > </p><p>Text</p></div> Pretty standard stuff.
This basic diagram might make it a little clearer, even though it isn't really technical, more conceptual:
My BottleUI.py (this is what I run to start the server):
from Populate import * # Everything in Populate can now be directly called.
# NOTE: Populate allows access to "bottle" and "Main"
# This ensures the css file is available
#route('/theCSS')
def static():
return static_file('main.css', root='static')
#route('/citadel/:game/:player')
def gamePage(game, player):
if not (game.isdigit()) or not (player.isdigit()):
return "Invalid page."
game = int(game)
player = int(player)
if ((game >= 0) and (game < listOfGames.__len__())) and ((player >= 0) and (player < listOfGames[game].listOfPlayers.__len__())):
return '''
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/theCSS">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<!-- Sample AJAX script below, change as needed -->
<script type="text/javascript">
$(document).ready(function() {
$('#chatForm').submit(function(e) {
$.ajax({
type: 'POST',
url: "/AddToChat/''' + str(game) + '''/''' + str(player) + '''",
success: function() {
$('#chatInput').val("");
}
});
e.preventDefault();
});
});
setInterval("updateChat();", 1000);
$(function() {
updateChat = function() {
$('#chatText').load('/GenerateChat/''' + str(game) + '''');
};
});
</script>
<!-- Script to scroll to bottom of divs - needs to be changed into called function -->
<script type="text/javascript">
window.onload = function () {
var objDiv = document.getElementById("gameFlow");
objDiv.scrollTop = objDiv.scrollHeight;
objDiv = document.getElementById("chatText");
objDiv.scrollTop = objDiv.scrollHeight;
};
</script>
</head>
<body>
<div id="container">
<!-- Make this have background-image with the game number displaying programmatically -->
<div id="banner">
<h1>Citadel - Game ''' + str(game) + ''', Player ''' + str(player) + '''</h1>
</div>
<div id="main">
<div id="leftPanel">
<div id="playerTotals">
<h4>Player Totals:</h4>
<div id="totalsText">
<p>Money:</p>
<p>Population:</p>
<p>Troops:</p>
<p>Friend:</p>
<p>Enemy:</p>
</div>
<!-- Player totals go here (money, population/limit, troops, friend, enemy) -->
<div id="totalsNums">
</div>
<div class="clear"></div>
</div>
<div class="leftSegment">
<h4>Troop Cards:</h4>
<!-- Player's troopCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Territory Cards:</h4>
<!-- Player's territoryCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Region Cards:</h4>
<!-- Player's regionCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Resource Cards:</h4>
<!-- Player's resourceCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Diplomacy Cards:</h4>
<!-- Player's diplomacyCards here -->
<select size=2>
</select>
</div>
<div id="chatPane">
<form id="chatForm" method="POST" action="/AddToChat/''' + str(game) + '''/''' + str(player) + '''">
<textarea name="theChatText" id="chatInput"></textarea>
<input id="chatSubmit" class="button" type="submit" value="Send" />
</form>
</div>
<div class="clear"></div>
</div>
<div id="rightPanel">
<!-- Game flow goes here (shows current player/phase, attacks with results, etc) -->
<div id="gameFlow">
</div>
<!-- Player turn stuff goes here (changes depending on which phase, etc) -->
<div id="playerActions">
</div>
<!-- Chat goes here (implement last) -->
<div id="chatText">
</div>
<div class="clear"></div>
</div>
</div>
</div>
</body>
</html>
'''
else:
return "Invalid page."
run(host='localhost', port=8080)
And here's my Populate.py (this is where my AJAX #route methods are stored):
"""
This module contains the bottle routs for AJAX population of the various parts
of the game page.
"""
from bottle import route, run, template, static_file, request
from Main import * # Everything in Main can now be directly called.
globalBegin()
#route('/AddToChat/:game/:player', method='POST')
def AddToChat(game, player):
theText = request.POST.get('theChatText', '').strip()
game = int(game)
player = int(player)
listOfGames[game].listOfPlayers[player].addChat(theText)
#route('/GenerateChat/:game')
def GenerateChat(game):
chatText = ""
game = int(game)
for line in listOfGames[game].chatList:
chatText += '<div class="chatLine"><p>'
chatText += line.player.name
chatText += ' > </p><p>'
chatText += line.text
chatText += '</p></div>'
return chatText
The problem is, the 'chatForm' form isn't working as intended. AddToChat() seems to think that request.POST.get('theChatText', '') is a NoneType when I try to submit text.
So yeah, I'm stumped as to why it's doing this. As far as I can see, 'theChatText' should be a valid key in the POST dict.
I'll also just state that all my core game logic works (even though it's pretty clear that isn't the problem here).
Any help is appreciated.
Original jQuery function:
$(document).ready(function() {
$('#chatForm').submit(function(e) {
$.ajax({
type: 'POST',
url: "/AddToChat/''' + str(game) + '''/''' + str(player) + '''",
success: function() {
$('#chatInput').val("");
}
});
e.preventDefault();
});
});
data: $(this).serialize(), needed to be added, like so:
$(document).ready(function() {
$('#chatForm').submit(function(e) {
$.ajax({
type: 'POST',
url: "/AddToChat/''' + str(game) + '''/''' + str(player) + '''",
data: $(this).serialize(),
success: function() {
$('#chatInput').val("");
}
});
e.preventDefault();
});
});
Otherwise the server (or Bottle in this case) won't be able to read the submitted form.

Categories