```json
{
  "sym_variables": [
    ("x0", "wooden pencils"),
    ("x1", "smoke detectors"),
    ("x2", "mechanical pencils")
  ],
  "objective_function": "1.14 * x0 + 7.94 * x1 + 2.93 * x2",
  "constraints": [
    "1 * x0 + 7 * x1 + 10 * x2 <= 70",
    "8 * x0 + 3 * x1 + 5 * x2 <= 80",
    "7 * x0 + 3 * x1 + 9 * x2 <= 78",
    "7 * x0 + 1 * x1 + 11 * x2 <= 122",
    "7 * x1 + 10 * x2 >= 15",
    "1 * x0 + 7 * x1 >= 21",
    "1 * x0 + 7 * x1 + 10 * x2 >= 21",
    "3 * x1 + 5 * x2 >= 18",
    "8 * x0 + 3 * x1 + 5 * x2 >= 18",
    "7 * x0 + 3 * x1 >= 11",
    "3 * x1 + 9 * x2 >= 24",
    "7 * x0 + 9 * x2 >= 14",
    "7 * x0 + 3 * x1 + 9 * x2 >= 14",
    "1 * x1 + 11 * x2 >= 36",
    "7 * x0 + 1 * x1 >= 23",
    "7 * x0 + 1 * x1 + 11 * x2 >= 23",
    "-5 * x0 + 3 * x2 >= 0",
    "-7 * x1 + 10 * x2 >= 0",
    "1 * x0 + 7 * x1 <= 61",
    "1 * x0 + 10 * x2 <= 66",
    "3 * x1 + 5 * x2 <= 29",
    "8 * x0 + 5 * x2 <= 55",
    "7 * x0 + 11 * x2 <= 87",
    "1 * x1 + 11 * x2 <= 47",
    "7 * x0 + 1 * x1 + 11 * x2 <= 96"
  ]
}
```

```python
import gurobipy as gp

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

# Create variables
wooden_pencils = model.addVar(vtype=gp.GRB.INTEGER, name="wooden_pencils")
smoke_detectors = model.addVar(vtype=gp.GRB.INTEGER, name="smoke_detectors")
mechanical_pencils = model.addVar(vtype=gp.GRB.INTEGER, name="mechanical_pencils")

# Set objective function
model.setObjective(1.14 * wooden_pencils + 7.94 * smoke_detectors + 2.93 * mechanical_pencils, gp.GRB.MINIMIZE)

# Add constraints
model.addConstr(1 * wooden_pencils + 7 * smoke_detectors + 10 * mechanical_pencils <= 70, "dollar cost")
model.addConstr(8 * wooden_pencils + 3 * smoke_detectors + 5 * mechanical_pencils <= 80, "employee satisfaction impact")
model.addConstr(7 * wooden_pencils + 3 * smoke_detectors + 9 * mechanical_pencils <= 78, "workplace safety impact")
model.addConstr(7 * wooden_pencils + 1 * smoke_detectors + 11 * mechanical_pencils <= 122, "storage space")
model.addConstr(7 * smoke_detectors + 10 * mechanical_pencils >= 15, "smoke_detectors and mechanical_pencils cost")
model.addConstr(1 * wooden_pencils + 7 * smoke_detectors >= 21, "wooden_pencils and smoke_detectors cost")
model.addConstr(1 * wooden_pencils + 7 * smoke_detectors + 10 * mechanical_pencils >= 21, "total cost")
model.addConstr(3 * smoke_detectors + 5 * mechanical_pencils >= 18, "smoke_detectors and mechanical_pencils employee satisfaction")
model.addConstr(8 * wooden_pencils + 3 * smoke_detectors + 5 * mechanical_pencils >= 18, "total employee satisfaction")
model.addConstr(7 * wooden_pencils + 3 * smoke_detectors >= 11, "wooden_pencils and smoke_detectors workplace safety")
model.addConstr(3 * smoke_detectors + 9 * mechanical_pencils >= 24, "smoke_detectors and mechanical_pencils workplace safety")
model.addConstr(7 * wooden_pencils + 9 * mechanical_pencils >= 14, "wooden_pencils and mechanical_pencils workplace safety")
model.addConstr(7 * wooden_pencils + 3 * smoke_detectors + 9 * mechanical_pencils >= 14, "total workplace safety")
model.addConstr(1 * smoke_detectors + 11 * mechanical_pencils >= 36, "smoke_detectors and mechanical_pencils storage")
model.addConstr(7 * wooden_pencils + 1 * smoke_detectors >= 23, "wooden_pencils and smoke_detectors storage")
model.addConstr(7 * wooden_pencils + 1 * smoke_detectors + 11 * mechanical_pencils >= 23, "total storage")
model.addConstr(-5 * wooden_pencils + 3 * mechanical_pencils >= 0, "constraint1")
model.addConstr(-7 * smoke_detectors + 10 * mechanical_pencils >= 0, "constraint2")
model.addConstr(1 * wooden_pencils + 7 * smoke_detectors <= 61, "wooden_pencils and smoke_detectors cost upper bound")
model.addConstr(1 * wooden_pencils + 10 * mechanical_pencils <= 66, "wooden_pencils and mechanical_pencils cost upper bound")
model.addConstr(3 * smoke_detectors + 5 * mechanical_pencils <= 29, "smoke_detectors and mechanical_pencils employee satisfaction upper bound")
model.addConstr(8 * wooden_pencils + 5 * mechanical_pencils <= 55, "wooden_pencils and mechanical_pencils employee satisfaction upper bound")
model.addConstr(7 * wooden_pencils + 11 * mechanical_pencils <= 87, "wooden_pencils and mechanical_pencils storage upper bound")
model.addConstr(1 * smoke_detectors + 11 * mechanical_pencils <= 47, "smoke_detectors and mechanical_pencils storage upper bound")
model.addConstr(7 * wooden_pencils + 1 * smoke_detectors + 11 * mechanical_pencils <= 96, "total storage upper bound")


# Optimize model
model.optimize()

# Print results
if model.status == gp.GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Wooden pencils: {wooden_pencils.x}")
    print(f"Smoke detectors: {smoke_detectors.x}")
    print(f"Mechanical pencils: {mechanical_pencils.x}")
    print(f"Objective value: {model.objVal}")
elif model.status == gp.GRB.INFEASIBLE:
    print("Model is infeasible.")
else:
    print(f"Optimization ended with status {model.status}")

```