Google Maps Directions API Calculating Incorrect Route - python

I'm creating a web application using Flask and the Google Maps Directions API, where I ask the user to enter a starting point, waypoints, and a destination, and pass this data to the Google API to render a map illustrating the user's route. No matter what these data points are, however, the API keeps interpreting all 3 data points as a single location, and renders the map accordingly. The rendered map is
here, where you can also see what this exact wrong address is.
The relevant source files are as follows:
run.py (where I call my template to render the Google Map in the "/finalize_new_trip" route):
from os.path import abspath, dirname, join
from flask import flash, Flask, Markup, redirect, render_template, url_for, request
from flask.ext.sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
import os
import googlemaps
from datetime import datetime
import json
_cwd = dirname(abspath(__file__))
SECRET_KEY = 'flask-session-insecure-secret-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + join(_cwd, 'TripLogger.db')
SQLALCHEMY_ECHO = True
WTF_CSRF_SECRET_KEY = 'this-should-be-more-random' #TODO: RANDOMIZE THIS KEY
app = Flask(__name__)
app.config.from_object(__name__)
db = SQLAlchemy(app)
class Trip(db.Model):
__tablename__ = 'trips'
id = db.Column(db.Integer, primary_key=True)
trip_name = db.Column(db.String, unique=True)
origin = db.Column(db.String)
destination = db.Column(db.String)
waypoints = db.Column(db.String) #SEE IF ANOTHER DATA TYPE IS BETTER- IDEALLY WANT TO STORE LIST OF STRINGS
def __init__(self, trip_name, origin, destination, waypoints):
self.trip_name = trip_name
self.origin = origin
self.destination = destination
self.waypoints = waypoints
def __repr__(self):
return '<Trip %r(name = %r, origin = %r, destination = %r)>' % (self.id, self.trip_name, self.origin, self.destination)
def __str__(self):
return self.trip_name
#CONSIDER CUSTOM VALIDATORS TO ENSURE THAT TRIP_NAME IS UNIQUE FOR A SPECIFIC USER/ACCOUNT
class TripForm(FlaskForm):
trip_name = StringField('Trip Name', validators=[DataRequired()])
origin = StringField('Origin', validators=[DataRequired()])
destination = StringField('Destination', validators=[DataRequired()])
waypoints = StringField('Waypoint')
#app.route("/", methods = ["GET", "POST"])
def index():
trip_form = TripForm(request.form)
if trip_form.validate_on_submit():
flash("Submitted new trip form, now redirecting to /finalize_new_trip to render map...")
return redirect(url_for("complete_trip", trip_name = trip_form.trip_name.data, origin = trip_form.origin.data, destination = trip_form.destination.data, waypoints = trip_form.waypoints.data))
return render_template("index.html", trip_form = trip_form)
# Route to display trip on map (in future, to modify trip details, e.g. waypoints)
#app.route("/finalize_new_trip", methods=["POST"])
def complete_trip():
return render_template("finish_new_trip.html", trip_name = request.args.get("trip_name"), origin = request.args.get("origin"), destination = request.args.get("destination"), waypoints = request.args.get("waypoints"))
# Store new trip data in database
#app.route("/store_trip", methods=["POST"])
def store_trip():
trip_data = request.form("trip_data")
return json.loads(trip_data)[0]
def query_to_list(query, include_field_names=True):
"""Turns a SQLAlchemy query into a list of data values."""
column_names = []
for i, obj in enumerate(query.all()):
if i == 0:
column_names = [c.name for c in obj.__table__.columns]
if include_field_names:
yield column_names
yield obj_to_list(obj, column_names)
def obj_to_list(sa_obj, field_order):
"""Takes a SQLAlchemy object - returns a list of all its data"""
return [getattr(sa_obj, field_name, None) for field_name in field_order]
if __name__ == "__main__":
app.debug = True
db.create_all()
app.run()
finish_new_trip.html (the template using the Google Maps API to render the map):
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<script async defer
src="https://maps.googleapis.com/maps/api/js?v=3.exp&key=<MY_API_KEY>&callback=getOriginCoordinates" type="text/javascript">
</script>
<title>Finish New Trip</title>
</head>
<body>
<div id="trip_name">
<h1> {{ trip_name }} </h1>
</div>
<div id="map"></div>
<div id="right-panel">
<div>
<form>
<input type="submit" id="finish-new-trip-button" value="Add My Trip!">
</form>
</div>
<div id="directions-panel"></div>
</div>
<script>
//Wrapper function to AJAX POST request that passes data back to server.
/*function passTripData() {
document.getElementById("finish-new-trip-button").onclick = function() {
// Retrieve data from displayed map (after user makes changes) after testing
$.post( "{{ url_for('store_trip') }}", {
trip_data: {
"trip_name": "{{ trip_name }}",
"origin": "{{ origin }}",
"destination": "{{ destination }}",
"waypoints": [
"{{ waypoints }}"
]
}
});
}
}*/
//Use Geocoding API to get coordinates for origin, destination, and waypoints.
function getOriginCoordinates() {
//passTripData(); // function that sets onClick event for "Submit" button
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': "{{ origin }}"}, function(results, status) {
if (status == 'OK') {
originCoordinates = results[0].geometry.location;
console.log("Origin coords: " + "lat = " + originCoordinates.lat() + ", lng = " + originCoordinates.lng());
getDestCoordinates(originCoordinates);
} else {
alert('Geocode of origin was not successful for the following reason: ' + status);
}
});
}
function getDestCoordinates(originCoords) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': "{{ destination }}"}, function(results, status) {
if (status == 'OK') {
destCoordinates = results[0].geometry.location;
console.log("Dest coords: " + "lat = " + destCoordinates.lat() + ", lng = " + destCoordinates.lng());
getWaypointCoordinates(originCoords, destCoordinates);
} else {
alert('Geocode of destination was not successful for the following reason: ' + status);
}
});
}
function getWaypointCoordinates(originCoords, destCoords) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': "{{ waypoints }}"}, function(results, status) {
if (status == 'OK') {
waypointCoordinates = results[0].geometry.location;
console.log("Waypoint coords: " + "lat = " + waypointCoordinates.lat() + ", lng = " + waypointCoordinates.lng());
initMap(originCoords, destCoords, waypointCoordinates);
} else {
alert('Geocode of waypoints was not successful for the following reason: ' + status);
}
});
}
function initMap(originCoords, destCoords, waypointCoords) {
var directionsService = new google.maps.DirectionsService;
var directionsDisplay = new google.maps.DirectionsRenderer;
//Center map between origin and destination.
//TEST LATLNG OBJECTS FIRST
console.log("Origin: lat=" + originCoords.lat() + ", lng=" + originCoords.lng());
console.log("Destination: lat=" + destCoords.lat() + ", lng=" + destCoords.lng());
var mapLatitudeCenter = (originCoords.lat() + destCoords.lat())/2;
var mapLongitudeCenter = (originCoords.lng() + destCoords.lng())/2;
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 6,
center: {lat: mapLatitudeCenter, lng: mapLongitudeCenter}
});
directionsDisplay.setMap(map);
calculateAndDisplayRoute(directionsService, directionsDisplay, originCoords, destCoords, waypointCoords);
}
function calculateAndDisplayRoute(directionsService, directionsDisplay, originCoords, destCoords, waypointCoords) {
var waypts = [];
waypts.push({
location: waypointCoords,
stopover: true
});
directionsService.route({
origin: originCoords,
destination: destCoords,
waypoints: waypts,
optimizeWaypoints: true,
travelMode: 'DRIVING'
}, function(response, status) {
if (status === 'OK') {
directionsDisplay.setDirections(response);
var route = response.routes[0];
var summaryPanel = document.getElementById('directions-panel');
summaryPanel.innerHTML = '';
// For each route, display summary information.
for (var i = 0; i < route.legs.length; i++) {
var routeSegment = i + 1;
summaryPanel.innerHTML += '<b>Route Segment: ' + routeSegment +
'</b><br>';
summaryPanel.innerHTML += route.legs[i].start_address + ' to ';
summaryPanel.innerHTML += route.legs[i].end_address + '<br>';
summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>';
}
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
</script>
</body>
</html>
When I test my app locally, the console displays the latitude/longitude coordinates of each data point (origin, destination, and waypoints), and they all correspond to this incorrect address in Italy. I also previously received a "404" error message, but I am not receiving that now.
I attempted to regenerate my API key and use this new key, but this didn't fix the problem. How can I go about resolving this issue?

When I change complete_trip(): to
#app.route("/finalize_new_trip", methods=["GET"])
def complete_trip():
return render_template("finish_new_trip.html", trip_name ='new trip', origin='new york, ny', destination='los angeles, california', waypoints='topeka, kansas')
and request the page, the results are perfect (a trip from NY to LA that goes through Topeka). You've got a lot of commented out code everywhere. I have a feeling that somewhere you're not getting the data you expect (for example, we can't see the form HTML used to post this data).

Related

Django multiple AJAX queries for images

I made a button the user can click and it makes a AJAX GET request to the backend class Image. The response is the image url. I paste the url into a image tag and display it on the template
models.py
class Image(models.Model):
img = models.ImageField()
views.py
def ajax(request):
from django.http import JsonResponse
if request.is_ajax():
image = request.FILES.get('data')
aaa = Image.objects.get(id=1)
aaa = str(aaa.img.url)
return JsonResponse({'image_query': aaa}, status=200)
return render(request, 'components/ajax.html')
AJAX (template)
<button id="getData" class="btn btn-success">Get</button>
<div id="seconds"></div>
...
<script>
$(document).ready(function () {
$('#getData').click(function () {
$.ajax({
url: 'ajax',
type: 'get',
data: {
data: $(this).text()
},
success: function (response) {
$('#seconds').append('<img src="' + response.image_query + '" width="300">')
}
})
})
})
</script>
Everything works fine and the image is rendered to the template! Now I dont want to query the image with ID=1, I want all images to be fetched to the template.
I tried to do by adding a for loop into the views.py but it only returns the first element.
if request.is_ajax():
image = request.FILES.get('data')
for i in range(1, 5):
aaa = Image.objects.get(id=i)
aaa = str(aaa.img.url)
return JsonResponse({'image_query': aaa}, status=200)
I don't know what to modify that it will query every image in the DB. Does someone have a solution for my problem?
It's returning the only first element because you are returning inside the for loop, instead put the return 1 indent back, and put all of the urls in array.
... # code you have above
images = []
for i in range(1, 5):
aaa = Image.objects.get(id=i)
images.append(aaa.img.url)) # append the url to the list
return JsonResponse({'images': images}, status=200) # 1 indent back
as a result you'll have to change your javascript code like this.
const success = (response) => {
// Loop through each of the links
for (let i=0; i < response.image_query.length; i++) {
$('#seconds').append('<img src="' + response.image_query[i] + '" width="300">')
}
}
// ... Other code
$.ajax({
url: 'ajax',
type: 'get',
data: {
data: $(this).text()
},
success: success,
});
Also be carefull with $('#seconds').append('<img src="' + response.image_query + '" width="300">'), appending raw html could cause an XSS attack, if you are not completely sure that response.image_query is safe do not do this. (Since it is a url it should be escaped)

Accessing a Moodle server's API via Python's request library

I'm trying to access the web services of a Moodle installation I have using Python's requests library. I have the API's documentation and an example project written in php (I haven't looked at php before and is way more difficult than I would expect for me to understand) but am really struggling to properly format the request. The site is returning invalid paramater detected so I'm pretty sure my endpoint, authorization token, and server config is working and it's just the format of the data that is letting me down.
First here is the error...
<?xml version="1.0" encoding="UTF-8" ?>
<EXCEPTION class="invalid_parameter_exception">
<ERRORCODE>invalidparameter</ERRORCODE>
<MESSAGE>Invalid parameter value detected</MESSAGE>
</EXCEPTION>
And now my code...
import requests
target = 'http://example.com/moodle/webservice/rest/server.php?'
moodle_create_token = 'xxx'
moodle_enrol_token = 'yyy'
url_payload = {
"wstoken":moodle_create_token,
"wsfunction":"core_user_create_users"
}
###not sure if I should just be passing this as a dict or some deeper more layered struct
payload = {
"username":"testuser",
"password":'testpass',
"firstname":'testf',
"lastname":'testl',
"email":"test#example.com",
"idnumber":"1234"
}
###not sure how to include the payload as the last argument in the function (currently data=)
###I feel like at this point I've just been throwing random data at it and hoping something sticks haha.
r=requests.post(target, params=url_payload, data=payload)
Here is the site's documentation
moodle api general structure
moodle api XML-RPC (PHP structure)
moodle api REST (POST parameters)
moodle response format 1
moodle response format 2
Finally the example in php.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>V6</title>
</head>
<body>
<?php
//load curl.php
require_once('curl.php');
function randomPassword() //according to Moodle password requirements
{
$part1 = "";
$part2 = "";
$part3 = "";
//alphanumeric LOWER
$alphabet = "abcdefghijklmnopqrstuwxyz";
$password_created = array(); //remember to declare $pass as an array
$alphabetLength = strlen($alphabet) - 1; //put the length -1 in cache
for ($i = 0; $i < 3; $i++)
{
$pos = rand(0, $alphabetLength); // rand(int $min , int $max)
$password_created[] = $alphabet[$pos];
}
$part1 = implode($password_created); //turn the array into a string
//echo"<br/>part1 = $part1";
//alphanumeric UPPER
$alphabet = "ABCDEFGHIJKLMNOPQRSTUWXYZ";
$password_created = array(); //remember to declare $pass as an array
$alphabetLength = strlen($alphabet) - 1; //put the length -1 in cache
for ($i = 0; $i < 3; $i++)
{
$pos = rand(0, $alphabetLength); // rand(int $min , int $max)
$password_created[] = $alphabet[$pos];
}
$part2 = implode($password_created); //turn the array into a string
//echo"<br/>part2 = $part2";
//alphanumeric NUMBER
$alphabet = "0123456789";
$password_created = array(); //remember to declare $pass as an array
$alphabetLength = strlen($alphabet) - 1; //put the length -1 in cache
for ($i = 0; $i < 2; $i++)
{
$pos = rand(0, $alphabetLength); // rand(int $min , int $max)
$password_created[] = $alphabet[$pos];
}
$part3 = implode($password_created); //turn the array into a string
//echo"<br/>part3 = $part3";
$password = $part1 . $part2 . $part3 . "#";
return $password;
}
function getCDate()
{
$format = "Ymd";
$fulldate = date($format);
//echo"<br/>fulldate = $fulldate";
return $fulldate;
}
function enrol($user_id, $course_id)
{
$role_id = 5; //assign role to be Student
$domainname = 'http://www.yoursite.eu'; //paste your domain here
$wstoken = '8486ed14f3ghjec8967a0229d0a28zzz'; //here paste your enrol token
$wsfunctionname = 'enrol_manual_enrol_users';
$enrolment = array( 'roleid' => $role_id, 'userid' => $user_id, 'courseid' => $course_id );
$enrolments = array($enrolment);
$params = array( 'enrolments' => $enrolments );
header('Content-Type: text/plain');
$serverurl = $domainname . "/webservice/rest/server.php?wstoken=" . $wstoken . "&wsfunction=" . $wsfunctionname;
$curl = new curl;
$restformat = ($restformat == 'json')?'&moodlewsrestformat=' . $restformat:'';
$resp = $curl->post($serverurl . $restformat, $params);
print_r($resp);
}
function getUserDetails()
{
$firstname = "TestUser";
$lastname = "TestUser";
$email = "TestUser#zzz.gr";
$city = "Thessaloniki";
$country = "EL";
$description= "ZZZ";
//assign username
//get first two letters of name and surname
//$strlength_user = strlen($firstname);
//$strlength_pass = strlen($lastname);
$rest_firstname = substr($firstname, 0, 2);
$rest_lastname = substr($lastname, 0, 2);
$part1 = $rest_firstname . $rest_lastname;
$part1 = strtolower($part1);
//echo"<br/>part1 = $part1";
$dt = getCDate();
$part2 = substr($dt, -4);
//echo"<br/>part2 = $part2";
$username = $part1 . "." . $part2;
echo"<br/>Username = $username";
//assign password
$password = randomPassword();
echo"<br/>Password = $password";
//call WS core_user_create_user of moodle to store the new user
$domainname = 'http://www.yoursite.eu';
$wstoken = 'ed1f6d3ebadg372f95f28cd96bd43zzz'; //here paste your create user token
$wsfunctionname = 'core_user_create_users';
//REST return value
$restformat = 'xml';
//parameters
$user1 = new stdClass();
$user1->username = $username;
$user1->password = $password;
$user1->firstname = $firstname;
$user1->lastname = $lastname;
$user1->email = $email;
$user1->auth = 'manual';
$user1->idnumber = 'numberID';
$user1->lang = 'en';
$user1->city = $city;
$user1->country = $country;
$user1->description = $description;
$users = array($user1);
$params = array('users' => $users);
//REST call
header('Content-Type: text/plain');
$serverurl = $domainname . "/webservice/rest/server.php?wstoken=" . $wstoken . "&wsfunction=" . $wsfunctionname;
$curl = new curl;
$restformat = ($restformat == 'json')?'&moodlewsrestformat=' . $restformat:'';
$resp = $curl->post($serverurl . $restformat, $params);
print_r($resp);\
//get id from $resp
$xml_tree = new SimpleXMLElement($resp);
print_r($xml_tree);
$value = $xml_tree->MULTIPLE->SINGLE->KEY->VALUE;
$user_id = intval(sprintf("%s",$value));
echo"<br/>user_id number = $user_id";
//enrol_manual_enrol_users
//for($i = 64; $i < 70; $i++) //where 64,65,66,67,68,69 are the six ids of the six courses of phase 1
for($i = 64; $i < 65; $i++)
{
echo "\nThe user has been successfully enrolled to course " . $i;
$course_id = $i;
enrol($user_id, $course_id);
}
}
getUserDetails();
?>
</body>
</html>
Here is an example drawn from mrcinv/moodle_api.py that shows the usage of Python's requests to hit the Moodle Web Services API:
from requests import get, post
# Module variables to connect to moodle api
KEY = "SECRET API KEY"
URL = "https://moodle.site.com"
ENDPOINT="/webservice/rest/server.php"
def rest_api_parameters(in_args, prefix='', out_dict=None):
"""Transform dictionary/array structure to a flat dictionary, with key names
defining the structure.
Example usage:
>>> rest_api_parameters({'courses':[{'id':1,'name': 'course1'}]})
{'courses[0][id]':1,
'courses[0][name]':'course1'}
"""
if out_dict==None:
out_dict = {}
if not type(in_args) in (list,dict):
out_dict[prefix] = in_args
return out_dict
if prefix == '':
prefix = prefix + '{0}'
else:
prefix = prefix + '[{0}]'
if type(in_args)==list:
for idx, item in enumerate(in_args):
rest_api_parameters(item, prefix.format(idx), out_dict)
elif type(in_args)==dict:
for key, item in in_args.items():
rest_api_parameters(item, prefix.format(key), out_dict)
return out_dict
def call(fname, **kwargs):
"""Calls moodle API function with function name fname and keyword arguments.
Example:
>>> call_mdl_function('core_course_update_courses',
courses = [{'id': 1, 'fullname': 'My favorite course'}])
"""
parameters = rest_api_parameters(kwargs)
parameters.update({"wstoken": KEY, 'moodlewsrestformat': 'json', "wsfunction": fname})
response = post(URL+ENDPOINT, parameters).json()
if type(response) == dict and response.get('exception'):
raise SystemError("Error calling Moodle API\n", response)
return response
class CourseList():
"""Class for list of all courses in Moodle and order them by id and idnumber."""
def __init__(self):
# TODO fullname atribute is filtered
# (no <span class="multilang" lang="sl">)
courses_data = call('core_course_get_courses')
self.courses = []
for data in courses_data:
self.courses.append(Course(**data))
self.id_dict = {}
self.idnumber_dict = {}
for course in self.courses:
self.id_dict[course.id] = course
if course.idnumber:
self.idnumber_dict[course.idnumber] = course
def __getitem__(self, key):
if 0<= key < len(self.courses):
return self.courses[key]
else:
raise IndexError
def by_id(self, id):
"Return course with given id."
return self.id_dict.get(id)
def by_idnumber(self, idnumber):
"Course with given idnumber"
return self.idnumber_dict.get(idnumber)
def update_courses(courses_to_update, fields):
"Update a list of courses in one go."
if not ('id' in fields):
fields.append('id')
courses = [{k: c.__dict__[k] for k in fields} for c in courses_to_update]
return call("core_course_update_courses",
courses = courses)
.. and also shows how to define custom classes for Course. In the same fashion one could create classes for User, Grades, etc.
Furthermore, there are some wrapper modules on PyPi, e.g. moodle, and moodle-ws-client.
Okay so I found a solution that works but I suspect it is a bit hodgepodge and not utilizing requests library to its fullest.
What I did was pass all the arguments as parameters in the url.
target = 'http://example.com/moodle/webservice/rest/server.php'
moodle_create_token = 'xxx'
payload = {
"wstoken":moodle_create_token,
"moodlewsrestformat":"json", #just to get response as json
"wsfunction":"core_user_create_users",
"users[0][username]":"testusername",
"users[0][password]":'testpassword',
"users[0][firstname]":'testfirstname',
"users[0][lastname]":'testlastname',
"users[0][email]":"testemail#example.com",
"users[0][idnumber]":"0000001"
}
r=requests.post(target, params=payload)
Obviously I won't usually have the data hard-coded as strings but apparently the list of dictionaries for url params will be.
I have made a python library named moodlepy
pip install moodlepy
Its easy to use, example
from moodle import Moodle
target = 'http://example.com/moodle/webservice/rest/server.php'
moodle_create_token = 'xxx'
moodle = Moodle(target, moodle_create_token)
r = moodle(
'core_user_create_users',
username="testusername",
password='testpassword',
firstname='testfirstname',
lastname='testlastname',
email="testemail#example.com",
idnumber="0000001"
) # return the data (dict, list, etc)
You can also use typed response, for example calling core_webservice_get_site_info
site_info = moodle.core.webservice.get_site_info()
site_info.username
site_info.version
Note: Not all functions are implemented (yet).

Create Google Cloud Function using API in Python

I'm working on a project with Python(3.6) & Django(1.10) in which I need to create a function at Google cloud using API request.
How can upload code in the form of a zip archive while creating that function?
Here's what I have tried:
From views.py :
def post(self, request, *args, **kwargs):
if request.method == 'POST':
post_data = request.POST.copy()
post_data.update({'user': request.user.pk})
form = forms.SlsForm(post_data, request.FILES)
print('get post request')
if form.is_valid():
func_obj = form
func_obj.user = request.user
func_obj.project = form.cleaned_data['project']
func_obj.fname = form.cleaned_data['fname']
func_obj.fmemory = form.cleaned_data['fmemory']
func_obj.entryPoint = form.cleaned_data['entryPoint']
func_obj.sourceFile = form.cleaned_data['sourceFile']
func_obj.sc_github = form.cleaned_data['sc_github']
func_obj.sc_inline_index = form.cleaned_data['sc_inline_index']
func_obj.sc_inline_package = form.cleaned_data['sc_inline_package']
func_obj.bucket = form.cleaned_data['bucket']
func_obj.save()
service = discovery.build('cloudfunctions', 'v1', http=views.getauth(), cache_discovery=False)
requ = service.projects().locations().functions().generateUploadUrl(parent='projects/' + func_obj.project + '/locations/us-central1', body={})
resp = requ.execute()
print(resp)
try:
auth = views.getauth()
# Prepare Request Body
req_body = {
"CloudFunction": {
"name": func_obj.fname,
"entryPoint": func_obj.entryPoint,
"timeout": '60s',
"availableMemoryMb": func_obj.fmemory,
"sourceArchiveUrl": func_obj.sc_github,
},
"sourceUploadUrl": func_obj.bucket,
}
service = discovery.build('cloudfunctions', 'v1beta2', http=auth, cachce_dicovery=False)
func_req = service.projects().locations().functions().create(location='projects/' + func_obj.project
+ '/locations/-',
body=req_body)
func_res = func_req.execute()
print(func_res)
return HttpResponse('Submitted',)
except:
return HttpResponse(status=500)
return HttpResponse('Sent!')
Updated Code below:
if form.is_valid():
func_obj = form
func_obj.user = request.user
func_obj.project = form.cleaned_data['project']
func_obj.fname = form.cleaned_data['fname']
func_obj.fmemory = form.cleaned_data['fmemory']
func_obj.entryPoint = form.cleaned_data['entryPoint']
func_obj.sourceFile = form.cleaned_data['sourceFile']
func_obj.sc_github = form.cleaned_data['sc_github']
func_obj.sc_inline_index = form.cleaned_data['sc_inline_index']
func_obj.sc_inline_package = form.cleaned_data['sc_inline_package']
func_obj.bucket = form.cleaned_data['bucket']
func_obj.save()
#######################################################################
# FIRST APPROACH FOR FUNCTION CREATION USING STORAGE BUCKET
#######################################################################
file_name = os.path.join(IGui.settings.BASE_DIR, 'media/archives/', func_obj.sourceFile.name)
print(file_name)
service = discovery.build('cloudfunctions', 'v1')
func_api = service.projects().locations().functions()
url_svc_req = func_api.generateUploadUrl(parent='projects/'
+ func_obj.project
+ '/locations/us-central1',
body={})
url_svc_res = url_svc_req.execute()
print(url_svc_res)
upload_url = url_svc_res['uploadUrl']
print(upload_url)
headers = {
'content-type': 'application/zip',
'x-goog-content-length-range': '0,104857600'
}
print(requests.put(upload_url, headers=headers, data=func_obj.sourceFile.name))
auth = views.getauth()
# Prepare Request Body
name = "projects/{}/locations/us-central1/functions/{}".format(func_obj.project, func_obj.fname,)
print(name)
req_body = {
"name": name,
"entryPoint": func_obj.entryPoint,
"timeout": "3.5s",
"availableMemoryMb": func_obj.fmemory,
"sourceUploadUrl": upload_url,
"httpsTrigger": {},
}
service = discovery.build('cloudfunctions', 'v1')
func_api = service.projects().locations().functions()
response = func_api.create(location='projects/' + func_obj.project + '/locations/us-central1',
body=req_body).execute()
pprint.pprint(response)
Now the function has been created successfully, but it fails because the source code doesn't upload to storage bucket, that's maybe something wrong at:
upload_url = url_svc_res['uploadUrl']
print(upload_url)
headers = {
'content-type': 'application/zip',
'x-goog-content-length-range': '0,104857600'
}
print(requests.put(upload_url, headers=headers, data=func_obj.sourceFile.name))
In the request body you have a dictionary "CloudFunction" inside the request. The content of "CloudFunction" should be directly in request.
request_body = {
"name": parent + '/functions/' + name,
"entryPoint": entry_point,
"sourceUploadUrl": upload_url,
"httpsTrigger": {}
}
I recomend using "Try this API" to discover the structure of projects.locations.functions.create .
"sourceArchiveUrl" and "sourceUploadUrl" can't appear together. This is explained in Resorce Cloud Function:
// Union field source_code can be only one of the following:
"sourceArchiveUrl": string,
"sourceRepository": { object(SourceRepository) },
"sourceUploadUrl": string,
// End of list of possible types for union field source_code.
In the rest of the answer I assume that you want to use "sourceUploadUrl". It requires you to pass it a URL returned to you by .generateUploadUrl(...).execute(). See documentation:
sourceUploadUrl -> string
The Google Cloud Storage signed URL used for source uploading,
generated by [google.cloud.functions.v1.GenerateUploadUrl][]
But before passing it you need to upload a zip file to this URL:
curl -X PUT "${URL}" -H 'content-type:application/zip' -H 'x-goog-content-length-range: 0,104857600' -T test.zip
or in python:
headers = {
'content-type':'application/zip',
'x-goog-content-length-range':'0,104857600'
}
print(requests.put(upload_url, headers=headers, data=data))
This is the trickiest part:
the case matters and it should be lowercase. Because the signature is calculated from a hash (here)
you need 'content-type':'application/zip'. I deduced this one logically, because documentation doesn't mention it. (here)
x-goog-content-length-range: min,max is obligatory for all PUT requests for cloud storage and is assumed implicitly in this case. More on it here
104857600, the max in previous entry, is a magical number which I didn't found mentioned anywhere.
where data is a FileLikeObject.
I also assume that you want to use the httpsTrigger. For a cloud function you can only choose one trigger field. Here it's said that trigger is a Union field. For httpsTrigger however that you can just leave it to be an empty dictionary, as its content do not affect the outcome. As of now.
request_body = {
"name": parent + '/functions/' + name,
"entryPoint": entry_point,
"sourceUploadUrl": upload_url,
"httpsTrigger": {}
}
You can safely use 'v1' instead of 'v1beta2' for .create().
Here is a full working example. It would be to complicated if I presented it to you as part of your code, but you can easily integrate it.
import pprint
import zipfile
import requests
from tempfile import TemporaryFile
from googleapiclient import discovery
project_id = 'your_project_id'
region = 'us-central1'
parent = 'projects/{}/locations/{}'.format(project_id, region)
print(parent)
name = 'ExampleFunctionFibonacci'
entry_point = "fibonacci"
service = discovery.build('cloudfunctions', 'v1')
CloudFunctionsAPI = service.projects().locations().functions()
upload_url = CloudFunctionsAPI.generateUploadUrl(parent=parent, body={}).execute()['uploadUrl']
print(upload_url)
payload = """/**
* Responds to any HTTP request that can provide a "message" field in the body.
*
* #param {Object} req Cloud Function request context.
* #param {Object} res Cloud Function response context.
*/
exports.""" + entry_point + """= function """ + entry_point + """ (req, res) {
if (req.body.message === undefined) {
// This is an error case, as "message" is required
res.status(400).send('No message defined!');
} else {
// Everything is ok
console.log(req.body.message);
res.status(200).end();
}
};"""
with TemporaryFile() as data:
with zipfile.ZipFile(data, 'w', zipfile.ZIP_DEFLATED) as archive:
archive.writestr('function.js', payload)
data.seek(0)
headers = {
'content-type':'application/zip',
'x-goog-content-length-range':'0,104857600'
}
print(requests.put(upload_url, headers=headers, data=data))
# Prepare Request Body
# https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions#resource-cloudfunction
request_body = {
"name": parent + '/functions/' + name,
"entryPoint": entry_point,
"sourceUploadUrl": upload_url,
"httpsTrigger": {},
"runtime": 'nodejs8'
}
print('https://{}-{}.cloudfunctions.net/{}'.format(region,project_id,name))
response = CloudFunctionsAPI.create(location=parent, body=request_body).execute()
pprint.pprint(response)
Open and upload a zip file like following:
file_name = os.path.join(IGui.settings.BASE_DIR, 'media/archives/', func_obj.sourceFile.name)
headers = {
'content-type': 'application/zip',
'x-goog-content-length-range': '0,104857600'
}
with open(file_name, 'rb') as data:
print(requests.put(upload_url, headers=headers, data=data))

Django - CSRF verification failed using Google oauth2

https://developers.google.com/identity/sign-in/web/backend-auth
Im following the google tutorial to sign in and authenticate with a backend server, what im ultimately trying to do is restrict the access to the application to only the emails with the domain name of a company.
I think the problems lies in the javascript:
function onSignIn(googleUser) {
// Useful data for your client-side scripts:
var profile = googleUser.getBasicProfile();
console.log("ID: " + profile.getId()); // Don't send this directly to your server!
console.log('Full Name: ' + profile.getName());
console.log('Given Name: ' + profile.getGivenName());
console.log('Family Name: ' + profile.getFamilyName());
console.log("Image URL: " + profile.getImageUrl());
console.log("Email: " + profile.getEmail());
// The ID token you need to pass to your backend:
var id_token = googleUser.getAuthResponse().id_token;
console.log("ID Token: " + id_token);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://yourbackend.example.com/tokensignin');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=' + id_token);
};
heres the python part:
def autenticar_usuario(request):
# (Receive token by HTTPS POST)
CLIENT_ID="CLIENT_ID"
APPS_DOMAIN_NAME='example.com'
if request.method == 'POST':
token = request.POST["idtoken"]
try:
idinfo = client.verify_id_token(token, CLIENT_ID)
# If multiple clients access the backend server:
if idinfo['aud'] not in [CLIENT_ID]:
raise crypt.AppIdentityError("Unrecognized client.")
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise crypt.AppIdentityError("Wrong issuer.")
if idinfo['hd'] != APPS_DOMAIN_NAME:
raise crypt.AppIdentityError("Wrong hosted domain.")
except crypt.AppIdentityError:
# Invalid token
userid = idinfo['sub']
return render(request, 'log_in.html')
and the error says:
'CSRF verification failed. Request aborted.'
html:
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Log In</title>
</head>
<body>
<meta name="google-signin-client_id" content="">
<div class="g-signin2" data-onsuccess="onSignIn"></div>
<script>
function onSignIn(googleUser) {
// Useful data for your client-side scripts:
var profile = googleUser.getBasicProfile();
console.log("ID: " + profile.getId()); // Don't send this directly to your server!
console.log('Full Name: ' + profile.getName());
console.log('Given Name: ' + profile.getGivenName());
console.log('Family Name: ' + profile.getFamilyName());
console.log("Image URL: " + profile.getImageUrl());
console.log("Email: " + profile.getEmail());
// The ID token you need to pass to your backend:
var id_token = googleUser.getAuthResponse().id_token;
console.log("ID Token: " + id_token);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://yourbackend.example.com/tokensignin');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=' + id_token);
};
</script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
</body>

Channel API Presence not triggering

I'm trying to handle disconnect / connect states using Presence in the Channel API.
Here are some of my code.
app.yaml
handlers:
- url: /(.*\.(gif|png|jpg|ico|js|css))
static_files: \1
upload: (.*\.(gif|png|jpg|ico|js|css))
- url: .*
script: main.py
inbound_services:
- channel_presence
main.py
class onConnect(webapp.RequestHandler):
def post(self):
for user in users:
users = User.all().fetch(1000)
client = client_id = self.request.get('from')
channel.send_message(user.channel,' connected');
class onDisconnect(webapp.RequestHandler):
def post(self):
Mainpage()
for user in users:
users = User.all().fetch(1000)
client = client_id = self.request.get('from')
channel.send_message(user.channel, ' disconnected');
application = webapp.WSGIApplication(
[('/', MainPage),
('/_ah/channel/connected/',onConnect),
('/_ah/channel/disconnected/',onDisconnect),
('/chat',handleChat)],
debug=True)
Javascript
<script>
openChannel = function(){
var token = '{{ token }}';
var channel = new goog.appengine.Channel(token);
var handler = {
'onopen': onOpened,
'onmessage': onMessage,
'onerror': function() {},
'onclose': function() {}
};
var socket = channel.open(handler);
socket.onopen = onOpened;
socket.onmessage = onMessage;
var chat = document.getElementById('chatinput');
chat.onkeyup = function(e){
if(e.keyCode == 13){
sendChat(this.value);
this.value = '';
}
}
}
sendMessage = function(path, opt_param) {
if (opt_param) {
path += '?' + opt_param;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', path, true);
xhr.send();
};
onOpened = function(){
console.log('Channel Opened');
var chatlog = document.getElementById('chatlog');
var msg = document.createElement('div');
msg.innerHTML = 'Channel Opened';
chatlog.appendChild(msg);
sendMessage('/chat','m='+'A User Joined.');
}
onMessage = function(m){
console.log('Message Recieved');
var chatlog = document.getElementById('chatlog');
var msg = document.createElement('div');
var d = new Date();
msg.innerHTML = d.toLocaleTimeString() + ': ' + m.data;
chatlog.appendChild(msg);
}
sendChat = function(msg){
console.log(msg);
sendMessage('/chat','m='+msg);
}
openChannel();
</script>
Using this code, connnect and disconnect is not triggering when a user closes their browser or whatever.
Are there anything wrong with this code?
Yes, route list is wrong. Put ('/', MainPage) in the end of the routes list. From webapp2 URI routing guide:
When the application receives a request, it tries to match each one in order until one matches, and then call the corresponding handler.

Categories