SampleSyncAdapter sample code.

This commit is contained in:
Megha Joshi
2009-11-18 14:54:03 -08:00
parent c220077d52
commit 00bf0f0296
29 changed files with 2969 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
application: samplesyncadapter
version: 1
runtime: python
api_version: 1
handlers:
- url: /auth
script: main.py
- url: /login
script: main.py
- url: /fetch_friend_updates
script: main.py
- url: /fetch_status
script: main.py
- url: /add_user
script: dashboard.py
- url: /edit_user
script: dashboard.py
- url: /users
script: dashboard.py
- url: /delete_friend
script: dashboard.py
- url: /edit_user
script: dashboard.py
- url: /add_credentials
script: dashboard.py
- url: /user_credentials
script: dashboard.py
- url: /add_friend
script: dashboard.py
- url: /user_friends
script: dashboard.py

View File

@@ -0,0 +1,273 @@
#!/usr/bin/python2.5
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Defines Django forms for inserting/updating/viewing data
to/from SampleSyncAdapter datastore."""
import cgi
import datetime
import os
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.db import djangoforms
from model import datastore
import wsgiref.handlers
class UserForm(djangoforms.ModelForm):
"""Represents django form for entering user info."""
class Meta:
model = datastore.User
class UserInsertPage(webapp.RequestHandler):
"""Inserts new users. GET presents a blank form. POST processes it."""
def get(self):
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/add_user">'
'<table>')
# This generates our shopping list form and writes it in the response
self.response.out.write(UserForm())
self.response.out.write('</table>'
'<input type="submit">'
'</form></body></html>')
def post(self):
data = UserForm(data=self.request.POST)
if data.is_valid():
# Save the data, and redirect to the view page
entity = data.save(commit=False)
entity.put()
self.redirect('/users')
else:
# Reprint the form
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/">'
'<table>')
self.response.out.write(data)
self.response.out.write('</table>'
'<input type="submit">'
'</form></body></html>')
class UserEditPage(webapp.RequestHandler):
"""Edits users. GET presents a form prefilled with user info
from datastore. POST processes it."""
def get(self):
id = int(self.request.get('user'))
user = datastore.User.get(db.Key.from_path('User', id))
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/edit_user">'
'<table>')
# This generates our shopping list form and writes it in the response
self.response.out.write(UserForm(instance=user))
self.response.out.write('</table>'
'<input type="hidden" name="_id" value="%s">'
'<input type="submit">'
'</form></body></html>' % id)
def post(self):
id = int(self.request.get('_id'))
user = datastore.User.get(db.Key.from_path('User', id))
data = UserForm(data=self.request.POST, instance=user)
if data.is_valid():
# Save the data, and redirect to the view page
entity = data.save(commit=False)
entity.updated = datetime.datetime.utcnow()
entity.put()
self.redirect('/users')
else:
# Reprint the form
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/edit_user">'
'<table>')
self.response.out.write(data)
self.response.out.write('</table>'
'<input type="hidden" name="_id" value="%s">'
'<input type="submit">'
'</form></body></html>' % id)
class UsersListPage(webapp.RequestHandler):
"""Lists all Users. In addition displays links for editing user info,
viewing user's friends and adding new users."""
def get(self):
users = datastore.User.all()
template_values = {
'users': users
}
path = os.path.join(os.path.dirname(__file__), 'templates', 'users.html')
self.response.out.write(template.render(path, template_values))
class UserCredentialsForm(djangoforms.ModelForm):
"""Represents django form for entering user's credentials."""
class Meta:
model = datastore.UserCredentials
class UserCredentialsInsertPage(webapp.RequestHandler):
"""Inserts user credentials. GET shows a blank form, POST processes it."""
def get(self):
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/add_credentials">'
'<table>')
# This generates our shopping list form and writes it in the response
self.response.out.write(UserCredentialsForm())
self.response.out.write('</table>'
'<input type="submit">'
'</form></body></html>')
def post(self):
data = UserCredentialsForm(data=self.request.POST)
if data.is_valid():
# Save the data, and redirect to the view page
entity = data.save(commit=False)
entity.put()
self.redirect('/users')
else:
# Reprint the form
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/add_credentials">'
'<table>')
self.response.out.write(data)
self.response.out.write('</table>'
'<input type="submit">'
'</form></body></html>')
class UserFriendsForm(djangoforms.ModelForm):
"""Represents django form for entering user's friends."""
class Meta:
model = datastore.UserFriends
exclude = ['deleted', 'username']
class UserFriendsInsertPage(webapp.RequestHandler):
"""Inserts user's new friends. GET shows a blank form, POST processes it."""
def get(self):
user = self.request.get('user')
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/add_friend">'
'<table>')
# This generates our shopping list form and writes it in the response
self.response.out.write(UserFriendsForm())
self.response.out.write('</table>'
'<input type = hidden name = "user" value = "%s">'
'<input type="submit">'
'</form></body></html>' % user)
def post(self):
data = UserFriendsForm(data=self.request.POST)
if data.is_valid():
user = self.request.get('user')
# Save the data, and redirect to the view page
entity = data.save(commit=False)
entity.username = user
query = datastore.UserFriends.all()
query.filter('username = ', user)
query.filter('friend_handle = ', entity.friend_handle)
result = query.get()
if result:
result.deleted = False
result.updated = datetime.datetime.utcnow()
result.put()
else:
entity.deleted = False
entity.put()
self.redirect('/user_friends?user=' + user)
else:
# Reprint the form
self.response.out.write('<html><body>'
'<form method="POST" '
'action="/add_friend">'
'<table>')
self.response.out.write(data)
self.response.out.write('</table>'
'<input type="submit">'
'</form></body></html>')
class UserFriendsListPage(webapp.RequestHandler):
"""Lists all friends for a user. In addition displays links for removing
friends and adding new friends."""
def get(self):
user = self.request.get('user')
query = datastore.UserFriends.all()
query.filter('deleted = ', False)
query.filter('username = ', user)
friends = query.fetch(50)
template_values = {
'friends': friends,
'user': user
}
path = os.path.join(os.path.dirname(__file__),
'templates', 'view_friends.html')
self.response.out.write(template.render(path, template_values))
class DeleteFriendPage(webapp.RequestHandler):
"""Processes delete friend request."""
def get(self):
user = self.request.get('user')
friend = self.request.get('friend')
query = datastore.UserFriends.all()
query.filter('username =', user)
query.filter('friend_handle =', friend)
result = query.get()
result.deleted = True
result.updated = datetime.datetime.utcnow()
result.put()
self.redirect('/user_friends?user=' + user)
def main():
application = webapp.WSGIApplication(
[('/add_user', UserInsertPage),
('/users', UsersListPage),
('/add_credentials', UserCredentialsInsertPage),
('/add_friend', UserFriendsInsertPage),
('/user_friends', UserFriendsListPage),
('/delete_friend', DeleteFriendPage),
('/edit_user', UserEditPage)
],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,14 @@
indexes:
# This index.yaml is automatically updated whenever the dev_appserver
# detects that a new type of query is run. If you want to manage the
# index.yaml file manually, remove the above marker line (the line
# saying "# AUTOGENERATED"). If you want to manage some indexes
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.
- kind: UserFriends
properties:
- name: username
- name: updated

View File

@@ -0,0 +1,173 @@
#!/usr/bin/python2.5
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Handlers for Sample SyncAdapter services.
Contains several RequestHandler subclasses used to handle post operations.
This script is designed to be run directly as a WSGI application.
Authenticate: Handles user requests for authentication.
FetchFriends: Handles user requests for friend list.
FriendData: Stores information about user's friends.
"""
import cgi
from datetime import datetime
from django.utils import simplejson
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from model import datastore
import wsgiref.handlers
class Authenticate(webapp.RequestHandler):
"""Handles requests for login and authentication.
UpdateHandler only accepts post events. It expects each
request to include username and password fields. It returns authtoken
after successful authentication and "invalid credentials" error otherwise.
"""
def post(self):
self.username = self.request.get('username')
self.password = self.request.get('password')
password = datastore.UserCredentials.get(self.username)
if password == self.password:
self.response.set_status(200, 'OK')
# return the password as AuthToken
self.response.out.write(password)
else:
self.response.set_status(401, 'Invalid Credentials')
class FetchFriends(webapp.RequestHandler):
"""Handles requests for fetching user's friendlist.
UpdateHandler only accepts post events. It expects each
request to include username and authtoken. If the authtoken is valid
it returns user's friend info in JSON format.It uses helper
class FriendData to fetch user's friendlist.
"""
def post(self):
self.username = self.request.get('username')
self.password = self.request.get('password')
self.timestamp = None
timestamp = self.request.get('timestamp')
if timestamp:
self.timestamp = datetime.strptime(timestamp, '%Y/%m/%d %H:%M')
password = datastore.UserCredentials.get(self.username)
if password == self.password:
self.friend_list = []
friends = datastore.UserFriends.get_friends(self.username)
if friends:
for friend in friends:
friend_handle = getattr(friend, 'friend_handle')
if self.timestamp is None or getattr(friend, 'updated') > self.timestamp:
if (getattr(friend, 'deleted')) == True:
friend = {}
friend['u'] = friend_handle
friend['d'] = 'true'
friend['i'] = str(datastore.User.get_user_id(friend_handle))
self.friend_list.append(friend)
else:
FriendsData(self.friend_list, friend_handle)
else:
if datastore.User.get_user_last_updated(friend_handle) > self.timestamp:
FriendsData(self.friend_list, friend_handle)
self.response.set_status(200)
self.response.out.write(toJSON(self.friend_list))
else:
self.response.set_status(401, 'Invalid Credentials')
class FetchStatus(webapp.RequestHandler):
"""Handles requests fetching friend statuses.
UpdateHandler only accepts post events. It expects each
request to include username and authtoken. If the authtoken is valid
it returns status info in JSON format.
"""
def post(self):
self.username = self.request.get('username')
self.password = self.request.get('password')
password = datastore.UserCredentials.get(self.username)
if password == self.password:
self.status_list = []
friends = datastore.UserFriends.get_friends(self.username)
if friends:
for friend in friends:
friend_handle = getattr(friend, 'friend_handle')
status_text = datastore.User.get_user_status(friend_handle)
user_id = datastore.User.get_user_id(friend_handle)
status = {}
status['i'] = str(user_id)
status['s'] = status_text
self.status_list.append(status)
self.response.set_status(200)
self.response.out.write(toJSON(self.status_list))
else:
self.response.set_status(401, 'Invalid Credentials')
def toJSON(self):
"""Dumps the data represented by the object to JSON for wire transfer."""
return simplejson.dumps(self.friend_list)
def toJSON(object):
"""Dumps the data represented by the object to JSON for wire transfer."""
return simplejson.dumps(object)
class FriendsData(object):
"""Holds data for user's friends.
This class knows how to serialize itself to JSON.
"""
__FIELD_MAP = {
'handle': 'u',
'firstname': 'f',
'lastname': 'l',
'status': 's',
'phone_home': 'h',
'phone_office': 'o',
'phone_mobile': 'm',
'email': 'e',
}
def __init__(self, friend_list, username):
obj = datastore.User.get_user_info(username)
friend = {}
for obj_name, json_name in self.__FIELD_MAP.items():
if hasattr(obj, obj_name):
friend[json_name] = str(getattr(obj, obj_name))
friend['i'] = str(obj.key().id())
friend_list.append(friend)
def main():
application = webapp.WSGIApplication(
[('/auth', Authenticate),
('/login', Authenticate),
('/fetch_friend_updates', FetchFriends),
('/fetch_status', FetchStatus),
],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,93 @@
#!/usr/bin/python2.5
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Represents user's contact information, friends and credentials."""
from google.appengine.ext import db
class User(db.Model):
"""Data model class to hold user objects."""
handle = db.StringProperty(required=True)
firstname = db.TextProperty()
lastname = db.TextProperty()
status = db.TextProperty()
phone_home = db.PhoneNumberProperty()
phone_office = db.PhoneNumberProperty()
phone_mobile = db.PhoneNumberProperty()
email = db.EmailProperty()
deleted = db.BooleanProperty()
updated = db.DateTimeProperty(auto_now_add=True)
@classmethod
def get_user_info(cls, username):
if username not in (None, ''):
query = cls.gql('WHERE handle = :1', username)
return query.get()
return None
@classmethod
def get_user_last_updated(cls, username):
if username not in (None, ''):
query = cls.gql('WHERE handle = :1', username)
return query.get().updated
return None
@classmethod
def get_user_id(cls, username):
if username not in (None, ''):
query = cls.gql('WHERE handle = :1', username)
return query.get().key().id()
return None
@classmethod
def get_user_status(cls, username):
if username not in (None, ''):
query = cls.gql('WHERE handle = :1', username)
return query.get().status
return None
class UserCredentials(db.Model):
"""Data model class to hold credentials for a Voiper user."""
username = db.StringProperty(required=True)
password = db.StringProperty()
@classmethod
def get(cls, username):
if username not in (None, ''):
query = cls.gql('WHERE username = :1', username)
return query.get().password
return None
class UserFriends(db.Model):
"""Data model class to hold user's friendlist info."""
username = db.StringProperty()
friend_handle = db.StringProperty(required=True)
updated = db.DateTimeProperty(auto_now_add=True)
deleted = db.BooleanProperty()
@classmethod
def get_friends(cls, username):
if username not in (None, ''):
query = cls.gql('WHERE username = :1', username)
friends = query.fetch(50)
return friends
return None

View File

@@ -0,0 +1,19 @@
<html>
<body>
<h1> Sample Sync Adapter </h1>
<p>
<h3> List of Users </h3>
<table>
{% for user in users %}
<tr><td>
<a
href="/edit_user?user={{ user.key.id}}">{{ user.firstname }}&nbsp; {{ user.lastname }} </a>
</td><td>&nbsp;&nbsp;<a href="/user_friends?user={{ user.handle }}">Friends</a> </td>
</tr>
{% endfor %}
</table>
</p>
<a href = "/add_user"> Insert More </a>

View File

@@ -0,0 +1,17 @@
<html>
<body>
<h1> Sample Sync Adapter </h1>
<p>
{{user}}'s friends
<table>
{% for friend in friends %}
<tr><td>
{{ friend.friend_handle }} </td><td> <a href="/delete_friend?user={{ user }}&friend={{friend.friend_handle}}">Remove</a>
</td></tr>
{% endfor %}
</table>
</p>
<a href = "/add_friend?user={{user}}"> Add More </a>