File size: 6,265 Bytes
e40294e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
from typing import Dict
from solverforge_legacy.solver import SolverStatus
from solverforge_legacy.solver.score import HardSoftDecimalScore

from . import domain
from .warehouse import WarehouseLocation, Side


# =============================================================================
# Domain to API Model Conversion
# =============================================================================

def location_to_model(location: WarehouseLocation) -> domain.WarehouseLocationModel:
    return domain.WarehouseLocationModel(
        shelving_id=location.shelving_id,
        side=location.side.name,
        row=location.row
    )


def product_to_model(product: domain.Product) -> domain.ProductModel:
    return domain.ProductModel(
        id=product.id,
        name=product.name,
        volume=product.volume,
        location=location_to_model(product.location)
    )


def order_item_to_model(item: domain.OrderItem) -> domain.OrderItemModel:
    return domain.OrderItemModel(
        id=item.id,
        order_id=item.order_id,
        product=product_to_model(item.product)
    )


def order_to_model(order: domain.Order) -> domain.OrderModel:
    return domain.OrderModel(
        id=order.id,
        items=[order_item_to_model(item) for item in order.items]
    )


def trolley_step_to_model(step: domain.TrolleyStep) -> domain.TrolleyStepModel:
    return domain.TrolleyStepModel(
        id=step.id,
        order_item=order_item_to_model(step.order_item),
        trolley=step.trolley.id if step.trolley else None,
        trolley_id=step.trolley_id
    )


def trolley_to_model(trolley: domain.Trolley) -> domain.TrolleyModel:
    return domain.TrolleyModel(
        id=trolley.id,
        bucket_count=trolley.bucket_count,
        bucket_capacity=trolley.bucket_capacity,
        location=location_to_model(trolley.location),
        steps=[step.id for step in trolley.steps]
    )


def solution_to_model(solution: domain.OrderPickingSolution) -> domain.OrderPickingSolutionModel:
    return domain.OrderPickingSolutionModel(
        trolleys=[trolley_to_model(t) for t in solution.trolleys],
        trolley_steps=[trolley_step_to_model(s) for s in solution.trolley_steps],
        score=str(solution.score) if solution.score else None,
        solver_status=solution.solver_status.name if solution.solver_status else None
    )


# =============================================================================
# API Model to Domain Conversion
# =============================================================================

def model_to_location(model: domain.WarehouseLocationModel) -> WarehouseLocation:
    return WarehouseLocation(
        shelving_id=model.shelving_id,
        side=Side[model.side],
        row=model.row
    )


def model_to_product(model: domain.ProductModel) -> domain.Product:
    return domain.Product(
        id=model.id,
        name=model.name,
        volume=model.volume,
        location=model_to_location(model.location)
    )


def model_to_solution(model: domain.OrderPickingSolutionModel) -> domain.OrderPickingSolution:
    """Convert API model to domain object."""
    # First pass: create all products and orders without cross-references
    products: Dict[str, domain.Product] = {}
    orders: Dict[str, domain.Order] = {}

    # Extract unique products and orders from trolley steps
    for step_model in model.trolley_steps:
        item_model = step_model.order_item
        product_model = item_model.product

        # Create product if not seen
        if product_model.id not in products:
            products[product_model.id] = model_to_product(product_model)

        # Create order if not seen
        order_id = item_model.order_id
        if order_id and order_id not in orders:
            orders[order_id] = domain.Order(id=order_id, items=[])

    # Second pass: create order items and trolley steps
    trolley_steps = []
    step_lookup: Dict[str, domain.TrolleyStep] = {}

    for step_model in model.trolley_steps:
        item_model = step_model.order_item
        product = products[item_model.product.id]
        order = orders.get(item_model.order_id) if item_model.order_id else None

        order_item = domain.OrderItem(
            id=item_model.id,
            order=order,
            product=product
        )

        # Add item to order's item list
        if order:
            order.items.append(order_item)

        step = domain.TrolleyStep(
            id=step_model.id,
            order_item=order_item
        )
        trolley_steps.append(step)
        step_lookup[step.id] = step

    # Third pass: create trolleys and set up relationships
    trolleys = []
    trolley_lookup: Dict[str, domain.Trolley] = {}

    for trolley_model in model.trolleys:
        trolley = domain.Trolley(
            id=trolley_model.id,
            bucket_count=trolley_model.bucket_count,
            bucket_capacity=trolley_model.bucket_capacity,
            location=model_to_location(trolley_model.location),
            steps=[]
        )
        trolleys.append(trolley)
        trolley_lookup[trolley.id] = trolley

        # Populate steps list
        for step_ref in trolley_model.steps:
            step_id = step_ref if isinstance(step_ref, str) else step_ref.id
            if step_id in step_lookup:
                step = step_lookup[step_id]
                trolley.steps.append(step)
                # Set shadow variable (for consistency, though solver will reset)
                step.trolley = trolley

    # Set up previous/next step references based on list order
    for trolley in trolleys:
        for i, step in enumerate(trolley.steps):
            step.previous_step = trolley.steps[i - 1] if i > 0 else None
            step.next_step = trolley.steps[i + 1] if i < len(trolley.steps) - 1 else None

    # Handle score
    score = None
    if model.score:
        score = HardSoftDecimalScore.parse(model.score)

    # Handle solver status
    solver_status = SolverStatus.NOT_SOLVING
    if model.solver_status:
        solver_status = SolverStatus[model.solver_status]

    return domain.OrderPickingSolution(
        trolleys=trolleys,
        trolley_steps=trolley_steps,
        score=score,
        solver_status=solver_status
    )