def anno_insert(self, request): """ Exposes an API endpoint to insert an anno for the current user. if current user doesn't exist, the user will be created first. """ user = auth_user(self.request_state.headers) # checking if same anno exists exist_anno = Anno.is_anno_exists(user, request) if exist_anno is not None: raise endpoints.BadRequestException("Duplicate anno(%s) already exists." % exist_anno.key.id()) entity = Anno.insert_anno(request, user) # find all hashtags tags = extract_tags_from_text(entity.anno_text.lower()) for tag, count in tags.iteritems(): # Write the cumulative amount per tag Tag.add_tag_total(tag, total=count) # index this document. strange exception here. put_search_document(entity.generate_search_document(), SearchIndexName.ANNO) # send push notifications ActivityPushNotifications.send_push_notification(first_user=user, anno=entity, action_type=AnnoActionType.CREATED) return entity.to_response_message(user)
def anno_my_stuff(self, request): """ Exposes an API endpoint to return all my anno list. """ user = auth_user(self.request_state.headers) anno_list = Anno.query_anno_by_author(user) vote_list = Vote.query_vote_by_author(user) for vote in vote_list: anno = Anno.get_by_id(vote.anno_key.id()) if anno is not None: anno_list.append(anno) flag_list = Flag.query_flag_by_author(user) for flag in flag_list: anno = Anno.get_by_id(flag.anno_key.id()) if anno is not None: anno_list.append(anno) followup_list = FollowUp.query_followup_by_author(user) for followup in followup_list: anno = Anno.get_by_id(followup.anno_key.id()) if anno is not None: anno_list.append(anno) anno_set = list(set(anno_list)) anno_message_list = [] for anno in anno_set: anno_message_list.append(anno.to_response_message()) return AnnoListMessage(anno_list=anno_message_list)
def anno_insert(self, request): """ Exposes an API endpoint to insert an anno for the current user. if current user doesn't exist, the user will be created first. """ user = auth_user(self.request_state.headers) if Anno.is_anno_exists(user, request): raise endpoints.BadRequestException("Duplicate anno already exists.") entity = Anno.insert_anno(request, user) return entity.to_response_message()
def anno_delete(self, request): """ Exposes an API endpoint to delete an existing anno. """ user = auth_user(self.request_state.headers) if request.id is None: raise endpoints.BadRequestException('id field is required.') anno = Anno.get_by_id(request.id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) Anno.delete(anno) return message_types.VoidMessage()
def vote_insert(self, request): """ Exposes an API endpoint to insert a vote for the current user. """ user = auth_user(self.request_state.headers) anno = Anno.get_by_id(request.anno_id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) vote = Vote() vote.anno_key = anno.key vote.creator = user.key if request.created is not None: vote.created = request.created vote.put() anno.vote_count += 1 anno.last_update_time = datetime.datetime.now() anno.last_activity = 'vote' anno.last_update_type = 'create' anno.put() # update user anno state UserAnnoState.insert(user=user, anno=anno, type=AnnoActionType.UPVOTED) # update vote in search document put_search_document(anno.generate_search_document(), SearchIndexName.ANNO) return vote.to_message()
def anno_merge(self, request): """ Exposes an API endpoint to merge(update only the specified properties) an anno. """ user = auth_user(self.request_state.headers) if request.id is None: raise endpoints.BadRequestException('id field is required.') anno = Anno.get_by_id(request.id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) anno.merge_from_message(request, user) # set last update time & activity anno.last_update_time = datetime.datetime.now() anno.last_activity = 'anno' anno.put() # update search document. put_search_document(anno.generate_search_document(), SearchIndexName.ANNO) # send notifications ActivityPushNotifications.send_push_notification(first_user=user, anno=anno, action_type=AnnoActionType.EDITED) # update last_read of UserAnnoState from model.userannostate import UserAnnoState UserAnnoState.update_last_read(user=user, anno=anno) return anno.to_response_message(user)
def list_favorite_apps(cls, user_key): # We are using "query" on key for getting anno data instead of "get" or "get_multi" # Getting anno using "query" is more memory efficient than using "get" or "get_multi", # we don't know why. # Getting anno using "query" also create index for this. from model.userannostate import UserAnnoState from model.anno import Anno userannostate_list = UserAnnoState.list_by_user(user_key, 50) anno_key_list = [ userannostate.anno for userannostate in userannostate_list if userannostate.anno is not None ] if len(anno_key_list): anno_list = Anno.query(ndb.AND(Anno.key.IN(anno_key_list), Anno.app != None) )\ .fetch(projection=[Anno.app]) app_key_list = [ anno.app for anno in anno_list ] app_key_list = sorted(app_key_list, key=app_key_list.count, reverse=True) unique_app_key_list = [] [ unique_app_key_list.append(app_key) for app_key in app_key_list if app_key not in unique_app_key_list ] app_list = ndb.get_multi(unique_app_key_list) else: app_list = [] favorite_apps_list = [] for app in app_list: if app: app_message = UserFavoriteApp(name=app.name, icon_url=(app.icon_url or ""), version=(app.version or "")) favorite_apps_list.append(app_message) return favorite_apps_list
def followup_insert(self, request): """ Exposes and API endpoint to insert a follow up for the current user. """ user = auth_user(self.request_state.headers) anno = Anno.get_by_id(request.anno_id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) followup = FollowUp() followup.anno_key = anno.key followup.creator = user.key followup.comment = request.comment if request.created is not None: followup.created = request.created followup.put() anno.followup_count += 1 anno.last_update_time = datetime.datetime.now() anno.last_activity = 'follwup' anno.last_update_type = 'create' anno.put() # update search document put_search_document(anno.generate_search_document()) return followup.to_message()
def anno_insert(self, request): """ Exposes an API endpoint to insert an anno for the current user. if current user doesn't exist, the user will be created first. """ user = auth_user(self.request_state.headers) exist_anno = Anno.is_anno_exists(user, request) if exist_anno is not None: raise endpoints.BadRequestException("Duplicate anno(%s) already exists." % exist_anno.key.id()) entity = Anno.insert_anno(request, user) # index this document. strange exception here. put_search_document(entity.generate_search_document()) return entity.to_response_message()
def flag_insert(self, request): """ Exposes an API endpoint to insert a flag for the current user. """ user = auth_user(self.request_state.headers) anno = Anno.get_by_id(request.anno_id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.') flag = Flag() flag.anno_key = anno.key flag.creator = user.key if request.created is not None: flag.created = request.created flag.put() anno.flag_count += 1 anno.last_update_time = datetime.datetime.now() anno.last_activity = 'flag' anno.last_update_type = 'create' anno.put() # update user anno state UserAnnoState.insert(user=user, anno=anno, type=AnnoActionType.FLAGGED) # update flag in search document put_search_document(anno.generate_search_document(), SearchIndexName.ANNO) return flag.to_message()
def flag_delete(self, request): """ Exposes an API endpoint to delete an existing flag. """ user = auth_user(self.request_state.headers) anno = None if request.id is None and request.anno_id is None: raise endpoints.BadRequestException('id or anno_id field is required.') if request.id is not None: flag = Flag.get_by_id(request.id) if flag is None: raise endpoints.NotFoundException('No flag entity with the id "%s" exists.' % request.id) anno = flag.anno_key.get() flag.key.delete() anno.flag_count -= 1 anno.put() elif request.anno_id is not None: anno = Anno.get_by_id(request.anno_id) for key in Flag.query(Flag.anno_key == anno.key, Flag.creator == user.key).iter(keys_only=True): key.delete() anno.flag_count -= 1 anno.put() put_search_document(anno.generate_search_document(), SearchIndexName.ANNO) return message_types.VoidMessage()
def anno_get(self, request): """ Exposes an API endpoint to get an anno detail by the specified id. """ try: user = auth_user(self.request_state.headers) except Exception: user = None if request.id is None: raise endpoints.BadRequestException('id field is required.') anno = Anno.get_by_id(request.id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) # set anno basic properties anno_resp_message = anno.to_response_message(user, list_message=False) # set anno association with followups followups = FollowUp.find_by_anno(anno) followup_messages = [ entity.to_message(team_key=request.team_key) for entity in followups ] anno_resp_message.followup_list = followup_messages # set anno association with votes/flags # if current user exists, then fetch vote/flag. if user is not None: anno_resp_message.is_my_vote = Vote.is_belongs_user(anno, user) anno_resp_message.is_my_flag = Flag.is_belongs_user(anno, user) # update last_read of UserAnnoState UserAnnoState.update_last_read(user=user, anno=anno) return anno_resp_message
def init_index_document(self, request): """ Exposes an API endpoint to insert search document for legacy documents. """ for anno in Anno.query(): logging.info("generating search document for anno(%s)." % anno.key.id()) put_search_document(anno.generate_search_document(), SearchIndexName.ANNO) return message_types.VoidMessage()
def anno_dashboard_list(self, request): user = auth_user(self.request_state.headers) limit = 10 if request.limit is not None: limit = request.limit curs = None if request.cursor is not None: try: curs = Cursor(urlsafe=request.cursor) except BadValueError: raise endpoints.BadRequestException('Invalid cursor %s.' % request.cursor) if request.query_type == AnnoQueryType.MY_MENTIONS: return Anno.query_by_my_mentions_for_dashboard(limit, curs, user) elif request.query_type == AnnoQueryType.ACTIVITY_COUNT: return Anno.query_by_count_for_dashboard(limit, curs, user, request.query_type) elif request.query_type == AnnoQueryType.VOTE_COUNT: return Anno.query_by_count_for_dashboard(limit, curs, user, request.query_type) elif request.query_type == AnnoQueryType.FLAG_COUNT: return Anno.query_by_count_for_dashboard(limit, curs, user, request.query_type) elif request.query_type == AnnoQueryType.ARCHIVED: return Anno.query_by_page_for_dashboard(limit, curs, user, query_by_archived=True) elif request.anno_id: return Anno.query_by_anno_for_dashboard(user, request.anno_id) else: return Anno.query_by_page_for_dashboard(limit, curs, user)
def anno_list(self, request): """ Exposes an API endpoint to retrieve a list of anno. """ user = auth_user(self.request_state.headers) limit = 10 if request.limit is not None: limit = request.limit curs = None if request.cursor is not None: try: curs = Cursor(urlsafe=request.cursor) except BadValueError: raise endpoints.BadRequestException('Invalid cursor %s.' % request.cursor) select_projection = None if request.select is not None: select_projection = request.select.split(',') if request.query_type == 'by_created': return Anno.query_by_app_by_created(request.app, limit, select_projection, curs) elif request.query_type == 'by_vote_count': return Anno.query_by_vote_count(request.app) elif request.query_type == 'by_flag_count': return Anno.query_by_flag_count(request.app) elif request.query_type == 'by_activity_count': return Anno.query_by_activity_count(request.app) elif request.query_type == 'by_last_activity': return Anno.query_by_last_activity(request.app) elif request.query_type == 'by_country': return Anno.query_by_country(request.app) else: return Anno.query_by_page(limit, select_projection, curs)
def delete_by_anno(cls, anno_id=None, anno_key=None): if anno_key is None: anno = Anno.get_by_id(anno_id) if anno_id else None anno_key = anno.key if anno else None if anno_key: followups = cls.query(cls.anno_key == anno_key).fetch() followup_key_list = [ followup.key for followup in followups ] ndb.delete_multi(followup_key_list)
def delete_by_anno(cls, anno_id=None, anno_key=None): if anno_key is None: anno = Anno.get_by_id(anno_id) if anno_id else None anno_key = anno.key if anno else None if anno_key: votes = cls.query(cls.anno_key == anno_key).fetch() vote_key_list = [ vote.key for vote in votes ] ndb.delete_multi(vote_key_list)
def delete_by_anno(cls, anno_id=None, anno_key=None): if anno_key is None: anno = Anno.get_by_id(anno_id) if anno_id else None anno_key = anno.key if anno else None if anno_key: userannostates = cls.query(cls.anno == anno_key).fetch() userannostate_key_list = [ userannostate.key for userannostate in userannostates ] ndb.delete_multi(userannostate_key_list)
def delete_by_anno(cls, anno_id=None, anno_key=None): if anno_key is None: anno = Anno.get_by_id(anno_id) if anno_id else None anno_key = anno.key if anno else None if anno_key: flags = cls.query(cls.anno_key == anno_key).fetch() flag_key_list = [ flag.key for flag in flags ] ndb.delete_multi(flag_key_list)
def update_notify(self, request): user = auth_user(self.request_state.headers) anno = Anno.get_by_id(request.anno_id) entity = UserAnnoState.get(user=user, anno=anno) if entity: entity.notify = request.notify entity.put() return message_types.VoidMessage()
def anno_delete(self, request): """ Exposes an API endpoint to delete an existing anno. """ user = auth_user(self.request_state.headers) if request.id is None: raise endpoints.BadRequestException('id field is required.') anno = Anno.get_by_id(request.id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) # send notifications ActivityPushNotifications.send_push_notification(first_user=user, anno=anno, action_type=AnnoActionType.DELETED) Anno.delete(anno) return message_types.VoidMessage()
def anno_get(self, request): """ Exposes an API endpoint to get an anno detail by the specified id. """ if request.id is None: raise endpoints.BadRequestException('id field is required.') anno = Anno.get_by_id(request.id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) return anno.to_response_message()
def list_users_by_anno(cls, anno_id=None, anno_key=None, projection=[]): if not anno_key: anno = Anno.get_by_id(anno_id) anno_key = anno.key if anno else None users = [] if anno_key: query = cls.query().filter(ndb.AND(cls.anno == anno_key, cls.notify == True)) users = query.fetch(projection=projection) return users
def migrate_photo_time_annos(cursor=None): team_key = 'us.orbe.Reko-Album' phototime_app = AppInfo.query().filter(AppInfo.lc_name == 'phototime').get() phototime_community = Community.getCommunityFromTeamKey(team_key=team_key) anno_list = Anno.query().filter(Anno.app == phototime_app.key).fetch() for anno in anno_list: anno.community = phototime_community.key user_email = anno.creator.get().user_email anno.creator = User.find_user_by_email(email=user_email, team_key=team_key).key anno.put()
def get(self): """ handle request for screenshot. sample: https://usersource-anno.appspot.com/screenshot?anno_id=5644572721938432 """ anno_id = self.request.get('anno_id') if anno_id is None or anno_id == '': self.response.set_status(400) self.response.out.write(u'anno_id parameter is required.') else: # is this an if-modified request if_modified_since = self.request.headers.get('If-Modified-Since') if_none_match = self.request.headers.get('If-None-Match') # Reply with a 304 # we could guess that if the Anno image was never modified # we do not even have to read the database and return a 304 # if (if_modified_since is not None and if_modified_since == anno.created.isoformat()) or \ # (if_none_match is not None and if_none_match == anno.created.isoformat()): # We trust the request blindly to save to DB read time if if_modified_since or if_none_match: self.response.status = 304 return anno = Anno.get_by_id(long(anno_id)) if anno is None: self.response.set_status(400) self.response.out.write('No anno entity with the id "%s" exists.' % anno_id) elif anno.image is None: self.response.set_status(404) self.response.out.write("This anno doesn't contain screenshot") else: # Header identifier head = list(anno.image[:32]) # Cache control and ETag support self.response.headers['Cache-Control'] = 'max-age=%d, public'%self.MAX_AGE self.response.headers['Last-Modified'] = anno.created.isoformat() self.response.headers['ETag'] = anno.created.isoformat() if self.HEADER_PNG == head[:len(self.HEADER_PNG)]: # PNG Header self.response.headers['Content-Type'] = 'image/png' elif self.HEADER_JPG == head[:len(self.HEADER_JPG)]: # JPEG Header self.response.headers['Content-Type'] = 'image/jpeg' else: # Defaulting to BMP for now self.response.headers['Content-Type'] = 'image/bmp' self.response.out.write(anno.image)
def anno_merge(self, request): """ Exposes an API endpoint to merge(update only the specified properties) an anno. """ if request.id is None: raise endpoints.BadRequestException('id field is required.') anno = Anno.get_by_id(request.id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) anno.merge_from_message(request) anno.put() return anno.to_response_message()
def anno_list(self, request): """ Exposes an API endpoint to retrieve a list of anno. """ limit = 10 # default limit is 10. if request.limit is not None: limit = request.limit curs = None if request.cursor is not None: try: curs = Cursor(urlsafe=request.cursor) except BadValueError: raise endpoints.BadRequestException('Invalid cursor %s.' % request.cursor) select_projection = None # todo: add projection validation, may need metadata. if request.select is not None: select_projection = request.select.split(',') if (curs is not None) and (select_projection is not None): annos, next_curs, more = Anno.query().fetch_page(limit, start_cursor=curs, projection=select_projection) elif (curs is not None) and (select_projection is None): annos, next_curs, more = Anno.query().fetch_page(limit, start_cursor=curs) elif (curs is None) and (select_projection is not None): annos, next_curs, more = Anno.query().fetch_page(limit, projection=select_projection) else: annos, next_curs, more = Anno.query().fetch_page(limit) if select_projection is not None: items = [entity.to_response_message_by_projection(select_projection) for entity in annos] else: items = [entity.to_response_message() for entity in annos] if more: return AnnoListMessage(anno_list=items, cursor=next_curs.urlsafe(), has_more=more) else: return AnnoListMessage(anno_list=items, has_more=more)
def anno_list(self, request): """ Exposes an API endpoint to retrieve a list of anno. """ user = auth_user(self.request_state.headers) limit = 10 if request.limit is not None: limit = request.limit is_plugin = request.is_plugin or False curs = None if request.cursor is not None: try: curs = Cursor(urlsafe=request.cursor) except BadValueError: raise endpoints.BadRequestException('Invalid cursor %s.' % request.cursor) select_projection = None if request.select is not None: select_projection = request.select.split(',') if request.query_type == AnnoQueryType.CREATED: return Anno.query_by_app_by_created(request.app, limit, select_projection, curs, user) elif request.query_type == AnnoQueryType.VOTE_COUNT: return Anno.query_by_vote_count(request.app, user) elif request.query_type == AnnoQueryType.FLAG_COUNT: return Anno.query_by_flag_count(request.app, user) elif request.query_type == AnnoQueryType.ACTIVITY_COUNT: return Anno.query_by_activity_count(request.app, user) elif request.query_type == AnnoQueryType.LAST_ACTIVITY: return Anno.query_by_last_activity(request.app, user) elif request.query_type == AnnoQueryType.COUNTRY: return Anno.query_by_country(request.app, user) elif request.query_type == AnnoQueryType.COMMUNITY: community = Community.get_by_id(request.community) return Anno.query_by_community(community, limit, select_projection, curs, user) elif request.query_type == AnnoQueryType.APP: app = AppInfo.get(request.app) return Anno.query_by_app(app, limit, select_projection, curs, user) else: return Anno.query_by_page(limit, select_projection, curs, user, is_plugin)
def anno_my_stuff(self, request): """ Exposes an API endpoint to return all my anno list. """ user = auth_user(self.request_state.headers) limit = request.limit or 10 curs = None if request.cursor is not None: try: curs = Cursor(urlsafe=request.cursor) except BadValueError: raise endpoints.BadRequestException('Invalid cursor %s.' % request.cursor) return Anno.query_my_anno(limit, curs, user)
def followup_insert(self, request): """ Exposes and API endpoint to insert a follow up for the current user. """ user = auth_user(self.request_state.headers) anno = Anno.get_by_id(request.anno_id) if anno is None: raise endpoints.NotFoundException('No anno entity with the id "%s" exists.' % request.id) followup = FollowUp() followup.anno_key = anno.key followup.creator = user.key followup.comment = request.comment followup.tagged_users = request.tagged_users if request.created is not None: followup.created = request.created followup.put() anno.followup_count += 1 anno.last_update_time = datetime.datetime.now() anno.last_activity = 'follwup' anno.last_update_type = 'create' anno.put() # update user anno state UserAnnoState.insert(user=user, anno=anno, type=AnnoActionType.COMMENTED) for tagged_user_id in followup.tagged_users: tagged_user = User.get_by_id(int(tagged_user_id)) if tagged_user: UserAnnoState.insert(user=tagged_user, anno=anno, type=AnnoActionType.TAGGEDUSER) # update search document put_search_document(anno.generate_search_document(), SearchIndexName.ANNO) put_search_document(followup.generate_search_document(), SearchIndexName.FOLLOWUP) # find all hashtags tags = extract_tags_from_text(followup.comment.lower()) for tag, count in tags.iteritems(): # Write the cumulative amount per tag Tag.add_tag_total(tag, total=count) # send notifications ActivityPushNotifications.send_push_notification(first_user=user, anno=anno, action_type=AnnoActionType.COMMENTED, comment=request.comment) return followup.to_message(request.team_key)