Django Google API Client Error..Trying to implement an application that adds events to a users google calendar after authentication - django

While using the OAuth authentication method I am unable to retrieve the authentication code from the url. Google redirects me to a url with code appended to its end
..I think I am making a mistake in the regex because after authenticating myself and giving the app the permission to manage my calendar I am redirected to
http://127.0.0.1/Main/addloanpremium/?code=4/2wi1hu0Gv8YZuKo79kc-kjCmw7qj0W2EyLYa3qzIe7w# ..How do I extract the code from the url..The above url opens on a new tab and due to my urls.py displays the same form page however when I try to access the 'code' value using request.GET.get('code') it does not give any value..
Exception Value: Can't convert 'NoneType' object to str implicitly
My views.py looks like:
def addloanpremium(request):
if request.method == 'GET':
loan=Loan()
premium=Premium()
user=request.user
#form=loan_pre_form(request.POST)
if(request.GET.get('typelp')=='loan'):
loan.user=user
#premium.user=user
#lots of code here..all values from fields are stored into the db
if(request.GET.get("iscalendar")=="True"):
print(" \n \n entered iscalendar =true \n")
print(" \n \n entered calendar now trying to add event \n \n")
SCOPES = 'https://www.googleapis.com/auth/calendar'
flow=auth2client.client.flow_from_clientsecrets('C:/Users/ghw/Desktop/Expenze/Main/client_secrets.json',SCOPES,redirect_uri='http://127.0.0.1:8000/Main/addloanpremium/')
storage = oauth2client.file.Storage('credentials.dat')
credentials = storage.get()
if not credentials or credentials.invalid:
auth_uri = flow.step1_get_authorize_url()
print("\n value of auth uri is "+auth_uri+"\n")
webbrowser.open(auth_uri)
auth_code = request.GET.get('code')
print("\n value of auth code is "+auth_code+"\n")
### I am getting the auth code wrong
credentials = flow.step2_exchange(auth_code)
storage.put(credentials)
http = httplib2.Http()
http = credentials.authorize(http)
CAL = build('calendar', 'v3', http=http)
.
.#lots of code here
.
return render(request,'Main/loan_pre_form.html',{})
my Main:urls.py:
url(r'^addloanpremium/$',views.addloanpremium,name='addloanpremium'),
my loan_pre_form.html contains
<form action=" {% url 'Main:addloanpremium' %}" method="GET">
Any help would be highly appreciated.

Based from this SO answer, functions return a value and if you don't specify one, it will implicitly return None. Exception Value: Can't convert 'NoneType' object to str implicitly error means that you try to use a NoneType as a string. I guess the problem is that this function doesn't return anything (in one case) so it actually returns NoneType.
Here is an example from Google documentation.
You can also check on these related threads:
Python 3.3.3 TypeError : Can't convert 'NoneType' object to str implicitly
TypeError: Can't convert 'int' object to str implicitly
Django: "Can't convert 'Name' object to str implicitly" on saving object
Hope this helps!

Related

django web scraper beautiful soup and urllib

iam using scraper to get some data for my database iam adding products from the other site using this code
def scrape():
path=''
counter=0
session= requests.Session()
session.headers={
"User-Agent":"my user agent"
}
url='some url'
content=session.get(url,verify=False).content
soup=bs4.BeautifulSoup(content,'html.parser')
result=soup.find_all('div',{'class':'column column-block block-list-large single-item'})
for i in result:
counter+=1
name=i.find_all('h1',{'class':'itemTitle'})[0]
price=i.find('h3',{'class':'itemPrice'})
image=i.find('img',{'class':'img-size-medium imageUrl'})['data-src']
path=f'pics/{counter}.jpg'
img=path
barcode=f'name{counter}'
description='this is my product'
urllib.request.urlretrieve(image,path)
cat=category.objects.get(id=140)
br=branch.objects.get(id=8)
products.objects.create(name=name.text,Barcode=barcode,branch=br,image=img,
description=description,price=price,category=cat)
scrape()
its downloading the image of the product but iam getting an error after this
value = value.resolve_expression(self.query, allow_joins=False, for_save=True)
TypeError: 'NoneType' object is not callable
This is most likely price=pricein create operation. price is not a valid value for the field it is an object. Can you change that part with price=price.text ?

DRF/Multi-tenant - How to specify tenant host (domain) in unit tests?

