```json
{
  "sym_variables": [
    ("x0", "hours worked by Hank"),
    ("x1", "hours worked by Paul"),
    ("x2", "hours worked by Mary")
  ],
  "objective_function": "2*x0 + 7*x1 + 7*x2",
  "constraints": [
    "16*x0 + 3*x2 >= 41",
    "21*x1 + 13*x2 >= 33",
    "7*x0 + 13*x2 >= 29",
    "7*x0 + 21*x1 + 13*x2 >= 73",
    "15*x0 + 7*x2 >= 84",
    "15*x0 + 5*x1 >= 83",
    "5*x1 + 7*x2 >= 64",
    "2*x0 + 7*x2 >= 15",
    "4*x1 + 7*x2 >= 22",
    "2*x0 + 4*x1 + 7*x2 >= 29",
    "16*x0 + 16*x1 <= 72",
    "16*x0 + 3*x2 <= 152",
    "16*x1 + 3*x2 <= 110",
    "16*x0 + 16*x1 + 3*x2 <= 110",
    "17*x1 + 2*x2 <= 106",
    "11*x0 + 17*x1 + 2*x2 <= 106",
    "7*x0 + 21*x1 <= 211",
    "21*x1 + 13*x2 <= 159",
    "7*x0 + 21*x1 + 13*x2 <= 159",
    "5*x1 + 7*x2 <= 210",
    "15*x0 + 5*x1 <= 202",
    "15*x0 + 5*x1 + 7*x2 <= 202",
    "4*x1 + 7*x2 <= 107",
    "2*x0 + 4*x1 + 7*x2 <= 107",
    "x0 >= 0",
    "x1 >= 0",
    "x2 >= 0"
  ]
}
```

```python
import gurobipy as gp

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

# Create variables
hank_hours = model.addVar(vtype=gp.GRB.CONTINUOUS, name="hank_hours")
paul_hours = model.addVar(vtype=gp.GRB.CONTINUOUS, name="paul_hours")
mary_hours = model.addVar(vtype=gp.GRB.CONTINUOUS, name="mary_hours")


# Set objective function
model.setObjective(2 * hank_hours + 7 * paul_hours + 7 * mary_hours, gp.GRB.MAXIMIZE)

# Add constraints
model.addConstr(16 * hank_hours + 3 * mary_hours >= 41)
model.addConstr(21 * paul_hours + 13 * mary_hours >= 33)
model.addConstr(7 * hank_hours + 13 * mary_hours >= 29)
model.addConstr(7 * hank_hours + 21 * paul_hours + 13 * mary_hours >= 73)
model.addConstr(15 * hank_hours + 7 * mary_hours >= 84)
model.addConstr(15 * hank_hours + 5 * paul_hours >= 83)
model.addConstr(5 * paul_hours + 7 * mary_hours >= 64)
model.addConstr(2 * hank_hours + 7 * mary_hours >= 15)
model.addConstr(4 * paul_hours + 7 * mary_hours >= 22)
model.addConstr(2 * hank_hours + 4 * paul_hours + 7 * mary_hours >= 29)
model.addConstr(16 * hank_hours + 16 * paul_hours <= 72)
model.addConstr(16 * hank_hours + 3 * mary_hours <= 152)
model.addConstr(16 * paul_hours + 3 * mary_hours <= 110)
model.addConstr(16 * hank_hours + 16 * paul_hours + 3 * mary_hours <= 110)
model.addConstr(17 * paul_hours + 2 * mary_hours <= 106)
model.addConstr(11 * hank_hours + 17 * paul_hours + 2 * mary_hours <= 106)
model.addConstr(7 * hank_hours + 21 * paul_hours <= 211)
model.addConstr(21 * paul_hours + 13 * mary_hours <= 159)
model.addConstr(7 * hank_hours + 21 * paul_hours + 13 * mary_hours <= 159)
model.addConstr(5 * paul_hours + 7 * mary_hours <= 210)
model.addConstr(15 * hank_hours + 5 * paul_hours <= 202)
model.addConstr(15 * hank_hours + 5 * paul_hours + 7 * mary_hours <= 202)
model.addConstr(4 * paul_hours + 7 * mary_hours <= 107)
model.addConstr(2 * hank_hours + 4 * paul_hours + 7 * mary_hours <= 107)

# Optimize model
model.optimize()

if model.status == gp.GRB.OPTIMAL:
    print(f"Optimal objective value: {model.objVal}")
    print(f"Hank's hours: {hank_hours.x}")
    print(f"Paul's hours: {paul_hours.x}")
    print(f"Mary's hours: {mary_hours.x}")
elif model.status == gp.GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print(f"Optimization terminated with status {model.status}")

```