diff --git a/app/api/oauth2.py b/app/api/oauth2.py new file mode 100644 index 0000000..a04f53d --- /dev/null +++ b/app/api/oauth2.py @@ -0,0 +1,47 @@ +from fastapi.security import OAuth2PasswordRequestForm +from pydantic import BaseModel +import requests + + +from fastapi import APIRouter, Depends, HTTPException + + +from app.oauth2 import CLIENT_ID, CLIENT_SECRET, TOKEN_URL + + +routes = APIRouter() + + +# Modelo de resposta para o token +class Token(BaseModel): + access_token: str + token_type: str + +# Rota de login para obter o token de acesso +@routes.post("/login", response_model=Token) +async def login(form_data: OAuth2PasswordRequestForm = Depends()): + # Dados para a requisição ao Keycloak + payload = { + 'client_id': CLIENT_ID, + 'client_secret': CLIENT_SECRET, + 'grant_type': 'password', + 'username': form_data.username, + 'password': form_data.password + } + + # Requisição ao Keycloak para obter o token + response = requests.post(TOKEN_URL, data=payload) + + if response.status_code != 200: + raise HTTPException( + status_code=401, + detail="Username or password incorrect" + ) + + token_data = response.json() + return { + "access_token": token_data["access_token"], + "token_type": "bearer" + } + + diff --git a/app/api/result.py b/app/api/result.py index aa55c26..ab67199 100644 --- a/app/api/result.py +++ b/app/api/result.py @@ -5,7 +5,7 @@ router = APIRouter() -@router.get("/pasture/{task_id}") +@router.get("/{task_id}") async def result_pasture(task_id): with MongoClient(os.environ.get("MONGOURI", "mongodb://mongodbjobs:27017")) as cliente: # Seleciona o banco de dados e a coleção diff --git a/app/api/task.py b/app/api/task.py index 74faba8..cf1b71c 100644 --- a/app/api/task.py +++ b/app/api/task.py @@ -1,24 +1,30 @@ from fastapi.responses import JSONResponse import geopandas as gpd + + +from app.models.oauth2 import UserInfo +from app.oauth2 import has_role from worker import gee_get_index_pasture from app.models.payload import PayloadSaveGeojson +import os -from fastapi import APIRouter, HTTPException, Request, Query +from fastapi import APIRouter, Depends, HTTPException, Request, Query from app.config import logger from celery.result import AsyncResult -from pydantic import UUID4 router = APIRouter() -@router.post("/savegeom") + +@router.post("/savegeom" ) async def savegeom( payload: PayloadSaveGeojson, request: Request, crs: int = Query(4326, description="EPSG code for the geometry"), - + user_data: UserInfo = Depends(has_role(['savegeom'])) # This should be a function that retrieves user data from the request. ): - MAX_HECTARES = 40_000 + + MAXHECTARES = os.environ.get('MAXHECTARES',40_000) geojson = payload.dict().get('geojson',gpd.GeoDataFrame()) logger.info(f"Received payload: {geojson}") try: @@ -27,11 +33,16 @@ async def savegeom( return HTTPException(status_code=400, detail="Empty GeoDataFrame or more than one feature.") if gdf.geometry.type[0] != "Polygon": return HTTPException(status_code=400, detail="Geometry must be a Polygon.") - if gdf.to_crs(5880).area.iloc[0] / 10_000 > MAX_HECTARES: - return HTTPException(status_code=400, detail=f"Geometry area must be less than {MAX_HECTARES} hectares.") + if gdf.to_crs(5880).area.iloc[0] / 10_000 > MAXHECTARES: + return HTTPException(status_code=400, detail=f"Geometry area must be less than {MAXHECTARES} hectares.") logger.info('Geometry is valid') + logger.info(user_data) try: - task = gee_get_index_pasture.delay(payload.dict()) + dict_payload = { + **payload.dict(), + 'request_user': user_data + } + task = gee_get_index_pasture.delay(dict_payload) except Exception as e: logger.exception(f"Failed to create task: {e}") raise HTTPException(status_code=400, detail="Failed to create task.") diff --git a/app/models/oauth2.py b/app/models/oauth2.py new file mode 100644 index 0000000..dac9cec --- /dev/null +++ b/app/models/oauth2.py @@ -0,0 +1,19 @@ +from typing import Optional +from pydantic import BaseModel, EmailStr + +class UserOauth2(BaseModel): + id: str + username: str + email: str + first_name: str + last_name: str + realm_roles: list + client_roles: list + + + +class UserInfo(BaseModel): + sub: str + preferred_username: str + email: Optional[EmailStr] = None + client_id: Optional[str] = None \ No newline at end of file diff --git a/app/models/payload.py b/app/models/payload.py index 4cb7815..c744562 100644 --- a/app/models/payload.py +++ b/app/models/payload.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, EmailStr from typing import List +from app.models.oauth2 import UserInfo from pydantic_geojson import FeatureModel, FeatureCollectionModel, PolygonModel class User(BaseModel): @@ -15,6 +16,11 @@ class LapigFeatureModel(FeatureModel): class LapigFeatureCollectionModel(FeatureCollectionModel): features: List[LapigFeatureModel] + +class ResultPayload(BaseModel): + user: User + geojson: LapigFeatureCollectionModel + request_user: UserInfo class PayloadSaveGeojson(BaseModel): user: User geojson: LapigFeatureCollectionModel \ No newline at end of file diff --git a/app/oauth2.py b/app/oauth2.py new file mode 100644 index 0000000..8ae368c --- /dev/null +++ b/app/oauth2.py @@ -0,0 +1,120 @@ +from typing import List +from fastapi.security import OAuth2AuthorizationCodeBearer +from keycloak import KeycloakOpenID # pip require python-keycloak +from fastapi import Security, HTTPException, status,Depends +from pydantic import Json +from app.models.oauth2 import UserInfo, UserOauth2 + +import os +from app.config import logger + +URLKEYCLOAK = f"{os.environ.get('SERVER_URL')}/realms/{os.environ.get('REALM')}/" + + +TOKEN_URL = f"{URLKEYCLOAK}protocol/openid-connect/token" +CLIENT_ID = os.environ.get('CLIENT_ID') +CLIENT_SECRET = os.environ.get('CLIENT_SECRET') +AUTHORIZATIONURL=f"{URLKEYCLOAK}protocol/openid-connect/auth" +CERTS=f"{URLKEYCLOAK}protocol/openid-connect/certs" +REALM=os.environ.get('REALM') + +# This is used for fastapi docs authentification +oauth2_scheme = OAuth2AuthorizationCodeBearer( + #refreshUrl=TOKEN_URL, + authorizationUrl=AUTHORIZATIONURL, # https://sso.example.com/auth/ + tokenUrl=TOKEN_URL, # https://sso.example.com/auth/realms/example-realm/protocol/openid-connect/token +) + +# This actually does the auth checks +# client_secret_key is not mandatory if the client is public on keycloak +keycloak_openid = KeycloakOpenID( + server_url=os.environ.get('SERVER_URL'), # https://sso.example.com/auth/ + client_id=CLIENT_ID, # backend-client-id + realm_name=REALM, # example-realm + client_secret_key=CLIENT_SECRET, # your backend client secret + verify=True +) + + + + + +async def get_idp_public_key(): + return ( + "-----BEGIN PUBLIC KEY-----\n" + f"{keycloak_openid.public_key()}" + "\n-----END PUBLIC KEY-----" + ) + +# Get the payload/token from keycloak +async def get_payload(token: str = Security(oauth2_scheme)) -> dict: + key= await get_idp_public_key() + logger.info(f"token {token}") + try: + + return keycloak_openid.decode_token( + token + ) + except Exception as e: + logger.exception(f"Error decoding token: {e}") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail=str(e), # "Invalid authentication credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + +def get_info_user(payload) -> UserInfo: + if not payload.get('email', None) is None: + return { + 'sub': payload['sub'], + 'preferred_username': payload['preferred_username'], + 'email': payload['email'] + } + else: + return { + 'sub': payload['sub'], + 'preferred_username': payload['preferred_username'], + 'client_id': payload.get('client_id') + } + + + +def has_role(role:List[str]) -> UserInfo: + logger.info(f"has_role {role}") + async def check_role(payload: dict = Depends(get_payload)): + + if not payload.get("resource_access", {}).get("app_task", {}): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Unauthorized to access this resource", + headers={"WWW-Authenticate": "Bearer"}, + ) + if not any([user_role in role for user_role in payload.get("resource_access", {}).get("app_task", {}).get("roles", [])]): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Insufficient privileges to access this resource", + headers={"WWW-Authenticate": "Bearer"}, + ) + + return get_info_user(payload) + return check_role + + +# Get user infos from the payload +async def get_user_info(payload: dict = Depends(get_payload)) -> UserOauth2: + try: + return UserOauth2( + id=payload.get("sub"), + username=payload.get("preferred_username"), + email=payload.get("email"), + first_name=payload.get("given_name"), + last_name=payload.get("family_name"), + realm_roles=payload.get("realm_access", {}).get("roles", []), + client_roles=payload.get("realm_access", {}).get("roles", []) + ) + except Exception as e: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=str(e), # "Invalid authentication credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) diff --git a/app/router.py b/app/router.py index 73c7875..30bcbcf 100644 --- a/app/router.py +++ b/app/router.py @@ -1,9 +1,10 @@ -from .api import task, result +from .api import task, result, oauth2 def created_routes(app): app.include_router(task.router, prefix="/api/task", tags=["Task Google Earth Engine"]) app.include_router(result.router, prefix="/api/result", tags=["Result Google Earth Engine"]) + app.include_router(oauth2.routes, prefix="/api/auth", tags=["Authentication"]) return app diff --git a/docker-compose.yml b/docker-compose.yml index a66fe70..cf07cb6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,16 +5,23 @@ services: context: . dockerfile: ./docker/prod/Dockerfile hostname: jobsgee - command: uv run uvicorn main:app --host 0.0.0.0 --port 8080 --reload + command: uv run uvicorn main:app --host 0.0.0.0 --port 8080 --reload environment: + - HTTPS=false - CELERY_BROKER_URL=redis://queejobs:6379/0 - CELERY_RESULT_BACKEND=redis://queejobs:6379/0 + - SERVER_URL=${SERVER_URL} + - CLIENT_ID=${CLIENT_ID} + - CLIENT_SECRET=${CLIENT_SECRET} + - ADMIN_CLIENT_SECRET=${ADMIN_CLIENT_SECRET} + - REALM=${REALM} + - CALLBACK_URI=${CALLBACK_URI} container_name: jobsgee privileged: true ports: - "8086:8080" volumes: - - .:/home/lapig + - .:/home/suporte networks: - web_lapig @@ -24,7 +31,7 @@ services: dockerfile: ./docker/prod/Dockerfile command: uv run celery -A worker.celery worker --loglevel=info --logfile=logs/celery.log volumes: - - .:/home/lapig + - .:/home/suporte - ./volumes/gee.json:/var/sec/gee.json - ./volumes/logs:/logs environment: @@ -42,7 +49,7 @@ services: ports: - 5556:5555 volumes: - - .:/home/lapig + - .:/home/suporte environment: - CELERY_BROKER_URL=redis://queejobs:6379/0 - CELERY_RESULT_BACKEND=redis://queejobs:6379/0 diff --git a/main.py b/main.py index a9f7627..60cdb8d 100644 --- a/main.py +++ b/main.py @@ -1,37 +1,81 @@ -import typing -import ee -import orjson -from fastapi import FastAPI, HTTPException +from contextlib import asynccontextmanager + +from fastapi import FastAPI, status +from fastapi.encoders import jsonable_encoder +from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse -from google.oauth2 import service_account -from app.config import settings, logger, start_logger +from starlette.exceptions import HTTPException as StarletteHTTPException +from fastapi.responses import RedirectResponse +from unidecode import unidecode +from app.config import logger, settings, start_logger from app.router import created_routes +import os +@asynccontextmanager +async def lifespan(app: FastAPI): + start_logger() + yield + logger.info("Shutting down GEE") + +app = FastAPI(lifespan=lifespan) -app = FastAPI() -@app.on_event("startup") -async def startup_event(): - start_logger() - #try: - # service_account_file = '/home/lapig/.local/gee.json' - # logger.debug(f"Initializing service account {service_account_file}") - # credentials = service_account.Credentials.from_service_account_file( - # service_account_file, - # scopes=["https://www.googleapis.com/auth/earthengine.readonly"], - # ) - # ee.Initialize(credentials) - - # print("GEE Initialized successfully.") - #except Exception as e: - # raise HTTPException(status_code=500, detail="Failed to initialize GEE") +@app.exception_handler(StarletteHTTPException) +async def http_exception_handler(request, exc): + start_code = exc.status_code + logger.info(exc) + + if request.url.path.split('/')[1] == 'api': + try: + return JSONResponse( + content={'status_code': start_code, 'message': exc.detail}, + status_code=start_code, + headers=exc.headers, + ) + except: + return JSONResponse( + content={'status_code': start_code, 'message': exc.detail}, + status_code=start_code + ) + + base_url = request.base_url + if settings.HTTPS: + base_url = f'{base_url}'.replace('http://', 'https://') + return { + 'request': request, + 'base_url': base_url, + 'info': '', + 'status': start_code, + 'message': exc.detail, + } + +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request, exc): + try: + return JSONResponse( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + + content=jsonable_encoder({'detail': unidecode(str(exc.errors())), 'body': unidecode(str(exc.body))}), + headers={ + 'X-Download-Detail': f'{unidecode(str(exc.errors()))}', + 'X-Download-Body': f'{unidecode(str(exc.body))}', + }, + ) + except Exception as e: + logger.exception(f'Validation exception: {e} {exc.errors()} {exc.body}') + return JSONResponse( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + content=jsonable_encoder({'detail': unidecode(str(exc.errors())), 'body': unidecode(str(exc.body))}), + ) + + @app.get("/") def read_root(): - return {"message": "Welcome to the GEE FastAPI"} + return RedirectResponse('/redoc') app = created_routes(app) diff --git a/pyproject.toml b/pyproject.toml index 4111ef7..a0ef327 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "celery>=5.4.0", "dynaconf>=3.2.6", "earthengine-api>=1.1.1", + "fastapi-keycloak-middleware>=1.1.0", "fastapi>=0.115.0", "flower>=2.0.1", "geemap>=0.34.5", @@ -18,7 +19,11 @@ dependencies = [ "pyamqp>=0.1.0.7", "pydantic-geojson>=0.1.1", "pydantic[email]>=2.9.2", + "pyjwt>=2.9.0", "pymongo>=4.9.1", + "python-keycloak>=4.4.0", + "python-multipart>=0.0.11", "redis>=5.1.0", + "unidecode>=1.3.8", "uvicorn>=0.31.0", ] diff --git a/uv.lock b/uv.lock index 17a40aa..dc05921 100644 --- a/uv.lock +++ b/uv.lock @@ -51,6 +51,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 }, ] +[[package]] +name = "async-property" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/12/900eb34b3af75c11b69d6b78b74ec0fd1ba489376eceb3785f787d1a0a1d/async_property-0.2.2.tar.gz", hash = "sha256:17d9bd6ca67e27915a75d92549df64b5c7174e9dc806b30a3934dc4ff0506380", size = 16523 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/80/9f608d13b4b3afcebd1dd13baf9551c95fc424d6390e4b1cfd7b1810cd06/async_property-0.2.2-py2.py3-none-any.whl", hash = "sha256:8924d792b5843994537f8ed411165700b27b2bd966cefc4daeefc1253442a9d7", size = 9546 }, +] + [[package]] name = "billiard" version = "4.2.1" @@ -126,6 +135,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, ] +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, +] + [[package]] name = "charset-normalizer" version = "3.3.2" @@ -268,6 +310,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2b/1e/1e726ba66eddf21c940821df8cf1a7d15cb165f0682d62161eaa5e93dae1/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927", size = 1314829 }, ] +[[package]] +name = "cryptography" +version = "43.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/ba/0664727028b37e249e73879348cc46d45c5c1a2a2e81e8166462953c5755/cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d", size = 686927 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/28/b92c98a04ba762f8cdeb54eba5c4c84e63cac037a7c5e70117d337b15ad6/cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d", size = 6223222 }, + { url = "https://files.pythonhosted.org/packages/33/13/1193774705783ba364121aa2a60132fa31a668b8ababd5edfa1662354ccd/cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062", size = 3794751 }, + { url = "https://files.pythonhosted.org/packages/5e/4b/39bb3c4c8cfb3e94e736b8d8859ce5c81536e91a1033b1d26770c4249000/cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962", size = 3981827 }, + { url = "https://files.pythonhosted.org/packages/ce/dc/1471d4d56608e1013237af334b8a4c35d53895694fbb73882d1c4fd3f55e/cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277", size = 3780034 }, + { url = "https://files.pythonhosted.org/packages/ad/43/7a9920135b0d5437cc2f8f529fa757431eb6a7736ddfadfdee1cc5890800/cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a", size = 3993407 }, + { url = "https://files.pythonhosted.org/packages/cc/42/9ab8467af6c0b76f3d9b8f01d1cf25b9c9f3f2151f4acfab888d21c55a72/cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042", size = 3886457 }, + { url = "https://files.pythonhosted.org/packages/a4/65/430509e31700286ec02868a2457d2111d03ccefc20349d24e58d171ae0a7/cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494", size = 4081499 }, + { url = "https://files.pythonhosted.org/packages/bb/18/a04b6467e6e09df8c73b91dcee8878f4a438a43a3603dc3cd6f8003b92d8/cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2", size = 2616504 }, + { url = "https://files.pythonhosted.org/packages/cc/73/0eacbdc437202edcbdc07f3576ed8fb8b0ab79d27bf2c5d822d758a72faa/cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d", size = 3067456 }, + { url = "https://files.pythonhosted.org/packages/8a/b6/bc54b371f02cffd35ff8dc6baba88304d7cf8e83632566b4b42e00383e03/cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d", size = 6225263 }, + { url = "https://files.pythonhosted.org/packages/00/0e/8217e348a1fa417ec4c78cd3cdf24154f5e76fd7597343a35bd403650dfd/cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806", size = 3794368 }, + { url = "https://files.pythonhosted.org/packages/3d/ed/38b6be7254d8f7251fde8054af597ee8afa14f911da67a9410a45f602fc3/cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85", size = 3981750 }, + { url = "https://files.pythonhosted.org/packages/64/f3/b7946c3887cf7436f002f4cbb1e6aec77b8d299b86be48eeadfefb937c4b/cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c", size = 3778925 }, + { url = "https://files.pythonhosted.org/packages/ac/7e/ebda4dd4ae098a0990753efbb4b50954f1d03003846b943ea85070782da7/cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1", size = 3993152 }, + { url = "https://files.pythonhosted.org/packages/43/f6/feebbd78a3e341e3913846a3bb2c29d0b09b1b3af1573c6baabc2533e147/cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa", size = 3886392 }, + { url = "https://files.pythonhosted.org/packages/bd/4c/ab0b9407d5247576290b4fd8abd06b7f51bd414f04eef0f2800675512d61/cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4", size = 4082606 }, + { url = "https://files.pythonhosted.org/packages/05/36/e532a671998d6fcfdb9122da16434347a58a6bae9465e527e450e0bc60a5/cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47", size = 2617948 }, + { url = "https://files.pythonhosted.org/packages/b3/c6/c09cee6968add5ff868525c3815e5dccc0e3c6e89eec58dc9135d3c40e88/cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb", size = 3070445 }, +] + [[package]] name = "cycler" version = "0.12.1" @@ -286,6 +357,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, ] +[[package]] +name = "deprecation" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/d3/8ae2869247df154b64c1884d7346d412fed0c49df84db635aab2d1c40e62/deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff", size = 173788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/c3/253a89ee03fc9b9682f1541728eb66db7db22148cd94f89ab22528cd1e1b/deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a", size = 11178 }, +] + [[package]] name = "dnspython" version = "2.6.1" @@ -369,6 +452,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/06/ab/a1f7eed031aeb1c406a6e9d45ca04bff401c8a25a30dd0e4fd2caae767c3/fastapi-0.115.0-py3-none-any.whl", hash = "sha256:17ea427674467486e997206a5ab25760f6b09e069f099b96f5b55a32fb6f1631", size = 94625 }, ] +[[package]] +name = "fastapi-keycloak-middleware" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "python-keycloak" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/74/942949094ef4bb66ad4a7c242b05c7aee2741e1da4163a24cd7d3e857601/fastapi_keycloak_middleware-1.1.0.tar.gz", hash = "sha256:d68e443f070a95ff7454e6a7f41737e95d2cd2264b6bf66c9c6554cbf8ec5773", size = 16429 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/ad/500df28e424188c2d992fa9b754366f6564782712c72e9578df76dd7ee9b/fastapi_keycloak_middleware-1.1.0-py3-none-any.whl", hash = "sha256:cc5cba9b24dd297f8bb2a77207df66c903b05c411863cd4c6fc79152b9c7cde2", size = 22300 }, +] + [[package]] name = "flower" version = "2.0.1" @@ -643,6 +739,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, ] +[[package]] +name = "httpcore" +version = "1.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/b0/5e8b8674f8d203335a62fdfcfa0d11ebe09e23613c3391033cbba35f7926/httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61", size = 83234 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/d4/e5d7e4f2174f8a4d63c8897d79eb8fe2503f7ecc03282fee1fa2719c2704/httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5", size = 77926 }, +] + [[package]] name = "httplib2" version = "0.22.0" @@ -655,6 +764,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854 }, ] +[[package]] +name = "httpx" +version = "0.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395 }, +] + [[package]] name = "humanize" version = "4.10.0" @@ -812,6 +937,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/93/858e87edc634d628e5d752ba944c2833133a28fa87bb093e6832ced36a3e/jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54", size = 214392 }, ] +[[package]] +name = "jwcrypto" +version = "1.5.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/db/870e5d5fb311b0bcf049630b5ba3abca2d339fd5e13ba175b4c13b456d08/jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039", size = 87168 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/58/4a1880ea64032185e9ae9f63940c9327c6952d5584ea544a8f66972f2fda/jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789", size = 92520 }, +] + [[package]] name = "kiwisolver" version = "1.4.7" @@ -875,6 +1013,7 @@ dependencies = [ { name = "dynaconf" }, { name = "earthengine-api" }, { name = "fastapi" }, + { name = "fastapi-keycloak-middleware" }, { name = "flower" }, { name = "geemap" }, { name = "geopandas" }, @@ -884,8 +1023,12 @@ dependencies = [ { name = "pyamqp" }, { name = "pydantic", extra = ["email"] }, { name = "pydantic-geojson" }, + { name = "pyjwt" }, { name = "pymongo" }, + { name = "python-keycloak" }, + { name = "python-multipart" }, { name = "redis" }, + { name = "unidecode" }, { name = "uvicorn" }, ] @@ -895,6 +1038,7 @@ requires-dist = [ { name = "dynaconf", specifier = ">=3.2.6" }, { name = "earthengine-api", specifier = ">=1.1.1" }, { name = "fastapi", specifier = ">=0.115.0" }, + { name = "fastapi-keycloak-middleware", specifier = ">=1.1.0" }, { name = "flower", specifier = ">=2.0.1" }, { name = "geemap", specifier = ">=0.34.5" }, { name = "geopandas", specifier = ">=1.0.1" }, @@ -904,9 +1048,13 @@ requires-dist = [ { name = "pyamqp", specifier = ">=0.1.0.7" }, { name = "pydantic", extras = ["email"], specifier = ">=2.9.2" }, { name = "pydantic-geojson", specifier = ">=0.1.1" }, + { name = "pyjwt", specifier = ">=2.9.0" }, { name = "pymongo", specifier = ">=4.9.1" }, - { name = "redis", specifier = ">=5.0.8" }, - { name = "uvicorn", specifier = ">=0.30.6" }, + { name = "python-keycloak", specifier = ">=4.4.0" }, + { name = "python-multipart", specifier = ">=0.0.11" }, + { name = "redis", specifier = ">=5.1.0" }, + { name = "unidecode", specifier = ">=1.3.8" }, + { name = "uvicorn", specifier = ">=0.31.0" }, ] [[package]] @@ -1299,6 +1447,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/89/bc88a6711935ba795a679ea6ebee07e128050d6382eaa35a0a47c8032bdc/pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd", size = 181537 }, ] +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + [[package]] name = "pydantic" version = "2.9.2" @@ -1375,6 +1532,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, ] +[[package]] +name = "pyjwt" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 }, +] + [[package]] name = "pymongo" version = "4.9.1" @@ -1502,6 +1668,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] +[[package]] +name = "python-keycloak" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-property" }, + { name = "deprecation" }, + { name = "httpx" }, + { name = "jwcrypto" }, + { name = "requests" }, + { name = "requests-toolbelt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/02/84b0f0b2031e017a028de4e7853dd4975e998f3b90b5e2106179695101b8/python_keycloak-4.4.0.tar.gz", hash = "sha256:f063f4c24f81817c66470b61dbdba5fa5555cc5c236c17a198909ece40482c69", size = 62420 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/77/9f7ed7b9f846951e136199842400ed3ec942866a6ba1f6a8d8655ed7e426/python_keycloak-4.4.0-py3-none-any.whl", hash = "sha256:b7c1e65b2d501ddf924797701d8bd1372f7772ad67532e689456d911e3790ad2", size = 76135 }, +] + +[[package]] +name = "python-multipart" +version = "0.0.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/53/8dcdebf0d2573ea363e82bb1c6d54d863796f0ae7703e5acdb91544bbd86/python_multipart-0.0.11.tar.gz", hash = "sha256:1d377f074b69a47dd204c990de57a7cf03d9b85695a3e57faec32d54b78e3e48", size = 34728 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/94/de592c06e7e7af593be38089a7f72acdc1fec30e7814501a939b0d33589b/python_multipart-0.0.11-py3-none-any.whl", hash = "sha256:0597982db95c0ce012d8bd91b8c9eb2b45c7e095122c05cd8fb7ee4c5bf78df6", size = 22580 }, +] + [[package]] name = "pytz" version = "2024.2" @@ -1525,11 +1717,11 @@ wheels = [ [[package]] name = "redis" -version = "5.0.8" +version = "5.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/48/10/defc227d65ea9c2ff5244645870859865cba34da7373477c8376629746ec/redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870", size = 4595651 } +sdist = { url = "https://files.pythonhosted.org/packages/9b/b6/7a4b49eadff6bdd1904ef96b7e1dede8754673fe4724969bfae3d418ad8d/redis-5.1.0.tar.gz", hash = "sha256:b756df1e4a3858fcc0ef861f3fc53623a96c41e2b1f5304e09e0fe758d333d40", size = 4607302 } wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/d1/19a9c76811757684a0f74adc25765c8a901d67f9f6472ac9d57c844a23c8/redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4", size = 255608 }, + { url = "https://files.pythonhosted.org/packages/fd/d6/37ba7502c5144c93e2e63c81fbfd69c8b97c275328eb85734ce996d3dc13/redis-5.1.0-py3-none-any.whl", hash = "sha256:fd4fccba0d7f6aa48c58a78d76ddb4afc698f5da4a2c1d03d916e4fd7ab88cdd", size = 261212 }, ] [[package]] @@ -1547,6 +1739,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, ] +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, +] + [[package]] name = "rsa" version = "4.9" @@ -1710,6 +1914,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, ] +[[package]] +name = "unidecode" +version = "1.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/89/19151076a006b9ac0dd37b1354e031f5297891ee507eb624755e58e10d3e/Unidecode-1.3.8.tar.gz", hash = "sha256:cfdb349d46ed3873ece4586b96aa75258726e2fa8ec21d6f00a591d98806c2f4", size = 192701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/b7/6ec57841fb67c98f52fc8e4a2d96df60059637cba077edc569a302a8ffc7/Unidecode-1.3.8-py3-none-any.whl", hash = "sha256:d130a61ce6696f8148a3bd8fe779c99adeb4b870584eeb9526584e9aa091fd39", size = 235494 }, +] + [[package]] name = "uritemplate" version = "4.1.1" @@ -1730,15 +1943,15 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.30.6" +version = "0.31.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/01/5e637e7aa9dd031be5376b9fb749ec20b86f5a5b6a49b87fabd374d5fa9f/uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788", size = 42825 } +sdist = { url = "https://files.pythonhosted.org/packages/0a/96/ee52d900f8e41cc35eaebfda76f3619c2e45b741f3ee957d6fe32be1b2aa/uvicorn-0.31.0.tar.gz", hash = "sha256:13bc21373d103859f68fe739608e2eb054a816dea79189bc3ca08ea89a275906", size = 77140 } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/8e/cdc7d6263db313030e4c257dd5ba3909ebc4e4fb53ad62d5f09b1a2f5458/uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5", size = 62835 }, + { url = "https://files.pythonhosted.org/packages/05/12/206aca5442524d16be7702d08b453d7c274c86fd759266b1f709d4ef43ba/uvicorn-0.31.0-py3-none-any.whl", hash = "sha256:cac7be4dd4d891c363cd942160a7b02e69150dcbc7a36be04d5f4af4b17c8ced", size = 63656 }, ] [[package]] diff --git a/worker.py b/worker.py index 66e70c7..6fddd59 100644 --- a/worker.py +++ b/worker.py @@ -1,19 +1,16 @@ import os -import time + import json from celery import Celery from app.utils.gee2chat import get_chat_pasture, get_chat_pasture_vigor -from app.models.payload import PayloadSaveGeojson +from app.models.payload import PayloadSaveGeojson, ResultPayload from celery.utils.log import get_task_logger - -logger = get_task_logger(__name__) - from pymongo import MongoClient - import ee import geemap +logger = get_task_logger(__name__) celery = Celery(__name__) celery.conf.broker_url = os.environ.get("CELERY_BROKER_URL", "redis://quees:6379/0") @@ -23,7 +20,7 @@ @celery.task(name="gee_get_index_pasture",bind=True) -def gee_get_index_pasture(self, payload: PayloadSaveGeojson): +def gee_get_index_pasture(self, payload: ResultPayload): geojson = payload.get('geojson') def gee_credentials(private_key_file): data = json.load(open(private_key_file)) @@ -207,41 +204,4 @@ def pasute_vigor_reduce(img): resultado = colecao.insert_one(result) return result - -if __name__ == '__main__': - gee_get_index_pasture({ - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": {}, - "geometry": { - "coordinates": [ - [ - [ - -48.19902411568995, - -15.50226885170754 - ], - [ - -48.19902411568995, - -15.613399185567161 - ], - [ - -48.035908961874185, - -15.613399185567161 - ], - [ - -48.035908961874185, - -15.50226885170754 - ], - [ - -48.19902411568995, - -15.50226885170754 - ] - ] - ], - "type": "Polygon" - } - } - ] -}) \ No newline at end of file + \ No newline at end of file