def _get_bounds(self):
     bounds = None
     if self.location.data:
         geocode = gmaps_api.lookup_address(self.location.data,
                                            language=self.locale.data)
         bounds = math.expand_bounds(geocode.latlng_bounds(),
                                     self.distance_in_km())
     return bounds
Esempio n. 2
0
 def create_from_location(cls, location):
     if location:
         geocode = gmaps_api.get_geocode(address=location)
         if not geocode:
             raise search_base.SearchException("Did not understand location: %s" % location)
         bounds = math.expand_bounds(geocode.latlng_bounds(), 200) # HACK?
     else:
         bounds = None
     self = cls(bounds=bounds)
     return self
def _get_bounds_for_fb_event(fb_event, check_places=False):
    # We don't need google-maps latlong accuracy. Let's cheat and use the fb_event for convenience if possible...
    location = fb_event['info'].get('place', {}).get('location', {})
    if location and location.get('latitude') is not None:
        latlong = (location['latitude'], location['longitude'])
        # TODO: Someday we'd like to use locations of Taiwan or Germany to grab a bunch of stuff in those bounds
        # but for now, FB doesn't send us proper lat-long-boxes for them, and I don't want to look up everything
        # just in case there are bigger bounds...so we accept the latlong as-is.
        bounds = math.expand_bounds((latlong, latlong), cities.NEARBY_DISTANCE_KM)
    else:
        logging.info('Looking up event %s LocationInfo', fb_event['info']['id'])
        # Places textsearch lookups turn out to be 10x-expensive against our quota
        # So we disable them here, and instead just rely on the 99% good address searches
        # It should fallback to places on un-geocodable addresses too...
        # But at least it won't try Places *in addition* to geocode lookups.
        location_info = event_locations.LocationInfo(fb_event, check_places=check_places)
        if location_info.geocode:
            bounds = math.expand_bounds(location_info.geocode.latlng_bounds(), cities.NEARBY_DISTANCE_KM)
        else:
            bounds = None
    return bounds
Esempio n. 4
0
def get_nearby_cities(latlng, country=None, distance=None):
    # We shrink it by two:
    # An event in Palo Alto could be thrown into a San Jose bucket
    # But an event in San Francisco, looking for "people who would go to SF event",
    # would want to include Palo Alto in its search radius....so would need to go 2x to San Jose
    # So instead of searching 200km in popular people for cities...let's try to be more specific about which person goes to which city
    distance = distance or NEARBY_DISTANCE_KM / 2
    southwest, northeast = math.expand_bounds((latlng, latlng), distance)
    nearby_cities = _get_contained_cities((southwest, northeast),
                                          country=country)
    nearby_cities = [
        x for x in nearby_cities if x.closer_than(latlng, distance)
    ]
    return nearby_cities
def _generate_results_for(city, week_start):
    start_time = week_start
    end_time = start_time + datetime.timedelta(days=8)

    latlng_bounds = ((city.latitude, city.longitude), (city.latitude,
                                                       city.longitude))
    city_bounds = math.expand_bounds(latlng_bounds, cities.NEARBY_DISTANCE_KM)
    search_query = search_base.SearchQuery(
        time_period=search_base.TIME_ALL_FUTURE,
        start_date=start_time,
        end_date=end_time,
        bounds=city_bounds)
    searcher = search.Search(search_query)
    search_results = searcher.get_search_results(full_event=True)
    return search_results
Esempio n. 6
0
def promote_events_to_user(user):
    # TODO: Adjust when we have iphone notifications
    if not android.can_notify(user):
        return

    logging.info("Promoting new events to user %s", user.fb_uid)
    # Only send notifications for Mike for now
    user = users.User.get_by_id(user.fb_uid)
    if not user:
        logging.error("No user found: %s", user.fb_uid)
        return
    if user.expired_oauth_token:
        logging.info("User has expired token, aborting: %s", user.fb_uid)
        return

    user_location = user.location
    if not user_location:
        return
    distance_in_km = user.distance_in_km()
    min_attendees = user.min_attendees

    # search for relevant events
    geocode = gmaps_api.get_geocode(address=user_location)
    if not geocode:
        return None
    bounds = math.expand_bounds(geocode.latlng_bounds(), distance_in_km)
    query = search_base.SearchQuery(time_period=search_base.TIME_UPCOMING,
                                    bounds=bounds,
                                    min_attendees=min_attendees)

    one_day_ago = time.mktime(
        (datetime.datetime.now() - datetime.timedelta(hours=24)).timetuple())

    search_query = search.Search(query)
    search_query.extra_fields = ['creation_time']
    search_results = search_query._get_candidate_doc_events()
    # TODO: can we move this filter into the search query itself??
    recent_events = [
        x.doc_id for x in search_results
        if x.field('creation_time').value > one_day_ago
    ]

    logging.info("Found %s search_results, %s new events", len(search_results),
                 len(recent_events))
    for event_id in recent_events:
        if android.add_notify(user, event_id):
            logging.info("Sent notification!")
