To solve this problem, we first need to convert the natural language description into a symbolic representation. Let's denote the number of apple pies as $x_1$ and the number of peach pies as $x_2$. The objective is to maximize profit, which can be represented as $7x_1 + 8x_2$, given the constraints:

- $x_1 \geq 15$ (at least 15 apple pies must be sold)
- $x_2 \geq 12$ (at least 12 peach pies must be sold)
- $x_1 \leq 20$ (no more than 20 apple pies can be made)
- $x_2 \leq 18$ (no more than 18 peach pies can be made)
- $x_1 + x_2 \leq 30$ (total number of pies cannot exceed 30 due to pie crust limitations)

The symbolic representation of the problem is:
```json
{
    'sym_variables': [('x1', 'number of apple pies'), ('x2', 'number of peach pies')],
    'objective_function': '7*x1 + 8*x2',
    'constraints': ['x1 >= 15', 'x2 >= 12', 'x1 <= 20', 'x2 <= 18', 'x1 + x2 <= 30']
}
```

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

```python
from gurobipy import *

# Create a model
m = Model("Pie_Shop_Profit")

# Define variables
x1 = m.addVar(vtype=GRB.INTEGER, name="apple_pies")
x2 = m.addVar(vtype=GRB.INTEGER, name="peach_pies")

# Set objective function
m.setObjective(7*x1 + 8*x2, GRB.MAXIMIZE)

# Add constraints
m.addConstr(x1 >= 15, "min_apple_pies")
m.addConstr(x2 >= 12, "min_peach_pies")
m.addConstr(x1 <= 20, "max_apple_pies")
m.addConstr(x2 <= 18, "max_peach_pies")
m.addConstr(x1 + x2 <= 30, "total_pies_limit")

# Optimize the model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Number of apple pies: {x1.x}")
    print(f"Number of peach pies: {x2.x}")
    print(f"Max Profit: {m.objVal}")
else:
    print("No optimal solution found")
```