To solve the optimization problem described, we first need to convert the natural language description into a symbolic representation. This involves defining variables, an objective function, and constraints.

Let's define:
- $x_1$ as the number of chairs produced,
- $x_2$ as the number of nightstands produced.

The objective is to maximize profits. Given that each chair sold yields a profit of $300 and each nightstand sold yields a profit of $500, the objective function can be represented as:
\[ \text{Maximize: } 300x_1 + 500x_2 \]

Now, let's consider the constraints based on the time availability for John and William:
- Each chair takes 2 hours of John's time, so $x_1$ chairs will take $2x_1$ hours.
- Each nightstand takes 5 hours of John's time, so $x_2$ nightstands will take $5x_2$ hours.
- John has 30 hours available, leading to the constraint: $2x_1 + 5x_2 \leq 30$.
- Each chair takes 4 hours of William's time, and each nightstand also takes 4 hours of William's time.
- William has 40 hours available, leading to the constraint: $4x_1 + 4x_2 \leq 40$.

Additionally, we have non-negativity constraints since the number of chairs and nightstands cannot be negative:
\[ x_1 \geq 0 \]
\[ x_2 \geq 0 \]

Thus, the symbolic representation of the problem is:
```json
{
    'sym_variables': [('x1', 'number of chairs'), ('x2', 'number of nightstands')],
    'objective_function': '300*x1 + 500*x2',
    'constraints': ['2*x1 + 5*x2 <= 30', '4*x1 + 4*x2 <= 40', 'x1 >= 0', 'x2 >= 0']
}
```

To solve this problem using Gurobi in Python, we'll write the following code:

```python
from gurobipy import *

# Create a new model
model = Model("Woodshop_Problem")

# Define variables
x1 = model.addVar(lb=0, vtype=GRB.INTEGER, name="chairs")
x2 = model.addVar(lb=0, vtype=GRB.INTEGER, name="nightstands")

# Set the objective function
model.setObjective(300*x1 + 500*x2, GRB.MAXIMIZE)

# Add constraints
model.addConstr(2*x1 + 5*x2 <= 30, "John's_time")
model.addConstr(4*x1 + 4*x2 <= 40, "William's_time")

# Optimize the model
model.optimize()

# Print the solution
if model.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Number of chairs: {x1.x}")
    print(f"Number of nightstands: {x2.x}")
    print(f"Maximum profit: ${300*x1.x + 500*x2.x}")
else:
    print("No optimal solution found")
```