def promote_events_to_user(user):
    # TODO: Adjust when we have iphone notifications
    if not android.can_notify(user):
        return

    logging.info("Promoting new events to user %s", user.fb_uid)
    # Only send notifications for Mike for now
    user = users.User.get_by_id(user.fb_uid)
    if not user:
        logging.error("No user found: %s", user.fb_uid)
        return
    if user.expired_oauth_token:
        logging.info("User has expired token, aborting: %s", user.fb_uid)
        return

    user_location = user.location
    if not user_location:
        return
    distance_in_km = user.distance_in_km()
    min_attendees = user.min_attendees

    # search for relevant events
    geocode = gmaps_api.lookup_address(user_location)
    if not geocode:
        return None
    bounds = math.expand_bounds(geocode.latlng_bounds(), distance_in_km)
    query = search_base.SearchQuery(time_period=search_base.TIME_UPCOMING, bounds=bounds, min_attendees=min_attendees)

    one_day_ago = time.mktime((datetime.datetime.now() - datetime.timedelta(hours=24)).timetuple())

    search_query = search.Search(query)
    search_query.extra_fields = ['creation_time']
    search_results = search_query._get_candidate_doc_events()
    # TODO: can we move this filter into the search query itself??
    recent_events = [x.doc_id for x in search_results if x.field('creation_time').value > one_day_ago]

    logging.info("Found %s search_results, %s new events", len(search_results), len(recent_events))
    for event_id in recent_events:
        if android.add_notify(user, event_id):
            logging.info("Sent notification!")
Esempio n. 8
0
    def get(self):
        data = {
            'location': self.request.get('location'),
            'keywords': self.request.get('keywords'),
            'locale': self.request.get('locale'),
        }
        # If it's 1.0 clients, or web clients, then grab all data
        if self.version == (1, 0):
            time_period = search_base.TIME_UPCOMING
        else:
            time_period = self.request.get('time_period')
        data['time_period'] = time_period
        form = search_base.SearchForm(data=data)

        if not form.validate():
            for field, errors in form.errors.items():
                for error in errors:
                    self.add_error(u"%s error: %s" % (
                        getattr(form, field).label.text,
                        error
                    ))

        if not form.location.data:
            city_name = None
            southwest = None
            northeast = None
            if not form.keywords.data:
                if self.version == (1, 0):
                    self.write_json_success({'results': []})
                    return
                else:
                    self.add_error('Please enter a location or keywords')
        else:
            place = gmaps_api.fetch_place_as_json(query=form.location.data, language=form.locale.data)
            if place['status'] == 'OK' and place['results']:
                geocode = gmaps_api.GMapsGeocode(place['results'][0])
                southwest, northeast = math.expand_bounds(geocode.latlng_bounds(), form.distance_in_km())
                city_name = place['results'][0]['formatted_address']
                # This will fail on a bad location, so let's verify the location is geocodable above first.
            else:
                if self.version == (1, 0):
                    self.write_json_success({'results': []})
                    return
                else:
                    self.add_error('Could not geocode location')

        self.errors_are_fatal()

        search_results = []
        distances = [50, 100, 170, 300]
        distance_index = 0
        while not search_results:
            form.distance.data = distances[distance_index]
            form.distance_units.data = 'miles'
            search_query = form.build_query()
            searcher = search.Search(search_query)
            # TODO(lambert): Increase the size limit when our clients can handle it. And improve our result sorting to return the 'best' results.
            searcher.limit = 500
            search_results = searcher.get_search_results(full_event=True)

            # Increase our search distance in the hopes of finding something
            distance_index += 1
            if distance_index == len(distances):
                # If we searched the world, then break
                break

        logging.info("Found %r events within %s %s of %s", form.keywords.data, form.distance.data, form.distance_units.data, form.location.data)
        onebox_links = onebox.get_links_for_query(search_query)

        json_results = []
        for result in search_results:
            try:
                json_result = canonicalize_event_data(result.db_event, result.event_keywords, self.version)
                json_results.append(json_result)
            except Exception as e:
                logging.exception("Error processing event %s: %s" % (result.event_id, e))

        title = self._get_title(city_name, form.keywords.data)

        json_response = {
            'results': json_results,
            'onebox_links': onebox_links,
            'title': title,
            'location': city_name,
            'query': data,
        }
        if southwest and northeast:
            json_response['location_box'] = {
                'southwest': {
                    'latitude': southwest[0],
                    'longitude': southwest[1],
                },
                'northeast': {
                    'latitude': northeast[0],
                    'longitude': northeast[1],
                },
            }
        self.write_json_success(json_response)
