Skip to content

Commit 9202928

Browse files
authored
Improved Dockerfile to allow IDEs to use the container as remote interpreter (#67)
Improved Dockerfile to allow IDEs to use the container as remote interpreter
1 parent 0ef5a3c commit 9202928

File tree

6 files changed

+83
-44
lines changed

6 files changed

+83
-44
lines changed

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
python 3.10.6
1+
python 3.10.11

Dockerfile

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
1-
FROM python:3.10.6-slim-bullseye AS base
1+
FROM python:3.10.11-slim-bullseye AS base
22

33
ENV LANG C.UTF-8
44
ENV LC_ALL C.UTF-8
55

6-
RUN apt update; \
7-
apt --yes --no-install-recommends install libpq5 libmariadb3
6+
RUN apt-get update; \
7+
apt-get --yes --no-install-recommends install libpq5 libmariadb3
88

9-
FROM base AS python
9+
FROM base AS python-dependencies
1010

11-
RUN apt update; \
12-
apt --yes --no-install-recommends install build-essential libpq-dev libmariadb-dev curl
13-
RUN curl -sSL https://install.python-poetry.org | python - --version 1.3.2
11+
RUN apt-get update; \
12+
apt-get --yes --no-install-recommends install build-essential libpq-dev libmariadb-dev curl
13+
RUN curl -sSL https://install.python-poetry.org | python - --version 1.4.2
1414

15-
COPY . .
16-
RUN POETRY_VIRTUALENVS_IN_PROJECT=true /root/.local/bin/poetry install --no-dev
15+
COPY pyproject.toml poetry.lock ./
16+
RUN POETRY_VIRTUALENVS_IN_PROJECT=true /root/.local/bin/poetry install --only=main
1717

1818
FROM base AS runtime
1919

20-
ENV PATH="/.venv/bin:$PATH"
20+
COPY --from=python-dependencies /.venv /.venv
21+
ENV PATH=/.venv/bin:$PATH
2122

22-
COPY . .
23+
COPY . /opt/project/
24+
WORKDIR /opt/project/
2325

24-
FROM runtime AS production
25-
26-
COPY --from=python /.venv /.venv
27-
ENTRYPOINT ["python", "run_providers.py"]
26+
ENTRYPOINT ["python", "run_scheduler.py"]

README.md

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Create a `.env` file from `.env.template` which will be read by docker compose:
3131

3232
Then start the external services and the providers scheduler:
3333

34-
- `docker compose --profile=application up --build`
34+
- `docker compose --profile=scheduler up --build`
3535

3636
Some providers need [winds-mobi-admin](https://github.com/winds-mobi/winds-mobi-admin#run-the-project-with-docker-compose-simple-way) running to get stations metadata.
3737

@@ -72,7 +72,7 @@ Then start the external services:
7272

7373
#### Run only a provider
7474

75-
- `PYTHONPATH=. dotenv -f .env.localhost run python providers/ffvl.py`
75+
- `dotenv -f .env.localhost run python providers/ffvl.py`
7676

7777
### Contributing
7878

@@ -89,7 +89,7 @@ You know a good weather station that would be useful for many paraglider pilots
8989
Awesome! Fork this repository and create a pull request with your new provider code. It's easy, look at the following
9090
example:
9191

92-
my_provider.py
92+
providers/my_provider.py
9393
```
9494
import arrow
9595
import requests
@@ -103,36 +103,79 @@ class MyProvider(Provider):
103103
104104
def process_data(self):
105105
self.log.info("Processing MyProvider data...")
106-
data = requests.get(
107-
"https://api.my-provider.com/stations.json", timeout=(self.connect_timeout, self.read_timeout)
108-
)
109-
for data_dict in data.json():
110-
station = self.save_station(
111-
provider_id=data_dict["id"],
112-
short_name=data_dict["name"],
106+
# data = requests.get(
107+
# "https://api.my-provider.com/stations.json", timeout=(self.connect_timeout, self.read_timeout)
108+
# ).json()
109+
data = [{
110+
"id": "station-1",
111+
"name": "Station 1",
112+
"latitude": 46.713,
113+
"longitude": 6.503,
114+
"status": "ok",
115+
"measures": [{
116+
"time": arrow.now().format("YYYY-MM-DD HH:mm:ssZZ"),
117+
"windDirection": 180,
118+
"windAverage": 10.5,
119+
"windMaximum": 20.1,
120+
"temperature": 25.7,
121+
"pressure": 1013,
122+
}]
123+
}]
124+
for station in data:
125+
winds_station = self.save_station(
126+
provider_id=station["id"],
127+
short_name=station["name"],
113128
name=None, # Lets winds.mobi provide the full name with the help of Google Geocoding API
114-
latitude=data_dict["latitude"],
115-
longitude=data_dict["longitude"],
116-
status=StationStatus.GREEN if data_dict["status"] == "ok" else StationStatus.RED,
129+
latitude=station["latitude"],
130+
longitude=station["longitude"],
131+
status=StationStatus.GREEN if station["status"] == "ok" else StationStatus.RED,
132+
url=f"https://my-provider.com/stations/{station['id']}",
117133
)
118134
119-
measure_key = arrow.get(data_dict["lastMeasure"]["time"], "YYYY-MM-DD HH:mm:ssZZ").int_timestamp
120-
measures_collection = self.measures_collection(station["_id"])
135+
measure_key = arrow.get(station["measures"][0]["time"], "YYYY-MM-DD HH:mm:ssZZ").int_timestamp
136+
measures_collection = self.measures_collection(winds_station["_id"])
121137
122138
if not self.has_measure(measures_collection, measure_key):
123139
new_measure = self.create_measure(
124-
for_station=station,
140+
for_station=winds_station,
125141
_id=measure_key,
126-
wind_direction=data_dict["lastMeasure"]["windDirection"],
127-
wind_average=Q_(data_dict["lastMeasure"]["windAverage"], ureg.meter / ureg.second),
128-
wind_maximum=Q_(data_dict["lastMeasure"]["windMaximum"], ureg.meter / ureg.second),
129-
temperature=Q_(data_dict["lastMeasure"]["temp"], ureg.degC),
130-
pressure=Pressure(qnh=Q_(data_dict["lastMeasure"]["pressure"], ureg.hPa)),
142+
wind_direction=station["measures"][0]["windDirection"],
143+
wind_average=Q_(station["measures"][0]["windAverage"], ureg.meter / ureg.second),
144+
wind_maximum=Q_(station["measures"][0]["windMaximum"], ureg.meter / ureg.second),
145+
temperature=Q_(station["measures"][0]["temperature"], ureg.degC),
146+
pressure=Pressure(station["measures"][0]["pressure"], qnh=None, qff=None),
131147
)
132-
self.insert_new_measures(measures_collection, station, [new_measure])
148+
self.insert_new_measures(measures_collection, winds_station, [new_measure])
133149
self.log.info("...Done !")
150+
151+
152+
def my_provider():
153+
MyProvider().process_data()
154+
155+
156+
if __name__ == "__main__":
157+
my_provider()
134158
```
135159

160+
##### And test it
161+
162+
Start the external services:
163+
164+
- `docker compose up`
165+
166+
Build a Docker image containing your new provider `providers/my_provider.py`:
167+
168+
- `docker build --tag=winds.mobi/my_provider .`
169+
170+
Then run your provider inside a container with:
171+
172+
- `docker run -it --rm --env-file=.env --network=winds-mobi-providers --entrypoint=python winds.mobi/my_provider -m providers.my_provider`
173+
174+
To avoid building a new image on every change, you can mount your local source to the container directory `/opt/project`
175+
with a Docker volume:
176+
177+
- `docker run -it --rm --env-file=.env --network=winds-mobi-providers --volume=$(pwd):/opt/project --entrypoint=python winds.mobi/my_provider -m providers.my_provider`
178+
136179
Licensing
137180
---------
138181

docker-compose.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ services:
1717
build:
1818
context: .
1919
profiles:
20-
- application
20+
- scheduler
2121
environment:
2222
MONGODB_URL: ${MONGODB_URL}
2323
REDIS_URL: ${REDIS_URL}

pyproject.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ description = "Python scripts that get the weather data from different providers
44
version = "0.0.0"
55
authors = ["winds.mobi"]
66
license = " AGPL-3.0-only"
7-
packages = [
8-
{ include = "winds_mobi_provider" },
9-
]
107

118
[tool.poetry.dependencies]
129
python = "3.10.*"

run_providers.py renamed to run_scheduler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pydantic import parse_obj_as
66

77

8-
def run_providers():
8+
def run_scheduler():
99
scheduler = BlockingScheduler()
1010
scheduler.configure(
1111
executors={
@@ -74,4 +74,4 @@ def run_providers():
7474

7575

7676
if __name__ == "__main__":
77-
run_providers()
77+
run_scheduler()

0 commit comments

Comments
 (0)