# HG changeset patch # User Paul Boddie # Date 1423325808 -3600 # Node ID afde370a7e01c61dceef1416da5353246301a097 # Parent 55345413b3b96de43d919106e0609378c71ab47d Generate empty days in the calendar, adding a control to show/hide them. diff -r 55345413b3b9 -r afde370a7e01 htdocs/styles.css --- a/htdocs/styles.css Sat Feb 07 00:47:14 2015 +0100 +++ b/htdocs/styles.css Sat Feb 07 17:16:48 2015 +0100 @@ -108,51 +108,50 @@ text-decoration: underline; } -/* Hiding/showing unused slots/periods. */ +/* Hiding/showing busy slots/periods or unused days/slots/periods. */ + +/* Hide the controls. */ -input#hideslots { - display: none; -} +input#hidebusy, +input#hidedays, +input#hideslots, + +/* Hide the enable labels when controls are already enabled. */ -input#hideslots:checked ~ .controls label.enable[for=hideslots] { - display: none; -} +input#hidebusy:checked ~ .controls label.enable[for=hidebusy], +input#hidedays:checked ~ .controls label.enable[for=hidedays], +input#hideslots:checked ~ .controls label.enable[for=hideslots], + +/* Hide the disable labels when controls are already disabled. */ -input#hideslots:not(:checked) ~ .controls label.disable[for=hideslots] { - display: none; -} +input#hidebusy:not(:checked) ~ .controls label.disable[for=hidebusy], +input#hidedays:not(:checked) ~ .controls label.disable[for=hidedays], +input#hideslots:not(:checked) ~ .controls label.disable[for=hideslots], +/* Hide calendar rows depending on the selected controls. */ + +input#hidebusy:checked ~ .calendar tr.slot.busy, +input#hidedays:checked ~ .calendar tr.separator.empty, +input#hidedays:checked ~ .calendar tr.slot.onlyslot.daystart.empty, +input#hideslots:checked ~ .calendar tr.separator.empty, input#hideslots:checked ~ .calendar tr.slot.daystart.empty { display: none; } -/* Hiding/showing busy slots/periods. */ - -input#hidebusy { - display: none; -} - -input#hidebusy:checked ~ .controls label.enable[for=hidebusy] { - display: none; -} - -input#hidebusy:not(:checked) ~ .controls label.disable[for=hidebusy] { - display: none; -} - -input#hidebusy:checked ~ .calendar tr.slot.busy { - display: none; -} +/* Show slot endpoints when hiding adjacent busy periods. */ input#hidebusy:checked ~ .calendar th.timeslot span.endpoint { display: block; } +/* Style the labels. */ + label.enable, label.disable { padding-left: 0.25em; } +label.hidedays, label.hideslots { border-left: 1em solid #faa; /* th.participantheading background-color */ } diff -r 55345413b3b9 -r afde370a7e01 imip_manager.py --- a/imip_manager.py Sat Feb 07 00:47:14 2015 +0100 +++ b/imip_manager.py Sat Feb 07 17:16:48 2015 +0100 @@ -37,8 +37,8 @@ get_end_of_day, get_start_of_day, get_start_of_next_day, \ get_timestamp, ends_on_same_day, to_timezone from imiptools.mail import Messenger -from imiptools.period import add_day_start_points, add_slots, convert_periods, \ - get_freebusy_details, \ +from imiptools.period import add_day_start_points, add_empty_days, add_slots, \ + convert_periods, get_freebusy_details, \ get_scale, have_conflict, get_slots, get_spans, \ partition_by_day from imiptools.profile import Preferences @@ -854,11 +854,14 @@ # The positioning of the control, paragraph and table are important here. page.input(name="hideslots", type="checkbox", value="hide", id="hideslots") + page.input(name="hidedays", type="checkbox", value="hide", id="hidedays") page.input(name="hidebusy", type="checkbox", value="hide", id="hidebusy") page.p(class_="controls") page.label("Hide busy time periods", for_="hidebusy", class_="hidebusy enable") page.label("Show busy time periods", for_="hidebusy", class_="hidebusy disable") + page.label("Hide empty days", for_="hidedays", class_="hidedays enable") + page.label("Show empty days", for_="hidedays", class_="hidedays disable") page.label("Hide unused time periods", for_="hideslots", class_="hideslots enable") page.label("Show unused time periods", for_="hideslots", class_="hideslots disable") page.p.close() @@ -977,8 +980,16 @@ partitioned_group_types.append(group_type) partitioned_group_sources.append(group_source) + # Add empty days. + + add_empty_days(days, partitioned, tzid) + + # Show the controls permitting day selection. + self.show_calendar_day_controls(days) + # Show the calendar itself. + page.table(cellspacing=5, cellpadding=5, class_="calendar") self.show_calendar_participant_headings(partitioned_group_types, partitioned_group_sources, group_columns) self.show_calendar_days(days, partitioned_groups, partitioned_group_types, group_columns) @@ -1071,21 +1082,31 @@ # Produce a heading and time points for each day. for day, intervals in all_days: + groups_for_day = [partitioned.get(day) for partitioned in partitioned_groups] + is_empty = True + + for slots in groups_for_day: + if not slots: + continue + + for active in slots.values(): + if active: + is_empty = False + break + page.thead() - page.tr() + page.tr(class_="separator%s" % (is_empty and " empty" or "")) page.th(class_="dayheading container", colspan=all_columns+1) self._day_heading(day) page.th.close() page.tr.close() page.thead.close() - groups_for_day = [partitioned.get(day) for partitioned in partitioned_groups] - page.tbody() - self.show_calendar_points(intervals, groups_for_day, partitioned_group_types, group_columns) + self.show_calendar_points(intervals, groups_for_day, partitioned_group_types, group_columns, is_empty) page.tbody.close() - def show_calendar_points(self, intervals, groups, group_types, group_columns): + def show_calendar_points(self, intervals, groups, group_types, group_columns, is_empty): """ Show the time 'intervals' along with period information from the given @@ -1113,6 +1134,7 @@ css = " ".join( ["slot"] + + (is_empty and ["onlyslot"] or []) + (have_active and ["busy"] or ["empty"]) + (continuation and ["daystart"] or []) ) diff -r 55345413b3b9 -r afde370a7e01 imiptools/period.py --- a/imiptools/period.py Sat Feb 07 00:47:14 2015 +0100 +++ b/imiptools/period.py Sat Feb 07 17:16:48 2015 +0100 @@ -20,7 +20,7 @@ """ from bisect import bisect_left, insort_left -from datetime import datetime +from datetime import datetime, timedelta from imiptools.dates import get_datetime, get_start_of_day, to_timezone # Time management with datetime strings. @@ -285,6 +285,23 @@ return d +def add_empty_days(days, partitioned, tzid): + + "Add empty days to 'days' between busy days." + + last_day = None + all_days = days.keys() + all_days.sort() + + for day in all_days: + if last_day: + empty_day = last_day + timedelta(1) + while empty_day < day: + days[empty_day] = [(get_start_of_day(empty_day, tzid), None)] + partitioned[empty_day] = {} + empty_day += timedelta(1) + last_day = day + def get_spans(slots): "Inspect the given 'slots', returning a mapping of event uids to spans."