def PUT_users(request, format): """ Respond to a "PUT users" request. We let the caller update the details of the currently logged-in user. """ params = apiHelper.get_params(request, resource_name="user") error = apiHelper.check_params(params, required_params=["token"], optional_params=["latitude", "longitude", "username", "password"]) if error != None: return error token = params['token'] if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) if "latitude" in params: user.latitude = params['latitude'] if "longitude" in params: user.longitude = params['longitude'] if "username" in params: user.username = params['username'] if "password" in params: user.set_password(params['password']) user.updated_at = datetime.datetime.now() user.save() return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
def GET_messages_count(request, format): """ Respond to the "GET messages/count" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="message") error = apiHelper.check_params(params, required_params=["token"], optional_params=[]) if error != None: return error token = params['token'] # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Calculate the total number of messages for all profiles belonging to this # user. Note that we have to do this separately for the sending and # receiving profiles. sender_total = Message.objects.filter(sender__user=user).count() recipient_total = Message.objects.filter(recipient__user=user).count() count = sender_total + recipient_total # Finally, return the total back to the caller. return apiHelper.response(count, format=format, status=HTTP_RESPONSE_GET_OK)
def GET_user_id(request, format): """ Respond to the "GET user_id" request. This "hidden" API call translates from a profile ID to a user ID. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name=None) error = apiHelper.check_params(params, required_params=["token", "profile_id"], optional_params=[]) if error != None: return error token = params['token'] profile_id = params['profile_id'] # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Get the requested Profile object. try: profile = Profile.objects.get(id=profile_id) except Profile.DoesNotExist: return HttpResponseBadRequest("No such profile") # Return the user associated with that profile. return apiHelper.response(profile.user.id, format=format, status=HTTP_RESPONSE_GET_OK)
def POST_user_numbers(request, format): """ Respond to a "POST user_numbers" request. """ params = apiHelper.get_params(request, "user_number") error = apiHelper.check_params(params, required_params=["token", "number"], optional_params=["country"]) if error != None: return error token = params['token'] number = params['number'] if "country" in params: country = params['country'] else: country = "US" if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Ask the "phonenumbers" library to format the phone number into E.164 # format. phone_number = phonenumbers.parse(number, country) phone_format = phonenumbers.PhoneNumberFormat.E164 formatted_number = phonenumbers.format_number(phone_number, phone_format) # If we already have a UserNumber record for this phone number, delete the # old one. try: user_num = UserNumber.objects.get(number=formatted_number) except UserNumber.DoesNotExist: user_num = None if user_num != None: user_num.delete() # ??? What if the number is owned by someone else? # Create the UserNumber record for this phone number. user_num = UserNumber() user_num.user = user user_num.number = formatted_number user_num.code = utils.calc_random_digits(num_digits=4) user_num.verified = False user_num.created_at = datetime.datetime.now() user_num.updated_at = datetime.datetime.now() user_num.save() # Ask our SMS gateway to send a verification message to the phone. _send_verification_code(user_num) # Finally, return the newly-created user number back to the caller. return apiHelper.response({'user_number' : user_num.to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK)
def POST_attachments(request, format): """ Respond to the "POST attachments" request. We let the caller add an attachment to an existing message. """ # Get our request parameters. params = apiHelper.get_params(request, resource_name=None) error = apiHelper.check_params(params, required_params=["token", "message_id", "attachment"], optional_params=[]) if error != None: return error token = params['token'] message_id = params['message_id'] file = params['attachment'] # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Get the Message to add the attachment to. try: message = Message.objects.get(id=message_id) except Message.DoesNotExist: message = None if message == None: return HttpResponseBadRequest("No such message") # Add the attachment to this message. attachment = Attachment() attachment.message = message attachment.created_at = datetime.datetime.now() attachment.updated_at = datetime.datetime.now() attachment.save() attachment.attachment.save(file.name, file) # Send out a notification for the message. message.send_notifications() # Update the message. message.updated_at = datetime.datetime.now() message.save() # Finally, return a copy of the newly-created attachment back to the # caller. return apiHelper.response({'attachment' : attachment.to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK)
def PUT_user_numbers(request, format): """ Respond to a "PUT user_numbers" request. """ params = apiHelper.get_params(request, "user_number") error = apiHelper.check_params(params, required_params=["token", "id"], optional_params=["code"]) if error != None: return error token = params['token'] id = params['id'] if "code" in params: code = params['code'] else: code = None if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Get the UserNumber record with the specified ID. try: user_num = UserNumber.objects.get(id=id) except UserNumber.DoesNotExist: return HttpResponseBadRequest("No such number") if user_num.user != user: return HttpResponseBadRequest("Not your number") # If the caller didn't specify a code, or the code is incorrect, send # another code. if code == None or user_num.code != code: user_num.code = utils.calc_random_digits(num_digits=4) user_num.verified = False user_num.updated_at = datetime.datetime.now() user_num.save() _send_verification_code(user_num) else: # If we get here, the verification code is correct -> verify the # number. user_num.verified = True user_num.updated_at = datetime.datetime.now() user_num.save() # Finally, return an empty response back to the caller. return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
def PUT_conversations_unhide(request, format): """ Respond to the "PUT conversations/unhide" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="conversation") error = apiHelper.check_params(params, required_params=["token", "profile_id", "conversation_id"], optional_params=[]) if error != None: return error token = params['token'] # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Check that the profile and conversation both exist, that the profile is # part of the conversation and that the currently logged-in user owns the # profile. try: profile = Profile.objects.get(id=params['profile_id']) except Profile.DoesNotExist: return HttpResponseBadRequest("No such profile") try: conversation = Conversation.objects.get(id=params['conversation_id']) except Conversation.DoesNotExist: return HttpResponseBadRequest("No such conversation") if conversation.profile1 != profile and conversation.profile2 != profile: return HttpResponseBadRequest("Profile not in conversation") if profile.user != user: return HttpResponseBadRequest("Not your profile") # Unhide this conversation for the given profile. if profile == conversation.profile1: conversation.hidden1 = False elif profile == conversation.profile2: conversation.hidden2 = False conversation.updated_at = datetime.datetime.now() conversation.save() # Finally, return an empty response back to the caller. return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
def PUT_revelations_unreveal(request, format): """ Respond to the "PUT revelations/unreveal" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="revelation") error = apiHelper.check_params( params, required_params=["token", "profile_id", "recipient_id", "field_name"], optional_params=[] ) if error != None: return error token = params["token"] field_name = params["field_name"] # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Check that the source and recipient profiles both exist, and that the # currently logged-in user owns the source profile. try: profile = Profile.objects.get(id=params["profile_id"]) except Profile.DoesNotExist: return HttpResponseBadRequest("No such profile") try: recipient = Profile.objects.get(id=params["recipient_id"]) except Profile.DoesNotExist: return HttpResponseBadRequest("No such recipient profile") if profile.user != user: return HttpResponseBadRequest("Not your profile") # Check that the field name is valid. if field_name not in Revelation.VALID_FIELD_NAMES: return HttpResponseBadRequest("Invalid field_name") # Remove the existing revelation for this field, if any. Revelation.objects.filter(profile=profile, recipient=recipient, field_name=field_name).delete() # Tell the push gateway that the profile has changed. push_gateway.send_profile(profile, recipient) # Finally, return an empty response back to the caller. return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
def GET_conversations(request, format): """ Respond to the "GET /conversations" API call. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="conversation") error = apiHelper.check_params(params, required_params=["token"], optional_params=["since"]) if error != None: return error token = params['token'] since = params.get("since") # Check that the user's session is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Attempt to parse the "since" parameter, if it was supplied. if since != None: try: since = datetime.datetime.strptime(since, "%Y-%m-%dT%H:%M:%SZ") except ValueError: return HttpResponseBadRequest("Invalid 'since' value") # Search for all conversations in which the given user is a participant. conversations = [] query = {'profile1__user' : user} if since != None: query['updated_at__gte'] = since for c in Conversation.objects.filter(**query): cdict = c.to_dict_for_profile(c.profile1) conversations.append({'conversation' : cdict}) query = {'profile2__user' : user} if since != None: query['updated_at__gte'] = since for c in Conversation.objects.filter(**query): cdict = c.to_dict_for_profile(c.profile2) conversations.append({'conversation' : cdict}) # Finally, return the list of matching conversations back to the caller. return apiHelper.response(conversations, format=format, status=HTTP_RESPONSE_GET_OK)
def POST_users(request, format): """ Respond to the "POST users" request. We create a new user and log them in. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="user") error = apiHelper.check_params(params, required_params=[], optional_params=["username", "password"]) if error != None: return error username = params.get("username") password = params.get("password") if username == "": username = None if password == "": password = None # Check that the given username doesn't already exist. if username != None: if User.objects.filter(username=username).exists(): return HttpResponseBadRequest("Duplicate User") # Create a new User object for this user. user = User() user.username = username if password != None: user.set_password(password) user.created_at = datetime.datetime.now() user.updated_at = datetime.datetime.now() user.save() # Open up a session for this new user. token = session.create(user) # Finally, return the newly-created user and the login token back to the # caller. return apiHelper.response({'user' : user.to_dict(), 'token' : token}, format=format, status=HTTP_RESPONSE_POST_OK)
def PUT_messages_read(request, format): """ Respond to the "PUT messages/read" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="message") error = apiHelper.check_params(params, required_params=["token", "ids"], optional_params=[]) if error != None: return error token = params['token'] msg_ids = [] for msg_id in params['ids'].split(","): msg_ids.append(msg_id) # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Mark each of the messages as read. for msg_id in msg_ids: try: message = Message.objects.get(id=msg_id) except Message.DoesNotExist: return HttpResponseBadRequest("No message with ID %s" % msg_id) if message.recipient.user != user: return HttpResponseBadRequest("You're not the recipient of the " + "message with ID %s" % msg_id) message.read = True message.updated_at = datetime.datetime.now() message.save() # Finally, return an empty response back to the caller. return apiHelper.response(None, format=format, status=HTTP_RESPONSE_PUT_OK)
def GET_version(request, format): """ Respond to the "GET version" request. We return a dictionary mapping client OS names to the minimum supported version number for that client. Note that the actual version numbers are stored in the api.client_versions module. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name=None) error = apiHelper.check_params(params, required_params=[], optional_params=[]) if error != None: return error return apiHelper.response(CLIENT_VERSIONS, format=format, status=HTTP_RESPONSE_GET_OK)
def PUT_sessions(request, format): """ Respond to a "PUT sessions" request. """ params = apiHelper.get_params(request, "session") error = apiHelper.check_params(params, required_params=["token", "code"]) if error != None: return error token = params['token'] code = params['code'] if not session.check_verification_code(token, code): return HttpResponseForbidden("Incorrect verification code") user = session.get_user(token) return apiHelper.response({'user' : user.to_dict(), 'token' : token}, format=format, status=HTTP_RESPONSE_PUT_OK)
def GET_users(request, format): """ Respond to a "GET users" request. We return the details of the currently logged-in user. """ params = apiHelper.get_params(request, resource_name="user") error = apiHelper.check_params(params, required_params=["token"]) if error != None: return error token = params['token'] if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) return apiHelper.response({'user' : user.to_dict()}, format=format, status=HTTP_RESPONSE_GET_OK)
def POST_devices(request, format): """ Respond to the "POST devices" request. We create a new device in the system, overwriting the existing device if it exists. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="device") error = apiHelper.check_params(params, required_params=["token", "notification_type", "notification_token"]) if error != None: return error token = params['token'] notification_type = params['notification_type'] notification_token = params['notification_token'] if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Delete any existing devices with the given notification type and token. Device.objects.filter(notification_type=notification_type, notification_token=notification_token).delete() # Create the new device. device = Device() device.user = user device.notification_type = notification_type device.notification_token = notification_token device.created_at = datetime.datetime.now() device.updated_at = datetime.datetime.now() device.save() # Finally, return the device back to our caller. return apiHelper.response({'device' : device.to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK)
def POST_messages_sms(request, format): """ Respond to the "POST messages/sms" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name=None) error = apiHelper.check_params(params, required_params=["To", "From", "Body"], optional_params=[]) if error != None: return error to_number = params['To'] from_number = params['From'] message_body = params['Body'] # Ask our SMS gateway to do all the work. sms_reply = sms_gateway.receive_message(to_number, from_number, message_body) # Finally, return the response back to the caller. response = [] response.append('<?xml version="1.0"?>') response.append('<Response>') if sms_reply != None: response.append('<Sms>') response.append(sms_reply) response.append('</Sms>') response.append('</Response>') return HttpResponse("\n".join(response), mimetype="text/xml", status=HTTP_RESPONSE_POST_OK)
def GET_profiles(request, format): """ Respond to the "GET profiles" request. We return a list of other people's profiles that have been revealed to the currently logged-in user. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="profile") error = apiHelper.check_params(params, required_params=["token"], optional_params=[]) if error != None: return error token = params['token'] # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Get the profiles that have been revealed to this user. profiles = [] for revelation in Revelation.objects.filter(recipient__user=user) \ .distinct("profile"): profile = revelation.profile.to_dict_revealed_to(revelation.recipient) profiles.append({'profile' : profile}) # Finally, return the list of revealed profiles back to the caller. return apiHelper.response(profiles, format=format, status=HTTP_RESPONSE_GET_OK)
def POST_messages(request, format): """ Respond to a "POST /messages" request. """ # Get our request parameters. params = apiHelper.get_params(request, "message") error = apiHelper.check_params(params, required_params=["token", "sender_id", "body"], optional_params=["guid", "recipient_id", "link_id", "message_type", "latitude", "longitude", "speed", "course", "message[attachments][][attachment]"]) if error != None: return error if "recipient_id" in params: has_link = False recipient_id = params['recipient_id'] elif "link_id" in params: has_link = True link_id = params['link_id'] else: return HttpResponseBadRequest("recipient_id or link_id required.") token = params['token'] sender_id = params['sender_id'] body = params['body'] if "guid" in params: guid = params['guid'] else: guid = None if "message_type" in params: message_type = params['message_type'] else: message_type = None # Maybe default to "text"??? attachments = [] if "message[attachments][][attachment]" in params: for attachment in params.getlist("message[attachments][][attachment]"): attachments.append(attachment) values = {} # Maps parameter name to decimal value or None. for param in ["latitude", "longitude", "speed", "course"]: if param in params: try: value = decimal.Decimal(params[param]) except ValueError: return HttpResponseBadRequest("Invalid '%s' value", param) else: value = None values[param] = value # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # See if there is already a message with that GUID value. If so, we return # that message rather than creating a new one. if guid not in ["", None]: try: existing_message = Message.objects.get(guid=guid) except Message.DoesNotExist: existing_message = None if existing_message != None: return apiHelper.response({'message' : existing_message.to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK) # If we've been supplied a link ID, convert the link to a profile so we can # send the message to that profile. if has_link: try: link = Link.objects.get(id=link_id) except Link.DoesNotExist: return HttpResponseBadRequest("Nonexistent link") recipient_id = link.profile.id # Get the sending and receiving profiles. Note that the recipient ID might # be specified using a link ID or a profile ID. try: sender_profile = Profile.objects.get(id=sender_id) except Profile.DoesNotExist: return HttpResponseBadRequest("Nonexistent sending profile") if sender_profile.user != user: return HttpResponseBadRequest("sender_id not your profile") try: recipient_profile = Profile.objects.get(id=recipient_id) except Profile.DoesNotExist: return HttpResponseBadRequest("Nonexistent recipient profile") # See if we already have a conversation for this sender and recipient. try: conversation = Conversation.objects.get(profile1=sender_profile, profile2=recipient_profile) except Conversation.DoesNotExist: try: conversation = Conversation.objects.get(profile1=recipient_profile, profile2=sender_profile) except Conversation.DoesNotExist: conversation = None if conversation != None: # Unhide the existing conversation for both parties. conversation.hidden1 = False conversation.hidden2 = False conversation.updated_at = datetime.datetime.now() conversation.save() else: # There's no existing conversation for these two profiles -> create # one. conversation = Conversation() conversation.profile1 = sender_profile conversation.profile2 = recipient_profile conversation.hidden1 = False conversation.hidden2 = False conversation.created_at = datetime.datetime.now() conversation.updated_at = datetime.datetime.now() conversation.save() if has_link: # Copy the tags from the link across to the conversation. for link_tag in LinkTag.objects.filter(link=link): conversation_tag = ConversationTag() conversation_tag.conversation = conversation conversation_tag.profile = link.profile conversation_tag.tag = link_tag.tag conversation_tag.save() # If the message had a lat/long coordinate, update the associated user's # last-known latitude and longitude. if values['latitude'] != None and values['longitude'] != None: user.latitude = values['latitude'] user.longitude = values['longitude'] user.updated_at = datetime.datetime.now() user.save() # Create the new Message object. message = Message() message.guid = guid message.conversation = conversation message.sender = sender_profile message.recipient = recipient_profile message.read = False message.message_type = message_type message.body = body message.sent_apn = False message.sent_android = False message.sent_sms = False message.latitude = values['latitude'] message.longitude = values['longitude'] message.speed = values['speed'] message.course = values['course'] message.created_at = datetime.datetime.now() message.updated_at = datetime.datetime.now() message.save() # Create Attachment objects for each of the message's attachments (if any). for file in attachments: attachment = Attachment() attachment.message = message attachment.created_at = datetime.datetime.now() attachment.updated_at = datetime.datetime.now() attachment.save() attachment.attachment.save(file.name, file) # Send out notifications for the newly-created message, if appropriate. send_notification = True # initially. if message_type == "image" and len(attachments) == 0: # We have to delay sending out the notification until after the # attachment has been received. send_notification = False if send_notification: message.send_notifications() # Finally, return a copy of the newly-created message back to the caller. return apiHelper.response({'message' : message.to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK)
def POST_links(request, format): """ Respond to the "POST links" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="link") if "number" in params: create_from_phone = True error = apiHelper.check_params(params, required_params=["number", "tag"], optional_params=[]) if error != None: return error elif "profile_id" in params: create_from_phone = False error = apiHelper.check_params(params, required_params=["token", "profile_id", "tag"], optional_params=[]) if error != None: return error else: return HttpResponseBadRequest("A phone number or a profile ID " + "must be supplied.") if create_from_phone: # Get the User, User Number and Profile for the supplied phone number. # Note that we automatically create these records if they don't already # exist. phone_number = params['number'] try: user_num = UserNumber.objects.get(number=phone_number) except UserNumber.DoesNotExist: user_num = None if user_num == None: # We don't know this phone number -> create new User and UserNumber # records for this phone number. user = User() user.created_at = datetime.datetime.now() user.updated_at = datetime.datetime.now() user.save() user_num = UserNumber() user_num.user = user user_num.number = phone_number user_num.verified = True user_num.created_at = datetime.datetime.now() user_num.updated_at = datetime.datetime.now() user_num.save() else: user = user_num.user # See if this user has a default profile. If not, create one. if Profile.objects.filter(user=user).exists(): profiles = Profile.objects.filter(user=user).order_by("id") if user.default_profile < 0 or user.default_profile >= len(profiles): # Set the default. profile = profiles[0] user.default_profile = 0 user.created_at = datetime.datetime.now() user.updated_at = datetime.datetime.now() user.save() else: # Use the default. profile = profiles[user.default_profile] else: # Create a new profile for this user. profile = Profile() profile.user = user profile.created_at = datetime.datetime.now() profile.updated_at = datetime.datetime.now() profile.save() user.default_profile = 0 user.updated_at = datetime.datetime.now() user.save() else: # We've been supplied a profile ID. Use that for the new Link object. token = params['token'] profile_id = params['profile_id'] if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) try: profile = Profile.objects.get(id=profile_id) except Profile.DoesNotExist: return HttpResponseBadRequest("Invalid profile ID") if profile.user != user: return HttpResponseBadRequest("Not your profile") link = Link() link.profile = profile link.created_at = datetime.datetime.now() link.updated_at = datetime.datetime.now() link.save() # Create the tags for this link. for tag in params.getlist("tag"): link_tag = LinkTag() link_tag.link = link link_tag.tag = tag link_tag.save() # Finally, return the newly-created Link back to the caller. return apiHelper.response({'link' : link.to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK)
def POST_revelations(request, format): """ Respond to the "POST revelations" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="revelation") error = apiHelper.check_params(params, required_params=["token", "profile_id", "recipient_id"], optional_params=[ "field_names", "revelations[][recipient_id]", "revelations[][profile_id]", "revelations[][field_name]"]) if error != None: return error token = params['token'] if params.get("field_names") != None: param_style = "NEW" elif params.get("revelations[][field_name]") != None: param_style = "OLD" else: return HttpResponseBadRequest("Invalid parameters") if param_style == "NEW": required_params = ["field_names"] prohibited_params = ["revelations[][recipient_id]", "revelations[][profile_id]", "revelations[][field_name]"] elif param_style == "OLD": required_params = ["revelations[][recipient_id]", "revelations[][profile_id]", "revelations[][field_name]"] prohibited_params = ["field_names"] else: required_params = [] prohibited_params = [] for param in required_params: if param not in params: return HttpResponseBadRequest("Missing '%s' parameter" % param) for param in prohibited_params: if param in params: return HttpResponseBadRequest("Unexpected '%s' parameter" % param) # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Check that the source and recipient profiles both exist, and that the # currently logged-in user owns the source profile. try: profile = Profile.objects.get(id=params['profile_id']) except Profile.DoesNotExist: return HttpResponseBadRequest("No such profile") try: recipient = Profile.objects.get(id=params['recipient_id']) except Profile.DoesNotExist: return HttpResponseBadRequest("No such recipient profile") if profile.user != user: return HttpResponseBadRequest("Not your profile") # Extract the list of fields to reveal. Note that how we do this depends # on the parameter style being used. if param_style == "NEW": fields_to_reveal = params['field_names'].split(",") else: fields_to_reveal = [] for field_name in params.getlist("revelations[][field_name]"): fields_to_reveal.append(field_name) # Check that the field names are all correct. for field_name in fields_to_reveal: if field_name not in Revelation.VALID_FIELD_NAMES: return HttpResponseBadRequest("Invalid field: %s" % field_name) # Remove the existing revelations for these source and destination # profiles. Revelation.objects.filter(profile=profile, recipient=recipient).delete() # Now add the new revelations to the database. revelations = [] for field_name in fields_to_reveal: revelation = Revelation() revelation.profile = profile revelation.recipient = recipient revelation.field_name = field_name revelation.created_at = datetime.datetime.now() revelation.updated_at = datetime.datetime.now() revelation.save() revelations.append({'revelation' : revelation.to_dict()}) # Tell the push gateway that the profile has changed. push_gateway.send_profile(profile, recipient) # Finally, return the newly-created revelations back to the caller. return apiHelper.response(revelations, format=format, status=HTTP_RESPONSE_POST_OK)
def GET_revelations(request, format): """ Respond to the "GET revelations" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="revelation") error = apiHelper.check_params(params, required_params=["token"], optional_params=["profile_id", "recipient_id"]) if error != None: return error token = params['token'] profile_id = params.get("profile_id") recipient_id = params.get("recipient_id") # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Check that the source and recipient profiles exist (if specified), and # that the recipient profile (if any) is owned by the currently logged-in # user. if profile_id != None: try: profile = Profile.objects.get(id=profile_id) except Profile.DoesNotExist: return HttpResponseBadRequest("No such profile") else: profile = None if recipient_id != None: try: recipient = Profile.objects.get(id=recipient_id) except Profile.DoesNotExist: return HttpResponseBadRequest("No such recipient profile") else: recipient = None if recipient != None and recipient.user != user: return HttpResponseBadRequest("Not your profile") # Find the matching set of revelations, filtering by the profile and # recipient, if any. kwargs = {} if profile != None: kwargs['profile'] = profile if recipient != None: # Find revelations made to the given recipient profile. kwargs['recipient'] = recipient else: # Find all revelations made to us. kwargs['recipient__user'] = user revelations = [] for revelation in Revelation.objects.filter(**kwargs): revelations.append({'revelation' : revelation.to_dict()}) # Finally, return the revelations back to the caller. return apiHelper.response(revelations, format=format, status=HTTP_RESPONSE_GET_OK)
def POST_profiles(request, format): """ Respond to the "POST profiles" request. We create a new profile for the currently logged-in user. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="profile") error = apiHelper.check_params(params, required_params=["token"], optional_params=["alias", "avatar", "image1", "image2", "image3", "image4", "url", "email", "location", "bio"]) if error != None: return error token = params['token'] alias = params.get("alias") avatar = params.get("avatar") image1 = params.get("image1") image2 = params.get("image2") image3 = params.get("image3") image4 = params.get("image4") url = params.get("url") email = params.get("email") location = params.get("location") bio = params.get("bio") # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Create a Profile object with the supplied parameters. profile = Profile() profile.user = user profile.alias = alias profile.url = url profile.email = email profile.location = location profile.bio = bio profile.created_at = datetime.datetime.now() profile.updated_at = datetime.datetime.now() profile.save() if avatar != None: profile.avatar_orig.save(avatar.name, avatar) if image1 != None: profile.image1_orig.save(image1.name, image1) if image2 != None: profile.image2_orig.save(image2.name, image2) if image3 != None: profile.image3_orig.save(image3.name, image3) if image4 != None: profile.image4_orig.save(image4.name, image4) # Finally, return the newly-created profile back to the caller. return apiHelper.response({'profile' : profile.own_to_dict()}, format=format, status=HTTP_RESPONSE_POST_OK)
def POST_conversations(request, format): """ Respond to the "POST conversations" request. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="conversation") error = apiHelper.check_params(params, required_params=["token", "sender_id"], optional_params=["number", "country", "recipient_id", "aka", "tag"]) if error != None: return error token = params['token'] sender_id = params['sender_id'] number = params.get("number") country = params.get("country") recipient_id = params.get("recipient_id") aka = params.get("aka") if "tag" in params: tags = params.getlist("tag") else: tags = [] if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # If the caller supplied a phone number and not a recipient ID, try to # match the phone number against an existing user. If the phone number is # new, we create a new user for that phone number on-the-fly. if number != None and recipient_id == None: # Normalize the phone number into E.164 format. if country == None: country = "US" phone_number = phonenumbers.parse(number, country) phone_format = phonenumbers.PhoneNumberFormat.E164 formatted_number = phonenumbers.format_number(phone_number, phone_format) # See if the phone number already exists in the system. try: recipient_number = UserNumber.objects.get(number=formatted_number) except UserNumber.DoesNotExist: recipient_number = None if recipient_number == None: # This is the first time we've encountered this phone number -> # create a new User record for this user, and attach it to the # phone number. recipient_user = User() recipient_user.created_at = datetime.datetime.now() recipient_user.updated_at = datetime.datetime.now() recipient_user.save() recipient_number = UserNumber() recipient_number.user = recipient_user recipient_number.number = formatted_number recipient_number.verified = True # Number is already validated. recipient_number.created_at = datetime.datetime.now() recipient_number.updated_at = datetime.datetime.now() recipient_number.save() else: recipient_user = recipient_number.user # We now have a User and UserNumber record for this phone number. # Check that we have a profile for that user; if not, we have to create # one on the fly. profiles = [] for profile in recipient_user.profile_set.order_by("id"): profiles.append(profile) if len(profiles) == 0: recipient_profile = Profile() recipient_profile.user = recipient_user recipient_profile.created_at = datetime.datetime.now() recipient_profile.updated_at = datetime.datetime.now() recipient_profile.save() else: recipient_profile = profiles[recipient_user.default_profile] # Finally, use the recipient profile for this conversation. recipient_id = recipient_profile.id else: # Check that the caller specified a recipient ID. if recipient_id == None: return HttpResponseBadRequest("Missing recipient_id parameter") # Get the sender and recipient profiles. try: sender_profile = Profile.objects.get(id=sender_id) except Profile.DoesNotExist: return HttpResponseBadRequest("Nonexistent sending profile") if sender_profile.user != user: return HttpResponseBadRequest("sender_id not your profile") try: recipient_profile = Profile.objects.get(id=recipient_id) except Profile.DoesNotExist: return HttpResponseBadRequest("Nonexistent recipient profile") # See if we already have a conversation for this sender and recipient. try: conversation = Conversation.objects.get(profile1=sender_profile, profile2=recipient_profile) except Conversation.DoesNotExist: try: conversation = Conversation.objects.get(profile1=recipient_profile, profile2=sender_profile) except Conversation.DoesNotExist: conversation = None if conversation != None: # Unhide the existing conversation for both parties. conversation.hidden1 = False conversation.hidden2 = False conversation.updated_at = datetime.datetime.now() conversation.save() else: # There's no existing conversation for these two profiles -> create # one. conversation = Conversation() conversation.profile1 = sender_profile conversation.profile2 = recipient_profile conversation.hidden1 = False conversation.hidden2 = False conversation.created_at = datetime.datetime.now() conversation.updated_at = datetime.datetime.now() conversation.save() # If either party has made revelations to the "PUBLIC" profile, copy # those revelations and make them directly to the other party. In this # way, changing the public revelations won't affect the revelations # made in this conversation. _copy_public_revelations(sender_profile, recipient_profile) _copy_public_revelations(recipient_profile, sender_profile) # Store the "aka" value for this conversation, if one was supplied. if aka != None: if sender_profile == conversation.profile1: conversation.aka1 = aka else: conversation.aka2 = aka conversation.updated_at = datetime.datetime.now() conversation.save() # If the caller supplied any "tag" values, use these to replace the # existing tags (if any). if len(tags) > 0: ConversationTag.objects.filter(conversation=conversation, profile=sender_profile).delete() for tag in tags: conversation_tag = ConversationTag() conversation_tag.conversation = conversation conversation_tag.profile = sender_profile conversation_tag.tag = tag conversation_tag.created_at = datetime.datetime.now() conversation_tag.updated_at = datetime.datetime.now() conversation_tag.save() # Finally, return the conversation back to the caller. cdict = conversation.to_dict_for_profile(sender_profile, include_revelations=True) return apiHelper.response({'conversation' : cdict}, format=format, status=HTTP_RESPONSE_POST_OK)
def PUT_profiles(request, format): """ Respond to the "PUT profiles" request. We let the caller update the details of an existing profile. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name="profile") error = apiHelper.check_params(params, required_params=["token", "id"], optional_params=["alias", "avatar", "image1", "image2", "image3", "image4", "url", "email", "location", "bio"]) if error != None: return error token = params['token'] profile_id = params['id'] alias = params.get("alias") avatar = params.get("avatar") image1 = params.get("image1") image2 = params.get("image2") image3 = params.get("image3") image4 = params.get("image4") url = params.get("url") email = params.get("email") location = params.get("location") bio = params.get("bio") # Check that the login token is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Get the Profile object we're updating. try: profile = Profile.objects.get(id=profile_id) except Profile.DoesNotExist: return HttpResponseBadRequest("No such profile") if profile.user != user: return HttpResponseBadRequest("Not your profile") # Update the Profile with the new values. if alias != None: profile.alias = alias if url != None: profile.url = url if email != None: profile.email = email if location != None: profile.location = location if bio != None: profile.bio = bio if avatar != None: if profile.avatar_orig != None: profile.avatar_orig.delete(save=False) profile.avatar_orig.save(avatar.name, avatar, save=False) if image1 != None: if profile.image1_orig != None: profile.image1_orig.delete(save=False) profile.image1_orig.save(image1.name, image, save=False) if image2 != None: if profile.image2_orig != None: profile.image2_orig.delete(save=False) profile.image2_orig.save(image2.name, image, save=False) if image3 != None: if profile.image3_orig != None: profile.image3_orig.delete(save=False) profile.image3_orig.save(image3.name, image, save=False) if image4 != None: if profile.image4_orig != None: profile.image4_orig.delete(save=False) profile.image4_orig.save(image4.name, image, save=False) profile.updated_at = datetime.datetime.now() profile.save() # Finally, return the updated profile back to the caller. return apiHelper.response({'profile' : profile.own_to_dict()}, format=format, status=HTTP_RESPONSE_PUT_OK)
def POST_sessions(request, format): """ Respond to a "POST sessions" request. """ params = apiHelper.get_params(request, "session") if "username" in params: error = apiHelper.check_params(params, required_params=["username", "password"]) login_via_username = True elif "number" in params: error = apiHelper.check_params(params, required_params=["number"], optional_params=["country"]) login_via_username = False else: return HttpResponseBadRequest("username/password or " + "phone number required") if error != None: return error if login_via_username: # Log the caller in using the supplied username and password. username = params['username'] password = params['password'] try: user = User.objects.get(username=username) except User.DoesNotExist: user = None if user == None or not user.check_password(password): return HttpResponseForbidden("Incorrect username or password") # Open up a session for this user. token = session.create(user) # Finally, return the user and the login token back to the caller. return apiHelper.response({'user' : user.to_dict(), 'token' : token}, format=format, status=HTTP_RESPONSE_POST_OK) else: # Log the caller in using the supplied phone number. number = params['number'] if "country" in params: country = params['country'] else: country = "US" # Ask the "phonenumbers" library to format the phone number into E.164 # format. phone_number = phonenumbers.parse(number, country) phone_format = phonenumbers.PhoneNumberFormat.E164 formatted_number = phonenumbers.format_number(phone_number, phone_format) # Open up a session for this phone number. token,verification_code = session.create_for_phone(formatted_number) # Send an SMS message to the supplied phone number. sms_gateway.send_message(text="code: " + verification_code + " -- " + settings.SYSTEM_NAME, from_user=None, from_profile=None, to_user=None, to_profile=None, to_phone_number=formatted_number, message_id=None) # Finally, return the formatted phone number and login token back to # the caller. return apiHelper.response({'number' : formatted_number, 'token' : token}, format=format, status=HTTP_RESPONSE_POST_OK)
def PUT_conversations(request, format): """ Respond to the "PUT /conversations" API call. """ # Get the request parameters. params = apiHelper.get_params(request, resource_name=None) error = apiHelper.check_params(params, required_params=["token", "conversation_id"], optional_params=["aka", "tag"]) if error != None: return error token = params['token'] conversation_id = params['conversation_id'] if "aka" in params: aka = params['aka'] else: aka = None if "tag" in params: tags = params.getlist("tag") else: tags = [] # Check that the user's session is still valid. if not session.validate(token): return HttpResponseBadRequest("Invalid token") user = session.get_user(token) # Get the conversation to update. try: conversation = Conversation.objects.get(id=conversation_id) except Conversation.DoesNotExist: return HttpResponseBadRequest("No such conversation") # Check that the current user is involved in the conversation, and remember # the associated profile. if conversation.profile1.user == user: profile = conversation.profile1 elif conversation.profile2.user == user: profile = conversation.profile2 else: return HttpResponseBadRequest("Not your conversation") # Update the conversation. if aka != None: if profile == conversation.profile1: conversation.aka1 = aka else: conversation.aka2 = aka conversation.updated_at = datetime.datetime.now() conversation.save() if len(tags) > 0: ConversationTag.objects.filter(conversation=conversation, profile=profile).delete() for tag in tags: conversation_tag = ConversationTag() conversation_tag.conversation = conversation conversation_tag.profile = profile conversation_tag.tag = tag conversation_tag.created_at = datetime.datetime.now() conversation_tag.updated_at = datetime.datetime.now() conversation_tag.save() # Finally, return updated conversation back to the caller. cdict = conversation.to_dict_for_profile(profile, include_revelations=True) return apiHelper.response({'conversation' : cdict}, format=format, status=HTTP_RESPONSE_PUT_OK)