Don't like ads? PRO users don't see any ads ;-)
Guest

UNSW Timetable Converter for 12s2 only

By: a guest on Jul 14th, 2012  |  syntax: Perl  |  size: 9.97 KB  |  hits: 43  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!/usr/bin/perl
  2.  
  3. # funswap.pl
  4. # EDIT: modified by Calvin,
  5. # Changes include:
  6. #   -changed $weekStart, starting date of semester (now works for 12s2)
  7. #   -changed $OUTFILE, output file name to timetable12s2.ics
  8. #   -changed $weekBeforeBreak
  9. #
  10. # written by Rowan Katekar (rowan.katekar@gmail.com)
  11. # released under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
  12. # http://creativecommons.org/licenses/by-nc-sa/3.0/
  13.  
  14. # USAGE SUGGESTION:
  15. # When importing into google calendar, create a special calendar just for this timetable.
  16. # That way, if something goes wrong you can delete the events from the calendar all at once, rather
  17. # than having to delete them one by one.
  18.  
  19. # uses DateTime module - install from cpan using
  20. # sudo apt-get install libdatetime-perl
  21.  
  22. # reads in UNSW timetable html file
  23. # outputs .ics file for importing into Outlook, Evolution, Google Calendar, ... etc.
  24. # assumes UNSW timetable html file is in the same folder as the perl script and called "timetable.html"
  25.  
  26. ## Edited by William Pickering (@YodaDaCoda)
  27. ## * Changed from cat timetable.html (linux only file handling) to native perl file handling.
  28. ##      Solves problems with things being printed to the terminal and generally not working
  29. ##      properly on Windows.
  30. ## * Changed date logic
  31. ##      Every single class was set to be on the next week from the previous class,
  32. ##      leading to classes being held years into the future.
  33.  
  34. # updated by Rowan Katekar again on 11-12-2011
  35. # *  Changed dates to semester 1 2012 dates.
  36. # *  Also changed the lecture name to the code of the subject
  37. # *  Subject title is now part of the description
  38.  
  39.  
  40. use DateTime;
  41.  
  42. # convert time of the format /(\d)?\d:\d\d[AP]M/ to a number from 0..23
  43. sub conv24($);
  44.  
  45. # convert a weekday abbreviation to a number from 1-5
  46. sub dayToNumber($);
  47.  
  48. # substitutes event values into iCal string & returns resultant string
  49. sub subInValues($$$$$);
  50.  
  51. ## Gets the minutes of a specified time string.
  52. sub getMins($);
  53.  
  54. # First day of semester (currently set to first day of semester for 2012 semester 1)
  55. $weekStart = DateTime->new( year=>2012, month=>7, day=>16, time_zone => 'Australia/Sydney' );
  56.  
  57. # Last week before mid-semester break
  58. $weekBeforeBreak = 7;
  59.  
  60. # destination of ical file
  61. $OUTFILE = "timetable12s2.ics";
  62.  
  63. # file to read from
  64. $INFILE = "timetable.html";
  65.  
  66. # crap to ignore
  67. @ignore =  ("Teaching", "Status", "Enrolled", "Units", "Campus", "Activity", "Section",
  68.             "Weeks", "Location", "Instructor", "bsdsSequence", "outerTable", "Key",
  69.             "printBt", "Print", "onClick", "Site Map", "Site Feedback", "Privacy Statement",
  70.             "Provider", "Copyright");
  71.  
  72. ##open files for reading/writing
  73. open OUTFILE, ">", $OUTFILE or die $!;
  74. open INFILE, "<", $INFILE or die $!;
  75.  
  76. # read in page
  77. @lines = <INFILE>;
  78. close INFILE;
  79.  
  80. ##convert from array to one long string
  81. $page = join (' ', @lines);
  82.  
  83. # split into sections based on subject
  84. @sections = split (/([A-Z]{4}[0-9]{4}\s-.*?)</, $page);
  85. shift @sections;
  86.  
  87. # do for each subject
  88. foreach $section (@sections) {
  89.    @useful = ();
  90.    # print subject title
  91.    if ($section =~ m/[A-Z]{4}[0-9]{4}\s-/) {
  92.       $subject = $section;
  93.    } else {
  94.       # extract everything that's not in html tags
  95.       @info = ($section =~ m/>([\w-:;,\s\(\)\&]*?[A-Z0-9]+[\w-:;,\s\)\&]*?)</g);
  96.       foreach (@info) {
  97.          push @useful, $_ if !(m/(data|row|table|sectionHeading)/ || m/^\s*\d+\s*$/);
  98.       }
  99.       # find times, classes, locations
  100.       foreach (@useful) {
  101.          $classes{$subject} .=  "\n" if  m/^Lecture|Tutorial|Laboratory$/;
  102.          s/^\s*// && s/\s*$//;
  103.          # ignore garbage
  104.          if (!m/^[A-Z0-9]+$/ or m/Lecture|Tutorial|Laboratory|Mon|Tue|Wed|Thu|Fri/) {
  105.             $garbage = 0;
  106.             foreach $word (@ignore) {
  107.                $garbage = 1 if ($_ =~ m/$word/);
  108.             }
  109.             $classes{$subject} .= "$_\n" if not $garbage;
  110.          }
  111.       }
  112.    }
  113. }
  114.  
  115. # go through classes for each subject
  116. foreach $subject (keys %classes) {
  117.    @lines = split /\n/, $classes{$subject};
  118.    $lesson = 0;
  119.    for $i (0..$#lines) {
  120.       # get day
  121.       if ($lines[$i] =~ m/\b(Mon|Tue|Wed|Thu|Fri)\b/) {
  122.          $classDays{$subject}[$lesson] = $1;
  123.          $lesson++;
  124.       }
  125.       # get weeks
  126.       if ($lines[$i] =~ m/^[\d,-]+$/) {
  127.          while ($lines[$i] =~ m/(\d+-\d+)/) {
  128.             $replacement = "";
  129.             $numbers = $1;
  130.             $numbers =~ m/(\d+)-(\d+)/;
  131.             $from = $1;
  132.             $to = $2;
  133.             $replacement .= "$_," for ($from..$to);
  134.             $replacement =~ s/,$//;
  135.             $lines[$i] =~ s/$numbers/$replacement/;
  136.          }
  137.          $classWeeks{$subject}[$lesson - 1] = $lines[$i]; # hax
  138.          $classLocation{$subject}[$lesson - 1] = $lines[$i + 1]; # get location
  139.       }
  140.       # get time
  141.       $classTimes{$subject}[$lesson - 1] = $1 if ($lines[$i] =~ m/(\d+:\d+[AP]M - \d+:\d+[AP]M)/);
  142.       # get class type (lecture/tute/lab)
  143.       $classType = $1 if ($lines[$i] =~ m/(Lecture|Tutorial|Laboratory)/ or $lines[$i] =~ m/(.*\bof\b.*)/ );
  144.       $classTypes{$subject}[$lesson] = $classType;
  145.    }
  146.    $classTypes{$subject}[$lesson] = ""; # moar hax
  147. }
  148.  
  149.  
  150. # ical 'header'
  151. $STARTCALENDAR = <<START;
  152. BEGIN:VCALENDAR
  153. PRODID:-//fun swap//hacked together perl//EN
  154. VERSION:2.0
  155. CALSCALE:GREGORIAN
  156. METHOD:PUBLISH
  157. START
  158.  
  159. # *START*, *END*, *EVENT* etc. are substituted with actual class details later on
  160. $EVENTSTRING = <<EVENT;
  161. BEGIN:VEVENT
  162. DTSTART:*START*
  163. DTEND:*END*
  164. SUMMARY:*TITLE*
  165. LOCATION:*LOCATION*
  166. DESCRIPTION:*DESCRIPTION*
  167. SEQUENCE:0
  168. END:VEVENT
  169. EVENT
  170.  
  171. # goes at the end of the produced file
  172. $ENDCALENDAR = "END:VCALENDAR";
  173.  
  174. print OUTFILE $STARTCALENDAR;
  175.  
  176. foreach $subject (keys %classes) {
  177.    foreach $lesson (0..length($classTypes{$subject})) {
  178.       if (defined $classDays{$subject}[$lesson]) {
  179.          @weeksClassIsOn = split (/,/,$classWeeks{$subject}[$lesson]);
  180.  
  181.          # convert start and end times to 24h time
  182.          @times = split (/ - /, $classTimes{$subject}[$lesson]);
  183.          $startClassH = conv24($times[0]);
  184.          $startClassM = getMins($times[0]);
  185.          $endClassH = conv24($times[1]);
  186.          $endClassM = getMins($times[0]);
  187.  
  188.          # format the title of the event as "[SUBJECT CODE] [Lecture|Tutorial|Laboratory]"
  189.          $subject;
  190.          @subjectCode = split(" - ", $subject);
  191.          $subjectCode = @subjectCode[0];
  192.          @subjectName = split(" - ", $subject);
  193.          $subjectName = @subjectName[1];
  194.          $subjectCode =~ s/^([A-Z]{4}[0-9]{4}).*$/$1/;
  195.          $classType = $classTypes{$subject}[$lesson];
  196.          $title = "$subjectCode $classType";
  197.          $description = "$subjectName";
  198.  
  199.          # location of event
  200.          $location = $classLocation{$subject}[$lesson];
  201.          
  202.          # add classes to calendar
  203.          for $week (1..13) {   #for each week in the timetable
  204.            
  205.  
  206.             if ($week ~~ @weeksClassIsOn) {   #if the class is on that week
  207.  
  208.                ##starting date
  209.                $weekDate = $weekStart->clone();
  210.                $weekDate->add( weeks => ($week -1) );
  211.                # skip the week of mid-session break
  212.                $weekDate->add( days => 7) if ($week > $weekBeforeBreak);
  213.  
  214.                $weekDate->add( days => dayToNumber($classDays{$subject}[$lesson]) - 1 );
  215.                
  216.                ##set the time the class starts
  217.                $startTime = $weekDate->clone();
  218.                $startTime->set_hour($startClassH);
  219.                $startTime->set_minute($startClassM);
  220.                
  221.                ##set the time the class ends
  222.                $endTime = $weekDate->clone();
  223.                $endTime->set_hour($endClassH);
  224.                $endTime->set_minute($endClassM);
  225.                
  226.                ##DateTime hackery pary one...
  227.                ##Time part of the DateTime stamp needs to be formatted in Zulu time for compatibility with google calendar.
  228.                ##If at least the time part is not in Zulu, all events show up as being one hour in duration.
  229.                ##Convert time to UTC...
  230.                $startTime->set_time_zone('UTC');
  231.                $endTime->set_time_zone('UTC');
  232.  
  233.                # convert DateTime formatting to iCal formatting
  234.                $startTime =~ s/[^0-9A-Z]//g;
  235.                $endTime =~ s/[^0-9A-Z]//g;
  236.  
  237.                ##DateTime hackery part 2...
  238.                ##Append Z for Zulu timezone to the times
  239.                $startTime = $startTime."Z";
  240.                $endTime = $endTime."Z";
  241.  
  242.                # substitute values into iCal event string
  243.                $icalEvent = subInValues($title, $location, $startTime, $endTime, $description);
  244.                
  245.                ## append to file
  246.                print OUTFILE $icalEvent;
  247.             }
  248.          }
  249.       }
  250.    }
  251. }
  252.  
  253. ## append the final line to the file... and we're done
  254. print OUTFILE $ENDCALENDAR;
  255. close OUTFILE;
  256.  
  257. ##return the minutes of a given time
  258. sub getMins($) {
  259.    my $mins = $_[0];
  260.    $mins =~ m/.*?:(.{2}).*?/;
  261.    $mins = $1;
  262.    return $mins;
  263. }
  264.  
  265. # convert a weekday abbreviation to a number from 1-5
  266. sub dayToNumber($) {
  267.    my $dayName = $_[0];
  268.    my %days = ("Mon" => 1,
  269.             "Tue" => 2,
  270.             "Wed" => 3,
  271.             "Thu" => 4,
  272.             "Fri" => 5);
  273.    return $days{$dayName};
  274. }
  275.  
  276. # convert time of the format /(\d)?\d:\d\d[AP]M/ to a number from 0..23
  277. sub conv24($) {
  278.    my $time = $_[0];
  279.    $hour = $time;
  280.    $hour =~ s/:.*$//;
  281.    $hour += 12  if ($time =~ m/PM/ && $hour < 12); # convert to 24h time
  282.    return $hour;
  283. }
  284.  
  285. # substitutes event values into iCal string & returns resultant string
  286. sub subInValues($$$$$) {
  287.    my ($title, $location, $startTime, $endTime, $description) = @_[0..4];
  288.  
  289.    my $icalEvent = $EVENTSTRING;
  290.    $icalEvent =~ s/\*START\*/$startTime/;
  291.    $icalEvent =~ s/\*END\*/$endTime/;
  292.    $icalEvent =~ s/\*TITLE\*/$title/;
  293.    $icalEvent =~ s/\*LOCATION\*/$location/;
  294.    $icalEvent =~ s/\*DESCRIPTION\*/$description/;
  295.  
  296.    return $icalEvent;
  297. }