Environment - Django, Rest Framework, Multi-tenant.
In my unit tests, I'm trying to hit an endpoint in a tenant schema (not in Public). It is failing because the host is (for example) example.com instead of demo1.example.com.
I've Googled (of course) but can't find how to specify the domain name part of a request. Other posts I've found say it's best to use "reverse" to get the URL instead of hard-coding it.
Here's my test:
def test_private_valid_authorization(self):
self.demo1_tenant.activate()
user = User.objects.get(username='test.admin#example.com')
token, created = Token.objects.get_or_create(user=user)
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
response = self.client.get(reverse('clients:client-list'))
self.assertEqual(response.status_code, 200, "Expected status code: 200; got %s." % response.status_code)
Tenants (and their schemas) are created in the setUpTestData() method, so self.demo1_tenant.activate() simply connects to an already existing schema.
Users are imported via fixtures, hence the "get" instead of "create_user".
I get the following result:
AssertionError: 403 != 200 : Expected status code: 200; got 403.
Adding diagnostic displays to the code, I've narrowed it down to the process_request() method of django_tenants/middleware/main.py - specifically the hostname_from_request() method. If I put 'demo1.example.com' instead of using the return value from hostname_from_request (which is just 'example.com') my test succeeds. I hope someone can point me in the right direction here.
I found the answer...
To specify the base domain name, you do this:
self.client.defaults['SERVER_NAME'] = 'example.com'
So I can use example.com or demo1.example.com or demo2.example.com as needed.
Updated 16 Apr 2022 to expand on my answer.
In my setUp() method of the TenantTestCase class (which in my case is extended from APITestCase to incorporate rest_framework), I initialize a dict() of client objects, which I can then reference in each test depending on which schema I want to access at that time.
Here's my setUp() method:
class TenantTestCase(APITestCase):
schemas = ['public', 'demo']
clients = {} # One client instance for each schema
tenants = {} # One tenant instance for each schema
# user_tokens holds user tokens for each combination of
# schema and user in the form <schema>.<user> - where
# <user> is the first part of the username (without
# "#example.com"). Example: demo.test.owner
# Note: user_tokens are initialized in the setUpTestData()
# method.
user_tokens = {}
def setUp(self):
for schema in self.schemas:
self.clients[schema] = APIClient()
subdomain = '' if schema == 'public' else schema + '.'
self.clients[schema].defaults['SERVER_NAME'] = '%s%s' % (subdomain, settings.API_DOMAIN)
super().setUp()
... other setup classes ...
Here's an example of how I use the above in a unittest:
class TenantPermissionTests(TenantTestCase):
def test_domain_view_access(self):
# Check unauthorized access.
self.tenants['public'].activate()
self.clients['public'].credentials(HTTP_AUTHORIZATION='Token invalid-token')
response = self.clients['public'].get(reverse('tenants:domain-list'))
self.assertEqual(response.status_code, 403, "Expected status code: 403; got %s." % response.status_code)
# Check view_domain (view own) access - Domains list.
self.tenants['demo'].activate()
self.clients['demo'].credentials(HTTP_AUTHORIZATION='Token ' + self.user_tokens['demo.test.owner'])
response = self.clients['demo'].get(reverse('tenants:domain-list'))
self.assertEqual(response.status_code, 200, "Expected status code: 200; got %s." % response.status_code)
self.assertEqual(len(response.data['results']), 1, "Expected 1 object; got %i." % len(response.data['results']))
I should explain the reason for the last line of the test case... We are not allowing tenants to create their own domains. If a tenant creates an account for "Acme", the system will create a single domain for them of acme.our-saas-site.com. So the test will only ever expect a single domain per tenant.

Storing the data in the console in a database and accessing it

