Here's how we can formulate this problem and the corresponding Gurobi code:

**Decision Variables:**

* `c`: Number of chairs produced
* `n`: Number of nightstands produced

**Objective Function:**

Maximize profit: `300c + 500n`

**Constraints:**

* **John's time:** `2c + 5n <= 30`
* **William's time:** `4c + 4n <= 40`
* **Non-negativity:** `c >= 0`, `n >= 0`


```python
import gurobipy as gp

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

# Create decision variables
c = model.addVar(vtype=gp.GRB.INTEGER, name="chairs")
n = model.addVar(vtype=gp.GRB.INTEGER, name="nightstands")

# Set objective function
model.setObjective(300 * c + 500 * n, gp.GRB.MAXIMIZE)

# Add constraints
model.addConstr(2 * c + 5 * n <= 30, "john_time")
model.addConstr(4 * c + 4 * n <= 40, "william_time")

# Optimize model
model.optimize()

# Print results
if model.status == gp.GRB.OPTIMAL:
    print(f"Optimal Solution Found:")
    print(f"Number of chairs to produce: {c.x}")
    print(f"Number of nightstands to produce: {n.x}")
    print(f"Maximum profit: {model.objVal}")
elif model.status == gp.GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print(f"Optimization terminated with status {model.status}")

```
