Пример #1
0
 def __init__(self, api_key=''):
     self._api_key = api_key
     self._request_handler = FMIRequestHandler(self._api_key)
     self._PATH_TO_STATIONS_CSV = "data/stations.csv"
     self._PATH_TO_QUERY_METADATA = "data/supported_queries.json"
     self._stations = self._load_station_metadata()
     self._supported_queries = self._load_supported_queries_metadata()
     self._parser = FMIxmlParser()
Пример #2
0
    def should_return_empty_list_if_only_400_errors(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))

        mock_fmirequest.return_value.get.side_effect = [RequestException('error', 400), RequestException('error', 400)]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)
        assert_equal(2, mock_instance.get.call_count)
        assert_equal(0, len(result))
Пример #3
0
    def should_raise_error_normally_if_error_code_is_something_else_than_400(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0,
                                            tzinfo=timezone))

        mock_fmirequest.return_value.get.side_effect = [RequestException('error', 'othererror'),
                                                        RequestException('error', 400)]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        with pytest.raises(RequestException) as e:
            handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        assert_equal('othererror', e.value.error_code)
        assert_equal(1, mock_instance.get.call_count)
Пример #4
0
    def should_call_fmirequest_in_two_parts_for_372_day_time_span(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))

        expected = [create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                       datetime(2011, 1, 8, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone)),
                    create_daily_query(datetime(2011, 1, 8, hour=0, minute=2, second=0, microsecond=0, tzinfo=timezone),
                                       datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))]

        expected_calls = [call(expected[0]), call(expected[1])]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        mock_instance.get.assert_has_calls(expected_calls)
        assert_equal(2, mock_instance.get.call_count)
        assert_equal(2, len(result))
Пример #5
0
    def should_return_available_data_if_first_part_of_request_does_not_exist(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))

        expected = [create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                       datetime(2011, 1, 8, hour=0, minute=1, second=0, microsecond=0,
                                                tzinfo=timezone))]

        expected_calls = [call(expected[0])]
        # On first request throw exception with code 400, then return return value normally
        # Simulates query where first part is not available, but second is
        mock_fmirequest.return_value.get.side_effect = [RequestException('error', 400), mock.DEFAULT]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        mock_instance.get.assert_has_calls(expected_calls)
        assert_equal(2, mock_instance.get.call_count)
        assert_equal(1, len(result))
Пример #6
0
    def should_get_year_in_one_request(mock_fmirequest):
        query = {'request': 'getFeature',
                 'storedquery_id': 'fmi::observations::weather::daily::multipointcoverage',
                 'fmisid': '1234',
                 'starttime': datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                 'endtime': datetime(2011, 1, 5, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone)
                 }

        expected = {'starttime': datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                    'endtime': datetime(2011, 1, 5, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                    'fmisid': '1234',
                    'request': 'getFeature',
                    'storedquery_id': 'fmi::observations::weather::daily::multipointcoverage'
                    }

        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        mock_instance.get.assert_has_calls([call(expected)])
        assert_equal(1, mock_instance.get.call_count)
        assert_equal(1, len(result))
Пример #7
0
def describe_fmi_request_handler():
    fmi_handler = FMIRequestHandler('apikey')
    _DAILY_REQUEST_MAX_RANGE_HOURS = 8928
    _REALTIME_REQUEST_MAX_RANGE_HOURS = 168

    @mock.patch('fmiapi.fmirequesthandler.FMIRequest', spec=True)
    def should_get_year_in_one_request(mock_fmirequest):
        query = {'request': 'getFeature',
                 'storedquery_id': 'fmi::observations::weather::daily::multipointcoverage',
                 'fmisid': '1234',
                 'starttime': datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                 'endtime': datetime(2011, 1, 5, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone)
                 }

        expected = {'starttime': datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                    'endtime': datetime(2011, 1, 5, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                    'fmisid': '1234',
                    'request': 'getFeature',
                    'storedquery_id': 'fmi::observations::weather::daily::multipointcoverage'
                    }

        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        mock_instance.get.assert_has_calls([call(expected)])
        assert_equal(1, mock_instance.get.call_count)
        assert_equal(1, len(result))

    @mock.patch('fmiapi.fmirequesthandler.FMIRequest', spec=True)
    def should_call_fmirequest_in_two_parts_for_372_day_time_span(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))

        expected = [create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                       datetime(2011, 1, 8, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone)),
                    create_daily_query(datetime(2011, 1, 8, hour=0, minute=2, second=0, microsecond=0, tzinfo=timezone),
                                       datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))]

        expected_calls = [call(expected[0]), call(expected[1])]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        mock_instance.get.assert_has_calls(expected_calls)
        assert_equal(2, mock_instance.get.call_count)
        assert_equal(2, len(result))

    @mock.patch('fmiapi.fmirequesthandler.FMIRequest', spec=True)
    def should_return_available_data_if_first_part_of_request_does_not_exist(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))

        expected = [create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                       datetime(2011, 1, 8, hour=0, minute=1, second=0, microsecond=0,
                                                tzinfo=timezone))]

        expected_calls = [call(expected[0])]
        # On first request throw exception with code 400, then return return value normally
        # Simulates query where first part is not available, but second is
        mock_fmirequest.return_value.get.side_effect = [RequestException('error', 400), mock.DEFAULT]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        mock_instance.get.assert_has_calls(expected_calls)
        assert_equal(2, mock_instance.get.call_count)
        assert_equal(1, len(result))

    @mock.patch('fmiapi.fmirequesthandler.FMIRequest', spec=True)
    def should_return_empty_list_if_only_400_errors(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone))

        mock_fmirequest.return_value.get.side_effect = [RequestException('error', 400), RequestException('error', 400)]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        result = handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)
        assert_equal(2, mock_instance.get.call_count)
        assert_equal(0, len(result))

    @mock.patch('fmiapi.fmirequesthandler.FMIRequest', spec=True)
    def should_raise_error_normally_if_error_code_is_something_else_than_400(mock_fmirequest):
        query = create_daily_query(datetime(2010, 1, 1, hour=0, minute=1, second=0, microsecond=0, tzinfo=timezone),
                                   datetime(2011, 1, 23, hour=0, minute=1, second=0, microsecond=0,
                                            tzinfo=timezone))

        mock_fmirequest.return_value.get.side_effect = [RequestException('error', 'othererror'),
                                                        RequestException('error', 400)]
        mock_instance = mock_fmirequest.return_value
        mock_instance.get.return_value = 'data'

        handler = FMIRequestHandler('apikey')
        with pytest.raises(RequestException) as e:
            handler.request(query, max_timespan=_DAILY_REQUEST_MAX_RANGE_HOURS, progress_callback=None)

        assert_equal('othererror', e.value.error_code)
        assert_equal(1, mock_instance.get.call_count)
