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_uuid = graphene.String()
|
||||||
product_name = graphene.String()
|
product_name = graphene.String()
|
||||||
supplier_uuid = graphene.String()
|
supplier_uuid = graphene.String()
|
||||||
|
supplier_name = graphene.String()
|
||||||
latitude = graphene.Float()
|
latitude = graphene.Float()
|
||||||
longitude = graphene.Float()
|
longitude = graphene.Float()
|
||||||
country = graphene.String()
|
country = graphene.String()
|
||||||
@@ -141,6 +142,25 @@ class OfferNodeType(graphene.ObjectType):
|
|||||||
distance_km = graphene.Float()
|
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):
|
class Query(graphene.ObjectType):
|
||||||
"""Root query."""
|
"""Root query."""
|
||||||
MAX_EXPANSIONS = 20000
|
MAX_EXPANSIONS = 20000
|
||||||
@@ -310,13 +330,14 @@ class Query(graphene.ObjectType):
|
|||||||
)
|
)
|
||||||
|
|
||||||
nearest_offers = graphene.List(
|
nearest_offers = graphene.List(
|
||||||
OfferNodeType,
|
OfferWithRouteType,
|
||||||
lat=graphene.Float(required=True, description="Latitude"),
|
lat=graphene.Float(required=True, description="Latitude"),
|
||||||
lon=graphene.Float(required=True, description="Longitude"),
|
lon=graphene.Float(required=True, description="Longitude"),
|
||||||
radius=graphene.Float(default_value=500, description="Search radius in km"),
|
radius=graphene.Float(default_value=500, description="Search radius in km"),
|
||||||
product_uuid=graphene.String(description="Filter by product UUID"),
|
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"),
|
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(
|
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)",
|
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
|
@staticmethod
|
||||||
def _build_routes(db, from_uuid, to_uuid, limit):
|
def _build_routes(db, from_uuid, to_uuid, limit):
|
||||||
"""Shared helper to compute K shortest routes between two nodes."""
|
"""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)
|
logger.error("Error finding nearest hubs: %s", e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def resolve_nearest_offers(self, info, lat, lon, radius=500, product_uuid=None, limit=50):
|
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."""
|
"""Find nearest offers to coordinates, optionally filtered by product. If hub_uuid provided, calculates routes."""
|
||||||
db = get_db()
|
db = get_db()
|
||||||
|
ensure_graph()
|
||||||
|
|
||||||
aql = """
|
aql = """
|
||||||
FOR offer IN nodes
|
FOR offer IN nodes
|
||||||
@@ -1497,13 +1544,33 @@ class Query(graphene.ObjectType):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
cursor = db.aql.execute(aql, bind_vars=bind_vars)
|
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 = []
|
offers = []
|
||||||
for node in cursor:
|
for node in offer_nodes:
|
||||||
offers.append(OfferNodeType(
|
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'],
|
uuid=node['_key'],
|
||||||
product_uuid=node.get('product_uuid'),
|
product_uuid=node.get('product_uuid'),
|
||||||
product_name=node.get('product_name'),
|
product_name=node.get('product_name'),
|
||||||
supplier_uuid=node.get('supplier_uuid'),
|
supplier_uuid=node.get('supplier_uuid'),
|
||||||
|
supplier_name=node.get('supplier_name'),
|
||||||
latitude=node.get('latitude'),
|
latitude=node.get('latitude'),
|
||||||
longitude=node.get('longitude'),
|
longitude=node.get('longitude'),
|
||||||
country=node.get('country'),
|
country=node.get('country'),
|
||||||
@@ -1513,8 +1580,9 @@ class Query(graphene.ObjectType):
|
|||||||
quantity=node.get('quantity'),
|
quantity=node.get('quantity'),
|
||||||
unit=node.get('unit'),
|
unit=node.get('unit'),
|
||||||
distance_km=node.get('distance_km'),
|
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
|
return offers
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Error finding nearest offers: %s", 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)
|
logger.error("Error finding route to coordinates: %s", e)
|
||||||
return None
|
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)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user