/// <summary> /// Divides list of ExportLecture(s) to groups of overlapping lectures. /// </summary> /// <returns> List of groups (group = another list) of ExportLecture(s) </returns> /// <param name="lectures">List of ExportLecture(s) to divide.</param> public List<List<ExportLecture>> divideToGroups(List<ExportLecture> lectures) { List<List<ExportLecture>> groups = new List<List<ExportLecture>>(); groups.Add(new List<ExportLecture>()); foreach (ExportLecture l in lectures) { var lastgroup = groups[groups.Count - 1]; if (lastgroup.Count < 1) { groups[groups.Count - 1].Add(l); } //Is lecture l starting sooner than end time of the last lecture in last group (with 10 minutes toleration)? else if (DateTime.Compare(l.StartTime, lastgroup.Max(lec => lec.StartTime + lec.Length - new TimeSpan(0, 10, 0))) < 0 && l.Day == lastgroup[lastgroup.Count - 1].Day) { //All lectures intersecting with l IEnumerable<ExportLecture> intersecting = lastgroup.Where(lec => (DateTime.Compare(l.StartTime, lec.StartTime + lec.Length - new TimeSpan(0, 10, 0)) < 0)); //Only have to worry about 3 and more lectures in one group //If lecture l is intersecting with all of lectures in lastgroup then it belongs to the group if (lastgroup.Count <= 1 || intersecting.Count() == lastgroup.Count) { lastgroup.Add(l); } //Otherwise create a new group with fake lectures else { List<ExportLecture> newGroup = new List<ExportLecture>(); //Fake lectures from all last group lectures foreach (ExportLecture lastGroupLec in lastgroup) { ExportLecture fakeLecture = null; //Treat intersecting lectures as not null if (intersecting.Contains(lastGroupLec)) { fakeLecture = new ExportLecture(null, lastGroupLec.Day, lastGroupLec.StartTime, lastGroupLec.Length, null, null, null, true); } //Non-intersecting lecture are null = empty slots newGroup.Add(fakeLecture); } //Find first empty slot in newGroup (null lecture) and swap with lecture l int index = newGroup.FindIndex(lecture => lecture == null); newGroup[index] = l; //Finally remove all empty slots and add the newGroup to the groups newGroup.RemoveAll(lecture => lecture == null); groups.Add(newGroup); lastgroup = newGroup; } } else { groups.Add(new List<ExportLecture>()); lastgroup = groups[groups.Count - 1]; lastgroup.Add(l); } } return groups; }
/// <summary> /// Generates string in iCal format. /// </summary> /// <returns> String following ICal format. </returns> /// <param name="lectures">List of lectures with IExportHodina interface to export.</param> /// <param name="semesterStart">Beginning of the semester. Only Year, Month and Day are used. </param> /// <param name="semesterEnd">End of the semester. Only Year, Month and Day are used. </param> public string generateICal(List<TimetableField> lectures, DateTime semesterStart, DateTime semesterEnd) { string header = "BEGIN:VCALENDAR" + System.Environment.NewLine + "VERSION:2.0" + System.Environment.NewLine + "PRODID:-//hacksw/handcal//NONSGML v1.0//EN" + System.Environment.NewLine; string body = ""; foreach (TimetableField lecture in lectures) { var exportLecture = new ExportLecture(lecture); body += generateEventFromExportHodina(exportLecture, semesterStart, semesterEnd); } string tail = "END:VCALENDAR"; return header + body + tail; }
public void TestGenerate() { ExportLecture a = new ExportLecture("PredmetA", DayOfWeek.Monday, new DateTime(1, 1, 1, 7, 30, 0), new TimeSpan(2, 0, 0), "OsobaA", "MistnostA", "#99999", true); ExportLecture b = new ExportLecture("PredmetB", DayOfWeek.Tuesday, new DateTime(1, 1, 1, 17, 0, 0), new TimeSpan(1, 0, 0), "OsobaB", "MistnostB", "#99999", true); var hodiny = new List<ExportLecture>(); hodiny.Add(a); hodiny.Add(b); DateTime semesterStart = new DateTime(2013, 9, 19); DateTime semesterEnd = new DateTime(2014, 2, 19); ICalGenerator gen = new ICalGenerator(); string actualOutput = gen.generateICal(hodiny, semesterStart, semesterEnd); string stamp = gen.dateTimeDateToICalString(DateTime.Today) + gen.hourToICalString(DateTime.Now.Hour, DateTime.Now.Minute); string semStartA = gen.dateTimeDateToICalString(gen.closestDayFromDateTime(semesterStart, a.Day)); string semStartB = gen.dateTimeDateToICalString(gen.closestDayFromDateTime(semesterStart, b.Day)); string semEnd = gen.dateTimeDateToICalString(semesterEnd); string goodOutput = "BEGIN:VCALENDAR" + System.Environment.NewLine + "VERSION:2.0" + System.Environment.NewLine + "PRODID:-//hacksw/handcal//NONSGML v1.0//EN" + System.Environment.NewLine + "BEGIN:VEVENT" + System.Environment.NewLine + "DTSTAMP:" + stamp + System.Environment.NewLine + "DTSTART:" + semStartA + "T073000" + System.Environment.NewLine + "DTEND:" + semStartA + "T093000" + System.Environment.NewLine + "RRULE:FREQ=WEEKLY;UNTIL=" + semEnd + "T000000Z" + System.Environment.NewLine + "SUMMARY:PredmetA" + System.Environment.NewLine + "LOCATION:MistnostA" + System.Environment.NewLine + "END:VEVENT" + System.Environment.NewLine + "BEGIN:VEVENT" + System.Environment.NewLine + "DTSTAMP:" + stamp + System.Environment.NewLine + "DTSTART:" + semStartB + "T170000" + System.Environment.NewLine + "DTEND:" + semStartB + "T180000" + System.Environment.NewLine + "RRULE:FREQ=WEEKLY;UNTIL=" + semEnd + "T000000Z" + System.Environment.NewLine + "SUMMARY:PredmetB" + System.Environment.NewLine + "LOCATION:MistnostB" + System.Environment.NewLine + "END:VEVENT" + System.Environment.NewLine + "END:VCALENDAR"; Assert.AreEqual<string>(goodOutput, actualOutput); }
private string generateEventFromExportHodina(ExportLecture lecture, DateTime semesterStart, DateTime semesterEnd) { string semStart = dateTimeDateToICalString( closestDayFromDateTime(semesterStart, lecture.Day)); string semEnd = dateTimeDateToICalString(semesterEnd); string head = "BEGIN:VEVENT" + System.Environment.NewLine; string stamp = "DTSTAMP:" + dateTimeDateToICalString(DateTime.Today) + hourToICalString(DateTime.Now.Hour, DateTime.Now.Minute) + System.Environment.NewLine; string start = "DTSTART:" + semStart + hourToICalString(lecture.StartTime.Hour, lecture.StartTime.Minute) + System.Environment.NewLine; string end = "DTEND:" + semStart + hourToICalString((lecture.StartTime + lecture.Length).Hour, (lecture.StartTime + lecture.Length).Minute) + System.Environment.NewLine; string repeat = "RRULE:FREQ=WEEKLY;UNTIL=" + semEnd + "T000000Z" + System.Environment.NewLine; string summarry = "SUMMARY:" + lecture.Name + System.Environment.NewLine; string loc = "LOCATION:" + lecture.Room + System.Environment.NewLine; string tail = "END:VEVENT" + System.Environment.NewLine; return head + stamp + start + end + repeat + summarry + loc + tail; }
private string RenderLecture(ExportLecture lecture, int split, int pos) { string res = ""; double height = 50.0 / split; double width = xSpan(lecture.Length); double x = xStartHour(lecture.StartTime); double y = yStartDay(lecture.Day); double dy = height * pos; //Borders res += String.Format(new NumberFormatInfo(), "<rect height=\"{0}\" width=\"{1}\" y=\"{2}\" x=\"{3}\" class=\"cardBack\" fill=\"#fff\"/>", height, width, y + dy, x) + System.Environment.NewLine; //x="{$x + 1}" y="{$y + $dy + 1}" width="{$width - 2}" height="{$height - 2}" res += String.Format(new NumberFormatInfo(), "<rect stroke-opacity=\"0.5\" fill-opacity=\"0\" height=\"{0}\" width=\"{1}\" stroke=\"{4}\" y=\"{2}\" x=\"{3}\" stroke-width=\"2\" class=\"cardFrame\"/>", height - 2, width - 2, y + dy + 1, x + 1, lecture.DepartementColor) + System.Environment.NewLine; if (split >= 3) { //Fills res += String.Format(new NumberFormatInfo(), "<rect height=\"{0}\" width=\"{1}\" y=\"{2}\" x=\"{3}\" class=\"cardTop\" fill=\"{4}\"/>", height / (4 - split), width, y + dy, x, lecture.DepartementColor) + System.Environment.NewLine; //Texts //x="{$x + $width div 2}" y="{$y + $dy + $height div 5 * 2 + 6}" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"stroke-width: 2px; stroke: #fff; stroke-linejoin: miter; font-size: 12px; font-weight: bold; text-anchor: middle; font-family: sans-serif;\">{2}</text>", y + dy + height / 5 * 2 + 6, x + width / 2, lecture.Name) + System.Environment.NewLine; //x="{$x + $width div 2}" y="{$y + $dy + $height div 5 * 2 + 6 }" //if red in krbalek then it have class=\"label periodic\" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"font-size: 12px; font-weight: bold; text-anchor: middle; font-family: sans-serif;\" class=\"label {3}\">{2}</text>", y + dy + height / 5 * 2 + 6, x + width / 2, lecture.Name, lecture.RegularLecture ? "" : "periodic") + System.Environment.NewLine; //Room //x="{$x + $width - 5}" y="{$y + $dy + $height - 3}" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"fill: #000; font-size: 9px; text-anchor: end; font-family: sans-serif;\" class=\"classroomText\">{2}</text>", y + dy + height - 5, x + width - 5, lecture.Room) + System.Environment.NewLine; //Lecturer //x="{$x + 5}" y="{$y + $dy + $height - 3}" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"fill: #000; font-size:9px; text-anchor: start; font-family: sans-serif;\" class=\"lecturerText\">{2}</text>", y + dy + height - 5, x + 5, lecture.Lecturer) + System.Environment.NewLine; } else { //Fills //x="{$x}" y="{$y + $dy}" width="{$width}" height="{$height - $height div (4 - $split)}" res += String.Format(new NumberFormatInfo(), "<rect height=\"{0}\" width=\"{1}\" y=\"{2}\" x=\"{3}\" class=\"cardTop\" fill=\"{4}\"/>", height - (height / (4 - split)), width, y + dy, x, lecture.DepartementColor) + System.Environment.NewLine; //x="{$x}" y="{$y + $dy + $height - $height * 1 div (4 - $split)}" width="{$width}" height="{$height div (4 - $split)}" res += String.Format(new NumberFormatInfo(), "<rect height=\"{0}\" width=\"{1}\" y=\"{2}\" x=\"{3}\" class=\"cardBottom\" fill=\"#fff\"/>", height / (4 - split), width, y + dy + (height - (height / (4 - split))), x) + System.Environment.NewLine; //Texts //x="{$x + $width div 2}" y="{$y + $dy + $height div 5 * 2 + 4 - $split * 2}" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"stroke-width: 2px; stroke: #fff; stroke-linejoin: miter; font-size: 12px; font-weight: bold; text-anchor: middle; font-family: sans-serif;\">{2}</text>", y + dy + height / 5 * 2 + 4 - split * 2, x + width / 2, lecture.Name) + System.Environment.NewLine; //x="{$x + $width div 2}" y="{$y + $dy + $height div 5 * 2 + 4 - $split * 2}" //if red in krbalek then it have class=\"label periodic\" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"font-size: 12px; font-weight: bold; text-anchor: middle; font-family: sans-serif;\" class=\"label {3}\">{2}</text>", y + dy + height / 5 * 2 + 4 - split * 2, x + width / 2, lecture.Name, lecture.RegularLecture ? "" : "periodic") + System.Environment.NewLine; //Room //x="{$x + $width - 5}" y="{$y + $dy + $height - 6 + $split}" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"fill: #000; font-size: 9px; text-anchor: end; font-family: sans-serif;\" class=\"classroomText\">{2}</text>", y + dy + height - 6 + split, x + width - 5, lecture.Room) + System.Environment.NewLine; //Lecturer //x="{$x + 5}" y="{$y + $dy + $height - 6 + $split}" res += String.Format(new NumberFormatInfo(), "<text y=\"{0}\" x=\"{1}\" style=\"fill: #000; font-size:9px; text-anchor: start; font-family: sans-serif;\" class=\"lecturerText\">{2}</text>", y + dy + height - 6 + split, x + 5, lecture.Lecturer) + System.Environment.NewLine; } return res; }