def test_private_event(): from server.models.event import Event, EventAttendee from server.models.user import User with client() as c: user1 = User.find(key='test') user2 = User.find(key='test2') make_friends(c, user1, user2) event_id = create_event(c, user1, 'test event 1', privacy='private') assert 1 == Event.select().count() assert 0 == Event.select().group(user1.group).count() assert 1 == Event.select().user(user1).count() assert 0 == Event.select().user(user2).count() event = Event.find(event_id) resp = api_post(c, user1, '/api/events/{}/invites'.format(event_id), {'invited_id': user2.id}) assert resp.status_code == 200, 'oops {}'.format(resp.data) assert 1 == Event.select().user(user2).count() count, page, results = EventAttendee.select().event(event).user( user2).execute() assert count == 1 assert results[0] == user1
def test_create_event(): from server.models.event import Event from server.models.user import User with client() as c: user1 = User.find(key='test') user2 = User.find(key='test2') create_event(c, user1, 'test event') resp = api_get(c, user1, '/api/events/') assert resp.status_code == 200, 'oops {}'.format(resp.data) data = ujson.loads(resp.data) assert data['objects'][0]['name'] == 'test event' assert data['objects'][0]['group'] == {'$ref': 'Group:1'} event_id = data['objects'][0]['id'] event = Event.find(event_id) assert user1.is_attending(event), 'user is attending the new event' assert 1 == Event.select().count() event_id = create_event(c, user1, 'test event 2') user1 = User.find(key='test') assert user1.get_attending_id() == event_id assert 2 == Event.select().count(), '2 events total now' count, page, results = Event.select().group(user1.group).execute() assert 1 == count, 'only 1 in the group though, since the other has no attendees now' assert results[0].name == 'test event 2'
def new_friend(user_id, friend_id): user = User.find(user_id) friend = User.find(friend_id) if not user.is_friend(friend): return min = epoch(datetime.utcnow() - timedelta(days=8)) # tells each friend about the event history of the other def capture_history(u, f): # capture each of the users posted photos with wigo_db.transaction(commit_on_select=False): for message in EventMessage.select().key(skey( u, 'event_messages')).min(min): if message.user and message.event: message.record_for_user(f) # capture the events being attended for event in Event.select().user(u).min(min): if u.is_attending(event) and f.can_see_event(event): event.add_to_user_attending(f, u) capture_history(user, friend) capture_history(friend, user)
def notify_on_friend(user_id, friend_id, accepted): user = User.find(user_id) friend = User.find(friend_id) if not accepted: message_text = '{} wants to be your friend on Wigo'.format(user.full_name.encode('utf-8')) else: message_text = '{} accepted your friend request'.format(user.full_name.encode('utf-8')) notification = Notification({ 'user_id': friend_id, 'type': 'friend.request' if not accepted else 'friend.accept', 'from_user_id': user.id, 'navigate': '/users/{}'.format(user_id) if accepted else '/find/users/user/{}'.format(user_id), 'badge': 1, 'message': message_text }) if accepted: notification.save() send_notification_push.delay(notification.to_primitive()) else: __send_notification_push(notification) # for imported users from wigo1, send them a push saying their friend just joined wigo2 if friend.status == 'imported': notification = Notification({ 'user_id': friend_id, 'type': 'system', 'message': '{} added you to {} Wigo Summer friend list. Update Wigo now!'.format( user.full_name.encode('utf-8'), ('his' if user.gender == 'male' else 'her' if user.gender == 'female' else 'their')) }) __send_notification_push(notification, api_version_num=1)
def capture_interaction(user_id, with_user_id, t, action='view'): if not client: return logger.debug('capturing prediction event data between {} and {}'.format( user_id, with_user_id)) user = User.find(user_id) with_user = User.find(with_user_id) tz = timezone(with_user.group.timezone) event_time = t.replace(tzinfo=UTC).astimezone(tz) r = client.set_user( user_id, event_time=user.created.replace(tzinfo=UTC).astimezone(tz)) if r.status not in (200, 201): raise Exception('Error returned from prediction io') r = client.set_item( with_user_id, {'categories': [str(with_user.group_id)]}, event_time=with_user.created.replace(tzinfo=UTC).astimezone(tz)) if r.status not in (200, 201): raise Exception('Error returned from prediction io') r = client.record_user_action_on_item(action, user_id, with_user_id, event_time=event_time) if r.status not in (200, 201): raise Exception('Error returned from prediction io')
def tell_friend_user_not_attending(user_id, event_id, friend_id): try: user = User.find(user_id) event = Event.find(event_id) friend = User.find(friend_id) except DoesNotExist: return if not user.is_attending(event): event.remove_from_user_attending(friend, user)
def tell_friend_user_attending(user_id, event_id, friend_id, notify=True): try: user = User.find(user_id) event = Event.find(event_id) friend = User.find(friend_id) except DoesNotExist: return if user.is_attending(event): event.add_to_user_attending(friend, user) if notify: friend_attending.send(None, event=event, user=friend, friend=user)
def user_invited(event_id, inviter_id, invited_id): try: event = Event.find(event_id) inviter = User.find(inviter_id) invited = User.find(invited_id) except DoesNotExist: return # make sure i am seeing all my friends attending now for friend in invited.friends_iter(): if friend.is_attending(event): event.add_to_user_attending(invited, friend)
def sendgrid_hook(): data = request.get_data() or request.form.get('data') or '' if not data: abort(400, message='JSON post data invalid') try: data = ujson.loads(data) except ValueError: abort(400, message='JSON post data invalid') for record in data: event = record.get('event') user = None user_id = record.get('user_id') if user_id: try: user = User.find(user_id) except DoesNotExist: pass if not user: try: user = User.find(email=record.get('email')) except DoesNotExist: pass # only update the user if not validated already, since if they are validated # the email_validated_status will be set if user and not user.email_validated: event = record.get('event') existing_event = user.email_validated_status # make sure events progress forward, don't allow "delivered"->"processed" sg_evt_ord = [ 'processed', 'dropped', 'deferred', 'delivered', 'bounce', 'open', 'click', 'unsubscribe', 'spamreport' ] if event in sg_evt_ord and existing_event in sg_evt_ord and \ sg_evt_ord.index(existing_event) >= sg_evt_ord.index(event): continue logger.info('updating email validation status for user "%s", %s' % (user.email, event)) user.email_validated_status = event user.save() return jsonify(success=True)
def test_update_user(): from server.models.user import User with client() as c: user = User.find(key='test') resp = api_post(c, user, '/api/users/me', { 'key': '123', 'bio': '321' }) assert resp.status_code == 200, 'oops {}'.format(resp.data) user = User.find(key='test') assert user.bio == '321' assert user.key == 'test', 'key should not change, blacklisted'
def decorated(*args, **kw): headers = {} kw['headers'] = headers if context_var == 'user' and 'user_id' in kw: user_id = kw['user_id'] if user_id == '(null)': return f(*args, **kw) context = g.user if user_id == 'me' else User.find(int(user_id)) else: context = getattr(g, context_var, None) if not context: return f(*args, **kw) last_change = wigo_db.get_redis().hget(skey(context, 'meta'), field) if last_change: last_change = datetime.utcfromtimestamp(float(last_change)) else: return f(*args, **kw) if last_change > datetime.utcnow(): # if last-change is set to the future, the intent is to disable if-modified-since # until that time. Last-Modified can't be set to the future or that doesn't work. headers['Last-Modified'] = http_date(datetime.utcnow()) else: headers['Last-Modified'] = http_date(last_change) if max_age: headers['Cache-Control'] = 'max-age={}'.format(max_age) if last_change and not is_resource_modified(request.environ, last_modified=last_change): return 'Not modified', 304, headers return f(*args, **kw)
def post(self): data = request.get_json() facebook_id = data.get('facebook_id') facebook_token = data.get('facebook_access_token') facebook_token_expires = datetime.utcnow() + timedelta( seconds=data.get('facebook_access_token_expires') or 1728000) birthdate = data.get('birthdate') education = data.get('education') work = data.get('work') properties = data.get('properties') if not facebook_id or not facebook_token: abort(400, message='Missing facebook id or token') user = User.find(facebook_id=facebook_id) if user.facebook_token != facebook_token: # hit the facebook api. if this fails, the token is invalid try: facebook = Facebook(facebook_token, facebook_token_expires) fb_user_info = facebook.get('me') if fb_user_info.get('id') != facebook_id: abort(403, message='Facebook token user id does not match passed in user id') facebook_token_expires = facebook.get_token_expiration() except FacebookTimeoutException, e: logger.error('timeout validating facebook token for user "%s"' % user.email) raise except FacebookTokenExpiredException, e: logger.warning('access token expired for user "%s"' % user.email) abort(400, message='Facebook token expired')
def notify_on_attendee(event_id, user_id): try: event = Event.find(event_id) user = User.find(user_id) if event.owner is None: return except DoesNotExist: return if event.is_expired or not user.is_attending(event): return targets = [5, 10, 20, 30, 50, 75, 100, 150, 200, 250, 300, 350, 400, 450, 500] num_attending = get_num_attending(event_id) target = next(reversed([t for t in targets if t <= num_attending]), None) if target is None: return rl_key = 'notify:event_creators:{}:{}:{}'.format(event.id, event.owner_id, target) with rate_limit(rl_key, event.expires) as limited: if not limited: notification = Notification({ 'user_id': event.owner_id, 'type': 'system', 'navigate': '/events/{}'.format(event_id), 'badge': 1, 'message': '{} people are going to your event {}'.format( num_attending, event.name.encode('utf-8')) }).save() send_notification_push.delay(notification.to_primitive())
def new_user(user_id, score=None): user = User.find(user_id) if user.status != 'waiting': return user_queue_key = skey('user_queue') if score is None: if user_id < 130000: score = time() + randint(60, 60 * 30) else: last_waiting = wigo_db.sorted_set_range(user_queue_key, -1, -1, True) if last_waiting: last_waiting_score = last_waiting[0][1] if last_waiting_score > (time() + (60 * 60 * 6)): score = last_waiting_score + 10 else: score = last_waiting_score + randint(0, 60) else: score = time() + randint(120, 60 * 20) wigo_db.sorted_set_add(user_queue_key, user_id, score, replicate=False) scheduler.schedule(datetime.utcfromtimestamp(score), process_waitlist, result_ttl=0, timeout=600)
def notify_on_eventmessage_vote(voter_id, message_id): try: voter = User.find(voter_id) message = EventMessage.find(message_id) except DoesNotExist: return user = message.user type = 'video' if message.media_mime_type == 'video/mp4' else 'photo' # don't send to self or if not friend if (voter_id == message.user_id) or (not user.is_friend(voter_id)): return with rate_limit('notify:vote:%s:%s:%s' % (message.user_id, message_id, voter_id), timedelta(hours=2)) as limited: if not limited: message_text = '{name} liked your {type} in {event}'.format( name=voter.full_name.encode('utf-8'), type=type, event=message.event.name.encode('utf-8')) notification = Notification({ 'user_id': message.user_id, 'type': 'eventmessage.vote', 'from_user_id': voter_id, 'navigate': '/users/me/events/{}/messages/{}'.format(message.event_id, message.id), 'message': message_text }).save() send_notification_push.delay(notification.to_primitive())
def process_waitlist(): from server.db import redis while True: lock = redis.lock('locks:process_waitlist', timeout=600) if lock.acquire(blocking=False): try: user_ids = wigo_db.sorted_set_range_by_score( skey('user_queue'), 0, time(), 0, 50) if user_ids: for user_id in user_ids: logger.info('unlocking user id {}'.format(user_id)) user = User.find(user_id) if user.is_waiting(): user.status = 'active' user.save() # remove from wait list wigo_db.sorted_set_remove(skey('user_queue'), user.id, replicate=False) else: break finally: lock.release()
def notify_on_friend_attending(event_id, user_id, friend_id): num_attending = get_num_attending(event_id, user_id) if num_attending < 5: return try: event = Event.find(event_id) user = User.find(user_id) except DoesNotExist: return if event.is_expired: return rl_key = 'notify:nofa:{}:{}'.format(event_id, user_id) with rate_limit(rl_key, event.expires) as limited: if not limited: friends = list(islice(EventAttendee.select().event(event).user(user).limit(6), 5)) if user in friends: friends.remove(user) num_attending -= 1 logger.info('notifying user {} of {} friends attending event {}'.format(user_id, num_attending, event_id)) if len(friends) >= 2: notification = Notification({ 'user_id': user.id, 'type': 'system', 'navigate': '/users/me/events/{}'.format(event_id), 'badge': 1, 'message': '{}, {}, and {} others are going to {}'.format( friends[0].full_name.encode('utf-8'), friends[1].full_name.encode('utf-8'), num_attending - 2, event.name.encode('utf-8')) }).save() send_notification_push.delay(notification.to_primitive())
def owner(self): if hasattr(self, 'owner_id') and self.owner_id: from server.models.user import User try: return User.find(self.owner_id) except DoesNotExist: logger.warn('user {} not found'.format(self.owner_id)) return None
def tell_friend_event_message(message_id, friend_id): try: message = EventMessage.find(message_id) friend = User.find(friend_id) except DoesNotExist: return message.record_for_user(friend)
def get(self, user_id, headers): user = User.find(self.get_id(user_id)) text = request.args.get('text') if text: sql = """ select users.id from data_int_sorted_sets inner join users on users.key = format('{{user:%%s}}', data_int_sorted_sets.value) where data_int_sorted_sets.key = '{{user:{}}}:friends' """.format(user.id) params = [] split = [('{}%%'.format(part)) for part in re.split(r'\s+', text.strip().lower())] for s in split: sql += "AND ((LOWER(first_name) LIKE %s) or " \ "(LOWER(last_name) LIKE %s))" params.append(s) params.append(s) sql += "ORDER BY first_name, last_name" with slave.execution_context(False) as ctx: results = list(slave.execute_sql(sql, params)) users = User.find([id[0] for id in results]) if g.user == user: for friend in users: friend.friend = True else: for friend, is_friend in g.user.are_friends(users).items(): friend.friend = is_friend return self.serialize_list(self.model, users), 200, headers else: query = self.select(User).user(user).friends() count, page, users = query.execute() if g.user == user: for friend in users: friend.friend = True else: for friend, is_friend in g.user.are_friends(users).items(): friend.friend = is_friend return self.serialize_list(User, users, count, page), 200, headers
def iterate_facebook(next=None): for facebook_id in facebook.get_friend_ids(next): try: friend = User.find(facebook_id=facebook_id) if should_suggest(friend.id): yield friend.id except DoesNotExist: pass
def send_email_verification(user_id, resend=False): if not Configuration.PUSH_ENABLED: return sendgrid = create_sendgrid() user = User.find(user_id) verify_code = wigo_db.get_new_code({ 'type': 'verify_email', 'user_id': user_id, 'email': user.email }) verify_link = '{}://{}/c/{}'.format( 'https' if Configuration.ENVIRONMENT != 'dev' else 'http', Configuration.WEB_HOST, verify_code) logger.info('generated verify code for user "%s", "%s"' % (user.email, verify_code)) first_name = user.first_name if not first_name: first_name = user.email msg = Mail() if resend: msg.set_subject('Everyone deserves a second chance') else: msg.set_subject('Welcome to Wigo') msg.set_from('Wigo <*****@*****.**>') if user.first_name and user.last_name: msg.add_to('%s <%s>' % (user.full_name, user.email)) else: msg.add_to(user.email) msg.set_text( "Hi %s\n\nPlease click the following link to verify your email address:\n\n%s\n\n" % (first_name, verify_link)) msg.set_html( render_template('confirmation_email.html', name=first_name, confirmation_link=verify_link, resend=resend)) msg.add_unique_arg('user_id', user.id) msg.add_category('verify') msg.add_filter('opentrack', 'enable', 0) msg.add_filter('subscriptiontrack', 'enable', 1) msg.add_filter('subscriptiontrack', 'replace', '-UNSUB-') sendgrid.send(msg) logger.info('sent verification email to "%s"' % user.email)
def test_event_messages(): from server.models.event import Event from server.models.user import User with client() as c: user1 = User.find(key='test') user2 = User.find(key='test2') event_id = create_event(c, user1, 'e1') event = Event.find(event_id) create_event_message(c, user1, event, 'test.jpg') assert 1 == EventMessage.select().event(event).count() assert 1 == EventMessage.select().event(event).user(user1).count() assert 0 == EventMessage.select().event(event).user(user2).count() make_friends(c, user1, user2) assert 1 == EventMessage.select().event(event).user(user2).count()
def get(self): user_ids = request.args.get('ids') text = request.args.get('text') if text: text = text.strip() if user_ids: user_ids = [ int(user_id.strip()) for user_id in user_ids.split(',') ] users = User.find(user_ids) return self.serialize_list(self.model, users, len(users), 1) elif text and len(text) > 1: sql = "SELECT id FROM users WHERE " text = text.encode('utf-8') params = [] split = [('{}%%'.format(part)) for part in re.split(r'\s+', text.strip().lower())] for index, s in enumerate(split): if index != 0: sql += ' AND ' sql += "((LOWER(first_name) LIKE %s) or (LOWER(last_name) LIKE %s))" params.append(s) params.append(s) sql += """ ORDER BY earth_distance( ll_to_earth({},{}), ll_to_earth(latitude, longitude) ), first_name, last_name LIMIT 50 """.format(g.user.group.latitude, g.user.group.longitude) with slave.execution_context(False) as ctx: results = list(slave.execute_sql(sql, params)) users = User.find([id[0] for id in results]) return self.serialize_list(self.model, users) else: count, page, instances = self.setup_query( self.model.select().group(g.group)).execute() return self.serialize_list(self.model, instances, count, page)
def test_update_user_group(): from server.models.group import Group from server.models.user import User with client() as c: user = User.find(key='test') g = user.group user.group_id = Group.find(code='san_diego').id user.save() assert User.find(key='test').group.name == 'San Diego' # posting with geo should change the users group resp = api_post(c, user, '/api/users/me', { 'bio': '321' }, lat=42.3584, lon=-71.0598) assert User.find(key='test').group.name == 'Boston'
def test_event_message_votes(): from server.models.event import Event from server.models.user import User from server.db import wigo_db with client() as c: user1 = User.find(key='test') user2 = User.find(key='test2') user3 = User.find(key='test3') make_friends(c, user2, user3) event_id = create_event(c, user1, 'e1') event = Event.find(event_id) message_id_1 = create_event_message(c, user1, event, 'test.jpg') message_id_2 = create_event_message(c, user1, event, 'test.jpg') message_1 = EventMessage.find(message_id_1) message_2 = EventMessage.find(message_id_2) resp = create_event_message_vote(c, user1, event, message_1) resp = create_event_message_vote(c, user2, event, message_2) resp = create_event_message_vote(c, user3, event, message_2) assert wigo_db.get_sorted_set_size(skey(message_1, 'votes')) == 1 assert wigo_db.get_sorted_set_size(skey(message_2, 'votes')) == 2 assert EventMessage.select().event(event).by_votes().get() == message_2 resp = api_get(c, user1, '/api/events/{}/messages/meta'.format(event_id)) data = ujson.loads(resp.data) assert 1 == data[str(message_id_1)]['num_votes'] make_friends(c, user1, user2) make_friends(c, user1, user3) resp = create_event_message_vote(c, user2, event, message_1) resp = create_event_message_vote(c, user3, event, message_1) assert EventMessage.select().event(event).by_votes().get() == message_1
def tell_friends_user_not_attending(user_id, event_id): try: user = User.find(user_id) event = Event.find(event_id) except DoesNotExist: return if not user.is_attending(event): for friend_id, score in wigo_db.sorted_set_iter(skey(user, 'friends')): tell_friend_user_not_attending.delay(user_id, event_id, friend_id)
def privacy_changed(user_id): # tell all friends about the privacy change user = User.find(user_id) with wigo_db.transaction(commit_on_select=False): for friend in user.friends_iter(): if user.privacy == 'public': wigo_db.set_remove(skey(friend, 'friends', 'private'), user_id) else: wigo_db.set_add(skey(friend, 'friends', 'private'), user_id)
def tell_friends_user_attending(user_id, event_id, notify=True): try: user = User.find(user_id) event = Event.find(event_id) except DoesNotExist: return if user.is_attending(event): for friend in user.friends_iter(): if friend.can_see_event(event): tell_friend_user_attending.delay(user_id, event_id, friend.id, notify)
def delete_friend(user_id, friend_id): user = User.find(user_id) friend = User.find(friend_id) if user.is_friend(friend): return def delete_history(u, f): with wigo_db.transaction(commit_on_select=False): for message in EventMessage.select().key(skey(u, 'event_messages')): if message.user and message.event: message.remove_for_user(f) for event in Event.select().user(u): if wigo_db.sorted_set_is_member(user_attendees_key(f, event), u.id): event.remove_from_user_attending(f, u) delete_history(user, friend) delete_history(friend, user)