def get_center_and_bounds(geocode, distance):
    southwest, northeast = math.expand_bounds(geocode.latlng_bounds(),
                                              distance)
    return geocode.latlng(), southwest, northeast
Esempio n. 10
0
def email_for_user(user, fbl, should_send=True):
    if not user.send_email or not user.email:
        return
    if user.weekly_email_send_date and user.weekly_email_send_date > datetime.datetime.now() - datetime.timedelta(days=3):
        logging.warning("Skipping user %s (%s) because last weekly email was sent on %s", user.fb_uid, user.full_name, user.weekly_email_send_date)
        # Sent the weekly email too recently, let's abort
        return
    fb_user = fbl.fetched_data(fb_api.LookupUser, fbl.fb_uid)
    if not 'profile' in fb_user:
        return

    user_location = user.location
    if not user_location:
        return
    distance_in_km = user.distance_in_km()
    min_attendees = user.min_attendees

    # search for relevant events
    geocode = gmaps_api.lookup_address(user_location)
    if not geocode:
        return None
    bounds = math.expand_bounds(geocode.latlng_bounds(), distance_in_km)
    query = search_base.SearchQuery(time_period=search_base.TIME_UPCOMING, bounds=bounds, min_attendees=min_attendees)
    fb_user = fbl.fetched_data(fb_api.LookupUser, fbl.fb_uid)

    search_results = search.Search(query).get_search_results()
    # Don't send email...
    if not search_results:
        return

    friends.decorate_with_friends(fbl, search_results)
    rsvp.decorate_with_rsvps(fbl, search_results)

    past_results, present_results, grouped_results = search.group_results(search_results, include_all=True)

    display = {}
    display['user'] = user
    display['fb_user'] = fb_user

    week_events = grouped_results[0]
    # Only send emails if we have upcoming events
    if not week_events.results:
        return None
    display['results'] = week_events.results

    jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader("templates"))
    jinja_env.filters['date_human_format'] = user.date_human_format
    jinja_env.globals['dd_event_url'] = urls.dd_event_url
    jinja_env.globals['raw_fb_event_url'] = urls.raw_fb_event_url
    jinja_env.globals['CHOOSE_RSVPS'] = rsvp.CHOOSE_RSVPS
    rendered = jinja_env.get_template('html_mail_summary.html').render(display)
    d = datetime.date.today()
    d = d - datetime.timedelta(days=d.weekday()) # round down to last monday
    logging.info("Rendered HTML:\n%s", rendered)
    message = mail.EmailMessage(
        sender="DanceDeets Events <*****@*****.**>",
        subject="Dance events for %s" % d.strftime('%b %d, %Y'),
        to=user.email,
        html=rendered
    )
    if should_send:
        # Update the last-sent-time here, so we any retryable errors don't cause emails to be multi-sent
        user = users.User.get_by_id(user.fb_uid)
        user.weekly_email_send_date = datetime.datetime.now()
        user.put()
        # And send the message now.
        message.send()
    return message
Esempio n. 11
0
def email_for_user(user, fbl, should_send=True):
    if not user.send_email or not user.email:
        return
    if user.weekly_email_send_date and user.weekly_email_send_date > datetime.datetime.now(
    ) - datetime.timedelta(days=3):
        logging.warning(
            "Skipping user %s (%s) because last weekly email was sent on %s",
            user.fb_uid, user.full_name, user.weekly_email_send_date)
        # Sent the weekly email too recently, let's abort
        return
    fb_user = fbl.fetched_data(fb_api.LookupUser, fbl.fb_uid)
    if not 'profile' in fb_user:
        return

    user_location = user.location
    if not user_location:
        return
    distance_in_km = user.distance_in_km()
    min_attendees = user.min_attendees

    # search for relevant events
    geocode = gmaps_api.get_geocode(address=user_location)
    if not geocode:
        return None
    bounds = math.expand_bounds(geocode.latlng_bounds(), distance_in_km)
    query = search_base.SearchQuery(time_period=search_base.TIME_UPCOMING,
                                    bounds=bounds,
                                    min_attendees=min_attendees)
    fb_user = fbl.fetched_data(fb_api.LookupUser, fbl.fb_uid)

    search_results = search.Search(query).get_search_results()
    # Don't send email...
    if not search_results:
        return

    friends.decorate_with_friends(fbl, search_results)
    rsvp.decorate_with_rsvps(fbl, search_results)

    past_results, present_results, grouped_results = search.group_results(
        search_results, include_all=True)

    display = {}
    display['user'] = user
    display['fb_user'] = fb_user

    week_events = grouped_results[0]
    # Only send emails if we have upcoming events
    if not week_events.results:
        return None
    display['results'] = week_events.results

    jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader("templates"))
    jinja_env.filters['date_human_format'] = user.date_human_format
    jinja_env.globals['dd_event_url'] = urls.dd_event_url
    jinja_env.globals['raw_fb_event_url'] = urls.raw_fb_event_url
    jinja_env.globals['CHOOSE_RSVPS'] = rsvp.CHOOSE_RSVPS
    rendered = jinja_env.get_template('html_mail_summary.html').render(display)
    d = datetime.date.today()
    d = d - datetime.timedelta(days=d.weekday())  # round down to last monday
    logging.info("Rendered HTML:\n%s", rendered)
    message = mail.EmailMessage(
        sender="DanceDeets Events <*****@*****.**>",
        subject="Dance events for %s" % d.strftime('%b %d, %Y'),
        to=user.email,
        html=rendered)
    if should_send:
        # Update the last-sent-time here, so we any retryable errors don't cause emails to be multi-sent
        user = users.User.get_by_id(user.fb_uid)
        user.weekly_email_send_date = datetime.datetime.now()
        user.put()
        # And send the message now.
        message.send()
    return message
