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

Let's define:
- $x_1$ as the number of bouquets of sunflowers sold.
- $x_2$ as the number of bouquets of roses sold.

The objective function, which aims to maximize profit, can be represented as:
\[7x_1 + 12x_2\]

The constraints are based on the available time for clipping and packaging, as well as the contractual obligation to sell at least 30 bouquets of sunflowers. These can be represented as follows:
1. Clipping time constraint: $4x_1 + 5x_2 \leq 1200$
2. Packaging time constraint: $3x_1 + 7x_2 \leq 800$
3. Sunflower sales contract: $x_1 \geq 30$

Thus, the symbolic representation of the problem is:
```json
{
  'sym_variables': [('x1', 'number of bouquets of sunflowers'), ('x2', 'number of bouquets of roses')],
  'objective_function': '7*x1 + 12*x2',
  'constraints': ['4*x1 + 5*x2 <= 1200', '3*x1 + 7*x2 <= 800', 'x1 >= 30']
}
```

Now, let's implement this problem using Gurobi in Python:

```python
from gurobipy import *

# Create a new model
m = Model("Tom's Florist")

# Define variables
x1 = m.addVar(lb=30, name="sunflowers")  # Number of bouquets of sunflowers, lower bound is 30 due to contract
x2 = m.addVar(name="roses")  # Number of bouquets of roses

# Set the objective function: maximize profit
m.setObjective(7*x1 + 12*x2, GRB.MAXIMIZE)

# Add constraints
m.addConstr(4*x1 + 5*x2 <= 1200, name="clipping_time")
m.addConstr(3*x1 + 7*x2 <= 800, name="packaging_time")

# Optimize the model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Sunflowers: {x1.x}")
    print(f"Roses: {x2.x}")
    print(f"Maximum Profit: {m.objVal}")
else:
    print("No optimal solution found")
```