I'm trying to refresh a Jenkins build console output in my Flask web application, however I am having trouble with the jQuery/AJAX to do it.
As you can see below, i'm just trying to get this working using a refresh button. Ideally I want to refresh {{buildinfo}} on a timer.
Currently my test function/jQuery is returning the error: Uncaught TypeError: Illegal invocation.
Heres the (working) function from my app.py I was using before I started down this path:
#app.route('/buildinfo/<job_id>/<job_number>', methods=['GET', 'POST'])
def buildInfo(job_id, job_number):
try:
console_output = server.get_build_console_output(job_id, int(job_number))
return render_template("buildinfo.html", buildinfo=console_output, job_id=job_id, job_number=job_number)
except Exception as e:
return render_template("buildinfo.html", error=str(e))
Here's the test function I have been using to receive the request and send back to the client:
#app.route('/_test')
def foo():
a = request.args.get('a', None, type=str)
b = request.args.get('b', 0, type=int)
bar = server.get_build_console_output(a, int(b))
return jsonify(result=bar)
And here is the buildinfo.html:
{% extends "base.html" %}
{% block content %}
<script type="text/javascript" src="{{ url_for('static', filename='jquery-1.12.0.min.js') }}"></script>
<script type="text/javascript">
var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
$(function() {
$('a#load').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_test', {
a: $('{{job_id}}'),
b: $('{{job_number}}')
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
<div class="col-md-10">
<h1>Build Info</h1>
<br>
<p>retrieving {{job_id}}, build number {{job_number}}</p>
<br>
<p>{{buildinfo}}</p>
<br>
<span id="result">?</span>
go
</div>
{% endblock %}
You're feeding a HTML elements to the ajax call, when you should be passing values. Change this:
a: $('{{job_id}}'),
b: $('{{job_number}}')
to this:
a: {{job_id}},
b: {{job_number}},
...
I'm not sure about the types you're sending, if you need to send strings- wrap quotes around the double-moustache, but this should get you going. See here for a similar issue.
For anyone in the same boat, I am able to refresh a Flask value using the following. I may update this answer to include clearInterval() once the build is complete. Thanks to GG_Python for pointing out my mistake.
app.py :
#app.route('/buildinfo/<job_id>/<job_number>', methods=['GET', 'POST'])
def buildInfo(job_id, job_number):
try:
console_output = server.get_build_console_output(job_id, int(job_number))
return render_template("buildinfo.html", buildinfo=console_output, job_id=job_id, job_number=job_number)
except Exception as e:
return render_template("buildinfo.html", error=str(e))
#app.route('/_test')
def foo():
a = request.args.get('a', None, type=str)
b = request.args.get('b', 0, type=int)
bar = server.get_build_console_output(a, int(b))
return jsonify(result=bar)
buildinfo.html :
{% extends "base.html" %}
{% block content %}
<script type="text/javascript" src="{{ url_for('static', filename='jquery-1.12.0.min.js') }}"></script>
<script type="text/javascript">
var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
<script type="text/javascript">
setInterval(function() {
$.getJSON($SCRIPT_ROOT + '/_test', {
a: "{{job_id}}",
b: {{job_number}}
}, function(data) {
$("#result").text(data.result);
});
return false;
}, 3000);
</script>
<div class="col-md-10">
<h1>Build Info</h1>
<br>
<p>retrieving {{job_id}}, build number {{job_number}}</p>
<br>
<p id="result">{{buildinfo}}</p>
<br>
</div>
{% endblock %}
Related
So, I've seen this question around a lot but the accepted answer isn't working for me. I have this chatroom-like site I'm working on, and while it works fine for most of it (I'm aware of a few other minor issues with my code but they're not part of the question), I'm having issues with the character encoding whenever my JS file is involved and I use sockets. I'll post my HTML and JS files now:
HTML:
{% extends "layout.html" %}
{% block heading %}
<meta charset="UTF-8">
<script type="text/javascript" src="{{ url_for('static', filename='js/chatroom.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/chatroom.css') }}">
{% endblock %}
{% block body %}
The Chatroom name is <span id="chatroom">{{ chatroom }}</span> and it's code is <span id="code">{{ code }}</span>
<div id="messages">
{% for message in messages %}
{% if message.author == online_user %}
<div class="own">{{ message.message }}</div>
{% else %}
<div class="other"><strong>{{ message.author }}</strong><br> {{ message.message }}</div>
{% endif %}
{% endfor %}
</div>
<textarea id="message" rows=5 cols=50></textarea>
<input type="button" id="send" value="Send">
{% endblock %}
JS:
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
document.querySelector('#send').onclick = () => {
const message = document.querySelector('#message').value;
socket.emit('send message', {'chatroom': {'code':document.querySelector('#code').innerHTML, 'name': document.querySelector('#chatroom').innerHTML}, 'message': message});
};
});
socket.on('broadcast message', data => {
if (data.message['chatroom']['code'] == document.querySelector('#code').innerHTML) {
const msg = document.createElement('div');
if (data.message['author'] == document.getElementById('current_user').innerHTML)
msg.innerHTML = data.message['message'];
else
msg.innerHTML = `${data.message['author']}: ${data.message['message']}`
document.querySelector('#messages').append(msg);
var listlen = document.getElementById('messages').getElementsByTagName('div').length;
while (listlen > 100) {
var msglist = document.getElementById('messages');
msglist.removeChild(msglist.childNodes[0]);
var listlen = document.getElementById('messages').getElementsByTagName('div').length;
};
};
});
});
Now, when the JS file and socketing isn't involved (for example, if I log into the site) the encoding works fine. However, when the textarea holds special characters such as Á (I'll use the word Ángel on both fields as an example), once I hit the send button (not before), the output is wrong.
Current output: {'chatroom': {'code': 'ouAY7mxCvhXb', 'name': 'UTF'}, 'author': 'Ángel', 'message': 'Ã\x81ngel'}
Expected output: {'chatroom': {'code': 'ouAY7mxCvhXb', 'name': 'UTF'}, 'author': 'Ángel', 'message': 'Ángel'}
The accepted answer for other questions marking this issue was adding the <meta charset="UTF-8"> line on my HTML, but I'm puzzled now since it hasn't worked. I also checked and my JS file is properly encoded to UTF-8 as well.
EDIT:
I'll add the server side code for the socket since it might as well help understand the output better. As seen here, the problem with the encoding only happens from what I pass from data (which can go wrong only on the message's content and chatroom's name, because the chatroom code is automatically generated and never includes special characters).
#socketio.on("send message")
def send_message(data):
data['message'] = data['message'].strip()
if len(data["message"]) > 0:
message = {'chatroom': data['chatroom'], 'author': session['username'], 'message': data['message']}
print(message)
stored_messages.append(message)
local_messages.append(message)
emit("broadcast message", {'message': message}, broadcast=True)
For future references if someone runs into this problem, I found out a way to deal with it. Instead of passing the raw message through the socket and expecting the program to automatically transform it to UTF-8 with the meta tag, I encoded it by adding an encoding function on the JS file and applying it to the message content.
function encode_utf8(s) {
return encodeURIComponent(s);
}
Former line 4 of JS file now looks like this: const message = encode_utf8(document.querySelector('#message').value);
That allows me send it encoded in a way my backend server can properly decode so it returns the expected output by using the urllib.parse library's unquote_plus.
#socketio.on("send message")
def send_message(data):
data['message'] = data['message'].strip()
print(data)
if len(data["message"]) > 0:
message = {'chatroom': data['chatroom'], 'author': session['username'], 'message': urllib.parse.unquote_plus(data['message'])} # <-- Modified line
print(message)
stored_messages.append(message)
local_messages.append(message)
emit("broadcast message", {'message': message}, broadcast=True)
I am trying to send an option tag the client choose, and process it into Python-Flask. This is what I've tried by now:
interactive.html
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js?version=3"></script>
<script type=text/javascript>
$(function() {
$('a#process_input').bind('click', function() {
$.getJSON('/background_process', {
select_classifier: $('input[name="select_classifier"]').val(),
}, function(data) {
$("#result").text(data.result);
});
return false;
});
});
</script>
</head>
<body>
<span>Please choose the option:</span>
<select name="select_classifier">
{% for o in data %}
<option value="{{ o.name }}">{{ o.name }}</option>
{% endfor %}
</select>
</body>
python file:
#app.route('/interactive', methods=['POST', 'GET'])
def function():
return render_template('interactive.html',
data=[{'name': 'option1'},
{'name': 'option2'},
{'name': 'option3'},])
#app.route('/background_process', methods=['POST', 'GET'])
def background_process():
classifier = request.args.get('select_classifier', 0, type=str)
print(classifier)
This print will give me the value 0, but what I was expected was option1, option2 or option3 which I chose. Which seems to be the problem here? Thank you in advance!
Your jQuery is trying to find the value of input[name="select_classifier"], but the element is not an input, it is a select. Try select[name="select_classifier"] instead.
I have been developing on firefox & the code works fine but when I tried to test on Chrome it just displays the {{err_msg}} which means angular is not working.
I am using Django 1.10 server with Rest Framework for managing get requests from the Angularjs 1.5.9 code.
I tested on Firfox, Midori & Chrome. It only works on Firefox!
I edited the hosts file so that givemecoupon.com:8000 redirects to 127.0.0.1:8000 which is my Django server.
Console error:
Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.5.9/$injector/modulerr?p0=givemecouponApp&p1=…Ic%20(https%3A%2F%2Fcode.angularjs.org%2F1.5.9%2Fangular.min.js%3A21%3A332)
at angular.js:38
at angular.js:4683
at q (angular.js:325)
at g (angular.js:4644)
at eb (angular.js:4566)
at c (angular.js:1803)
at Ic (angular.js:1824)
at ue (angular.js:1709)
at angular.js:32379
at HTMLDocument.b (angular.js:3251)
my app:
'use strict';
var givemecouponApp = angular.module('givemecouponApp', []);
givemecouponApp.controller('CouponsCtrl', ['$scope', '$http', 'givemecouponService', function CouponsCtrl($scope, $http, givemecouponService) {
$scope.finderloader=true;
$scope.err_msg = 'no error';
var myDataPromise = givemecouponService.getData();
myDataPromise.then(function(result) {
if(result.data == "Not a valid course!" || result.data == "No course passed!"){
$scope.err_msg = result.data;
}else{
$scope.coupons = result.data;
}
}, function ( result ) {
if(result.status == 429){
$scope.err_msg = "Too many requests!";
}
// TODO: handle the error somehow
}).finally(function() {
// called no matter success or failure
$scope.finderloader = false;
});
}]);
//service for getting coupon codes
givemecouponApp.factory('givemecouponService', function($http) {
var getData = function() {
// Angular $http() and then() both return promises themselves
return $http({method:"GET", url:slug_json_url}).then(function(result){ // What we return here is the data that will be accessible
// to us after the promise resolves
return result;
});
};
return { getData: getData };
});
Template:
{% load static %}
<!DOCTYPE html>
<html ng-app="givemecouponApp">
<head>
<meta charset="utf-8">
<title>Course Page</title>
<script src="https://code.angularjs.org/1.5.9/angular.min.js" type="text/javascript"></script>
<script src="https://code.angularjs.org/1.5.9/angular-resource.min.js" type="text/javascript"></script>
<link rel="stylesheet" href="{% static 'css/spinkit.css' %}">
</head>
<body ng-controller="CouponsCtrl">
<div>
{% block content %}
<h1>Course Page</h1>
{{course_slug_url}}
{% endblock content %}
{% verbatim %}
<div>Coupons:
<div>{{err_msg}}</div>
<div ng-show="finderloader" class="sk-wave">
<h3>Looking for Coupons</h3>
<div class="sk-rect sk-rect1"></div>
<div class="sk-rect sk-rect2"></div>
<div class="sk-rect sk-rect3"></div>
<div class="sk-rect sk-rect4"></div>
<div class="sk-rect sk-rect5"></div>
</div>
<h2 ng-repeat="coupon in coupons" ng-show="coupon.id==1">
{{coupon.meta_title}}
</h2>
<h2 ng-repeat="coupon in coupons" ng-show="coupon.id==1">
Original Price: {{coupon.original_price}}
</h2>
<ul ng-repeat="coupon in coupons">Code: {{coupon.code}}
<li>Discount price: {{coupon.discounted_price}}</li>
<li>Discount rate: {{coupon.discount_rate}}</li>
<li>Valid untill: {{coupon.end_time}}</li>
{{coupon.deeplink}}
</ul>
</div>
</div>
{% endverbatim %}
{% block pass_slug %}
<script type="text/javascript">
slug_json_url = "http://givemecoupon.com:8000/api/?format=json&slug={{course_slug_url}}";
</script>
{% endblock pass_slug %}
<script src="http://givemecoupon.com:8000/static/js/app.js" type="text/javascript" charset="utf-8" async defer></script>
</body>
</html>
What is the problem???
After hours of search I managed to solve the problem.
I just changed
<script src="http://givemecoupon.com:8000/static/js/app.js" type="text/javascript" charset="utf-8" async defer></script>
to:
<script src="http://givemecoupon.com:8000/static/js/app.js" type="text/javascript"></script>
it was SublimeText snippet that created the first code which caused the problem.
It looks the async is what causes the AngularJs to not work in Chrome & break the whole script!
Asynchronous execution <script async>
Don’t care when the script will be available? Asynchronous is the best of both worlds: HTML parsing may continue and the script will be executed as soon as it’s ready. I’d recommend this for scripts such as Google Analytics.
Then after fixing the Async problem I got CORS error
XMLHttpRequest cannot load http://givemecoupon.com:8000/api/?format=json&slug=arduino-sbs. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.givemecoupon.com:8000' is therefore not allowed access.
Which I solved by the help of https://github.com/ottoyiu/django-cors-headers/
by adding these lines to the settings.py file:
INSTALLED_APPS = [
#django generated
#3rd party
'rest_framework',
'corsheaders', #manage Cross-side API requests
#my apps
]
MIDDLEWARE = [
#cors headers
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
#django generated
]
# CORS allow anyone to get API calls
CORS_ORIGIN_ALLOW_ALL = True
I'm trying to get a simple AJAX request to work in Symfony2. It seems that the request is never reaching the controller. Instead of alerting the number '123', the AJAX request returns the HTML of the current page (index.html.twig). I have a feeling that since the pattern of the AJAX route is pointing to the current page I am on, the AJAX response is being filled with the output of the current page rather than getting the response from the Controller. Is there any way that I can get just the value from the Controller? Any help would be greatly appreciated.
Here is my Twig template (index.html.twig):
{% extends 'base.html.twig' %}
{% block body %}
<a href="123" class="test">Test</div>
{% endblock %}
{% block javascripts %}
{% javascripts '#AppBundle/js/jquery.js' %}
<script type="text/javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery('.test').on('click', function(){
var id = jQuery(this).attr('href');
jQuery.post('{{path('task_ajax')}}',
{data: id},
function(response){
alert(response);
}
);
return false;
});
});
</script>
{% endblock %}
Here is my routing.yml file:
task_ajax:
pattern: /jobs
defaults: { _controller: AppBundle:Ajax:task }
requirements:
_method: POST
Here is my controller:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class AjaxController extends Controller
{
public function taskAction() {
$request = $this->container->get('request');
$data = $request->query->get('data');
return new Response($data);
}
}
check routing order. Move Ajax route to first place.
I am trying to fetch JSON response from my Django App but response is not working :
Here is my views.py :
import json
import traceback
from django.http import HttpResponse
from django.template import Context,loader
from django.template import RequestContext
from django.shortcuts import render_to_response
from eScraperInterfaceApp.eScraperUtils import eScraperUtils
#------------------------------------------------------------------------------
def renderError(message):
"""
This function displays error message
"""
t = loader.get_template("error.html")
c = Context({ 'Message':message})
return HttpResponse(t.render(c))
def index(request,template = 'index.html',
page_template = 'index_page.html' ):
"""
This function handles request for index page
"""
try:
context = {}
contextList = []
utilsOBJ = eScraperUtils()
q = {"size" : 300000,
"query" :{ "match_all" : { "boost" : 1.2 }}}
results = utilsOBJ.search(q)
for i in results['hits']['hits']:
contextDict = i['_source']
contextDict['image_paths'] = json.loads(contextDict['image_paths'])
contextList.append(contextDict)
context.update({'contextList':contextList,'page_template': page_template})
if request.is_ajax(): # override the template and use the 'page' style instead.
template = page_template
return render_to_response(
template, context, context_instance=RequestContext(request) )
except :
return renderError('%s' % (traceback.format_exc()))
def search (request,template = 'index.html',
page_template = 'index_page.html' ):
try:
if request.method == 'POST':
context = {}
contextList = []
keyWord = request.POST['keyWord']
print keyWord
utilsOBJ = eScraperUtils()
results = utilsOBJ.search('productCategory:%(keyWord)s or productSubCategory:%(keyWord)s or productDesc:%(keyWord)s' % {'keyWord' : keyWord})
for i in results['hits']['hits']:
contextDict = i['_source']
contextDict['image_paths'] = json.loads(contextDict['image_paths'])
contextList.append(contextDict)
context.update({'contextList':contextList,'page_template': page_template})
if request.is_ajax(): # override the template and use the 'page' style instead.
template = page_template
return HttpResponse(template, json.dumps(context), context_instance=RequestContext(request),
content_type="application/json")
except :
return renderError('%s' % (traceback.format_exc()))
#------------------------------------------------------------------------------
index.html :
<html>
<head>
<title>Fashion</title>
<link rel="stylesheet" type="text/css" href="static/css/style.css">
</head>
<body>
<form action="">
{% csrf_token %}
<input id="query" type="text" />
<input id="search-submit" type="button" value="Search" />
</form>
<div class="product_container">
<ul class="product_list">
<div class="endless_page_template">
{% include page_template %}
</div>
</ul>
</div>
<div class="product_container">
<ul class="product_list">
<div class="endless_page_template">
{% include page_template %}
</div>
</ul>
</div>
{% block js %}
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="static/js/endless_on_scroll.js"></script>
<script src="static/js/endless-pagination.js"></script>
<script>
$.endlessPaginate({paginateOnScroll: true,
endless_on_scroll_margin : 10,
paginateOnScrollChunkSize: 5
});</script>
<script type="text/javascript">
$("#search-submit").click(function() {
// Get the query string from the text field
var query = $("#query").val();
alert(query);
data = { 'csrfmiddlewaretoken': '{{ csrf_token }}', 'keyWord' : query};
// Retrieve a page from the server and insert its contents
// into the selected document.
$.ajax({
type: "POST",
url: '/search/',
data: data,
success: function(context,status){
alert("Data: " + context + "Status: " + status);
},
error: function( error ){
alert( error );
},
contentType: "application/json; charset=utf-8",
dataType: 'json',
});
});
</script>
{% endblock %}
</body>
</html>
index_page.html :
{% load endless %}
{% paginate 10 contextList %}
{% for item in contextList %}
<li >
<a href="{{ item.productURL }}" ><img src="/images/{{ item.image_paths.0 }}/" height="100" width="100" border='1px solid'/></a>
<br>
<span class="price">
<span class="mrp">MRP : {{ item.productMRP}}</span>
{% if item.productPrice %}<br>
<span class="discounted_price">Offer Price : {{ item.productPrice}}</span>
{%endif%}
</span>
</li>
{% endfor %}
{% show_more "even more" "working" %}
What i want is to fetch the server response and update the contextList .... But it is not working....
The problem you're facing is that you're trying to update a Django template compiled variable called context_list this won't happen magically by using AJAX since Django has precompiled the HTML you're serving when you hit the page initially.
You can't circumvent this but you can work around it and below I'll show you two ways of doing it.
Your success() handler in your jQuery script that you have that manages the search gets the newly HTML that you've recompiled but you're not using it anywhere ergo, it won't work.
In this case you're just logging some stuff out to console. (This would be where you're using option #2.)
You have two options as I see it. (It's a lot of code so I'll give you the pointers and not the implementation, won't strip you of all the fun!)
I'll give you a quick run down of both and then you can decide which way you want to take.
You can use jQuerys $.load() method in order to fetch a
recompiled template.
You can use $.getJSON() and get the
objects in JSON format and then update the HTML accordingly.
First option - $.load()
#Django
def search_view(keyword, request):
search_result = SearchModel.objects.filter(some_field=keyword)
t = loader.get_template("some_result_template.html")
c = Context({'search_results':search_result})
return render(t.render(c))
#jQuery
$("#result").load("url_to_your_search", { 'keyword': 'search_me' } );
Second option - $.getJSON()
#Django
from django.core import serializers
def search_view(keyword, request):
search_result = SearchModel.objects.filter(some_field=keyword)
json_data = serializers.serialize('json', search_result)
return HttpResponse(json_data, mimetype='application/json')
#jQuery
$.getJSON('url_to_your_search_view', { keyword: "search_me" }, function(json_data) {
var items = [];
$.each(json_data, function(key, val) {
items.push('<li id="' + key + '">' + val + '</li>');
});
$('<ul/>', {
'class': 'my-new-list',
html: items.join('')
}).appendTo('body');
});
Now the JSON code is taken straight from the docs, so you would have to modify it but you get the gist of what's needed in order for yours to work.