I'm trying to schedule sports matches into timeslots and would like to not have empty timeslots during the day (so finish as early as possible). I think isEqual and isNotEqual should help, but stuck in the syntax of the Python version. I think I'm close (relevant code below)
in domain.py
@problem_fact
class Timeslot:
def __init__(self, id, match_date, date_str, start_time, end_time):
self.id = id
self.match_date = match_date
self.date_str = date_str # java does not have date or datetime equivalent so str also needed
self.start_time = start_time
self.end_time = end_time
@planning_id
def get_id(self):
return self.id
def __str__(self):
return (
f"Timeslot("
f"id={self.id}, "
f"match_date={self.match_date}, "
f"date_str={self.date_str}, "
f"start_time={self.start_time}, "
f"end_time={self.end_time})"
in constraints.py
def fill_pitches_from_start(constraint_factory):
# A pitch should not be empty if possible
return constraint_factory \
.from_(TimeslotClass).ifNotExists(MatchClass, Joiners.equal(Timeslot.get_id(), Match.get_timeslot() ) ) \
.join(TimeslotClass).ifExists(MatchClass, Joiners.equal(Timeslot.get_id(), Match.get_timeslot())) \
.filter(lambda slot1, slot2: datetime.combine(slot1.date_str, slot1.timeslot.start_time) < datetime.combine(slot2.date_str, slot2.start_time) ) \
.penalize("Pitch Empty with Later pitches populated", HardSoftScore.ofSoft(10))
This generates and expected error: TypeError: get_id() missing 1 required positional argument: 'self'
But I can't work out the correct syntax - perhaps using lambda?
You are close; this should work:
def fill_pitches_from_start(constraint_factory):
# A pitch should not be empty if possible
return constraint_factory \
.forEach(TimeslotClass).ifNotExists(MatchClass, Joiners.equal(lambda timeslot: timeslot.get_id(), lambda match: match.get_timeslot() ) ) \
.join(TimeslotClass).ifExists(MatchClass, Joiners.equal(lambda timeslot1, timeslot2: timeslot2.get_id(), lambda match: match.get_timeslot())) \
.filter(lambda slot1, slot2: datetime.combine(slot1.date_str, slot1.timeslot.start_time) < datetime.combine(slot2.date_str, slot2.start_time) ) \
.penalize("Pitch Empty with Later pitches populated", HardSoftScore.ofSoft(10))
It can probably be improved using Joiners.lessThan
, but that would require an update to optapy
first so Joiners can work with any Python type. (Will update this answer when optapy
is updated to support said feature).