Kevin
Kevin

Reputation: 4351

Update Profile Picture Google Apps / Google App Engine

I am writing a small app on Google App Engine to update the pictures in our users profiles. It takes there username and an image and does a put to there profile, uploading the image. Here is what i have:

import atom.data
import gdata.data
import gdata.contacts.client
import gdata.contacts.data
import cgi
import wsgiref.handlers

from google.appengine.api import users
from google.appengine.ext import webapp

email = '[email protected]'
password = 'password'
domain = 'domain.com'

gd_client = gdata.contacts.client.ContactsClient(domain=domain)
gd_client.ClientLogin(email, password, 'photoUpdate')


class PicPage(webapp.RequestHandler):
    def get(self):
        self.response.out.write("""<html><head><title>Sasaki Photo Uploader</title>
                                    <link type="text/css" rel="stylesheet" href="/stylesheets/form.css"></head>
                                    <body>
                                    <form action="/" enctype="multipart/form-data" method="post">
                                    <div><label>Person Name</label></div>
                                    <div><textarea name="name" rows="2" columns "60"></textarea></div>
                                    <div><label>Image</label></div>
                                    <div><input type="file" name="img"/></div>
                                    <div><input type="submit" value="Upload" /></div>
                                    </form>
                                    </body>
                                    </html>""")

    def post(self):
        person_name = self.request.get('name')
        img_img = self.request.get('img')
        profile_url = 'https://www.google.com/m8/feeds/photos/profile/domain.com/%s' % person_name
        media_object = img_img
        print(profile_url)
        profile = gd_client.GetProfile(profile_url)
        print(profile)
        gd_client.ChangePhoto(media_object, profile)
        self.redirect('/')

def main():
  application = webapp.WSGIApplication(
                                       [('/', PicPage)
                                        ],
                                       debug=True)

  wsgiref.handlers.CGIHandler().run(application)

if __name__=="__main__":
  main()

When I run this it returns the error:

 Traceback (most recent call last):
  File "C:\Program Files (x86)\Google\google_appengine\google\appengine\ext\webapp\_webapp25.py", line 703, in __call__
    handler.post(*groups)
  File "C:\GAE_Local_Files\picupload\sasakipic.py", line 40, in post
    profile = gd_client.GetProfile(profile_url)
  File "C:\GAE_Local_Files\picupload\gdata\contacts\client.py", line 375, in get_profile
    auth_token=auth_token, **kwargs)
  File "C:\GAE_Local_Files\picupload\gdata\client.py", line 652, in get_entry
    desired_class=desired_class, **kwargs)
  File "C:\GAE_Local_Files\picupload\gdata\client.py", line 278, in request
    version=get_xml_version(self.api_version))
  File "C:\GAE_Local_Files\picupload\atom\core.py", line 520, in parse
    tree = ElementTree.fromstring(xml_string)
  File "<string>", line 106, in XML
ParseError: not well-formed (invalid token): line 1, column 0

i am not sure if this is because I am passing the profile url off as a string or uploading the pic wrong. Any advice, much appreciated.

EDIT Added full stack trace

Upvotes: 3

Views: 1224

Answers (3)

JSDBroughton
JSDBroughton

Reputation: 4034

I have tried to use the script you posted above including the fix you've highlighted as a fix to the invalid token error, but for me that sprang a different error:

UnknownSize: Each part of the body must have a known size.

To rectify this I amended the line

media_object = img_img

to read:

media_object = gdata.data.MediaSource(img_img)

because the MediaSource object has implicit size and is one of accepted client.py parameters – except that a new error now springs up and i'm at a loss to know what to address.

RequestError: Server responded with: 400, Malformed Content-Type

i have tried adding a content-type='image/*' parameter to the ChangePhoto but it has no affect.

Any ideas?

error stream:

Traceback (most recent call last):
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/webapp/_webapp25.py", line 703, in __call__
    handler.post(*groups)
  File " ...trim... /main.py", line 126, in post
    gd_client.ChangePhoto(media_object, profile_url, content_type='image/*')
  File " ...trim... /gdata/contacts/client.py", line 288, in change_photo
    ifmatch_header=ifmatch_header, **kwargs)
  File " ...trim... /atom/client.py", line 142, in put
    http_request=http_request, data=data, **kwargs)
  File " ...trim... /gdata/client.py", line 319, in request
    RequestError)
RequestError: Server responded with: 400, Malformed Content-Type

Upvotes: 1

Kevin
Kevin

Reputation: 4351

Well, the answer lies in the issue that I pointed out in my edit. That line:

profile = gd_client.GetProfile(profile_url)

Is trying to query the picture located in the profile, but if it doesn't exist it breaks, so I edit the try statement to pass the profile url directly:

try:
    gd_client.ChangePhoto(media_object, profile_url)

It worked great. This is a great little tool to update picture using App Engine, and you don't even have to upload it, just run it locally on a app engine test server. I want to try and add some functionality, like image cropping, or resizing before the upload, large images look fine on the contact profile page, but can get distorted in the chat image.

Upvotes: 1

Moishe Lettvin
Moishe Lettvin

Reputation: 8471

I believe the problem is you need to authenticate the request to the API. See here:

http://code.google.com/apis/contacts/docs/3.0/developers_guide.html#Auth

and look at this sample app for how the oauth flow works:

http://code.google.com/p/google-api-python-client/source/browse/samples/appengine/main.py

You might have a second problem that you need to specify the content-type of the file, but the first step is authenticating to get the profile.

Upvotes: 0

Related Questions