Skip to content

Commit

Permalink
Dockerfile update and package restructuring
Browse files Browse the repository at this point in the history
  • Loading branch information
shamlymajeed committed Mar 14, 2024
1 parent cefc548 commit 6e0b39b
Show file tree
Hide file tree
Showing 19 changed files with 75 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,6 @@ datastore_pb2.py
datastore_pb2_grpc.py
# Ignore all the copied protobuf directories, as the root contains the source of truth.
**/protobuf

# Ignore cf standard file, as it is generated while docker build.
cf_standard_names_v84.txt
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ services:
ports:
- "8009:8001"
environment:
- DSHOST=${DSHOST:-store}
- DSPORT=${DSPORT:-50050}
- DATASTORE_HOST=${DATASTORE_HOST:-store}
- DATASTORE_PORT=${DATASTORE_PORT:-50050}
depends_on:
store:
condition: service_healthy
Expand Down
22 changes: 11 additions & 11 deletions ingest/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,32 @@ RUN apt-get update \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY "./protobuf/datastore.proto" "/protobuf/datastore.proto"
COPY "./api" "${DOCKER_PATH}/api/"
COPY "requirements.txt" "${DOCKER_PATH}/"
COPY "./src/" "/ingest/src"
COPY "./pyproject.toml" "/ingest/pyproject.toml"
COPY "./setup.py" "/ingest/"
COPY "requirements.txt" "${DOCKER_PATH}/api/"
COPY "./src/" "${DOCKER_PATH}/src/"
COPY "./pyproject.toml" "${DOCKER_PATH}/pyproject.toml"
COPY "./setup.py" "${DOCKER_PATH}/setup.py"

RUN pip install --no-cache-dir --upgrade -r "${DOCKER_PATH}/requirements.txt"
RUN pip install --no-cache-dir --upgrade -r "${DOCKER_PATH}/api/requirements.txt"
RUN pip install --no-cache-dir --upgrade pybind11

# hadolint ignore=DL3013
WORKDIR /ingest/src/ingest/bufr/
WORKDIR "${DOCKER_PATH}"/src/ingest/bufr/
RUN make
WORKDIR /

# hadolint ignore=DL3013
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir --upgrade "/ingest/"
# Compiling the protobuf file
RUN python -m grpc_tools.protoc \
--proto_path="protobuf" "protobuf/datastore.proto" \
--python_out="${DOCKER_PATH}" \
--grpc_python_out="${DOCKER_PATH}"

WORKDIR "${DOCKER_PATH}"

RUN python "${DOCKER_PATH}/api/generate_standard_name.py"

WORKDIR "${DOCKER_PATH}"
# hadolint ignore=DL3013
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir --upgrade "${DOCKER_PATH}/"
CMD ["gunicorn", "api.main:app", "--workers=4", "--worker-class=uvicorn.workers.UvicornWorker", "--bind=0.0.0.0:8001"]
File renamed without changes.
2 changes: 1 addition & 1 deletion ingest/api/generate_standard_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
)

# Find all 'entry' elements and get their 'id' attribute
with open("cf_standard_names_v84.txt", "w") as file:
with open("api/cf_standard_names_v84.txt", "w") as file:
for entry in root.findall("entry"):
file.write(f'{entry.get("id")}\n')
10 changes: 5 additions & 5 deletions ingest/src/ingest/main.py → ingest/api/ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import pkg_resources
from jsonschema import Draft202012Validator

from ingest.datastore import DatastoreConnection
from ingest.messages import messages
from ingest.send_mqtt import MQTTConnection
from api.datastore import DatastoreConnection
from api.messages import messages
from api.send_mqtt import MQTTConnection

logger = logging.getLogger(__name__)

