Add hubsList, suppliersList, productsList resolvers and update nearestOffers
All checks were successful
Build Docker Image / build (push) Successful in 1m24s
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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user