Add hubsList, suppliersList, productsList resolvers and update nearestOffers
All checks were successful
Build Docker Image / build (push) Successful in 1m24s

- Add hubsList resolver for paginated hub list
- Add suppliersList resolver for paginated supplier list
- Add productsList resolver for paginated product list
- Update nearestOffers to support hubUuid parameter with route calculation
- Add OfferWithRouteType for offers with embedded routes
- Add supplier_name to OfferNodeType
This commit is contained in:
Ruslan Bakiev
2026-01-26 13:55:02 +07:00
parent 81f86b6538
commit 9ff7927463

View File

@@ -130,6 +130,7 @@ class OfferNodeType(graphene.ObjectType):
product_uuid = graphene.String()
product_name = graphene.String()
supplier_uuid = graphene.String()
supplier_name = graphene.String()
latitude = graphene.Float()
longitude = graphene.Float()
country = graphene.String()
@@ -141,6 +142,25 @@ class OfferNodeType(graphene.ObjectType):
distance_km = graphene.Float()
class OfferWithRouteType(graphene.ObjectType):
"""Offer with route information to destination."""
uuid = graphene.String()
product_uuid = graphene.String()
product_name = graphene.String()
supplier_uuid = graphene.String()
supplier_name = graphene.String()
latitude = graphene.Float()
longitude = graphene.Float()
country = graphene.String()
country_code = graphene.String()
price_per_unit = graphene.String()
currency = graphene.String()
quantity = graphene.String()
unit = graphene.String()
distance_km = graphene.Float()
routes = graphene.List(lambda: RoutePathType)
class Query(graphene.ObjectType):
"""Root query."""
MAX_EXPANSIONS = 20000
@@ -310,13 +330,14 @@ class Query(graphene.ObjectType):
)
nearest_offers = graphene.List(
OfferNodeType,
OfferWithRouteType,
lat=graphene.Float(required=True, description="Latitude"),
lon=graphene.Float(required=True, description="Longitude"),
radius=graphene.Float(default_value=500, description="Search radius in km"),
product_uuid=graphene.String(description="Filter by product UUID"),
hub_uuid=graphene.String(description="Hub UUID - if provided, calculates routes to this hub"),
limit=graphene.Int(default_value=50, description="Max results"),
description="Find nearest offers to coordinates (optionally filtered by product)",
description="Find nearest offers to coordinates with optional routes to hub",
)
nearest_suppliers = graphene.List(
@@ -337,6 +358,31 @@ class Query(graphene.ObjectType):
description="Get route from offer to target coordinates (finds nearest hub to coordinate)",
)
# New unified list endpoints
hubs_list = graphene.List(
NodeType,
limit=graphene.Int(default_value=50, description="Max results"),
offset=graphene.Int(default_value=0, description="Offset for pagination"),
country=graphene.String(description="Filter by country name"),
transport_type=graphene.String(description="Filter by transport type"),
description="Get paginated list of logistics hubs",
)
suppliers_list = graphene.List(
SupplierType,
limit=graphene.Int(default_value=50, description="Max results"),
offset=graphene.Int(default_value=0, description="Offset for pagination"),
country=graphene.String(description="Filter by country name"),
description="Get paginated list of suppliers from graph",
)
products_list = graphene.List(
ProductType,
limit=graphene.Int(default_value=50, description="Max results"),
offset=graphene.Int(default_value=0, description="Offset for pagination"),
description="Get paginated list of products from graph",
)
@staticmethod
def _build_routes(db, from_uuid, to_uuid, limit):
"""Shared helper to compute K shortest routes between two nodes."""
@@ -1469,9 +1515,10 @@ class Query(graphene.ObjectType):
logger.error("Error finding nearest hubs: %s", e)
return []
def resolve_nearest_offers(self, info, lat, lon, radius=500, product_uuid=None, limit=50):
"""Find nearest offers to coordinates, optionally filtered by product."""
def resolve_nearest_offers(self, info, lat, lon, radius=500, product_uuid=None, hub_uuid=None, limit=50):
"""Find nearest offers to coordinates, optionally filtered by product. If hub_uuid provided, calculates routes."""
db = get_db()
ensure_graph()
aql = """
FOR offer IN nodes
@@ -1497,13 +1544,33 @@ class Query(graphene.ObjectType):
try:
cursor = db.aql.execute(aql, bind_vars=bind_vars)
offer_nodes = list(cursor)
logger.info("Found %d offers near (%.3f, %.3f) within %d km", len(offer_nodes), lat, lon, radius)
# If hub_uuid provided, calculate routes for each offer
if hub_uuid:
nodes_col = db.collection('nodes')
hub = nodes_col.get(hub_uuid)
if not hub:
logger.warning("Hub %s not found for route calculation", hub_uuid)
hub_uuid = None
offers = []
for node in cursor:
offers.append(OfferNodeType(
for node in offer_nodes:
routes = []
# Calculate route to hub if hub_uuid provided
if hub_uuid:
route_result = self.resolve_offer_to_hub(info, node['_key'], hub_uuid)
if route_result and route_result.routes:
routes = route_result.routes
offers.append(OfferWithRouteType(
uuid=node['_key'],
product_uuid=node.get('product_uuid'),
product_name=node.get('product_name'),
supplier_uuid=node.get('supplier_uuid'),
supplier_name=node.get('supplier_name'),
latitude=node.get('latitude'),
longitude=node.get('longitude'),
country=node.get('country'),
@@ -1513,8 +1580,9 @@ class Query(graphene.ObjectType):
quantity=node.get('quantity'),
unit=node.get('unit'),
distance_km=node.get('distance_km'),
routes=routes,
))
logger.info("Found %d offers near (%.3f, %.3f) within %d km", len(offers), lat, lon, radius)
return offers
except Exception as e:
logger.error("Error finding nearest offers: %s", e)
@@ -1614,6 +1682,114 @@ class Query(graphene.ObjectType):
logger.error("Error finding route to coordinates: %s", e)
return None
def resolve_hubs_list(self, info, limit=50, offset=0, country=None, transport_type=None):
"""Get paginated list of logistics hubs."""
db = get_db()
aql = """
FOR node IN nodes
FILTER node.node_type == 'logistics' OR node.node_type == null
FILTER node.product_uuid == null
LET types = node.transport_types != null ? node.transport_types : []
FILTER @transport_type == null OR @transport_type IN types
FILTER @country == null OR node.country == @country
SORT node.name ASC
LIMIT @offset, @limit
RETURN node
"""
try:
cursor = db.aql.execute(aql, bind_vars={
'transport_type': transport_type,
'country': country,
'offset': offset,
'limit': limit,
})
hubs = []
for node in cursor:
hubs.append(NodeType(
uuid=node['_key'],
name=node.get('name'),
latitude=node.get('latitude'),
longitude=node.get('longitude'),
country=node.get('country'),
country_code=node.get('country_code'),
synced_at=node.get('synced_at'),
transport_types=node.get('transport_types') or [],
edges=[],
))
logger.info("Returning %d hubs (offset=%d, limit=%d)", len(hubs), offset, limit)
return hubs
except Exception as e:
logger.error("Error getting hubs list: %s", e)
return []
def resolve_suppliers_list(self, info, limit=50, offset=0, country=None):
"""Get paginated list of suppliers from graph."""
db = get_db()
aql = """
FOR node IN nodes
FILTER node.node_type == 'supplier'
FILTER @country == null OR node.country == @country
SORT node.name ASC
LIMIT @offset, @limit
RETURN node
"""
try:
cursor = db.aql.execute(aql, bind_vars={
'country': country,
'offset': offset,
'limit': limit,
})
suppliers = []
for node in cursor:
suppliers.append(SupplierType(
uuid=node['_key'],
name=node.get('name'),
latitude=node.get('latitude'),
longitude=node.get('longitude'),
))
logger.info("Returning %d suppliers (offset=%d, limit=%d)", len(suppliers), offset, limit)
return suppliers
except Exception as e:
logger.error("Error getting suppliers list: %s", e)
return []
def resolve_products_list(self, info, limit=50, offset=0):
"""Get paginated list of products from graph."""
db = get_db()
aql = """
FOR node IN nodes
FILTER node.node_type == 'offer'
FILTER node.product_uuid != null
COLLECT product_uuid = node.product_uuid INTO offers
LET first_offer = FIRST(offers).node
SORT first_offer.product_name ASC
LIMIT @offset, @limit
RETURN {
uuid: product_uuid,
name: first_offer.product_name
}
"""
try:
cursor = db.aql.execute(aql, bind_vars={
'offset': offset,
'limit': limit,
})
products = [ProductType(uuid=p['uuid'], name=p.get('name')) for p in cursor]
logger.info("Returning %d products (offset=%d, limit=%d)", len(products), offset, limit)
return products
except Exception as e:
logger.error("Error getting products list: %s", e)
return []
schema = graphene.Schema(query=Query)