Пример #8
0
 def set_apikey(self, api_key):
     self._api_key = api_key
     self._request_handler = FMIRequestHandler(self._api_key)
Пример #9
0
class FMIApi:
    """
    Provides a simple interface to interact with FMI API by providing basic functions to get
    data from FMI's open data service.
    """
    def __init__(self, api_key=''):
        self._api_key = api_key
        self._request_handler = FMIRequestHandler(self._api_key)
        self._PATH_TO_STATIONS_CSV = "data/stations.csv"
        self._PATH_TO_QUERY_METADATA = "data/supported_queries.json"
        self._stations = self._load_station_metadata()
        self._supported_queries = self._load_supported_queries_metadata()
        self._parser = FMIxmlParser()

    def set_apikey(self, api_key):
        self._api_key = api_key
        self._request_handler = FMIRequestHandler(self._api_key)

    def get_apikey(self):
        return self._api_key

    def get_data(self, params, callback_function=None, change_to_parsing=None):
        if params[
                "storedquery_id"] == "fmi::observations::weather::daily::multipointcoverage":
            # Special logic for daily observations
            params['endtime'] += datetime.timedelta(
                days=1
            )  # add one day to end time to get final day into result too

        data = self._request_handler.request(
            params,
            max_timespan=params['max_hours_range'],
            progress_callback=callback_function)
        # Notify ui that moving to parsing phase
        if change_to_parsing is not None:
            change_to_parsing()

        try:
            return self._parser.parse(data,
                                      progress_callback=callback_function)
        except NoDataException:
            # Augment date data to exception and raise it again
            raise NoDataException(starttime=params['starttime'],
                                  endtime=params['endtime'])

    def _load_station_metadata(self):
        """ FMI apparently didn't provide an API-endpoint to get list of all the stations. For now, we load the
        required station information from CSV-file. Change to a api-endpoint if one becomes (or is already?) available.
        """
        stations = []
        with open(self._PATH_TO_STATIONS_CSV, "r", encoding="utf8") as file:
            reader = csv.DictReader(file, [
                "Name", "FMISID", "LPNN", "WMO", "lat", "lon", "Altitude",
                "Group", "Since"
            ],
                                    delimiter=";")
            for row in reader:
                stations.append(row)
        return stations

    def _load_supported_queries_metadata(self):
        with open(self._PATH_TO_QUERY_METADATA, "r", encoding="utf8") as file:
            queries = json.load(file)
        return queries

    def get_stations(self):
        return self._stations

    def get_supported_queries(self):
        return self._supported_queries

    def get_catalogue_of_station(self, fmisid):
        # Add extra metadata for each dataset which are required for queries and translations
        # in short data which is not provided by catalogue service. See supported_queries.json
        datasets = fmicatalogservice.get_station_metadata(fmisid)
        augmented = []
        for ds in datasets:
            for sq in self._supported_queries:
                if re.search(sq['id'], ds['identifier']):
                    augmented.append({**ds, **sq})
                    break

        return augmented

    def get_index_of_station(self, place_name):
        for i in range(0, len(self._stations)):
            if self._stations[i]["Name"] == place_name:
                return i
        return -1