diff --git a/generate_calendar_dates.php b/generate_calendar_dates.php index 65725c7..0907461 100755 --- a/generate_calendar_dates.php +++ b/generate_calendar_dates.php @@ -8,13 +8,17 @@ /* - Teaching period configuration. It's easily extensible to a new teaching period simply by adding a new specification for that period. The key should be mixed-case alphanumeric (i.e., no whitespace, no punctuation or other special characters). The components of each period specification are: + Date of the Monday of the first academic week of the year. Teaching weeks start on Monday, so this is a convenient base point to start from. This will need to be updated annually. +*/ +$first_monday = new DateTime( "2012-01-02" ); + + +/* + Teaching period configuration. This will need to be updated annually. It's easily extensible to a new teaching period simply by adding a new specification for that period. The key should be mixed-case alphanumeric (i.e., no whitespace, no punctuation or other special characters). The components of each period specification are: - start_date: The date of the first day of the first week of the period. + first_week: The academic week number of the first week of the period. - num_weeks: The total number of weeks in the period, /including/ any - breaks (e.g., 13 teaching weeks plus 1 week mid-semester - break = 14 weeks). + last_week: The academic week number of the last week of the period. break_weeks: A list of breaks that occur during the teaching period. Each is specified by the start (break_start) and end @@ -25,15 +29,11 @@ The list should ideally be in ascending order, but the script doesn't assume this and sorts the list anyway. Weeks - are numbered by absolute calendar week number (i.e., week - 1 is the first calendar week of the year). The date of the - first Monday of the year is stored in $first_monday below - (everything is Monday-indexed). + are numbered by academic week number rather than week within + the teaching period. */ -$first_monday = new DateTime( "2012-01-02" ); - $periods = array( - 'SummerSchool' => array( + 'SS' => array( 'first_week' => 2, 'last_week' => 7, 'break_weeks' => array( @@ -43,7 +43,7 @@ ), ), ), - 'SemesterOne' => array( + 'S1' => array( 'first_week' => 9, 'last_week' => 22, 'break_weeks' => array( @@ -53,7 +53,7 @@ ), ), ), - 'SemesterTwo' => array( + 'S2' => array( 'first_week' => 28, 'last_week' => 41, 'break_weeks' => array( @@ -63,7 +63,7 @@ ), ), ), - 'FullYear' => array( + 'FY' => array( 'first_week' => 9, 'last_week' => 41, 'break_weeks' => array( @@ -84,140 +84,409 @@ ); -// A couple of handy date intervals: four days to the end of the current week, -// and seven days to the start of next week. -$plus_four_days = new DateInterval( 'P4D' ); -$plus_seven_days = new DateInterval( 'P7D' ); +// We need a condition to validate the period code in the templates. +$period_condition = sprintf( "( @period = '%s' )", implode( "' ) or ( @period = '", array_keys( $periods ) ) ); + +// We also a list of valid period code values for the error string. +$period_list = sprintf( '"%s"', implode( '", "', array_keys( $periods ) ) ); + +// The period and week variables are defined identically in both templates, so +// let's define them just once here to ensure consistency. +$shared_variables = << + + + + + + + + Attribute "period" must be one of the values {$period_list}. + + + + + + + + + + The "week" attribute is required. + + + +EOT; -// Header boilerplate. +// Generate the file header and the wrapper templates. print<< - - - + + + + + + + + + + + + + + EOT; -// printf( " - +// Generate the XSLT functions that do all the work. These essentially end up being huge parameterised lookup tables. +generate_function( $first_monday, $periods, 'format-teaching-date' ); - -foreach ( $periods as $period_name => $period_data ) -{ - $week_start = clone( $first_monday ); - // Jump forward the correct number of weeks to the start of the teaching period. - $week_start->add( new DateInterval( sprintf( "P%dD", ( $period_data['first_week'] - 1 ) * 7 ) ) ); - - $week_number = 0; - $num_breaks = 0; - - // Grab the first break week off the front of the list. - sort( $period_data['break_weeks'] ); - $break_week = array_shift( $period_data['break_weeks'] ); - - for ( $week = $period_data['first_week']; $week <= $period_data['last_week']; $week++ ) - { - $week_end = clone( $week_start ); - $week_end->add( $plus_four_days ); - - // Handle break weeks. - if ( ( $week >= $break_week['break_starts'] ) && ( $week <= $break_week['break_ends'] ) ) - { - if ( $week == $break_week['break_starts'] ) - { - $num_breaks++; - - // Work out the end date of the break. - $break_end = clone( $week_start ); - // Note: +4 days to get to Friday. - $break_end->add( - new DateInterval( - sprintf( "P%dD", ( ( $break_week['break_ends'] - $break_week['break_starts'] ) * 7 ) + 4 ) - ) - ); - - // Output template for inclusion in paper calendar. - printf( "\t\n", - $period_name, - $num_breaks, - $period_name, - $num_breaks - ); - printf( "\t\t%s\n", - // If the week start and end dates are in the same month, we only - // need to output the month name once. - ( $week_start->format( 'n' ) == $break_end->format( 'n' ) ) ? $week_start->format( 'j' ) : $week_start->format( 'j M' ) - ); - print( "\t\t\n" ); - printf( "\t\t\n", $break_end->format( 'j M' ) ); - - print( "\t\n\n" ); - } // if current week == first week of break - - if ( $week == $break_week['break_ends'] ) - { - // Grab the next break week off the front of the list. - $break_week = array_shift( $period_data['break_weeks'] ); - } // if current week == last week of break - } // if current week is a break week - else - { - $week_number++; - - // Output template for inclusion in paper calendar. - printf( "\t\n", - $period_name, - $week_number, - $period_name, - $week_number - ); - - // If the week start and end dates are in the same month, we only - // need to output the month name once. - if ( $week_start->format( 'n' ) == $week_end->format( 'n' ) ) - { - printf( "\t\t%s\n", $week_start->format( 'j' ) ); - print( "\t\t\n" ); - printf( "\t\t%s\n", $week_end->format( 'j' ) ); - print( "\t\t\n" ); - printf( "\t\t%s\n", $week_end->format( 'M' ) ); - } // if same month - else - { - printf( "\t\t%s\n", $week_start->format( 'j M' ) ); - print( "\t\t\n" ); - printf( "\t\t%s\n", $week_end->format( 'j M' ) ); - } // else different months - - printf( "\t\n\n" ); - } // else normal teaching week - - $week_start->add( $plus_seven_days ); - } -} +generate_function( $first_monday, $periods, 'format-teaching-date-range' ); // Footer boilerplate. -print( "\n" ); +print( "\n" ); + +exit; + + +//////////////////////////////////////////////////////////////////////////////// + + +function generate_function( $first_monday, $periods, $function_name ) +{ + // A couple of handy date intervals: four days to the end of the current week, + // and seven days to the start of next week. + $plus_four_days = new DateInterval( 'P4D' ); + $plus_seven_days = new DateInterval( 'P7D' ); + + + // Initial boilerplate for the function. + switch ( $function_name ) + { + case 'format-teaching-date': + print<< + + + + + + + + + +EOT; + break; + + case 'format-teaching-date-range': + print<< + + + + + + + + + +EOT; + break; + + default: // WTF?!? + print "Unrecognised function name \"$function_name\", terminating.\n"; + exit; + break; + } // switch function name + + // Generate XSLT code for each teaching period. + foreach ( $periods as $period_name => $period_data ) + { + print<< + + +EOT; + + $week_start = clone( $first_monday ); + // Jump forward the correct number of weeks to the start of the teaching period. + $week_start->add( new DateInterval( sprintf( "P%dD", ( $period_data['first_week'] - 1 ) * 7 ) ) ); + + $week_number = 0; + $num_breaks = 0; + + // Grab the first break week off the front of the list. + sort( $period_data['break_weeks'] ); + $break_week = array_shift( $period_data['break_weeks'] ); + + // Generate XSLT code for each teaching week and breaks within the period. + for ( $week = $period_data['first_week']; $week <= $period_data['last_week']; $week++ ) + { + $week_end = clone( $week_start ); + $week_end->add( $plus_four_days ); + + // Handle breaks. + if ( ( $week >= $break_week['break_starts'] ) && ( $week <= $break_week['break_ends'] ) ) + { + if ( $week == $break_week['break_starts'] ) + { + $num_breaks++; + + // Work out the end date of the break. + $break_end = clone( $week_start ); + // Note: +4 days to get to Friday. + $break_end->add( + new DateInterval( + sprintf( "P%dD", ( ( $break_week['break_ends'] - $break_week['break_starts'] ) * 7 ) + 4 ) + ) + ); + + switch ( $function_name ) + { + case 'format-teaching-date': + generate_date( "B{$num_breaks}", $week_start ); + break; + + case 'format-teaching-date-range': + if ( $week_start->format( 'n' ) == $break_end->format( 'n' ) ) + { + generate_same_month_date_range( "B{$num_breaks}", $week_start, $break_end ); + } // if break start and break end are in the same month + else + { + generate_different_month_date_range( "B{$num_breaks}", $week_start, $break_end ); + } // else break start and break end are in different months + break; + + default: // WTF?!? + print "Unrecognised function name \"$function_name\", terminating.\n"; + exit; + break; + } // switch function name + } // if current week == first week of break + + if ( $week == $break_week['break_ends'] ) + { + // Grab the next break week off the front of the list. + $break_week = array_shift( $period_data['break_weeks'] ); + } // if current week == last week of break + } // if current week is a break week + else + { + $week_number++; + + switch ( $function_name ) + { + case 'format-teaching-date': + generate_date( $week_number, $week_start ); + break; + + case 'format-teaching-date-range': + if ( $week_start->format( 'n' ) == $week_end->format( 'n' ) ) + { + generate_same_month_date_range( $week_number, $week_start, $week_end ); + } // if break start and break end are in the same month + else + { + generate_different_month_date_range( $week_number, $week_start, $week_end ); + } // else break start and break end are in different months + break; + + default: // WTF?!? + print "Unrecognised function name \"$function_name\", terminating.\n"; + exit; + break; + } // switch function name + } // else normal teaching week + + $week_start->add( $plus_seven_days ); + } // for each week + + // Terminating boilerplate for this teaching period. + print<< + + Invalid {$period_name} week specification " + + ". + + + + + +EOT; + } // foreach teaching period + + // Terminating boilerplate for the function. + print<< + + + + + +EOT; +} // generate_function + + +//////////////////////////////////////////////////////////////////////////////// + + +function generate_date( $week_spec, $date ) +{ + print<< + + + +EOT; +} // generate_same_month_date_range + + +//////////////////////////////////////////////////////////////////////////////// + + +function generate_same_month_date_range( $week_spec, $start_date, $end_date ) +{ + print<< + + + + + + + + + +EOT; +} // generate_same_month_date_range + + +function generate_different_month_date_range( $week_spec, $start_date, $end_date ) +{ + print<< + + + + + + + + +EOT; +} // generate_different_month_date_range + ?>