Пример #1
0
    def zone(self, user):
        """Returns the time zone at the user's home address."""

        try:
            home = user.get('home')
            if not home:
                raise DataError('Missing home address')
            location = self._geocoder[home]
            return timezone(location.timezone)
        except (AstralError, KeyError) as e:
            raise DataError(e)
Пример #2
0
    def rewrite_cron(self, cron, reference, user):
        """Replace references to sunrise and sunset in a cron expression."""
        # Skip if there is nothing to rewrite.
        if 'sunrise' not in cron and 'sunset' not in cron:
            return cron

        # Replace H:M:S in the reference time so that we get the correct sunrise / set
        reference_midnight = reference.replace(hour=0, minute=0, second=0, microsecond=0)

        # Determine the first two days of the cron expression after the
        # reference, which covers all candidate sunrises and sunsets.
        yesterday = reference_midnight - timedelta(days=1)
        midnight_cron = cron.replace('sunrise', '0 0').replace('sunset', '0 0')
        try:
            first_day = croniter(midnight_cron, yesterday).get_next(datetime)
            second_day = croniter(midnight_cron, first_day).get_next(datetime)
        except ValueError as e:
            raise DataError(e)

        zone = self._local_time.zone(user)
        try:
            home = self._astral[user.get('home')]
        except (AstralError, KeyError) as e:
            raise DataError(e)

        # Calculate the closest future sunrise time and replace the term in the
        # cron expression with minutes and hours.
        if 'sunrise' in cron:
            sunrises = map(lambda x: home.sunrise(x).astimezone(zone),
                           [first_day, second_day])
            next_sunrise = min(filter(lambda x: x >= reference_midnight, sunrises))
            sunrise_cron = cron.replace('sunrise', '%d %d' % (
                next_sunrise.minute, next_sunrise.hour))
            info('Rewrote cron: (%s) -> (%s), reference %s' % (
                cron,
                sunrise_cron,
                reference_midnight.strftime('%A %B %d %Y %H:%M:%S %Z')))
            return sunrise_cron

        # Calculate the closest future sunset time and replace the term in the
        # cron expression with minutes and hours.
        if 'sunset' in cron:
            sunsets = map(lambda x: home.sunset(x).astimezone(zone),
                          [first_day, second_day])
            next_sunset = min(filter(lambda x: x >= reference_midnight, sunsets))
            sunset_cron = cron.replace('sunset', '%d %d' % (next_sunset.minute,
                                                            next_sunset.hour))
            info('Rewrote cron: (%s) -> (%s), reference %s' % (
                cron,
                sunset_cron,
                reference_midnight.strftime('%A %B %d %Y %H:%M:%S %Z')))
            return sunset_cron
Пример #3
0
    def image(self, user, width, height):
        """Generates the current commute image."""

        # Extract the directions data.
        try:
            directions = self._google_maps.directions(user)
            status = directions['status']
            if status != 'OK':
                try:
                    error_message = directions['error_message']
                    raise DataError(error_message)
                except KeyError:
                    raise DataError(status)
            routes = directions['routes']
            route = routes[0]
            polyline = route['overview_polyline']['points']
            summary = route['summary']
            leg = route['legs'][0]  # Expect one leg.
            try:
                duration = leg['duration_in_traffic']['text']
            except KeyError:
                duration = leg['duration']['text']
        except (DataError, IndexError, KeyError) as e:
            raise ContentError(e)

        # Get the static map with the route as an image.
        try:
            image = self._google_maps.map_image(width,
                                                height,
                                                polyline=polyline)
        except DataError as e:
            raise ContentError(e)

        # Draw the directions text inside a centered box.
        if summary:
            directions_text = '%s via %s' % (duration, summary)
        else:
            directions_text = duration
        draw_text(directions_text,
                  font_spec=SUBVARIO_CONDENSED_MEDIUM,
                  text_color=DIRECTIONS_TEXT_COLOR,
                  anchor='center',
                  box_color=DIRECTIONS_BOX_COLOR,
                  box_padding=DIRECTIONS_BOX_PADDING,
                  border_color=DIRECTIONS_BORDER_COLOR,
                  border_width=DIRECTIONS_BORDER_WIDTH,
                  image=image)

        return image
