Schedules are sometimes best understood in a “calendar page” format. A hypothetical scenario illustrates design considerations and code.
Your team runs daily events scheduled by a parent organization. Details about future events are sent to you every month or so via spreadsheet.
Each of your team members requires only a moment to sit down at a computer, open a spreadsheet, and find the rows and columns outlining the day’s work. However, you’d like to spare your people even this minor hassle, and more broadly, you have misgivings about spreadsheets for exchanging schedule data. While pondering these issues, an idea comes to mind…
Here is some random data for illustration. We’ll assume events are run on most, but not necessarily all business days, and that each event is characterized by a type and lead. An eventDirectory data structure something like this would normally be wrangled from spreadsheets or other external sources.
In[]:=
SeedRandom[123];eventDirectory=Module[{(*NotethereisnoneedtosorteventDays*)eventDays=RandomSample[#,400]&@DayRange[DateObject["1 January 2020"],DateObject["31 December 2021"],"BusinessDay"],eventTypes={"Alpha","Beta","Gamma","Delta"},eventLeads={"Ms Arial","Mr Baskerville","Ms Courier","Mr Helvetica","Ms Monaco","Mr Optima","Ms Palatino","Mr Tahoma"}},#"eventLead"RandomChoice[eventLeads],"eventType"RandomChoice[eventTypes],"eventDetails"ResourceFunction["RandomFortune"][](*RandomWord[RandomInteger[{3,6}]]*)&/@eventDays];
A look at the data is always worthwhile. In order to display both the keys and values, the eventDirectory has here been turned from an association into a list.
In[]:=
Manipulate[Column[Normal[eventDirectory]〚index;;index+5〛],Style["eventDirectory","Subsubsection"],{index,1,Length[eventDirectory]-5,1},SaveDefinitionsTrue]
Out[]=
This rule converts a DateObject into a formatted Item that will represent a day in a calendar. Formatting includes a background color keyed to event type, and event details displayed as a tooltip.
Also, the ItemSize option for Item does not seem to work on the cloud; width is ok, but hight shrinks to remove whitespace beneath content. A couple of invisible Z's provide a fix, but will need to be removed when using this code on desktop.
In[]:=
do_DateObjectModule[{dayNumber=Style[DateValue[do,"Day"],12,Bold,GrayLevel[0.25]],moonPhase=MoonPhase[do,"Icon"]/.Graphics[{gl_,{__},mp__},opts__]Graphics[{gl,Disk[],mp},AspectRatio1,BaselinePositionBottom,ImageSize9],eventCharacteristics=Lookup[eventDirectory,do,"eventLead""N/A","eventType""N/A"],eventTypeToColor=MapThread[Rule,{{"Alpha","Beta","Gamma","Delta","N/A"},Lighter[ColorData[1,"ColorList"],0.8]〚1;;4〛~Join~{White}}](*endMapThread*),tooltip=Tooltip[#,Lookup[eventDirectory,do,"eventDetails"{"No Event Today"}]["eventDetails"]]&},Item[tooltip@Column[{Row[{dayNumber,Spacer[8],moonPhase}],Row[{"Day of Year: ",DateValue[do,"ISOYearDay"]}],Row[{"Lead: ",eventCharacteristics["eventLead"]}],Row[{"Event Type: ",eventCharacteristics["eventType"]}],Invisible["z"],Invisible["z"],Invisible["z"]}/."N/A"Style["N/A",Gray],Alignment{Left,Center},BaseStyle{"Text",Small}](*endColumn*),Alignment{Left,Top},BackgroundeventTypeToColor[eventCharacteristics["eventType"]],FrameTrue,ItemSize{8,8}(*sizeofIteminemsandlineheights*)](*endItem*)](*endModule*);dateObjectToFormattedDay=%;
Functionality of dateObjectToFormattedDay is illustrated here.
In[]:=
{DateObject["1 March 2021"],Spacer[5],DateObject["9 March 2021"],Spacer[5],DateObject["11 March 2021"],Spacer[5],DateObject["16 March 2021"],Spacer[5],DateObject["18 March 2021"]}/.dateObjectToFormattedDay//Grid[{#}]&
Out[]=
|
|
|
|
|
A calendar page is simply a Grid populated with these formatted day Items. Note that Framed is used here only to prevent the bottom of the calendar from being chopped off; it seems not be needed on the desktop.
In[]:=
Clear[monthNames,dayOfWeekNames,createCalendarPage];monthNames=;dayOfWeekNames=;createCalendarPage[month_Integer,year_Integer]:=Module[{days=DateRange[#,DatePlus[#,{{1,"Month"},{-1,"Day"}}]]&[DateObject[{year,month,1}]],splitDays,formattedDays,monthName=monthNames〚month〛},splitDays=Split[days,DateValue[#2,"DayName"]≠Sunday&]/.{first_List,middle__,last_List}{PadLeft[first,7,""],middle,PadRight[last,7,""]};formattedDays=splitDays/.dateObjectToFormattedDay;Framed[#,FrameStyleNone]&@Column[{Style[Row[{monthName," ",year}],"Subsubsection"],Grid[Join[dayOfWeekNames,formattedDays],Alignment{Left,Top},FrameStyleThin]},Center](*endColumn*)](*endModule*)
In[]:=
monthNumberToNameRules=Rule@@@({Range[12],monthNames});Manipulate[Magnify[createCalendarPage[month,year],scale],{{scale,1},0.2,2},{year,{2020,2021}},{month,monthNumberToNameRules,ControlTypeSetterBar},SaveDefinitionsTrue]
Out[]=
Though calendar pages are not quite graphics, their quasi-graphic nature can open up display options: for example, they can work well on a large monitor in a common area, or on a website for quick reference on a cell phone. They, along with TimelinePlot for showing past and future milestones, can make schedules--is it possible?--somewhat interesting.
You use these ideas to present your team’s accomplishments at The Big Meeting, and everyone is so overwhelmingly impressed that you are promoted on the spot to Executive Head Honcho. Congratulations--you are a hero!

