def save_event(self): """ This method takes dictionary containing event data. It first checks if any event is there with given info (user_id, social_network_id, social_network_event_id), then it updates the existing event otherwise creates a new event in getTalent database. Call this method after successfully publishing event on social network. :return event.id: id for event in getTalent database, that was created or updated :rtype event.id : int """ data = self.data sn_event_id = data['social_network_event_id'] social_network_id = data['social_network_id'] event = Event.get_by_user_id_social_network_id_vendor_event_id( self.user.id, social_network_id, sn_event_id) try: # if event exists in database, then update existing one. if event: del data['id'] event.update(**data) else: # event not found in database, create a new one event = Event(**data) Event.save(event) except Exception as error: logger.exception( 'save_event: Event was not updated/saved in Database. ' 'user_id: %s, event_id: %s, social network: %s(id: %s). Error:%s' % (self.user.id, event.id, self.social_network.name, self.social_network.id, error.message)) raise EventNotSaveInDb( 'Error occurred while saving event in database') return event.id
def _get_event(user_id, social_network_id, social_network_event_id): """ This searches the event in database for given parameters. :param positive user_id: If of user :param positive social_network_id: Id of Meetup social-network :param string social_network_event_id: Id of event on Meetup website """ db.session.commit() event = Event.get_by_user_id_social_network_id_vendor_event_id( user_id, social_network_id, social_network_event_id) if not event: # TODO: If event is going to happen in future, we should import that event here raise EventNotFound( 'Event is not present in db, social_network_event_id is %s. User Id: %s' % (social_network_event_id, user_id))
def import_eventbrite_event(user_id, event_url, action_type): """ This celery task retrieves user event from eventbrite and then saves or """ with app.app_context(): logger = app.config[TalentConfigKeys.LOGGER] logger.info('Going to process Eventbrite Event: %s' % event_url) try: eventbrite = SocialNetwork.get_by_name('Eventbrite') eventbrite_sn = EventbriteSocialNetwork( user_id=user_id, social_network_id=eventbrite.id) eventbrite_event_base = EventbriteEventBase( headers=eventbrite_sn.headers, user_credentials=eventbrite_sn.user_credentials, social_network=eventbrite_sn.user_credentials.social_network) if action_type in [ACTIONS['created'], ACTIONS['published']]: logger.info('Event Published on Eventbrite, Event URL: %s' % event_url) eventbrite_event_base.get_event(event_url) elif action_type == ACTIONS['unpublished']: event_id = event_url.split('/')[-2] event_id = int(event_id) event_in_db = Event.get_by_user_id_social_network_id_vendor_event_id( user_id, eventbrite.id, event_id) if event_in_db: if eventbrite_event_base.delete_event( event_in_db.id, delete_from_vendor=False): logger.info( 'Event has been marked as is_deleted_from_vendor in gt database: %s' % event_in_db.to_json()) else: logger.info( 'Event could not be marked as is_deleted_from_vendor in gt database: %s' % event_in_db.to_json()) else: logger.info( "Event unpublished from Eventbrite but it does not exist, don't worry. Event URL: %s" % event_url) elif action_type == ACTIONS['updated']: pass # We are handling update action yet because it causes duplicate entries except Exception: logger.exception('Failed to save event. URL: %s' % event_url)
def save_or_update_event(self, event_data): """ This method takes event dictionary data and save that event in database or update the existing one if there is already event there with this data. :param event_data: event dict :rtype: Event """ event = Event.get_by_user_id_social_network_id_vendor_event_id( self.user.id, self.social_network.id, event_data['social_network_event_id']) event_data['is_deleted_from_vendor'] = 0 event_data['is_hidden'] = 0 if event: event.update(**event_data) logger.info('Event updated successfully : %s' % event.to_json()) else: event = Event(**event_data) Event.save(event) logger.info('Event imported successfully : %s' % event.to_json()) return event
def process_meetup_event(event): """ This celery task is for an individual event to be processed. When `rsvp_events_importer` task finds that some event belongs to getTalent user, it passes this event to this task for further processing. In this task, we create meetup objects for social network and event and the finally save this event by mapping meetup event fields to gt event fields. If event already exists in database, it is updated. If an event contains venue information, is is save in `venue` table or updated an existing venue. :param dict event: event dictionary from meetup """ with app.app_context(): logger = app.config[TalentConfigKeys.LOGGER] # if same event is received multiple times, accept first and reject remaining meetup_event_lock_key = json.dumps(event) if not redis_store.get(meetup_event_lock_key): # set lock for 5 minutes redis_store.set(meetup_event_lock_key, True, 5 * 60) else: logger.info('Already received this Meetup Event: %s.' % event) return 'Done' logger.info('Going to process Meetup Event: %s' % event) try: time.sleep( 5 ) # wait for event creation api to save event in database otherwise there can be duplicate # event created in database (one by api and other by importer) groups = MeetupGroup.get_all_records_by_group_id( event['group']['id']) meetup = SocialNetwork.get_by_name('Meetup') # have to change in try catch as well try: meetup_sn = MeetupSocialNetwork(user_id=groups[0].user.id, social_network_id=meetup.id) except HitLimitReached: meetup_sn = MeetupSocialNetwork(user_id=groups[0].user.id, social_network_id=meetup.id, validate_token=False) meetup_event_base = Meetup( user_credentials=meetup_sn.user_credentials, social_network=meetup, headers=meetup_sn.headers) if event['status'] in [ MEETUP_EVENT_STATUS['upcoming'], MEETUP_EVENT_STATUS['suggested'], MEETUP_EVENT_STATUS['proposed'] ]: event_data = meetup_event_base.fetch_event(event['id']) gt_event_data, gt_venue_data = meetup_event_base.event_sn_to_gt_mapping( event_data) for group_user in groups: try: group_user_credentials = UserSocialNetworkCredential.get_by_user_and_social_network_id\ (group_user.user.id, meetup.id) meetup_event_obj = Meetup( user_credentials=group_user_credentials, social_network=meetup, headers=meetup_sn.headers) if gt_venue_data: gt_venue_data['user_id'] = group_user.user_id venue_in_db = meetup_event_obj.save_or_update_venue( gt_venue_data) gt_event_data['venue_id'] = venue_in_db.id gt_event_data['user_id'] = group_user.user_id meetup_event_obj.save_or_update_event(gt_event_data) except Exception as e: logger.error( "An error occured while saving event, error: %s" % e) elif event['status'] in [ 'canceled', MEETUP_EVENT_STATUS['deleted'] ]: social_network_event_id = event['id'] for group_user in groups: try: event_in_db = Event.get_by_user_id_social_network_id_vendor_event_id( group_user.user_id, meetup.id, social_network_event_id) if event_in_db: meetup_event_base.user = group_user.user if meetup_event_base.delete_event( event_in_db.id, delete_from_vendor=False): logger.info( 'Meetup event has been marked as is_deleted_from_vendor in gt database: %s' % event_in_db.to_json()) else: logger.info( 'Event could not be marked as is_deleted_from_vendor in gt database: %s' % event_in_db.to_json()) else: logger.info( "Meetup event not found in database. event:`%s`." % event) except Exception as e: logger.error( "An error occured while marking event deleted, error: %s" % e) except Exception: logger.exception('Failed to save event: %s' % event) rollback()
def get_attendee(self, rsvp): """ :param rsvp: rsvp is likely the response of social network API. :type rsvp: dict :return: attendee :rtype: object - This function is used to get the data of candidate related to given rsvp. It attaches all the information in attendee object. attendees is a utility object we share in calls that contains pertinent data. - This method is called from process_rsvps() defined in RSVPBase class. :Example: attendee = self.get_attendee(rsvp) **See Also** .. seealso:: process_rsvps() method in RSVPBase class inside social_network_service/rsvp/base.py for more insight. """ try: data = self.graph.get_object('v2.4/' + rsvp['id'], fields='first_name, last_name, name, ' 'email, location, address, ' 'link, picture') except facebook.GraphAPIError: logger.exception( "get_attendee: Couldn't get Facebook's attendee info. " "user_id: %s, social_network_rsvp_id: %s" % (self.user.id, rsvp['id'])) raise if 'location' in data: try: location = self.graph.get_object('v2.4/' + data['location']['id'], fields='location') except facebook.GraphAPIError: logger.exception( "get_attendee: Couldn't get location info (Facebook). " "user_id: %s, social_network_rsvp_id: %s" % (self.user.id, rsvp['id'])) raise if 'location' in location: location = location['location'] else: location = {} if data: try: attendee = Attendee() attendee.first_name = data.get('first_name', '') attendee.last_name = data.get('last_name', '') attendee.full_name = data.get('name', '') attendee.email = data.get('email', '') attendee.city = location.get('city', '') attendee.country = location.get('country', '') attendee.latitude = location.get('latitude') attendee.longitude = location.get('longitude') attendee.zip = location.get('zip') attendee.social_profile_url = data.get('link', '') attendee.picture_url = data['picture']['data']['url'] \ if 'picture' in data and 'data' in data['picture']\ and 'url' in data['picture']['data'] else '' attendee.gt_user_id = self.user.id attendee.social_network_id = self.social_network.id # we are using profile_id here as we don't have any # rsvp_id for this vendor. attendee.vendor_rsvp_id = rsvp['id'] # We do not have 'time' of rsvp from Facebook API response. # We cannot use datetime.now() here because every time we run # importer, we will have duplicate candidates as their added # time will not be same as the added time of previous record. # So for now, we are saving it as blank. attendee.added_time = ' ' attendee.vendor_img_link = \ "<img class='pull-right' " \ "style='width:60px;height:30px' " \ "src='/web/static/images/activities/facebook_logo.png'/>" social_network_event_id = rsvp['vendor_event_id'] if rsvp['rsvp_status'].strip() == 'attending' \ or rsvp['rsvp_status'].strip() == 'maybe': attendee.rsvp_status = 'yes' else: attendee.rsvp_status = 'no' event = Event.get_by_user_id_social_network_id_vendor_event_id( self.user.id, self.social_network.id, social_network_event_id) if event: attendee.event = event return attendee else: raise EventNotFound( 'Event is not present in db, ' 'social_network_event_id is ' '%s. User Id: %s' % (social_network_event_id, self.user.id)) except Exception: raise
def get_attendee(self, rsvp): """ :param rsvp: rsvp is likely the response of social network API. :type rsvp: dict :return: attendee :rtype: object - This function is used to get the data of candidate related to given rsvp. It attaches all the information in attendee object. attendees is a utility object we share in calls that contains pertinent data. - This method is called from process_rsvps() defined in RSVPBase class. :Example: attendee = self.get_attendee(rsvp) - RSVP data return from Meetup looks like In case of RSVPs importer: { 'group': { 'group_lat': 24.860000610351562, 'created': 1439953915212, 'join_mode': 'open', 'group_lon': 67.01000213623047, 'urlname': 'Meteor-Karachi', 'id': 17900002 }, 'created': 1438040123000, 'rsvp_id': 1562651661, 'mtime': 1438040194000, 'event': { 'name': 'Welcome to Karachi - Meteor', 'id': '223588917' 'event_url': 'http://www.meetup.com/Meteor-Karachi/events/223588917/', 'time': 1440252000000, }, 'member': { 'name': 'kamran', 'member_id': 190405794 }, 'guests': 1, 'member_photo': { 'thumb_link': 'http://photos3.meetupstatic.com/photos/member/c/b/1/0/thumb_248211984.jpeg', 'photo_id': 248211984, 'highres_link': 'http://photos3.meetupstatic.com/photos/member/c/b/1/0/highres_248211984.jpeg', 'photo_link': 'http://photos3.meetupstatic.com/photos/member/c/b/1/0/member_248211984.jpeg' }, 'response': 'yes' } From streaming API: { u'group': { u'group_city': u'Denver', u'group_lat': 39.68, u'group_urlname': u'denver-metro-chadd-support', u'group_name': u'Denver-Metro CHADD (Children and Adults with ADHD) Meetup', u'group_lon': -104.92, u'group_topics': [ {u'topic_name': u'ADHD', u'urlkey': u'adhd'}, {u'topic_name': u'ADHD Support', u'urlkey': u'adhd-support'}, {u'topic_name': u'Adults with ADD', u'urlkey': u'adults-with-add'}, {u'topic_name': u'Families of Children who have ADD/ADHD', u'urlkey': u'families-of-children-who-have-add-adhd'}, {u'topic_name': u'ADHD, ADD', u'urlkey': u'adhd-add'}, {u'topic_name': u'ADHD Parents with ADHD Children', u'urlkey': u'adhd-parents-with-adhd-children'}, {u'topic_name': u'Resources for ADHD', u'urlkey': u'resources-for-adhd'}, {u'topic_name': u'Parents of Children with ADHD', u'urlkey': u'parents-of-children-with-adhd'}, {u'topic_name': u'Support Groups for Parents with ADHD Children', u'urlkey': u'support-groups-for-parents-with-adhd-children'}, {u'topic_name': u'Educators Training on AD/HD', u'urlkey': u'educators-training-on-ad-hd'}, {u'topic_name': u'Adults with ADHD', u'urlkey': u'adults-with-adhd'} ], u'group_state': u'CO', u'group_id': 1632579, u'group_country': u'us' }, u'rsvp_id': 1639776896, u'venue': {u'lat': 39.674759, u'venue_id': 3407262, u'lon': -104.936317, u'venue_name': u'Denver Academy-Richardson Hall'}, u'visibility': u'public', u'event': { u'event_name': u'Manage the Impact of Technology on Your Child and Family with Lana Gollyhorn', u'event_id': u'235574682', u'event_url': u'https://www.meetup.com/denver-metro-chadd-support/events/235574682/', u'time': 1479778200000 }, u'member': { u'member_name': u'Valerie Brown', u'member_id': 195674019 }, u'guests': 0, u'mtime': 1479312043215, u'response': u'yes' } In both cases, we get info of attendee from Meetup API and response looks like { u'status': u'active', u'city': u'Horsham', u'name': u'Katalin Nimmerfroh', u'other_services': {}, u'country': u'gb', u'topics': [ {u'name': u'Musicians', u'urlkey': u'musicians', u'id': 173}, {u'name': u'Acting', u'urlkey': u'acting', u'id': 226} ], u'lon': -0.33, u'joined': 1456995423000, u'id': 200820968, u'state': u'P6', u'link': u'http://www.meetup.com/members/200820968', u'photo': { u'thumb_link': u'http://photos2.meetupstatic.com/photos/member/4/f/f/9/thumb_254420473.jpeg', u'photo_id': 254420473, u'highres_link': u'http://photos2.meetupstatic.com/photos/member/4/f/f/9/highres_254420473.jpeg', u'base_url': u'http://photos2.meetupstatic.com', u'type': u'member', u'photo_link': u'http://photos4.meetupstatic.com/photos/member/4/f/f/9/member_254420473.jpeg' }, u'lat': 51.07, u'visited': 1472805623000, u'self': {u'common': {}}} - So we will get the member data and issue a member call to get more info about member so we can later save him as a candidate. **See Also** .. seealso:: process_rsvps() method in RSVPBase class inside social_network_service/rsvp/base.py for more insight. """ if self.rsvp_via_importer: social_network_event_id = rsvp['event']['id'] else: social_network_event_id = rsvp['event']['event_id'] event = Event.get_by_user_id_social_network_id_vendor_event_id(self.user.id, self.social_network.id, social_network_event_id) if not event: raise EventNotFound('Event is not present in db, social_network_event_id is %s. ' 'User Id: %s' % (social_network_event_id, self.user.id)) member_url = self.api_url + '/member/' + str(rsvp['member']['member_id']) # Sleep for 10 / 30 seconds to avoid throttling time.sleep(0.34) response = http_request('GET', member_url, headers=self.headers, user_id=self.user.id) if response.ok: data = response.json() attendee = Attendee() attendee.first_name = data['name'].split(" ")[0] if len(data['name'].split(" ")) > 1: attendee.last_name = data['name'].split(" ")[1] else: attendee.last_name = ' ' attendee.full_name = data['name'] attendee.city = data['city'] attendee.email = '' # Meetup API does not expose this attendee.country = data['country'] attendee.social_profile_url = data['link'] # attendee.picture_url = data['photo']['photo_link'] attendee.gt_user_id = self.user.id attendee.social_network_id = self.social_network.id attendee.rsvp_status = rsvp['response'] attendee.vendor_rsvp_id = rsvp['rsvp_id'] # TODO: This won't work now, need to figure out a way attendee.vendor_img_link = "<img class='pull-right' " \ "style='width:60px;height:30px'" \ " src='/web/static/images" \ "/activities/meetup_logo.png'/>" # get event from database epoch_time = rsvp['mtime'] dt = milliseconds_since_epoch_to_dt(epoch_time) attendee.added_time = dt attendee.event = event return attendee
def get_attendee_for_rsvp_via_webhook(self, rsvp): """ :param rsvp: rsvp is likely the response of social network API. :type rsvp: dict :return: attendee :rtype: object Webhook returns data for RSVP as: { u'config': { u'action': u'order.placed', u'user_id': u'149011448333', u'endpoint_url': u'https://emails.ngrok.io/webhook/1', u'webhook_id': u'274022' }, u'api_url': u'https://www.eventbriteapi.com/v3/orders/573384540/' } we extract order_id from above response and treat it as social_network_rsvp_id. So, rsvp object in this method looks like { 'rsvp_id': 573384540 } When we request Eventbrite API with above rsvp_id, we get response as { u'status': u'placed', u'first_name': u'Muhammad', u'last_name': u'Aslam', u'name': u'Muhammad Aslam', u'created': u'2016-11-29T15:52:15Z', u'event_id': u'29641929810', u'changed': u'2016-11-29T15:52:16Z', u'email': u'*****@*****.**', u'costs': { u'payment_fee': {u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00' }, u'gross': { u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00'}, u'eventbrite_fee': {u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00'}, u'tax': {u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00'}, u'base_price': {u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00'} }, u'time_remaining': None, u'id': u'575508020', u'resource_uri': u'https://www.eventbriteapi.com/v3/orders/575508020/' } - This function is used to get the data of candidate related to given rsvp. It attaches all the information in attendee object. `attendees` is a utility object we share in calls that contains pertinent data. - This method is called from get_attendee() defined in this file. :Example: attendee = self.get_attendee_for_rsvp_via_webhook(rsvp) **See Also** .. seealso:: get_attendee() method in this class. """ order_url = get_url(self, Urls.ORDER).format(rsvp['rsvp_id']) order_response = http_request('GET', order_url, headers=self.headers, user_id=self.user.id) if order_response.ok: data = order_response.json() # get event_id social_network_event_id = data['event_id'] event = Event.get_by_user_id_social_network_id_vendor_event_id( self.user.id, self.social_network.id, social_network_event_id) if not event: raise EventNotFound( 'Event is not present in db, social_network_event_id is %s. User Id: %s' % (social_network_event_id, self.user.id)) created_datetime = datetime.strptime(data['created'][:19], "%Y-%m-%dT%H:%M:%S") attendee = Attendee() attendee.event = event attendee.first_name = data['first_name'] attendee.full_name = data['name'] attendee.last_name = data['last_name'] attendee.added_time = created_datetime attendee.rsvp_status = 'yes' if data[ 'status'] == 'placed' else data['status'] attendee.email = data['email'] attendee.vendor_rsvp_id = rsvp['rsvp_id'] attendee.gt_user_id = self.user.id attendee.social_network_id = self.social_network.id # TODO: This won't work now, need to figure out a way attendee.vendor_img_link = \ "<img class='pull-right'" \ " style='width:60px;height:30px' " \ "src='/web/static/images/activities/eventbrite_logo.png'/>" # GET attendees of event rsvps_url = get_url(self, Urls.RSVPS).format( event.social_network_event_id) response_attendees = http_request('GET', rsvps_url, headers=self.headers) event_attendees = response_attendees.json()['attendees'] for event_attendee in event_attendees: # Get profile url of candidate to save if event_attendee['created'] == order_response.json( )['created']: # In case of Eventbrite, we have a Attendee object created # on Eventbrite website. We save that link as profile url. attendee.social_profile_url = event_attendee[ 'resource_uri'] break return attendee
def get_attendee(self, rsvp): """ :param dict rsvp: is likely the response from social network API. :return: attendee :rtype: Attendee rsvp object looks like { u'profile': { u'first_name': u'Muhammad', u'last_name': u'Basit', u'addresses': { u'home': {}, u'ship': {}, u'work': {}, u'bill': {} }, u'email': u'*****@*****.**', u'name': u'Muhammad Basit' }, u'status': u'Attending', u'checked_in': False, u'created': u'2016-11-23T12:03:40Z', u'event_id': u'29640427316', u'refunded': False, u'changed': u'2016-11-23T12:03:40Z', u'ticket_class_name': u'Early Bird', u'answers': [], u'cancelled': False, u'costs': { u'payment_fee': { u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00' }, u'gross': { u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00' }, u'eventbrite_fee': { u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00' }, u'tax': { u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00' }, u'base_price': { u'currency': u'USD', u'display': u'$0.00', u'value': 0, u'major_value': u'0.00' } }, u'affiliate': None, u'team': None, u'barcodes': [ { u'status': u'unused', u'barcode': u'574038679721387572001', u'checkin_type': 0, u'changed': u'2016-11-23T12:03:40Z', u'created': u'2016-11-23T12:03:40Z' } ], u'ticket_class_id': u'58034349', u'quantity': 1, u'order_id': u'574038679', u'id': u'721387572', u'resource_uri': u'https://www.eventbriteapi.com/v3/events/29640427316/attendees/721387572/' } - This function is used to get the data of candidate related to given rsvp. It attaches all the information in attendee object. attendees is a utility object we share in calls that contains pertinent data. - This method is called from process_rsvps() defined in RSVPBase class. :Example: attendee = self.get_attendee(rsvp) **See Also** .. seealso:: process_rsvps() method in RSVPBase class inside social_network_service/rsvp/base.py """ if not self.rsvp_via_importer: return self.get_attendee_for_rsvp_via_webhook(rsvp) # get event_id social_network_event_id = rsvp['event_id'] event = Event.get_by_user_id_social_network_id_vendor_event_id( self.user.id, self.social_network.id, social_network_event_id) if not event: logger.info( 'Event is not present in db, social_network_event_id is %s. User Id: %s' % (social_network_event_id, self.user.id)) return None created_datetime = datetime.strptime(rsvp['created'][:19], "%Y-%m-%dT%H:%M:%S") attendee = Attendee() attendee.first_name = rsvp['profile']['first_name'] attendee.full_name = rsvp['profile']['name'] attendee.last_name = rsvp['profile']['last_name'] attendee.added_time = created_datetime attendee.rsvp_status = 'yes' if rsvp['status'].lower( ) == 'attending' else rsvp['status'] attendee.email = rsvp['profile']['email'] attendee.vendor_rsvp_id = rsvp['id'] attendee.gt_user_id = self.user.id attendee.social_network_id = self.social_network.id attendee.vendor_img_link = \ "<img class='pull-right'" \ " style='width:60px;height:30px' " \ "src='/web/static/images/activities/eventbrite_logo.png'/>" # Save profile url of candidate to save attendee.social_profile_url = rsvp['resource_uri'] attendee.event = event return attendee