```json
{
  "sym_variables": [
    ("x0", "chicken breasts"),
    ("x1", "potatoes"),
    ("x2", "tomatoes"),
    ("x3", "milkshakes")
  ],
  "objective_function": "3.29*x0 + 7.22*x1 + 7.31*x2 + 4.91*x3",
  "constraints": [
    "1*x0 + 3*x3 >= 28",
    "8*x1 + 9*x2 >= 29",
    "1*x0 + 8*x1 + 9*x2 + 3*x3 >= 29",
    "5*x0 + 6*x2 >= 31",
    "7*x1 + 4*x3 >= 33",
    "7*x1 + 6*x2 >= 26",
    "5*x0 + 7*x1 + 6*x2 + 4*x3 >= 26",
    "1*x1 + 1*x2 >= 41",
    "3*x0 + 1*x2 >= 38",
    "1*x2 + 8*x3 >= 18",
    "3*x0 + 1*x2 + 8*x3 >= 39",
    "1*x1 + 1*x2 + 8*x3 >= 39",
    "3*x0 + 1*x2 + 8*x3 >= 22",
    "1*x1 + 1*x2 + 8*x3 >= 22",
    "3*x0 + 1*x1 + 1*x2 + 8*x3 >= 22",
    "5*x0 + 7*x1 <= 139",
    "7*x1 + 4*x3 <= 108",
    "5*x0 + 4*x3 <= 85",
    "7*x1 + 6*x2 <= 139",
    "5*x0 + 7*x1 + 6*x2 + 4*x3 <= 159",
    "5*x0 + 7*x1 + 4*x3 <= 61",
    "5*x0 + 7*x1 + 6*x2 <= 54",
    "3*x0 + 8*x3 <= 126",
    "1*x1 + 8*x3 <= 161",
    "1*x0 + 8*x1 + 9*x2 + 3*x3 <= 120",
    "5*x0 + 7*x1 + 6*x2 + 4*x3 <= 160",
    "3*x0 + 1*x1 + 1*x2 + 8*x3 <= 165"
  ]
}
```

```python
import gurobipy as gp

# Create a new model
m = gp.Model("food_optimization")

# Create variables
chicken = m.addVar(vtype=gp.GRB.INTEGER, name="chicken")
potatoes = m.addVar(vtype=gp.GRB.INTEGER, name="potatoes")
tomatoes = m.addVar(vtype=gp.GRB.INTEGER, name="tomatoes")
milkshakes = m.addVar(vtype=gp.GRB.INTEGER, name="milkshakes")

# Set objective function
m.setObjective(3.29 * chicken + 7.22 * potatoes + 7.31 * tomatoes + 4.91 * milkshakes, gp.GRB.MINIMIZE)

# Add constraints
m.addConstr(chicken + 3 * milkshakes >= 28, "calcium_constraint1")
m.addConstr(8 * potatoes + 9 * tomatoes >= 29, "calcium_constraint2")
m.addConstr(chicken + 8 * potatoes + 9 * tomatoes + 3 * milkshakes >= 29, "calcium_constraint3")
m.addConstr(5*chicken + 6*tomatoes >= 31, "iron_constraint1")
m.addConstr(7 * potatoes + 4 * milkshakes >= 33, "iron_constraint2")
m.addConstr(7 * potatoes + 6 * tomatoes >= 26, "iron_constraint3")
m.addConstr(5 * chicken + 7 * potatoes + 6 * tomatoes + 4 * milkshakes >= 26, "iron_constraint4")
m.addConstr(potatoes + tomatoes >= 41, "fiber_constraint1")
m.addConstr(3 * chicken + tomatoes >= 38, "fiber_constraint2")
m.addConstr(tomatoes + 8 * milkshakes >= 18, "fiber_constraint3")
m.addConstr(3 * chicken + tomatoes + 8 * milkshakes >= 39, "fiber_constraint4")
m.addConstr(potatoes + tomatoes + 8 * milkshakes >= 39, "fiber_constraint5")
m.addConstr(3 * chicken + tomatoes + 8 * milkshakes >= 22, "fiber_constraint6")
m.addConstr(potatoes + tomatoes + 8 * milkshakes >= 22, "fiber_constraint7")
m.addConstr(3 * chicken + potatoes + tomatoes + 8 * milkshakes >= 22, "fiber_constraint8")
m.addConstr(5 * chicken + 7 * potatoes <= 139, "iron_constraint5")
m.addConstr(7 * potatoes + 4 * milkshakes <= 108, "iron_constraint6")
m.addConstr(5 * chicken + 4 * milkshakes <= 85, "iron_constraint7")
m.addConstr(7 * potatoes + 6 * tomatoes <= 139, "iron_constraint8")
m.addConstr(5 * chicken + 7 * potatoes + 6 * tomatoes + 4 * milkshakes <= 159, "iron_constraint9")
m.addConstr(5 * chicken + 7 * potatoes + 4 * milkshakes <= 61, "iron_constraint10")
m.addConstr(5 * chicken + 7 * potatoes + 6 * tomatoes <= 54, "iron_constraint11")
m.addConstr(3 * chicken + 8 * milkshakes <= 126, "fiber_constraint9")
m.addConstr(potatoes + 8 * milkshakes <= 161, "fiber_constraint10")


# Resource Constraints
m.addConstr(chicken + 8*potatoes + 9*tomatoes + 3*milkshakes <= 120, "calcium_limit")
m.addConstr(5*chicken + 7*potatoes + 6*tomatoes + 4*milkshakes <= 160, "iron_limit")
m.addConstr(3*chicken + potatoes + tomatoes + 8*milkshakes <= 165, "fiber_limit")


# Optimize model
m.optimize()

# Print results
if m.status == gp.GRB.OPTIMAL:
    print('Obj: %g' % m.objVal)
    print('chicken:', chicken.x)
    print('potatoes:', potatoes.x)
    print('tomatoes:', tomatoes.x)
    print('milkshakes:', milkshakes.x)
elif m.status == gp.GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print("Optimization ended with status:", m.status)

```