Пример #4
0
    def _home_location(self, user):
        """Gets the location of the user's home address."""

        try:
            home = user.get('home')
            return self.geocoder[home]
        except (AstralError, KeyError) as e:
            raise DataError(e)
Пример #5
0
    def _route_url(self, home, work, travel_mode):
        """Constructs the URL for the Directions API request."""

        if not home:
            raise DataError('Missing home address')

        if not work:
            raise DataError('Missing work address')

        if not travel_mode:
            raise DataError('Missing travel mode')

        url = DIRECTIONS_URL
        url += '?key=%s' % self.google_maps_api_key
        url += '&origin=%s' % quote(home)
        url += '&destination=%s' % quote(work)
        url += '&mode=%s' % travel_mode
        url += '&departure_time=now'

        return url
Пример #6
0
    def _download_map(self, polyline=None, markers=None, marker_icon=None,
                      hide_map=False):
        """Downloads the image data from the Google Static Map API."""

        image_url = self._static_map_url(polyline=polyline, markers=markers,
                                         marker_icon=marker_icon,
                                         hide_map=hide_map)

        try:
            image_response = get(image_url).content
        except RequestException as e:
            raise DataError(e)
        image_data = BytesIO(image_response)

        return image_data
Пример #7
0
    def _request_icon(self, location):
        """Requests the current weather icon from the OpenWeather API."""

        # Look up the current weather conditions at the location.
        request_url = OPEN_WEATHER_URL % (
            location.latitude, location.longitude, self._open_weather_api_key)

        try:
            response_json = get(request_url).json()
            icon = response_json['current']['weather'][0]['icon']
        except (RequestException, JSONDecodeError, KeyError) as e:
            raise DataError(e)

        info('Weather: %s' % icon)
        return icon
Пример #8
0
    def _request_icon(self, location):
        """Requests the current weather icon from the Dark Sky API."""

        # Look up the weather forecast at the location.
        forecast_url = FORECAST_URL % (self.dark_sky_api_key,
                                       location.latitude, location.longitude)
        try:
            forecast = get(forecast_url).json()
        except (RequestException, JSONDecodeError) as e:
            raise DataError(e)

        # Get the icon encoding the current weather.
        icon = forecast['currently']['icon']
        info('Weather: %s' % icon)

        return icon
Пример #9
0
    def directions(self, user):
        """Gets the directions from the user's home to work."""

        # Get the user's addresses.
        try:
            home = user.get('home')
            work = user.get('work')
            travel_mode = user.get('travel_mode')
        except KeyError as e:
            raise DataError(e)

        # Make the Directions API request.
        directions_url = self._route_url(home, work, travel_mode)
        directions = get(directions_url).json()

        return directions
Пример #10
0
    def is_daylight(self, user):
        """Calculate whether the sun is currently up."""
        # Find the sunrise and sunset times for today.
        time = self._local_time.now(user)
        zone = self._local_time.zone(user)
        try:
            home = self._astral[user.get('home')]
        except (AstralError, KeyError) as e:
            raise DataError(e)
        sunrise = home.sunrise(time).astimezone(zone)
        sunset = home.sunset(time).astimezone(zone)

        is_daylight = time > sunrise and time < sunset

        info('Daylight: %s (%s)' % (is_daylight,
                                    time.strftime('%A %B %d %Y %H:%M:%S %Z')))

        return is_daylight
Пример #11
0
    def _download_map(self,
                      width,
                      height,
                      polyline=None,
                      markers=None,
                      marker_icon=None,
                      hide_map=False):
        """Downloads the image data from the Google Static Map API."""

        image_url = self._static_map_url(width,
                                         height,
                                         polyline=polyline,
                                         markers=markers,
                                         marker_icon=marker_icon,
                                         hide_map=hide_map)

        try:
            return get(image_url).content
        except RequestException as e:
            raise DataError(e)