Esempio n. 12
0
 def get_bounds(self):
     bounds = None
     if self.location.data:
         geocode = gmaps_api.lookup_address(self.location.data, language=self.locale.data)
         bounds = math.expand_bounds(geocode.latlng_bounds(), self.distance_in_km())
     return bounds
Esempio n. 13
0
    def get(self):
        data = {
            'location': self.request.get('location'),
            'keywords': self.request.get('keywords'),
            'locale': self.request.get('locale'),
        }
        # If it's 1.0 clients, or web clients, then grab all data
        if self.version == (1, 0):
            time_period = search_base.TIME_UPCOMING
        else:
            time_period = self.request.get('time_period')
        data['time_period'] = time_period
        form = search_base.SearchForm(data=data)

        if not form.validate():
            for field, errors in form.errors.items():
                for error in errors:
                    self.add_error(u"%s error: %s" %
                                   (getattr(form, field).label.text, error))

        if not form.location.data:
            city_name = None
            southwest = None
            northeast = None
            if not form.keywords.data:
                if self.version == (1, 0):
                    self.write_json_success({'results': []})
                    return
                else:
                    self.add_error('Please enter a location or keywords')
        else:
            place = gmaps_api.fetch_place_as_json(query=form.location.data,
                                                  language=form.locale.data)
            if place['status'] == 'OK' and place['results']:
                geocode = gmaps_api.GMapsGeocode(place['results'][0])
                southwest, northeast = math.expand_bounds(
                    geocode.latlng_bounds(), form.distance_in_km())
                city_name = place['results'][0]['formatted_address']
                # This will fail on a bad location, so let's verify the location is geocodable above first.
            else:
                if self.version == (1, 0):
                    self.write_json_success({'results': []})
                    return
                else:
                    self.add_error('Could not geocode location')

        self.errors_are_fatal()

        search_results = []
        distances = [50, 100, 170, 300]
        distance_index = 0
        while not search_results:
            form.distance.data = distances[distance_index]
            form.distance_units.data = 'miles'
            search_query = form.build_query()
            searcher = search.Search(search_query)
            # TODO(lambert): Increase the size limit when our clients can handle it. And improve our result sorting to return the 'best' results.
            searcher.limit = 500
            search_results = searcher.get_search_results(full_event=True)

            # Increase our search distance in the hopes of finding something
            distance_index += 1
            if distance_index == len(distances):
                # If we searched the world, then break
                break

        logging.info("Found %r events within %s %s of %s", form.keywords.data,
                     form.distance.data, form.distance_units.data,
                     form.location.data)
        onebox_links = onebox.get_links_for_query(search_query)

        json_results = []
        for result in search_results:
            try:
                json_result = canonicalize_event_data(result.db_event,
                                                      result.event_keywords)
                json_results.append(json_result)
            except Exception as e:
                logging.exception("Error processing event %s: %s" %
                                  (result.event_id, e))

        title = self._get_title(city_name, form.keywords.data)

        json_response = {
            'results': json_results,
            'onebox_links': onebox_links,
            'title': title,
            'location': city_name,
            'query': data,
        }
        if southwest and northeast:
            json_response['location_box'] = {
                'southwest': {
                    'latitude': southwest[0],
                    'longitude': southwest[1],
                },
                'northeast': {
                    'latitude': northeast[0],
                    'longitude': northeast[1],
                },
            }
        self.write_json_success(json_response)