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 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 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 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 to_response_message(self, user, list_message=True): """ Convert anno model to AnnoResponseMessage. """ user_message = None if self.creator is not None: user_info = self.creator.get() user_message = UserMessage(display_name=user_info.display_name, image_url=user_info.image_url) app = self.app.get() if self.app else None app_name = app.name if app else self.app_name app_icon_url = app.icon_url if app else None app_version = app.version if app else self.app_version if list_message: anno_read_status = False if user: from model.userannostate import UserAnnoState anno_read_status = UserAnnoState.is_read(user, self) last_activity_user = UserAnnoState.last_activity_user(self) circle_level_value = None if (self.community and self.circle_level > 0): circle_level_value = Community.getCircleLevelValue(self.community, self.circle_level) anno_message = AnnoResponseMessage(id=self.key.id(), anno_text=self.anno_text, anno_type=self.anno_type, app_name=app_name, app_icon_url=app_icon_url, created=self.created, creator=user_message, last_update_time=self.last_update_time, last_activity=self.last_activity, last_update_type=self.last_update_type, anno_read_status=anno_read_status, last_activity_user=last_activity_user, circle_level_value=circle_level_value ) else: anno_message = AnnoResponseMessage(id=self.key.id(), anno_text=self.anno_text, anno_type=self.anno_type, level=self.level, device_model=self.device_model, app_name=app_name, app_version=app_version, app_icon_url=app_icon_url, os_name=self.os_name, os_version=self.os_version, created=self.created, creator=user_message, draw_elements=self.draw_elements, screenshot_is_anonymized=self.screenshot_is_anonymized, vote_count=self.vote_count, flag_count=self.flag_count, followup_count=self.followup_count, last_update_time=self.last_update_time, last_activity=self.last_activity, last_update_type=self.last_update_type) return anno_message
def insert_anno(cls, message, user): """ create a new anno model from request message. """ itemCount = 0 appinfo, community = getAppAndCommunity(message, user) circle_level = 0 if community: itemCount = cls.countItemsForTeam(community.key) circle_level = UserRole.getCircleLevel(user, community) if message.circle_level and (message.circle_level <= circle_level): circle_level = message.circle_level entity = cls(anno_text=message.anno_text, anno_type=message.anno_type, level=message.level, device_model=message.device_model, os_name=message.os_name, os_version=message.os_version, creator=user.key, draw_elements=message.draw_elements, image=message.image, screenshot_is_anonymized=message.screenshot_is_anonymized, geo_position=message.geo_position, flag_count=0, vote_count=0, followup_count=0, latitude=message.latitude, longitude=message.longitude, screen_info=message.screen_info) # set appinfo and community entity.app = appinfo.key entity.community = community.key if community else None entity.circle_level = circle_level # set created time if provided in the message. if message.created is not None: entity.created = message.created # use google map api to retrieve country information and save into datastore. if message.latitude is not None and message.longitude is not None: entity.country = get_country_by_coordinate(message.latitude, message.longitude) # set last update time & activity entity.last_update_time = datetime.datetime.now() entity.last_activity = 'UserSource' entity.last_update_type = 'create' anno_key = entity.put() entity.anno_id = anno_key.id() entity.put() # update user anno state from model.userannostate import UserAnnoState UserAnnoState.insert(user=user, anno=entity, type=AnnoActionType.CREATED) if community: itemCount = cls.query_count_by_community(community_key=community.key) if itemCount == 0: send_first_anno_email(community.name, user.display_name) return entity
def update_userannostate_schema_from_anno_action(cls, cursor=None): activity_list, cursor, more = cls.query()\ .order(cls.created)\ .fetch_page(BATCH_SIZE, start_cursor=cursor) for activity in activity_list: user = activity.creator.get() anno = activity.anno_key.get() modified = activity.created if user and anno: UserAnnoState.insert(user=user, anno=anno, modified=modified) if more: update_userannostate_schema_from_anno_action(cls=cls, cursor=cursor)
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)
def getEngagedUsers(cls, anno_id, auth_user, include_auth_user=False): from model.userannostate import UserAnnoState userannostates = UserAnnoState.list_users_by_anno(anno_id=anno_id, projection=[UserAnnoState.user]) users = [] for userannostate in userannostates: current_user = userannostate.user.get() users.append(UserMessage(id=current_user.key.id(), user_email=current_user.user_email, display_name=current_user.display_name, image_url=current_user.image_url)) # removing auth_user if auth_user: if include_auth_user: if not any(user_info.user_email == auth_user.user_email for user_info in users): users.append(UserMessage(id=auth_user.key.id(), user_email=auth_user.user_email, display_name=auth_user.display_name, image_url=auth_user.image_url)) else: [ users.remove(user_info) for user_info in users if user_info.user_email == auth_user.user_email ] # sorting users alphabetically return sorted(users, key=lambda user_info: user_info.display_name.lower())
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 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 delete(cls, anno): anno_id = "%d" % anno.key.id() # deleting UserAnnoState of anno from model.userannostate import UserAnnoState UserAnnoState.delete_by_anno(anno_key=anno.key) # deleting FollowUp of anno from model.follow_up import FollowUp FollowUp.delete_by_anno(anno_key=anno.key) # deleting Vote of anno from model.vote import Vote Vote.delete_by_anno(anno_key=anno.key) # deleting Flag of anno from model.flag import Flag Flag.delete_by_anno(anno_key=anno.key) anno.key.delete() index = search.Index(name=SearchIndexName.ANNO) index.delete(anno_id)
def anno_teamnotes_insert(self, request): anno = Anno.get_by_id(request.id) user = auth_user(self.request_state.headers) if anno: anno.team_notes = request.team_notes UserAnnoState.tag_users(anno, anno.tagged_users, request.tagged_users) anno.tagged_users = request.tagged_users anno.put() mentions = [] for tagged_user in request.tagged_users: user_info = User.get_by_id(int(tagged_user)) is_auth_user = user_info.user_email == user.user_email mentions.append(AnnoMentionsResponseMessage(id=user_info.key.id(), display_name=user_info.display_name, user_email=user_info.user_email, image_url=user_info.image_url, is_auth_user=is_auth_user)) return AnnoTeamNotesMetadataMessage(tags=parseTeamNotesForHashtags(request.team_notes), mentions=mentions)
def update_userannostate_schema(cursor=None): userannostate_list, cursor, more = UserAnnoState.query().fetch_page(BATCH_SIZE, start_cursor=cursor) userannostate_update_list = [] for userannostate in userannostate_list: if not userannostate.tagged: userannostate.tagged = False userannostate_update_list.append(userannostate) if len(userannostate_update_list): ndb.put_multi(userannostate_update_list) if more: update_userannostate_schema(cursor=cursor)
def send_push_notification(cls, first_user, anno, action_type, comment=""): """ Send push notification for anno actions :param ndb.Model first_user: "******" datastore of user who did anno action :param ndb.Model anno: "anno" datastore :param str action_type: one of the :py:class:`.AnnoActionType` :param str comment: comment made on an anno """ # get list of device ids to which push notification to be sent notf_device = cls.get_noft_devices(first_user, anno, action_type) # create push notification message for iOS and Android devices notf_msg = cls.create_notf_msg(first_user, anno, action_type, comment) # if action is "deleted" then delete all UserAnnoState related to that anno if action_type == AnnoActionType.DELETED: UserAnnoState.delete_by_anno(anno_key=anno.key) for platform, devices in notf_device.iteritems(): message, data = notf_msg[platform] if len(devices): PushTaskQueue.add(message=message, ids=devices, typ=platform.lower(), data=data)
def query_my_anno(cls, limit, curs, user): if user: from model.userannostate import UserAnnoState userannostate_list = UserAnnoState.list_by_user(user_key=user.key) anno_id_list = [ userannostate.anno.id() for userannostate in userannostate_list ] anno_message_list = [] more = False if len(anno_id_list): query = cls.query(cls.anno_id.IN(anno_id_list)).order(-cls.last_update_time, cls.key) anno_list, next_curs, more = query.fetch_page(limit, start_cursor=curs) anno_message_list = [ anno.to_response_message(user) for anno in anno_list if anno is not None ] if more: return AnnoListMessage(anno_list=anno_message_list, cursor=next_curs.urlsafe(), has_more=more) else: return AnnoListMessage(anno_list=anno_message_list, has_more=more) else: return AnnoListMessage(anno_list=[])
def query_by_my_mentions_for_dashboard(cls, limit, curs, user): query = cls.query() query = query.order(-cls.created) query = filter_anno_by_user(query, user, True) from model.userannostate import UserAnnoState userannostate_list = UserAnnoState.query().filter(ndb.AND(UserAnnoState.user == user.key, UserAnnoState.tagged == True)).fetch() anno_list = [ userannostate.anno.id() for userannostate in userannostate_list] if len(anno_list): query = query.filter(cls.anno_id.IN(anno_list)) annos, next_curs, more = query.fetch_page(limit, start_cursor=curs) items = [entity.to_dashboard_response_message(user) for entity in annos] if more: return AnnoDashboardListMessage(anno_list=items, cursor=next_curs.urlsafe(), has_more=more) else: return AnnoDashboardListMessage(anno_list=items, has_more=more) else: return AnnoDashboardListMessage(anno_list=[])
def get_noft_devices(cls, first_user, anno, action_type): """ Get list of device ids to which notification to be sent :param ndb.Model first_user: "******" datastore of user who did anno action :param ndb.Model anno: "anno" datastore :param str action_type: one of the :py:class:`.AnnoActionType` :returns: list of device ids for iOS and Android devices :rtype: dict """ interested_user_deviceids = {PlatformType.IOS: [], PlatformType.ANDROID: []} community_manager_deviceids = {PlatformType.IOS: [], PlatformType.ANDROID: []} # get all interested users for anno if action_type is other than "created" if action_type != AnnoActionType.CREATED: interested_user_list = UserAnnoState.list_users_by_anno( anno_key=anno.key, projection=[UserAnnoState.user, UserAnnoState.last_read] ) interested_user_deviceids = cls.list_deviceid(interested_user_list) # anno is in community-scope then send notification to all managers of that community if anno.community: community_id = anno.community.id() community_manager_list = UserRole.community_user_list(community_id=community_id, only_managers=True) community_manager_deviceids = cls.list_deviceid(community_manager_list) # merging device ids of interested users and community managers notf_devices = { platform: list(set(interested_user_deviceids[platform] + community_manager_deviceids[platform])) for platform in interested_user_deviceids } # removing first user from push notification task if first_user.device_id and first_user.device_type: if first_user.device_id in notf_devices[first_user.device_type]: notf_devices[first_user.device_type].remove(first_user.device_id) return notf_devices
def get_unread_count(self, request): return UserUnreadMessage(unread_count=UserAnnoState.get_unread_count(request))
def update_userannostate_schema_from_anno(anno): user = anno.creator.get() modified = anno.last_update_time if user: UserAnnoState.insert(user=user, anno=anno, modified=modified)