Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds developer quality of life dev enhancements #138

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
ignore = E203, W503
6 changes: 3 additions & 3 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[MESSAGES CONTROL]

disable=print-statement,
singleton-comparison,
disable=singleton-comparison,
no-member,
too-few-public-methods,
protected-access,
Expand All @@ -10,7 +9,8 @@ disable=print-statement,
duplicate-code,
import-error,
nan-comparison,
consider-using-set-comprehension
consider-using-set-comprehension,
consider-using-f-string,

[BASIC]

Expand Down
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
PACKAGE_NAME = meteostat

.PHONY: all help lint tests run

all: help

help: ## Show this help
@echo 'Usage: make COMMAND'
@echo
@echo "Commands:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'

lint: ## Run black, pylint and flake8
black --check $(PACKAGE_NAME) ./tests
pylint $(PACKAGE_NAME)
flake8 $(PACKAGE_NAME)

tests: ## Run tests with coverage and linting
pytest --version
pytest tests/ --log-cli-level=INFO --cov-branch --cov=$(PACKAGE_NAME) --cov-report xml

format: ## Format the Python code using black
black $(PACKAGE_NAME)
black tests/
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ The Meteostat Python library is divided into multiple classes which provide acce
* [Data Sources](https://dev.meteostat.net/sources.html)
* [Terms & License](https://dev.meteostat.net/terms.html)

### Class Diagram
![Classes](classes.png)


### Package Diagram
![Packages](packages.png)

## Example

Let's plot 2018 temperature data for Vancouver, BC:
Expand Down Expand Up @@ -69,6 +76,16 @@ Take a look at the expected output:

Instructions on building and testing the Meteostat Python package can be found in the [documentation](https://dev.meteostat.net/python/contributing.html). More information about the Meteostat bulk data interface is available [here](https://dev.meteostat.net/bulk/).

## Developer setup for contributions

- Fork the repository
- Create a new python virtual environment
- Activate virtual environment
- run pip install -U pip [get the latest version of pip]
- run pip install -r requirements_dev.in [install all packages required to hack the code]
- You can now use the Makefile to run tests and check style formatting as well as generate coverage information
- Hack Away

## Donating

If you want to support the project financially, you can make a donation using one of the following services:
Expand Down
Binary file added classes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 8 additions & 8 deletions meteostat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
__appname__ = "meteostat"
__version__ = "1.6.7"

from .interface.base import Base
from .interface.timeseries import TimeSeries
from .interface.stations import Stations
from .interface.point import Point
from .interface.hourly import Hourly
from .interface.daily import Daily
from .interface.monthly import Monthly
from .interface.normals import Normals
from .interface.base import Base # noqa
from .interface.timeseries import TimeSeries # noqa
from .interface.stations import Stations # noqa
from .interface.point import Point # noqa
from .interface.hourly import Hourly # noqa
from .interface.daily import Daily # noqa
from .interface.monthly import Monthly # noqa
from .interface.normals import Normals # noqa
2 changes: 0 additions & 2 deletions meteostat/core/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def clear_cache(cls, max_age: int = None) -> None:
"""

if os.path.exists(cls.cache_dir + os.sep + cls.cache_subdir):

# Set max_age
if max_age is None:
max_age = cls.max_age
Expand All @@ -63,7 +62,6 @@ def clear_cache(cls, max_age: int = None) -> None:

# Go through all files
for file in os.listdir(cls.cache_dir + os.sep + cls.cache_subdir):

# Get full path
path = os.path.join(cls.cache_dir + os.sep + cls.cache_subdir, file)

Expand Down
7 changes: 0 additions & 7 deletions meteostat/core/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ def processing_handler(

# Multi-core processing
if cores > 1 and len(datasets) > 1:

# Create process pool
with Pool(cores) as pool:

# Process datasets in pool
output = pool.starmap(load, datasets)

Expand All @@ -41,10 +39,8 @@ def processing_handler(

# Multi-thread processing
elif threads > 1 and len(datasets) > 1:

# Create process pool
with ThreadPool(threads) as pool:

# Process datasets in pool
output = pool.starmap(load, datasets)

Expand All @@ -54,7 +50,6 @@ def processing_handler(

# Single-thread processing
else:

for dataset in datasets:
output.append(load(*dataset))

Expand All @@ -77,7 +72,6 @@ def load_handler(
"""

try:

# Read CSV file from Meteostat endpoint
df = pd.read_csv(
endpoint + path,
Expand All @@ -94,7 +88,6 @@ def load_handler(
)

except (FileNotFoundError, HTTPError):

# Create empty DataFrane
df = pd.DataFrame(columns=[*types])

Expand Down
1 change: 0 additions & 1 deletion meteostat/interface/daily.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def __init__(
model: bool = True, # Include model data?
flags: bool = False, # Load source flags?
) -> None:

# Initialize time series
self._init_time_series(loc, start, end, model, flags)

Expand Down
8 changes: 4 additions & 4 deletions meteostat/interface/hourly.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,14 @@ def _set_time(
"""

# Don't use chunks if full dataset is requested
if start == None:
if start is None:
self.chunked = False

if timezone:
# Save timezone
self._timezone = timezone

if start and end:

# Initialize time zone
timezone = pytz.timezone(self._timezone)

Expand All @@ -124,7 +123,9 @@ def _set_time(
end = timezone.localize(end, is_dst=None).astimezone(pytz.utc)

if self.chunked:
self._annual_steps = [start.year + i for i in range(end.year - start.year + 1)]
self._annual_steps = [
start.year + i for i in range(end.year - start.year + 1)
]

self._start = start
self._end = end
Expand All @@ -138,7 +139,6 @@ def __init__(
model: bool = True, # Include model data?
flags: bool = False, # Load source flags?
) -> None:

# Set time zone and adapt period
self._set_time(start, end, timezone)

Expand Down
7 changes: 0 additions & 7 deletions meteostat/interface/meteodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,10 @@ def _load_data(self, station: str, year: Union[int, None] = None) -> None:

# Check if file in cache
if self.max_age > 0 and file_in_cache(path, self.max_age):

# Read cached data
df = pd.read_pickle(path)

else:

# Get data from Meteostat
df = load_handler(
self.endpoint, file, self._columns, self._types, self._parse_dates
Expand Down Expand Up @@ -119,7 +117,6 @@ def _get_data(self) -> None:
"""

if len(self._stations) > 0:

# Get list of datasets
datasets = self._get_datasets()

Expand All @@ -143,9 +140,7 @@ def _resolve_point(
return None

if method == "nearest":

if adapt_temp:

# Join elevation of involved weather stations
data = self._data.join(stations["elevation"], on="station")

Expand All @@ -156,7 +151,6 @@ def _resolve_point(
data = data.drop("elevation", axis=1).round(1)

else:

data = self._data

if self.granularity == Granularity.NORMALS:
Expand All @@ -168,7 +162,6 @@ def _resolve_point(
).agg("first")

else:

# Join score and elevation of involved weather stations
data = self._data.join(stations[["score", "elevation"]], on="station")

Expand Down
1 change: 0 additions & 1 deletion meteostat/interface/monthly.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ def __init__(
model: bool = True, # Include model data?
flags: bool = False, # Load source flags?
) -> None:

# Set start date
if start is not None:
start = start.replace(day=1)
Expand Down
1 change: 0 additions & 1 deletion meteostat/interface/normals.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def __init__(
start: int = None,
end: int = None,
) -> None:

# Set list of weather stations
if isinstance(loc, pd.DataFrame):
self._stations = loc.index
Expand Down
4 changes: 1 addition & 3 deletions meteostat/interface/point.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class Point:
_alt: int = None

def __init__(self, lat: float, lon: float, alt: int = None) -> None:

self._lat = lat
self._lon = lon
self._alt = alt
Expand Down Expand Up @@ -90,7 +89,7 @@ def get_stations(
# Apply inventory filter
if freq and start and end:
age = (datetime.now() - end).days
if model == False or age > 180:
if model is False or age > 180:
stations = stations.inventory(freq, (start, end))

# Apply altitude filter
Expand All @@ -110,7 +109,6 @@ def get_stations(

# Score values
if self.radius:

# Calculate score values
stations["score"] = (
(1 - (stations["distance"] / self.radius)) * self.weight_dist
Expand Down
9 changes: 3 additions & 6 deletions meteostat/interface/stations.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,10 @@ def _load(self) -> None:

# Check if file in cache
if self.max_age > 0 and file_in_cache(path, self.max_age):

# Read cached data
df = pd.read_pickle(path)

else:

# Get data from Meteostat
df = load_handler(
self.endpoint, file, self._columns, self._types, self._parse_dates, True
Expand All @@ -102,7 +100,6 @@ def _load(self) -> None:
self._data = df

def __init__(self) -> None:

# Get all weather stations
self._load()

Expand Down Expand Up @@ -179,12 +176,12 @@ def inventory(

if required is True:
# Make sure data exists at all
temp._data = temp._data[(pd.isna(temp._data[freq + "_start"]) == False)]
temp._data = temp._data[(pd.isna(temp._data[freq + "_start"]) is False)]

elif isinstance(required, tuple):
# Make sure data exists across period
temp._data = temp._data[
(pd.isna(temp._data[freq + "_start"]) == False)
(pd.isna(temp._data[freq + "_start"]) is False)
& (temp._data[freq + "_start"] <= required[0])
& (
temp._data[freq + "_end"] + timedelta(seconds=temp.max_age)
Expand All @@ -195,7 +192,7 @@ def inventory(
else:
# Make sure data exists on a certain day
temp._data = temp._data[
(pd.isna(temp._data[freq + "_start"]) == False)
(pd.isna(temp._data[freq + "_start"]) is False)
& (temp._data[freq + "_start"] <= required)
& (
temp._data[freq + "_end"] + timedelta(seconds=temp.max_age)
Expand Down
3 changes: 0 additions & 3 deletions meteostat/interface/timeseries.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,10 @@ def _load_flags(self, station: str, year: Union[int, None] = None) -> None:

# Check if file in cache
if self.max_age > 0 and file_in_cache(path, self.max_age):

# Read cached data
df = pd.read_pickle(path)

else:

# Get data from Meteostat
df = load_handler(
self.endpoint,
Expand Down Expand Up @@ -99,7 +97,6 @@ def _get_flags(self) -> None:
"""

if len(self._stations) > 0:

# Get list of datasets
datasets = self._get_datasets()

Expand Down
1 change: 0 additions & 1 deletion meteostat/series/aggregate.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ def aggregate(self, freq: str = None, spatial: bool = False):
"""

if self.count() > 0 and not self._data.isnull().values.all():

# Create temporal instance
temp = copy(self)

Expand Down
1 change: 0 additions & 1 deletion meteostat/series/interpolate.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def interpolate(self, limit: int = 3):
"""

if self.count() > 0 and not self._data.isnull().values.all():

# Create temporal instance
temp = copy(self)

Expand Down
1 change: 0 additions & 1 deletion meteostat/series/normalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ def normalize(self):
temp = copy(self)

if temp._start and temp._end and temp.coverage() < 1:

# Create result DataFrame
result = pd.DataFrame(columns=temp._columns[temp._first_met_col :])

Expand Down
Binary file added packages.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pandas>=1.1
pytz
numpy
matplotlib
wheel
twine
Loading