/*
* Copyright 2009 Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package de.timefinder.core.io.winqd;
import de.timefinder.data.algo.DataPoolSettings;
import de.timefinder.algo.constraint.DifferentDayConstraint;
import de.timefinder.algo.constraint.MinGapsConstraint;
import de.timefinder.core.io.text.ImportText;
import de.timefinder.data.DataPool;
import de.timefinder.data.Event;
import de.timefinder.data.Location;
import de.timefinder.data.Person;
import de.timefinder.data.Role;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class ImportWinQD extends ImportText {
private Log logger = LogFactory.getLog(getClass());
private String courseInstancesFile = "Kurse.txt";
private BufferedReader courseInstancesReader;
private String eventAssignmentFile = "Belegung.txt";
private BufferedReader eventAssignmentReader;
private Map<String, WinQDCourse> courses = new TreeMap();
private Map<String, Person> students = new TreeMap<String, Person>();
private Map<String, Person> teachers = new TreeMap<String, Person>();
private int semester = 11;
public ImportWinQD(DataPool dataPool, DataPoolSettings settings, File folder) {
super(dataPool, settings, folder);
}
@Override
protected void initReaders() {
try {
eventAssignmentReader = new BufferedReader(
new InputStreamReader(new FileInputStream(new File(getFolder(), eventAssignmentFile)), "cp1252"));
} catch (Exception ex) {
logger.fatal("Konnte Datei nicht öffnen " + eventAssignmentFile, ex);
}
try {
courseInstancesReader = new BufferedReader(
new InputStreamReader(new FileInputStream(new File(getFolder(), courseInstancesFile)), "cp1252"));
} catch (Exception ex) {
logger.fatal("Konnte Datei nicht öffnen " + courseInstancesFile, ex);
}
}
public void setSemester(int semester) {
this.semester = semester;
}
@Override
protected void readData() {
String line = null;
int lineCounter = 0;
int dataCounter = -1;
Person currentPerson = null;
int indexKind = -1;
int indexShortName = -1;
int indexHoursCourse = -1;
int indexHours11 = -1;
int indexHours12 = -1;
try {
while ((line = eventAssignmentReader.readLine()) != null) {
lineCounter++;
if (dataCounter >= 0)
dataCounter++;
if (line.contains("Schuljahr")) {
dataCounter = 0;
} else if (dataCounter == 3) {
currentPerson = new Person();
currentPerson.setName(line.trim());
addStudent(currentPerson);
} else if (dataCounter == 4) {
currentPerson.setDescription(line.trim());
} else if (dataCounter == 6 || line.contains("Angebot*")) {
dataCounter = 6;
indexKind = line.indexOf("Angebot*");
indexHoursCourse = line.indexOf("Std");
indexHours11 = line.indexOf("11/1");
indexHours12 = line.indexOf("12/1");
indexShortName = line.indexOf("Wahl");
} else if (dataCounter > 6 && dataCounter < 54) {
if (currentPerson == null)
logger.fatal("Person ist null!? " + lineCounter);
String courseLongName = null;
String shortName = null;
Integer courseHours = null;
Integer hours11 = null;
Integer hours12 = null;
if (indexKind >= 0 && indexKind < line.length()) {
courseLongName = line.substring(0, indexKind).trim();
if (courseLongName.startsWith("PRO"))
courseLongName = courseLongName.split(" ")[1];
if (courseLongName.length() < 1)
continue;
}
// TODO every seminar is different!
if (line.contains("Wissenschaftspropädeutisches")) {
indexHoursCourse += 4;
indexHours11 += 4;
}
if (indexHoursCourse >= 0 && indexHours11 >= 0
&& indexHours11 < line.length() && indexHoursCourse < indexHours11) {
String subStr = line.substring(indexHoursCourse, indexHours11).trim();
if (subStr.length() > 0)
courseHours = Integer.parseInt(subStr.split(" ")[0].trim());
}
if (indexHours11 >= 0 && indexHours12 >= 0
&& indexHours12 < line.length() && indexHours11 < indexHours12) {
String subStr = line.substring(indexHours11, indexHours12).trim();
if (subStr.length() > 0)
hours11 = Integer.parseInt(subStr.split(" ")[0].trim());
}
if (indexHours12 >= 0 && indexShortName >= 0
&& indexShortName < line.length() && indexHours12 < indexShortName) {
String subStr = line.substring(indexHours12, indexShortName).trim();
if (subStr.length() > 0)
hours12 = Integer.parseInt(subStr.split(" ")[0].trim());
}
if (indexShortName >= 0 && indexShortName < line.length()) {
String subStr = line.substring(indexShortName).trim();
if (subStr.length() > 0)
shortName = subStr.split(":")[0].trim();
}
if (shortName == null)
continue;
if ("Kunst PRO".equals(courseLongName)) {
// "Kunst PRO" und Kunst können als identisch angesehen werden
// wer Kunst PRO wählt, geht in den normalen Kunstunterricht
shortName = "KU";
} else if (courseLongName.startsWith("Projekt-Seminar")) {
// jeder Schüler hat zur gleichen Zeit ein Seminar
shortName = "P" + shortName;
} else if (courseLongName.startsWith("Wissenschaftspropädeutisches Seminar")) {
shortName = "W" + shortName;
}
WinQDCourse course = courses.get(shortName);
if (course == null) {
course = new WinQDCourse();
course.setShortName(shortName);
course.setName(courseLongName);
if (courseHours != null)
course.setHours(getHours(courseHours));
addCourse(course);
}
Integer hours = null;
if (semester == 11 && hours11 != null)
hours = hours11;
if (semester == 12 && hours12 != null)
hours = hours12;
if (hours != null)
course.getStudents().add(currentPerson);
}
} // while all lines of eventAssignmentFile
logger.info(courses.size() + " Kurse gefunden!");
} catch (Exception ex) {
throw new RuntimeException(eventAssignmentFile + " - Fehler in Zeile " + lineCounter
+ " (Datensatzzeile:" + dataCounter + ") : " + line, ex);
}
lineCounter = 0;
dataCounter = - 1;
try {
while ((line = courseInstancesReader.readLine()) != null) {
lineCounter++;
if (dataCounter >= 0)
dataCounter++;
if (line.contains("Kursleiter")) {
dataCounter = 0;
continue;
}
if (dataCounter < 2)
continue;
// TNZ = Teilnehmerzahl
//0 1 2 3 4 5 6 7 8
//Nr|Kurs |Art |Kursleiter |Fach 11|Fach 12|TNZ|TZ1|TZ2|..
String[] columns = line.split("\\|");
if (columns.length != 10)
continue;
int no = Integer.parseInt(columns[0].trim());
if (no == 0)
continue;
String instanceName = columns[1].trim();
String courseShortName = columns[4].trim();
if (instanceName.startsWith("1W") || instanceName.startsWith("1P")) {
int fromIndex = indexOfLower(1, instanceName);
int index = indexOfNumber(1, instanceName);
courseShortName = instanceName.substring(fromIndex, index).toUpperCase();
// geografie
if ("EK".equals(courseShortName))
courseShortName = "GEO";
// chemie
if ("CH".equals(courseShortName))
courseShortName = "C";
// sport
if ("SP".equals(courseShortName))
courseShortName = "SPO";
if (fromIndex > 1)
courseShortName = instanceName.charAt(fromIndex - 1) + courseShortName;
}
WinQDCourse course = courses.get(courseShortName);
if (course == null) {
logger.fatal("Missing course:" + courseShortName);
continue;
}
String name = columns[3].trim();
currentPerson = teachers.get(name);
if (currentPerson == null) {
currentPerson = new Person();
currentPerson.setName(name);
addTeacher(currentPerson);
}
course.addInstance(instanceName, currentPerson);
}
} catch (Exception ex) {
throw new RuntimeException(courseInstancesFile + " - Fehler in Zeile " + lineCounter
+ " (Datensatzzeile:" + dataCounter + ") : " + line, ex);
}
logger.info(courses.values());
fillSimpleDataPool();
}
public void fillSimpleDataPool() {
Map<Long, Object> lDao = simpleDaos.get(Location.class);
Map<Long, Object> eDao = simpleDaos.get(Event.class);
Map pDao = simpleDaos.get(Person.class);
long pCounter = 0;
long eCounter = 0;
for (Person p : students.values()) {
pDao.put(pCounter++, p);
}
for (Person p : teachers.values()) {
pDao.put(pCounter++, p);
}
for (long i = 0; i < 20; i++) {
Location loc = new Location("Raum " + i, 40);
lDao.put(i, loc);
}
// courses
for (WinQDCourse course : courses.values()) {
if (course.getInstances().size() == 0) {
logger.fatal("No instances found for:" + course);
continue;
}
if (course.getStudents().size() == 0) {
logger.warn("No students found for:" + course);
continue;
}
splitPersons(course.getStudents(), course.getInstances());
// instances
for (WinQDCourseInstance cInstance : course.getInstances()) {
// appointments
List<Event> eventsForDiffDay = new ArrayList<Event>();
DifferentDayConstraint diffDayConstr = new DifferentDayConstraint(settings, eventsForDiffDay);
diffDayConstr.setWeight(1000.0f);
int appointmentCounter = 0;
for (Integer integ : course.getHours()) {
Event ev = newEvent();
ev.setStart(0);
ev.setDuration(integ);
ev.setName(course.getName() + " (" + cInstance.getName()
+ "/" + course.getInstances().size() + ")\n"
+ ++appointmentCounter + "/" + course.getHours().size());
eDao.put(eCounter++, ev);
eventsForDiffDay.add(ev);
cInstance.getTeacher().addEvent(ev, Role.TEACHER, true);
for (Person p : cInstance.getStudents()) {
p.addEvent(ev, Role.STUDENT, true);
}
}
for (Person p : cInstance.getStudents()) {
MinGapsConstraint minGapConstr = new MinGapsConstraint(settings, p.getEvents());
minGapConstr.setWeight(1.0f);
p.putConstraint(minGapConstr);
}
// bind the constraint only if it makes sense
if (eventsForDiffDay.size() > 1) {
for (Event ev : eventsForDiffDay) {
ev.putConstraint(diffDayConstr);
}
}
}
}
}
public int getTeacherNumber() {
return teachers.size();
}
public int getStudentsNumber() {
return students.size();
}
WinQDCourse getCourse(String string) {
return courses.get(string);
}
Collection<WinQDCourse> getCourses() {
return courses.values();
}
void addCourse(WinQDCourse course) {
courses.put(course.getShortName(), course);
}
void addStudent(Person currentPerson) {
students.put(currentPerson.getName(), currentPerson);
}
void addTeacher(Person currentPerson) {
teachers.put(currentPerson.getName(), currentPerson);
}
public static List<Integer> getHours(int hours) {
int no = hours / 2;
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < no; i++) {
list.add(2);
}
int rest = hours % 2;
if (rest != 0)
list.add(rest);
return list;
}
/**
* Spreads the list of persons specified with students into the instances
*/
static void splitPersons(Collection<Person> students,
Collection<WinQDCourseInstance> instances) {
int instancesSize = instances.size();
if (instancesSize == 0)
return;
int personsPerInstance = students.size() / instancesSize;
if (students.size() % instancesSize != 0)
personsPerInstance++;
WinQDCourseInstance current = null;
Iterator<WinQDCourseInstance> iter = instances.iterator();
int i = 0;
for (Person person : students) {
if (i++ % personsPerInstance == 0) {
current = iter.next();
}
current.getStudents().add(person);
}
if (iter.hasNext())
throw new IllegalStateException("Instance-Iterator has additional entries:" + iter.next()
+ ", instances:" + instancesSize + ", students:" + students.size());
}
static int indexOfNumber(int from, String instanceName) {
for (int ii = from; ii < instanceName.length(); ii++) {
if (Character.isDigit(instanceName.charAt(ii)))
return ii;
}
return -1;
}
static int indexOfLower(int from, String instanceName) {
for (int ii = from; ii < instanceName.length(); ii++) {
if (Character.isLowerCase(instanceName.charAt(ii)))
return ii;
}
return -1;
}
}