Here's the Gurobi code to solve the optimization problem:

```python
from gurobipy import Model, GRB

# Create a new model
model = Model("Work Allocation")

# Create variables
peggy = model.addVar(vtype=GRB.INTEGER, name="peggy")  # Integer hours for Peggy
paul = model.addVar(vtype=GRB.CONTINUOUS, name="paul")  # Continuous hours for Paul
hank = model.addVar(vtype=GRB.INTEGER, name="hank")  # Integer hours for Hank

# Set objective function
model.setObjective(7 * peggy + 3 * paul + 3 * hank, GRB.MAXIMIZE)

# Add constraints
model.addConstr(peggy * 1 + paul * 1 + hank * 8 >= 12, "c1")  # Minimum combined productivity
model.addConstr(peggy * 3 + hank * 2 <= 27, "c2")  # Paperwork competence (Peggy & Hank)
model.addConstr(paul * 7 + hank * 2 <= 27, "c3")  # Paperwork competence (Paul & Hank)
model.addConstr(peggy * 3 + paul * 7 <= 34, "c4")  # Paperwork competence (Peggy & Paul)
model.addConstr(peggy * 3 + paul * 7 + hank * 2 <= 34, "c5")  # Paperwork competence (All)
model.addConstr(paul * 1 + hank * 8 <= 29, "c6")  # Productivity (Paul & Hank)
model.addConstr(peggy * 1 + paul * 1 <= 33, "c7")  # Productivity (Peggy & Paul)
model.addConstr(peggy * 1 + paul * 1 + hank * 8 <= 33, "c8")  # Productivity (All)


# Optimize model
model.optimize()

# Print results
if model.status == GRB.OPTIMAL:
    print('Obj: %g' % model.objVal)
    print('Peggy:', peggy.x)
    print('Paul:', paul.x)
    print('Hank:', hank.x)
elif model.status == GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print(f"Optimization ended with status {model.status}")

```
