Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.*;

public class TimetableApp {

//TODO andrea schuman name udpate in excel file, update in the db as well or at least check over this
private static final Logger LOGGER = LoggerFactory.getLogger(TimetableApp.class);
private static final String YAML_FILE_PATH = "constants/config.yaml";
private static final boolean PRINT_DETAILED_SUMMARY = true;
Expand Down Expand Up @@ -102,9 +102,9 @@ public static void main(String[] args) throws Exception{
});
}

// storeResults(solution);
ResultSaver resultSaver = new ResultSaver(solution);
resultSaver.saveSolution();
resultSaver.teacherTimesToJson();

return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import org.acme.schooltimetabling.TimetableApp;
import org.acme.schooltimetabling.apiCalls.teacherEndpoint.TeacherRecord;
import org.acme.schooltimetabling.constants.Constants;
import org.acme.schooltimetabling.constants.Days;
import org.acme.schooltimetabling.constants.Preference;
import org.acme.schooltimetabling.domain.teacher.Faculty;
import org.acme.schooltimetabling.domain.teacher.Teacher;
import org.acme.schooltimetabling.helperClasses.BitSetHelper;
Expand Down Expand Up @@ -98,7 +100,8 @@ else if(val.equalsIgnoreCase("acceptable")){
conflict.or(bsRep);
}
}
Teacher teacher = new Teacher(TeacherGenerator.getNextTeacherID(), nonCanonName, preferences, acceptable, conflict);
Teacher teacher = new Teacher(TeacherGenerator.getNextTeacherID(), Constants.TEACHER_NAME_TO_CANON.get(nonCanonName),
preferences, acceptable, conflict, Preference.parsePref((String) extraFields.get("gap")));
if(teacherRecord.isFaculty()) teacher = new Faculty(teacher);
return teacher;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -23,6 +24,10 @@
* fit anywhere else, like a class.
*/
public class Constants {
/**
* Global time formatter
*/
public static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("h:mma");
/**
* Set to True for testing. Helps by pass some checks
* */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.acme.schooltimetabling.constants;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public enum Preference {

AGREE, NEUTRAL, DISAGREE;

private static final Logger LOGGER = LoggerFactory.getLogger(Process.class);

/**
* <p>case insensitive maps ["", "disagree"] -> {@link #DISAGREE}; ["agree"] -> {@link #AGREE};
* ["neutral"] -> {@link #NEUTRAL}</p>
* <p>if the string doesn't match any of these options. The program will exit until resolved</p>
* @param pref String representation of preference
* @return enum representation or error/exits if no match is found
*/
public static Preference parsePref(String pref){
if("".equals(pref) || "disagree".equalsIgnoreCase(pref)) return Preference.DISAGREE;
else if("agree".equalsIgnoreCase(pref)) return Preference.AGREE;
else if("neutral".equalsIgnoreCase(pref)) return Preference.NEUTRAL;
LOGGER.error("When reading a survey unknown preference was encountered '{}'.... EXITING", pref);
System.exit(1);
throw new IllegalStateException(String.format("Unknown preference '%s' encountered while reading survey", pref));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ai.timefold.solver.core.api.domain.lookup.PlanningId;
import ai.timefold.solver.core.api.domain.variable.PlanningVariable;
import org.acme.schooltimetabling.constants.Constants;
import org.acme.schooltimetabling.constants.Days;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.Timeslot;
import org.acme.schooltimetabling.domain.teacher.Teacher;
Expand All @@ -12,7 +13,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Map;

@PlanningEntity(difficultyComparatorClass = LessonComparator.class)
//@PlanningEntity(comparator = LessonComparator.class)
Expand All @@ -37,7 +43,6 @@ public class Lesson {
public boolean hasLecture, hasLabAct;
public int lecHours, labActHours;
public Teacher teacherObj;
private Integer linker = null;


@PlanningVariable
Expand All @@ -55,38 +60,16 @@ private Lesson() {
* https://docs.timefold.ai/timefold-solver/latest/using-timefold-solver/modeling-planning-problems#planningId*/

/* Test factory methods */

/**
* No linker
*/
public static Lesson test_buildLesson(String Id, int lecSection, String courseName, String teacherName, String modifiers,
String courseConfig, int courseID, Teacher teacherObj, Timeslot timeslot, Room room){
return new Lesson(Id, lecSection, courseName, teacherName, modifiers, courseConfig, courseID, teacherObj
, timeslot, room);
}
/**
* with linker
*/
public static Lesson test_buildLesson(String Id, int lecSection, String courseName, String teacherName, String modifiers,
String courseConfig, int courseID, Teacher teacherObj, Timeslot timeslot, Room room,
Integer linker){
return new Lesson(Id, lecSection, courseName, teacherName, modifiers, courseConfig, courseID, teacherObj
, timeslot, room, linker);
}

/* Test constructor(s)*/

private Lesson(String Id, int lecSection, String courseName, String teacherName, String modifiers,
String courseConfig, int courseID, Teacher teacherObj, Timeslot timeslot, Room room, Integer linker){
this(Id, lecSection, courseName, teacherName, modifiers, courseConfig, courseID, teacherObj
, timeslot, room);
this.linker = linker;
}

private Lesson(String Id, int lecSection, String courseName, String teacherName, String modifiers,
String courseConfig, int courseID, Teacher teacherObj, Timeslot timeslot, Room room){
// /*calling normal constructor used during setup*/
// this(Id, lecSection, courseName, teacherName, modifiers, courseConfig, courseID, teacherObj, null);
/*the courseConfig stream is assumed to come in the format
* E-L-A where E is the number of lecture units, L is the number of
* lab units, and A is the number of activity units */
Expand Down Expand Up @@ -131,7 +114,7 @@ private Lesson(String Id, int lecSection, String courseName, String teacherName,
* @param teacherObj teacher object associated with the <i>teacherName</i>
*/
public Lesson(String Id, int lecSection, String courseName, String modifiers,
String courseConfig, Teacher teacherObj, Integer linker){
String courseConfig, Teacher teacherObj){
/*the courseConfig stream is assumed to come in the format
* E-L-A where E is the number of lecture units, L is the number of
* lab units, and A is the number of activity units */
Expand All @@ -156,7 +139,6 @@ public Lesson(String Id, int lecSection, String courseName, String modifiers,
/*TODO check if we can delete this field*/
this.teacherName = teacherObj.getName();
this.modifiers = modifiers;
this.linker = linker;
}

/**
Expand Down Expand Up @@ -250,10 +232,6 @@ public Teacher getTeacherObj() {
return teacherObj;
}

public Integer getLinker(){
return linker;
}

public boolean isStudio(){
return Constants.STUDIO_STYLE_COURSES.contains(this.courseName);
}
Expand Down Expand Up @@ -313,4 +291,35 @@ public BitSet maskOutCmprs(){
copy.and(ScheduleConfig.getCompressOutMask());
return copy;
}

public List<Map<String, String>> toJson(){
List<Map<String, String>> res = new ArrayList<>();
if (timeslot == null) {
return res;
}

BitSet schedule = new BitSet();
schedule.or(timeslot.getAllTimesBitSet());
LocalTime BASE_TIME = LocalTime.of(7, 0);

for (Days day : Days.values()) {
int offset = BitSetHelper.DAY_OFFSET.get(day);
BitSet dayBits = schedule.get(offset, offset + BitSetHelper.MAX_BITS_PER_DAY);
int start = dayBits.nextSetBit(0);
while (start != -1) {

int end = dayBits.nextClearBit(start) - 1;
LocalTime startTime = BASE_TIME.plusMinutes(start * 30L);
LocalTime endTime = BASE_TIME.plusMinutes((end + 1) * 30L);
res.add(Map.of(
"day", day.name(),
"start", startTime.format(Constants.TIME_FMT),
"end", endTime.format(Constants.TIME_FMT)
));
start = dayBits.nextSetBit(end + 1);
}
}

return res;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.acme.schooltimetabling.TimetableApp;
import org.acme.schooltimetabling.constants.Constants;
import org.acme.schooltimetabling.constants.Days;
import org.acme.schooltimetabling.constants.Preference;
import org.acme.schooltimetabling.helperClasses.BitSetHelper;
import org.acme.schooltimetabling.helperClasses.ParseInput;
import org.acme.schooltimetabling.helperClasses.ScheduleConfig;
Expand Down Expand Up @@ -78,6 +79,10 @@ public Faculty(int id, String name, BitSet preferences, BitSet acceptable, BitSe
super(id, name, preferences, acceptable, conflict);
}

public Faculty(int id, String name, BitSet preferences, BitSet acceptable, BitSet conflict, Preference gap) {
super(id, name, preferences, acceptable, conflict, gap);
}

public Faculty(Teacher teacher){
super(teacher);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.acme.schooltimetabling.domain.teacher;

import org.acme.schooltimetabling.constants.Preference;

import java.util.BitSet;

public class Teacher {
Expand All @@ -12,6 +14,12 @@ public class Teacher {
public BitSet preferences;
/*impossible timeslots*/
public BitSet conflict;
private Preference gapPref;

public Teacher(int id, String name, BitSet preferences, BitSet acceptable, BitSet conflict, Preference gapPref){
this(id, name, preferences, acceptable, conflict);
this.gapPref = gapPref;
}

public Teacher(int id, String name, BitSet preferences, BitSet acceptable, BitSet conflict) {
this.id = id;
Expand All @@ -27,6 +35,7 @@ protected Teacher(Teacher copyMe){
this.preferences = (BitSet) copyMe.preferences.clone();
this.acceptable = (BitSet) copyMe.acceptable.clone();
this.conflict = (BitSet) copyMe.conflict.clone();
this.gapPref = copyMe.gapPref;
}

public int getId() {
Expand All @@ -48,4 +57,6 @@ public BitSet getPreferences() {
public BitSet getConflict() {
return conflict;
}

public Preference getGapPref(){ return gapPref; }
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
package org.acme.schooltimetabling.helperClasses;
import org.acme.schooltimetabling.constants.Constants;
import org.acme.schooltimetabling.constants.Days;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.BitSet;
import java.util.EnumSet;
import java.util.Map;

public class BitSetHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(BitSetHelper.class);
private static final int MONDAY_OFFSET = 0;
private static final int TUESDAY_OFFSET = 30;
private static final int WEDNESDAY_OFFSET = 60;
private static final int THURSDAY_OFFSET = 90;
private static final int FRIDAY_OFFSET = 120;
public static final int MONDAY_OFFSET = 0;
public static final int TUESDAY_OFFSET = 30;
public static final int WEDNESDAY_OFFSET = 60;
public static final int THURSDAY_OFFSET = 90;
public static final int FRIDAY_OFFSET = 120;
private static final int NUM_OF_BITS = 150;
private static final int PRIME_TIME_DAY_START_OFFSET = 4;
private static final int PRIME_TIME_DAY_END_OFFSET = 16;
private static final int MAX_BITS_PER_DAY = 30;
public static final int MAX_BITS_PER_DAY = 30;
public static final BitSet NON_PRIME_TIME_MASK;
public static final BitSet PRIME_TIME_MASK;
public static final Map<Days, Integer> DAY_OFFSET = Map.of(
Days.MONDAY, BitSetHelper.MONDAY_OFFSET,
Days.TUESDAY, BitSetHelper.TUESDAY_OFFSET,
Days.WEDNESDAY, BitSetHelper.WEDNESDAY_OFFSET,
Days.THURSDAY, BitSetHelper.THURSDAY_OFFSET,
Days.FRIDAY, BitSetHelper.FRIDAY_OFFSET
);

static {
PRIME_TIME_MASK = new BitSet();
Expand Down Expand Up @@ -179,4 +189,28 @@ public static BitSet old_surveyBitset(String header) throws Exception{

return bitset;
}


public static BitSet timeJsonToBs(Map<String, String> time) {
if (time == null || time.get("day") == null || time.get("start") == null || time.get("end") == null) {
throw new IllegalArgumentException("Time map must contain day, start, and end");
}
LocalTime start = LocalTime.parse(time.get("start"), Constants.TIME_FMT);
LocalTime end = LocalTime.parse(time.get("end"), Constants.TIME_FMT);
LocalTime earliest = LocalTime.of(7, 0);
LocalTime latest = LocalTime.of(22, 0);
if (start.isBefore(earliest) || end.isAfter(latest) || !end.isAfter(start)) {
throw new IllegalArgumentException("Times must be between 7:00AM and 10:00PM and end after start");
}
if ((start.getMinute() % 30) != 0 || (end.getMinute() % 30) != 0) {
throw new IllegalArgumentException("Times must fall exactly on the hour or half-hour");
}
Days day = Days.valueOf(time.get("day").toUpperCase());
int startBlock = (int) ChronoUnit.MINUTES.between(earliest, start) / 30;
int endBlock = (int) ChronoUnit.MINUTES.between(earliest, end) / 30;
int dayOffset = DAY_OFFSET.get(day);
BitSet bitSet = new BitSet();
bitSet.set(dayOffset + startBlock, dayOffset + endBlock);
return bitSet;
}
}
Loading
Loading