Пример #12
0
    def map_image(self,
                  width,
                  height,
                  polyline=None,
                  markers=None,
                  marker_icon=None):
        """Creates a map image with optional route or markers."""

        # Get the static map as an image.
        image_data = self._download_map(width,
                                        height,
                                        polyline=polyline,
                                        markers=markers,
                                        marker_icon=marker_icon)
        with BytesIO(image_data) as buffer:
            image = Image.open(buffer).convert('RGB')

        # Catch map size restrictions.
        if image.width != width or image.height != height:
            raise DataError('Requested a %dx%d map but got %dx%d. Try this: ht'
                            'tps://developers.google.com/maps/documentation/ma'
                            'ps-static/start#Largerimagesizes' %
                            (width, height, image.width, image.height))

        # NOTE: Unfortunately, making the copyright text readable has been
        #       interpreted as being against the Google Maps Platform Terms of
        #       Service: https://cloud.google.com/maps-platform/terms
        # # Replace the copyright text with a more readable pixel font.
        # copyright_text = self._copyright_text(width, height,
        #                                       polyline=polyline,
        #                                       markers=markers,
        #                                       marker_icon=marker_icon)
        # draw_text(copyright_text,
        #           font_spec=SCREENSTAR_SMALL_REGULAR,
        #           text_color=COPYRIGHT_TEXT_COLOR,
        #           anchor='bottom_right',
        #           box_color=COPYRIGHT_BOX_COLOR,
        #           box_padding=COPYRIGHT_BOX_PADDING,
        #           image=image)

        return image
Пример #13
0
    def rewrite_cron(self, cron, reference, user, forward=True):
        """Replaces references to sunrise and sunset in a cron expression."""

        # Skip if there is nothing to rewrite.
        if 'sunrise' not in cron and 'sunset' not in cron:
            return cron

        # Determine the two days surrounding the cron expression for the
        # reference time, which covers all candidate sunrises and sunsets.
        yesterday = reference - timedelta(days=2)
        midnight_cron = cron.replace('sunrise', '0 0').replace('sunset', '0 0')
        try:
            prev_day = croniter(midnight_cron, yesterday).get_next(datetime)
            current_day = croniter(midnight_cron, prev_day).get_next(datetime)
            next_day = croniter(midnight_cron, current_day).get_next(datetime)

        except ValueError as e:
            raise DataError(e)

        zone = self._local_time.zone(user)
        try:
            home = self._astral[user.get('home')]
        except (AstralError, KeyError) as e:
            raise DataError(e)

        # Set the candidate days and filter based on direction
        candidate_days = []
        direction_filter = None
        sorter = None

        def forward_filter(x):
            return x >= reference

        def backward_filter(x):
            return x <= reference

        if forward:
            candidate_days.extend([current_day, next_day])
            direction_filter = forward_filter
            sorter = min
        else:
            candidate_days.extend([prev_day, current_day])
            direction_filter = backward_filter
            sorter = max

        # Calculate the closest sunrise time and replace the term in the
        # cron expression with minutes and hours.
        if 'sunrise' in cron:
            sunrises = map(
                    lambda x: home.sunrise(x).astimezone(zone),
                    candidate_days)
            next_sunrise = sorter(filter(direction_filter, sunrises))
            sunrise_cron = cron.replace('sunrise', '%d %d' % (
                next_sunrise.minute, next_sunrise.hour))
            info('Rewrote cron: (%s) -> (%s), reference %s' % (
                cron,
                sunrise_cron,
                reference.strftime('%A %B %d %Y %H:%M:%S %Z')))
            return sunrise_cron

        # Calculate the closest future sunset time and replace the term in the
        # cron expression with minutes and hours.
        if 'sunset' in cron:
            sunsets = map(
                    lambda x: home.sunset(x).astimezone(zone),
                    candidate_days)
            next_sunset = sorter(filter(direction_filter, sunsets))
            sunset_cron = cron.replace('sunset', '%d %d' % (next_sunset.minute,
                                                            next_sunset.hour))
            info('Rewrote cron: (%s) -> (%s), reference %s' % (
                cron,
                sunset_cron,
                reference.strftime('%A %B %d %Y %H:%M:%S %Z')))
            return sunset_cron