diff --git a/backend/api/v1/__init__.py b/backend/api/v1/__init__.py index d6edc80..2e69738 100644 --- a/backend/api/v1/__init__.py +++ b/backend/api/v1/__init__.py @@ -14,6 +14,7 @@ from .alarms import router as alarms_router from .cellsites import router as cellsites_router from .simulator import router as simulator_router from .robots import router as robots_router +from .gqlapi import graphql_app as test_graphql origins: list[str] = settings.cors_origins.split(",") @@ -58,5 +59,7 @@ def create_app() -> FastAPI: app.include_router(alarms_router, prefix="/api/v1") app.include_router(simulator_router, prefix="/api/v1") app.include_router(robots_router, prefix="/api/v1") + # Graphql test + app.include_router(test_graphql, prefix="/graphql") return app diff --git a/backend/api/v1/cellsites/__init__.py b/backend/api/v1/cellsites/__init__.py index cfa31dc..f4699e6 100644 --- a/backend/api/v1/cellsites/__init__.py +++ b/backend/api/v1/cellsites/__init__.py @@ -10,7 +10,7 @@ router = APIRouter(prefix="/cellsites", tags=["cellsites"]) def get_cellsites() -> list[dict[Any, Any]]: # type: ignore conn = db.connect_to_db() try: - with db.connect_to_db() as conn: + with conn: return db.get_cellsites(conn) except Exception as e: logger.error(f"Error fetching cellsites: {e}", exc_info=True) @@ -23,7 +23,7 @@ def get_cellsites() -> list[dict[Any, Any]]: # type: ignore def get_cellsite(id: int) -> list[dict[str, Any]]: # type: ignore conn = db.connect_to_db() try: - with db.connect_to_db() as conn: + with conn: return db.get_cellsite(conn, id) except Exception as e: logger.error(f"Error fetching cellsite {id}: {e}", exc_info=True) diff --git a/backend/api/v1/gqlapi/__init__.py b/backend/api/v1/gqlapi/__init__.py new file mode 100644 index 0000000..7e3da72 --- /dev/null +++ b/backend/api/v1/gqlapi/__init__.py @@ -0,0 +1,87 @@ +# https://fastapi.tiangolo.com/how-to/graphql/#graphql-with-strawberry + +import typing +import strawberry +from strawberry.fastapi import GraphQLRouter +import database as db +from logger import get_logger +logger = get_logger(__name__) + +def get_sites(): + conn = db.connect_to_db() + try: + with db.connect_to_db() as conn: + sites = db.get_cellsites(conn) + ql_list = [] + for site in sites: + print(site) + ql_list.append( + Cellsite( + id=site['id'], + lat=site['lat'], + lon=site['lon'] + ) + ) + return ql_list + except Exception as e: + logger.error(f"Error fetching cellsite {id}: {e}", exc_info=True) + return [] + finally: + conn.close() + +def get_incidents(): + conn = db.connect_to_db() + try: + with conn: + incidents = db.get_incidents(conn) + ql_list = [] + for incident in incidents: + print(incident) + ql_list.append( + Incident( + id=incident['id'], + text=incident['text'], + severity=incident['severity'], + status=incident['status'], + created_by=incident['created_by'], + site_id=incident['site_id'] + ) + ) + return ql_list + except Exception as e: + logger.error(f"Error fetching cellsite {id}: {e}", exc_info=True) + return [] + finally: + conn.close() + +@strawberry.type +class Cellsite: + id: int + lat: float + lon: float + +# TODO: Add incident and join with cellsite + +# TODO: Query a site ID and get all related incidents +# TODO: Query an incident and get all related site id + +@strawberry.type +class Incident: + id: int + text: str + severity: int + status: str + created_by: str + # site_id: typing.List[Cellsite] + site_id: int + +@strawberry.type +class Query: + cellsites: typing.List[Cellsite] = strawberry.field(resolver=get_sites) + incidents: typing.List[Incident] = strawberry.field(resolver=get_incidents) + + + +schema = strawberry.Schema(query=Query) + +graphql_app = GraphQLRouter(schema) diff --git a/backend/etl.py b/backend/etl.py index 0a7fb89..d7f62ce 100644 --- a/backend/etl.py +++ b/backend/etl.py @@ -7,7 +7,8 @@ from simulator.sim_config import ALARM_TEXTS, TICKET_TEXTS, ASSIGNEES, SITE_IDS from config import settings def main() -> None: - # create_cellsites_table() + # get_csv_file() + create_cellsites_table() create_alarms_table() create_incidents_table() # create_change_table() diff --git a/backend/pyproject.toml b/backend/pyproject.toml index c0e53e3..d1a6d18 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -8,4 +8,5 @@ dependencies = [ "fastapi[standard]>=0.135.2", "pydantic-settings>=2.13.1", "requests>=2.32.5", + "strawberry-graphql[fastapi]>=0.315.2", ] diff --git a/backend/uv.lock b/backend/uv.lock index 1e7f31f..b5d891d 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -40,6 +40,7 @@ dependencies = [ { name = "fastapi", extra = ["standard"] }, { name = "pydantic-settings" }, { name = "requests" }, + { name = "strawberry-graphql", extra = ["fastapi"] }, ] [package.metadata] @@ -47,6 +48,7 @@ requires-dist = [ { name = "fastapi", extras = ["standard"], specifier = ">=0.135.2" }, { name = "pydantic-settings", specifier = ">=2.13.1" }, { name = "requests", specifier = ">=2.32.5" }, + { name = "strawberry-graphql", extras = ["fastapi"], specifier = ">=0.315.2" }, ] [[package]] @@ -120,6 +122,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "cross-web" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/83/b5ef04565acc065387dda3a4fbf0c4cfb6bab805c81b66b2bc5b5ac9a282/cross_web-0.6.0.tar.gz", hash = "sha256:ae90570802615365ca1a781117b43bfd0d6cd3bf611649d24c3a206a82a693c9", size = 331315, upload-time = "2026-04-13T14:29:12.718Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/a2/dab06d9b80cb76c700883186a9a2e6fd103342c9b4def4d88f5787796e17/cross_web-0.6.0-py3-none-any.whl", hash = "sha256:bdebf0c08d02f3a48cf67b6904d3a6d8fd8cab2cd905592ab96ab00b259cd582", size = 24820, upload-time = "2026-04-13T14:29:11.198Z" }, +] + [[package]] name = "dnspython" version = "2.8.0" @@ -249,6 +263,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/36/a7af08d233624515d9a0f5d41b7a01a51fd825b8c795e41800215a3200e7/fastar-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:34f646ac4f5bed3661a106ca56c1744e7146a02aacf517d47b24fd3f25dc1ff6", size = 460604, upload-time = "2026-03-20T14:26:40.771Z" }, ] +[[package]] +name = "graphql-core" +version = "3.2.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/c5/36aa96205c3ecbb3d34c7c24189e4553c7ca2ebc7e1dd07432339b980272/graphql_core-3.2.8.tar.gz", hash = "sha256:015457da5d996c924ddf57a43f4e959b0b94fb695b85ed4c29446e508ed65cf3", size = 513181, upload-time = "2026-03-05T19:55:37.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/41/cb887d9afc5dabd78feefe6ccbaf83ff423c206a7a1b7aeeac05120b2125/graphql_core-3.2.8-py3-none-any.whl", hash = "sha256:cbee07bee1b3ed5e531723685369039f32ff815ef60166686e0162f540f1520c", size = 207349, upload-time = "2026-03-05T19:55:35.911Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -373,6 +396,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + [[package]] name = "pydantic" version = "2.12.5" @@ -468,6 +500,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +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, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.2" @@ -614,6 +658,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "starlette" version = "1.0.0" @@ -626,6 +679,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, ] +[[package]] +name = "strawberry-graphql" +version = "0.315.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cross-web" }, + { name = "graphql-core" }, + { name = "packaging" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/d1/2d5f735f9b9afa564114fe319b84d1d9f32f180b574410d57c947ae626e6/strawberry_graphql-0.315.2.tar.gz", hash = "sha256:c962f7a78f6a82954bd04fcf4ea70c189bad52bc5f67314275aacfd4514ac446", size = 222640, upload-time = "2026-04-26T12:25:17.595Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/17/6353b26473e7cca3f96f4695d3baf5d73decc954616f5679fa02517edc49/strawberry_graphql-0.315.2-py3-none-any.whl", hash = "sha256:b6a1c5b89e3e5be849acf727b8d0de8906d02da2bf049108629960ee568cf043", size = 324988, upload-time = "2026-04-26T12:25:15.23Z" }, +] + +[package.optional-dependencies] +fastapi = [ + { name = "fastapi" }, + { name = "python-multipart" }, +] + [[package]] name = "typer" version = "0.24.1"