Expand All @@ -33,15 +33,15 @@ def __init__(
self.uuid_prefix = uuid_prefix

if not schema_path:
self.schema_path = pkg_resources.resource_filename("esoh", "schemas")
self.schema_path = pkg_resources.resource_filename("src", "schemas")
else:
self.schema_path = schema_path
if not schema_file:
self.schema_file = "e-soh-message-spec.json"
else:
self.schema_file = schema_file

esoh_mqtt_schema = os.path.join(self.schema_path, self.schema_file)

with open(esoh_mqtt_schema, "r") as file:
self.esoh_mqtt_schema = json.load(file)
self.schema_validator = Draft202012Validator(self.esoh_mqtt_schema)
Expand Down
6 changes: 3 additions & 3 deletions ingest/api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from fastapi import UploadFile
from pydantic import BaseModel

from api.ingest import IngestToPipeline
from api.model import JsonMessageSchema
from ingest.main import IngestToPipeline


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -37,7 +37,7 @@ class Response(BaseModel):
}


@app.post("/upload/nc")
@app.post("/nc")
async def upload_netcdf_file(files: UploadFile, input_type: str = "nc"):
try:
ingester = IngestToPipeline(
Expand All @@ -54,7 +54,7 @@ async def upload_netcdf_file(files: UploadFile, input_type: str = "nc"):
return Response(status_message=str(e), status_code=500)


@app.post("/upload/bufr")
@app.post("/bufr")
async def upload_bufr_file(files: UploadFile, input_type: str = "bufr"):
try:
ingester = IngestToPipeline(
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion ingest/api/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Content(BaseModel):

@model_validator(mode="after")
def check_standard_name_match(self) -> "Content":
with open("cf_standard_names_v84.txt", "r") as file:
with open("api/cf_standard_names_v84.txt", "r") as file:
standard_names = {line.strip() for line in file}
if self.standard_name not in standard_names:
raise ValueError(f"{self.standard_name} not a CF Standard name")
Expand Down
File renamed without changes.
25 changes: 0 additions & 25 deletions ingest/api/test_app.py

This file was deleted.

1 change: 1 addition & 0 deletions ingest/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ fastapi == 0.108.0
grpcio-tools==1.58.0
uvicorn==0.25.0
python-multipart~=0.0.9
requests == 2.31.0
Empty file added ingest/src/__init__.py
Empty file.
Empty file added ingest/test/__init__.py
Empty file.
3 changes: 2 additions & 1 deletion ingest/test/test_automatic_filetype_detection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest
from esoh.ingest.main import IngestToPipeline

from api.ingest import IngestToPipeline


@pytest.mark.parametrize(
Expand Down
2 changes: 1 addition & 1 deletion ingest/test/test_datastore_ingest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from esoh.ingest.datastore import DatastoreConnection
from api.datastore import DatastoreConnection


def test_datastore_ingest():
Expand Down
3 changes: 2 additions & 1 deletion ingest/test/test_ingest_to_pipeline_errors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import xarray as xr
from esoh.ingest.main import IngestToPipeline

from api.ingest import IngestToPipeline


def test_build_message_errors():
Expand Down
33 changes: 33 additions & 0 deletions ingest/test/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import json

import pytest
from fastapi.testclient import TestClient

from api.main import app


@pytest.fixture(scope="module")
def test_app():
client = TestClient(app)
yield client


def test_post_json_success(test_app, monkeypatch):
with open("test/test_data/test_payload.json", "r") as file:
json_data = json.load(file)
monkeypatch.setattr("api.main.IngestToPipeline.ingest", lambda *a: ("Success", 200))

response = test_app.post("/json?input_type=json", json=json_data)
assert response.status_code == 200

assert response.json() == {"status_message": "Success", "status_code": 200}


def test_post_json_failure(test_app, monkeypatch):
with open("test/test_data/test_payload.json", "r") as file:
json_data = json.load(file)
monkeypatch.setattr("api.main.IngestToPipeline.ingest", lambda *a: ("Success", 200))

response = test_app.post("/json?input_type=json", json=json_data)
assert response.status_code == 200
assert response.json() == {"status_message": "Success", "status_code": 200}
19 changes: 10 additions & 9 deletions ingest/test/test_verify_json_payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,25 @@

import pytest
import xarray as xr
from esoh.ingest.bufr.bufresohmsg_py import init_bufr_schema_py
from esoh.ingest.bufr.bufresohmsg_py import init_bufrtables_py
from esoh.ingest.bufr.bufresohmsg_py import init_oscar_py
from esoh.ingest.main import IngestToPipeline
from jsonschema import Draft202012Validator
from jsonschema import ValidationError

from api.ingest import IngestToPipeline
from ingest.bufr.bufresohmsg_py import init_bufr_schema_py
from ingest.bufr.bufresohmsg_py import init_bufrtables_py
from ingest.bufr.bufresohmsg_py import init_oscar_py


@pytest.mark.timeout(1000)
@pytest.mark.parametrize("bufr_file_path", glob.glob("test/test_data/bufr/*.buf*"))
def test_verify_json_payload_bufr(bufr_file_path):
# Load the schema
with open("src/esoh/schemas/e-soh-message-spec.json", "r") as file:
with open("src/schemas/e-soh-message-spec.json", "r") as file:
e_soh_mqtt_message_schema = json.load(file)

init_bufrtables_py("")
init_oscar_py("./src/esoh/ingest/bufr/oscar/oscar_stations_all.json")
init_bufr_schema_py("./src/esoh/schemas/bufr_to_e_soh_message.json")
init_oscar_py("./src/ingest/bufr/oscar/oscar_stations_all.json")
init_bufr_schema_py("./src/schemas/bufr_to_e_soh_message.json")
msg_build = IngestToPipeline(None, None, "testing", testing=True)

json_payloads = msg_build._build_messages(bufr_file_path, input_type="bufr")
Expand All @@ -36,7 +37,7 @@ def test_verify_json_payload_bufr(bufr_file_path):
@pytest.mark.parametrize("netcdf_file_path", glob.glob("test/test_data/met_norway/*.nc"))
def test_verify_json_payload_metno_netcdf(netcdf_file_path):
# Load the schema
with open("src/esoh/schemas/e-soh-message-spec.json", "r") as file:
with open("src/schemas/e-soh-message-spec.json", "r") as file:
e_soh_mqtt_message_schema = json.load(file)

ds = xr.load_dataset(netcdf_file_path)
Expand All @@ -55,7 +56,7 @@ def test_verify_json_payload_metno_netcdf(netcdf_file_path):

@pytest.mark.parametrize("netcdf_file_path", glob.glob("test/test_data/knmi/*.nc"))
def test_verify_json_payload_knmi_netcdf(netcdf_file_path):
with open("src/esoh/schemas/e-soh-message-spec.json", "r") as file:
with open("src/schemas/e-soh-message-spec.json", "r") as file:
e_soh_mqtt_message_schema = json.load(file)

ds = xr.load_dataset(netcdf_file_path)
Expand Down

1 comment on commit 6e0b39b

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API Unit Test Coverage Report
FileStmtsMissCoverMissing
\_\_init\_\_.py00100% 
datastore_pb2.py584621%24–69
datastore_pb2_grpc.py432347%37–52, 85–87, 92–94, 99–101, 106–108, 112–136, 174, 191, 208, 225
dependencies.py481667%26–27, 34, 41, 62–69, 77–84
grpc_getter.py13838%12–15, 19–22
locustfile.py15150%1–31
main.py22386%27, 37, 47
metadata_endpoints.py19479%17, 34–67, 71
formatters
   \_\_init\_\_.py70100% 
   covjson.py46687%58, 65–69, 73–76
routers
   \_\_init\_\_.py00100% 
   edr.py671184%38–63, 143–144, 188–189, 205
   records.py00100% 
TOTAL33813261% 

API Unit Test Coverage Summary

Tests Skipped Failures Errors Time
16 0 💤 8 ❌ 0 🔥 4.114s ⏱️

Please sign in to comment.