I'm building a web app which accesses the location of the user when a particular button is pressed for this I'm using the HTML geolocation api.
Below is the location.js file:
`var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
x.innerHTML = "Latitude: " + position.coords.latitude +
"<br>Longitude: " + position.coords.longitude;
console.log(position.coords.latitude)
console.log(position.coords.longitude)
}
Below is the snippet of the HTML file:
<button onclick="getLocation()">HELP</button>
<p id="demo"></p>
<script src="../static/location.js"></script>
What I want to do is send this information ( i.e. longitude/latitude of the user ) to list of e-mails associated with that user but I don't know how to store this data and access this after the button is pressed.
It would be of great use if someone could get me started on how to save this data corresponding to the user and accessing it from the database.
If you want to store this info to a django DB, then if might be easier to do this in a django view. This could be a RedirectView that just redirects to the same view after the button is clicked.
I have previously used a downloaded DB of the GeoLite2-City.mmdb, which might not always be up to date, but is ok.
You can get the ip address of a request in django with the ipware library. Then convert it into a python IP object in IPy. You can then use the geoip library to get the information out of the DB.
Import the following libraries;
from ipware.ip import get_ip
from IPy import IP
import geoip2.database
Then your method to get the IPs would be something like
class MyRedirectView(RedirectView)
def get_redirect_url(self, request, *args, **kwargs):
## Write some code to handle the redirect url first ##
ip_address = get_ip(self.request)
"""Ensure that the IP address is a valid IP first"""
try:
IP(ip_address)
except Exception:
logger.exception("GEOIP2 error: ")
"""Then get the IP location"""
geo_path = settings.GEOIP_PATH
reader = geoip2.database.Reader(geo_path + '/GeoLite2-City.mmdb')
try:
response = reader.city(ip_address)
city = response.city.name
country = response.country.name
### Some code here to save to your DB
return super(MyRedirectView, self).get_redirect_url(*args, **kwargs)
If you need a much more accurate IP location service you could involce an API call to something like http://ip-api.com/. But then you would have to wait for this response before serving the next view.

OAuth1Session Encoding of URL Django

Alright, I need some assistance with what I think is an easy question, but after digging for quite a while I'll leave it to you brilliant people to show me why I'm not!
I'm attempting to access a provider api, with the URL I'm attempting to access being: .../proposals/AnotherTestProposal/city_furniture/items?filter=description$CONT"Ag" :: this is the way they want it passed, and it should return all items who's descriptions contain the string "Ag". I know I have two of those currently.
I'm using Django 1.9.4, and requests_oauthlib to create an OAuth1Session, I'm doing this successfully also, because I can access resources without URL params. The trouble is I can't get the "?filter=description..." part to encode properly and it's giving me a 401.
When I render the .contents to HTML I get:
{"status": 404, "message": "", "data": [], "arguments": {"id": "items?filter=description$CONT\"Ag\"", "collection": "city_furniture"}}
which is telling telling me that the "AG" part is being escaped, which I don't want. As in: I don't want \"Ag\", I want "Ag".
So, my question: how can I pass the URL, with params, so they are not containing the slashes as these are causing the URL to be invalid and therefore keeping me from accessing the data correctly?
Other, possibly irrelevant info:
The params part of the URL string I'm passing to the OAuth1Session object now is: '/city_furniture/items%3Ffilter%3Ddescription%24CONT%22Ag%22'
An example of filtering from the API's website: proposals/master/roads/items?filter=description$CONT"highway"
I tried passing an 'encoding' arg to .get (in order to change the encoding used by to_native_string) but requests rejected it saying it was an invalid arg
Per comments, some additional code.
Using a function name get_protected_code() to get the OAuth info passed correctly, then in views.py:
api_filter_url = settings.IW_API_MODEL_COLLECTION + '/' + model_id + '/' + settings.IW_API_PROPOSAL_COLLECTION + '/' + proposal_name + '/city_furniture/items%3Ffilter%3Ddescription%24CONT%22Ag%22'
json_model_info_pull = get_protected_data(api_filter_url)
find_vendor = json_model_info_pull.content
def get_protected_data(url_str):
## ...stuffs to create OAuth1Session...
adsk_pull = OAuth1Session(key,
client_secret=secret,
resource_owner_key=oauth_token_use,
resource_owner_secret=oauth_token_secret_use,
)
headers = {'accept': 'application/vnd.autodesk.infraworks-v1+json'}
api_url = settings.ADSK_IW_INFO_BASE_URL + url_str
json_model_info_pull = adsk_pull.get(api_url, headers=headers)
return json_model_info_pull
Looks like you're passing the parameters incorrectly by appending them to the end of the URL in URL-encoding, which requests is respecting as intentional, and the API endpoint is translating in an unintended manner.
From the documentation for requests, you should provide a params keyword argument to requests.get: a dict containing the key-value pairs that should be encoded and sent as the query string for the request. For example, to run a query against the GitHub API, we can pass an API token as a query parameter:
requests.get('http://api.github.com/user',
params={ 'access_token' : MY_OAUTH_TOKEN })
The resultant request will contain a query string with an access_token parameter set to the value stored in MY_OAUTH_TOKEN and escaped properly, as needed. (Such tokens typically contain = characters, for example, that are invalid inside query string values.)

Why am I getting this Authentication required error even though I am using my client id and client secret for the Foursquare API?

I getting back into Python and wanted to use the pyfoursquare package to access the Foursquare API. I'm trying to get information about venues using the venues method in the API class. I'm primarily trying to find out whether a venue page is verified with Foursquare or not. When I provide my client id, client secret, and venue id I keep getting back an error that states "Authentication required", which doesn't makes sense because I'm providing that information. Any help would be great. Thank you.
import pyfoursquare as foursquare
client_id = ""
client_secret = ""
callback = ""
auth = foursquare.OAuthHandler(client_id, client_secret, callback)
api = foursquare.API(auth)
result = api.venues("4e011a3e62843b639cfa9449")
print result[0].name
Let me know if you would like to see the error message. Thanks again.
I believe you are skipping the step of grabbing your OAuth2 access token, so you're not technically authenticated.
Have a look at the following instructions, under "How to Use It":
https://github.com/marcelcaraciolo/foursquare
The lines that might be useful to you are:
#First Redirect the user who wish to authenticate to.
#It will be create the authorization url for your app
auth_url = auth.get_authorization_url()
print 'Please authorize: ' + auth_url
#If the user accepts, it will be redirected back
#to your registered REDIRECT_URI.
#It will give you a code as
#https://YOUR_REGISTERED_REDIRECT_URI/?code=CODE
code = raw_input('The code: ').strip()
#Now your server will make a request for
#the access token. You can save this
#for future access for your app for this user
access_token = auth.get_access_token(code)
print 'Your access token is ' + access_token