Skip to content

Commit

Permalink
Merge pull request #6 from tryolabs/develop
Browse files Browse the repository at this point in the history
Release 1
  • Loading branch information
FlorenciaOjeda authored Jun 27, 2024
2 parents 03e16b1 + 3fd0eb0 commit a511ebf
Show file tree
Hide file tree
Showing 20 changed files with 793 additions and 33 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.env
76 changes: 76 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: 'Continuous Delivery'

on:
push:
branches:
- "main"

jobs:
build:
runs-on: ubuntu-latest
steps:
# Checkout code
- name: Checkout code
uses: actions/checkout@v3

# Authenticate to Google Cloud
- name: Authenticate to Google Cloud
env:
GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
run: |
echo "$GOOGLE_CREDENTIALS" > /tmp/google-credentials.json
gcloud auth activate-service-account --key-file=/tmp/google-credentials.json
# Build and push Docker image
- name: Build and push Docker image
env:
CONTAINER_IMAGE_URL: ${{ secrets.CONTAINER_IMAGE_URL }}
GCLOUD_REGION: ${{ secrets.GCLOUD_REGION }}
run: |
docker build -t $CONTAINER_IMAGE_URL:latest .
gcloud auth configure-docker $GCLOUD_REGION
docker push $CONTAINER_IMAGE_URL:latest
deploy:
needs: build
runs-on: ubuntu-latest
steps:
# Checkout code
- name: Checkout code
uses: actions/checkout@v3

# Authenticate to Google Cloud
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
credentials_json: '${{ secrets.GOOGLE_CREDENTIALS }}'

# Deploy container to Cloud Run and capture URL
- name: Deploy container to Cloud Run
env:
CONTAINER_IMAGE_URL: ${{ secrets.CONTAINER_IMAGE_URL }}
GCLOUD_REGION: ${{ secrets.GCLOUD_REGION }}
GCLOUD_PROJECT_ID: ${{ secrets.GCLOUD_PROJECT_ID }}
id: deploy
run: |
echo "Deployment running."
URL=$(gcloud run deploy latam-challenge \
--image=$CONTAINER_IMAGE_URL:latest \
--platform=managed \
--allow-unauthenticated \
--region=$GCLOUD_REGION \
--port=8000 \
--project=$GCLOUD_PROJECT_ID \
--format="value(status.url)")
echo "::set-output name=url::$URL"
echo "Image URL: $URL"
# Set STRESS_URL environment variable for later use
- name: Set STRESS_URL as env variable to use later the Makefile
run: echo "STRESS_URL=${{ steps.deploy.outputs.url }}" >> $GITHUB_ENV

# Run stress tests
- name: Run stress tests
run: |
make stress-test STRESS_URL=${{ env.STRESS_URL }}
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: 'Continuous Integration'

on:
pull_request:
branches:
- "develop"

jobs:
test:
runs-on: ubuntu-latest
steps:
# Checkout code
- name: Checkout code
uses: actions/checkout@v3

# Setup Python
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.9"

# Cache dependencies to speed up workflow
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
# Install dependencies
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt -r requirements-dev.txt -r requirements-test.txt
# Run tests
- name: Run tests
run: |
make model-test
make api-test
21 changes: 20 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Environments
.env
.venv
env/
*env/
venv/

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
reports/

__pycache__/
/challenge/models
25 changes: 23 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# syntax=docker/dockerfile:1.2
FROM python:latest
# put you docker configuration here
FROM python:3.9-slim

# Set the working directory
WORKDIR /app

# Install build dependencies
RUN apt-get update && \
apt-get install -y build-essential gcc libssl-dev

# Copy requirements files for the container
COPY requirements*.txt ./

# Install Python dependencies
RUN pip install -r requirements.txt -r requirements-dev.txt

# Copy the entire project into the container
COPY . .

# Expose port
EXPOSE 8000

# Run the application inside the container
CMD ["uvicorn", "challenge.api:app", "--host", "0.0.0.0", "--port", "8000"]
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ install: ## Install dependencies
pip install -r requirements-test.txt
pip install -r requirements.txt

STRESS_URL = http://127.0.0.1:8000
STRESS_URL = https://latam-challenge-7eedzyco2q-uc.a.run.app
.PHONY: stress-test
stress-test:
# change stress url to your deployed app
Expand All @@ -42,4 +42,4 @@ api-test: ## Run tests and coverage

.PHONY: build
build: ## Build locally the python artifact
python setup.py bdist_wheel
python setup.py bdist_wheel
91 changes: 87 additions & 4 deletions challenge/api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,73 @@
import fastapi
from enum import Enum
import logging
from typing import List

app = fastapi.FastAPI()
import pandas as pd
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from fastapi.exceptions import RequestValidationError

from challenge.model import DelayModel

app = FastAPI()

delay_model = DelayModel()

class FlightType(str, Enum):
NATIONAL = 'N'
INTERNATIONAL = 'I'

class Airline(str, Enum):
AMERICAN_AIRLINES = 'American Airlines'
AIR_CANADA = 'Air Canada'
AIR_FRANCE = 'Air France'
AEROMEXICO = 'Aeromexico'
AEROLINEAS_ARGENTINAS = 'Aerolineas Argentinas'
AUSTRAL = 'Austral'
AVIANCA = 'Avianca'
ALITALIA = 'Alitalia'
BRITISH_AIRWAYS = 'British Airways'
COPA_AIR = 'Copa Air'
DELTA_AIR = 'Delta Air'
GOL_TRANS = 'Gol Trans'
IBERIA = 'Iberia'
KLM = 'K.L.M.'
QANTAS_AIRWAYS = 'Qantas Airways'
UNITED_AIRLINES = 'United Airlines'
GRUPO_LATAM = 'Grupo LATAM'
SKY_AIRLINE = 'Sky Airline'
LATIN_AMERICAN_WINGS = 'Latin American Wings'
PLUS_ULTRA_LINEAS_AEREAS = 'Plus Ultra Lineas Aereas'
JETSMART_SPA = 'JetSmart SPA'
OCEANAIR_LINHAS_AEREAS = 'Oceanair Linhas Aereas'
LACSA = 'Lacsa'

class FlightData(BaseModel):
"""
Model to represent the data for each flight.
"""
OPERA: Airline
TIPOVUELO: FlightType
MES: int = Field(..., ge=1, le=12, description="Month value must be between 1 and 12")

class FlightsRequest(BaseModel):
"""
Model to represent the request body containing multiple flights.
"""
flights: List[FlightData]


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""
Handle request validation errors by returning a 400 status code.
"""
logging.error("Validation error: %s", exc)
return JSONResponse(
status_code=400,
content={"detail": exc.errors()},
)

@app.get("/health", status_code=200)
async def get_health() -> dict:
Expand All @@ -9,5 +76,21 @@ async def get_health() -> dict:
}

@app.post("/predict", status_code=200)
async def post_predict() -> dict:
return
async def predict_delays(request: FlightsRequest) -> dict:
"""
Predict delays for given flight data.
Args:
request (FlightsRequest): The flight data for which to predict delays.
Returns:
dict: A dictionary containing the predictions.
"""
try:
flight_data = pd.DataFrame([flight.dict() for flight in request.flights])
preprocessed_data = delay_model.preprocess(flight_data)
predictions = delay_model.predict(preprocessed_data)
return {"predict": predictions}
except Exception as e:
logging.error("An error occurred: %s", e)
raise HTTPException(status_code=500, detail="An internal error occurred")
Loading

0 comments on commit a511ebf

Please sign in to comment.