To solve this problem, we will define two decision variables: `donuts` (the number of batches of donuts to produce) and `cookies` (the number of batches of cookies to produce). The objective is to maximize the total profit.

The constraints are based on the time availability for Jake and Jill. For Jake, the total time spent on producing donuts and cookies should not exceed 200 minutes. Since each batch of donuts takes 20 minutes and each batch of cookies takes 5 minutes of Jake's time, we can express this constraint as `20*donuts + 5*cookies <= 200`. Similarly, for Jill, with each batch of donuts taking 10 minutes and each batch of cookies taking 25 minutes, the total time should not exceed 300 minutes: `10*donuts + 25*cookies <= 300`.

Given that profit per batch of donuts is $20 and per batch of cookies is $15, our objective function to maximize profit would be `20*donuts + 15*cookies`.

Here's how we translate these constraints and the objective into Gurobi code in Python:

```python
from gurobipy import *

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

# Define decision variables
donuts = m.addVar(vtype=GRB.INTEGER, name="donuts")
cookies = m.addVar(vtype=GRB.INTEGER, name="cookies")

# Objective function: Maximize profit
m.setObjective(20*donuts + 15*cookies, GRB.MAXIMIZE)

# Constraints
m.addConstr(20*donuts + 5*cookies <= 200, "Jake_Time")
m.addConstr(10*donuts + 25*cookies <= 300, "Jill_Time")

# Non-negativity constraints (assuming you can't produce negative batches)
m.addConstr(donuts >= 0, "Non_Neg_Donuts")
m.addConstr(cookies >= 0, "Non_Neg_Cookies")

# Optimize the model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found.")
    print(f"Donuts to produce: {donuts.x}")
    print(f"Cookies to produce: {cookies.x}")
    print(f"Maximum Profit: ${20*donuts.x + 15*cookies.x}")
else:
    print("No optimal solution found. The model may be infeasible.")
```