5

This question is a follow up on my previous question on how to test if a date is on the calendar.

Here I want to ask how to further modify my code to reduce the work of entering all the events one by one. As you see in the minimal example below (the same as on the previous question except for the dates of the practical courses), I currently have to add each date individually. How could I modify the calendar code taking care of printing the events to the calendar to understand the following command structure:

\newcommand{\practicalCourses}{
    {Course Name}/{beginDate}/{endDate},%
    {Other Course Name}/{other beginDate}/{other endDate}%
}

The code should loop through all the dates between the beginDate and the endDate of each individual course (Course Name and Other Course Name in this example), then test if that date is on the calendar (between calbegindate and calenddate, see previous question) and if so, print the course name to the respective day.


MWE

% %%%%%%%%
% Preamble
% %%%%%%%%

\documentclass[10pt, a4paper, landscape]{article}
\usepackage[margin = .5cm, nofoot]{geometry}

% Tikz
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{calendar}

% Logic and Tools
\usepackage{xparse}

% Fonts
\RequirePackage{lmodern}
\renewcommand*\familydefault{\sfdefault}

% Color
\definecolor{definedcolor}{HTML}{00CC00}

% Calculation
\ExplSyntaxOn
    \DeclareExpandableDocumentCommand{\eval}{m}{\int_eval:n {#1}}
\ExplSyntaxOff

% Custom appearance
\newcommand{\practicalCourse}[2]{%
    \node [text=black, anchor = north west, text width = 3.3cm ] at ($(cal-#1.north west)+(3.5em, -0.2em)$) {\scriptsize{#2}};
}

% Variables
\newcommand{\currentyear}{\the\year}
\newcommand{\nextyear}{\eval{\currentyear + 1}}

\newcommand{\calbegindate}{\currentyear-10-01}
\newcommand{\calenddate}{\nextyear-03-31}

\newcommand{\practicalCourses}{%
    {Foo}/\currentyear-10-09,%
    {Foo}/\currentyear-10-10,%
    {Foo}/\currentyear-10-11,%
    {Foo}/\currentyear-10-12,%
    {Foo}/\currentyear-10-13,%
    {Foo}/\currentyear-10-16,%
    {Foo}/\currentyear-10-17,%
    {Foo}/\currentyear-10-18,%
    {Foo}/\currentyear-10-19,%
    {Foo}/\currentyear-10-20,%
    {Foo}/\currentyear-10-23,%
    {Foo}/\currentyear-10-24,%
    {Foo}/\currentyear-10-25,%
    {Foo}/\currentyear-10-26,%
    {Foo}/\currentyear-10-27,%
    {Bar}/\currentyear-11-06,%
    {Bar}/\currentyear-11-07,%
    {Bar}/\currentyear-11-08,%
    {Bar}/\currentyear-11-09,%
    {Bar}/\currentyear-11-10,%
    {Bar}/\currentyear-11-13,%
    {Bar}/\currentyear-11-14,%
    {Bar}/\currentyear-11-15,%
    {Bar}/\currentyear-11-16,%
    {Bar}/\currentyear-11-17%
}

% What I want to do:
% \newcommand{\practicalCourse}{%
%     {Foo}/{\currentyear-10-09}/{\currentyear-11-10},%
%     {Bar}/{\currentyear-11-06}/{\currentyear-11-17}
% }

% Define searchable object (\ifdate{PracticalCourse})
\ExplSyntaxOn
    \clist_new:N \g_practical_course_clist%
    \int_new:N \l_practical_course_int%
    \foreach \i/\j in \practicalCourses {%
        \pgfcalendardatetojulian{\j}{\l_practical_course_int}%
        \clist_gput_right:Nx \g_practical_course_clist {%
            \int_to_arabic:n { \l_practical_course_int }%
        }%
    }%
    \cs_new_protected_nopar:Nn \practical_course_test:n {%
        \int_set:Nn \l_tmpa_int {#1}%
            \clist_if_in:NVT \g_practical_course_clist \l_tmpa_int {%
            \pgfcalendarmatchestrue%
        }%
    }%
    \cs_generate_variant:Nn \practical_course_test:n {x}%
    \NewDocumentCommand \testpraktikum { m } {%
        \practical_course_test:x { #1 }%
    }%
\ExplSyntaxOff

\tikzset{
    /pgf/calendar/PracticalCourse/.code={%
        \testpraktikum{\pgfcalendarifdatejulian}%
    },
}


% %%%%%%%%%%%%%%
% Begin Document
% %%%%%%%%%%%%%%

\begin{document}
    \centering
    \begin{tikzpicture}[every day/.style={anchor = north}]
        \calendar[
            dates = \calbegindate to \calenddate,
            name = cal,
            day yshift = 3em,
            day code = {
                \node[name = \pgfcalendarsuggestedname, every day, minimum height = .53cm, text width = 4.4cm, draw = gray] {\tikzdaytext};
                \draw (-1.8cm, -.1ex) node [anchor = west, font=\footnotesize] {\pgfcalendarweekdayshortname{\pgfcalendarcurrentweekday}};
            },
            execute before day scope={
                \ifdate{day of month = 1} {
                    \pgftransformxshift{4.8cm}
                    \draw (0,0) node [minimum height = .53cm, text width = 4.4cm, fill = definedcolor, text = white, draw = definedcolor, text centered] {\textbf{\pgfcalendarmonthname{\pgfcalendarcurrentmonth}\strut}};
                }{}
                \ifdate{workday} {
                    \tikzset{every day/.style = {fill = white}}
                    \ifdate{PracticalCourse}{%
                        \tikzset{every day/.style = {fill = olive!30}}%
                    }{}
                }{}
                \ifdate{Saturday} {
                    \tikzset{every day/.style = {fill = definedcolor!10}}%
                }{}
                \ifdate{Sunday} {
                    \tikzset{every day/.style = {fill = definedcolor!20}}%
                }{}
            },
            execute at begin day scope = {
                \pgftransformyshift{-.53*\pgfcalendarcurrentday cm}
            }
        ];
        \foreach \subject/\eventdate in \practicalCourses {
            \practicalCourse{\eventdate}{\subject}
        }
    \end{tikzpicture}
\end{document}
Sam
  • 2,958

1 Answers1

2

I've changed some stuff around.

The core of it is a key where we can control both condition (between two dates) and note (Foo or Bar).

This can be done with the key

/tikz/if=(<cond>) <true> else <false>

where <true> and <else> can either by [<style>] or {<code>} and the else <false> part is optional.

For typesetting the note I've used a label that is xshift = 3.5em away from the left side of the day node, but vertically aligned at the baseline (base west anchors).

Since you have overlapping dates, we can't just use label=[<opts>]<dir>:#1 because then we would get multiple labels on top of each other.

I've adjusted the day code so that the day note trys possible label with the value from collected labels (when it still is accessible – doing label=\pgfkeysvalueof{/tikz/collected label} would not work because the node options are inside their own group/scope.

But we also don't want every day node to try to set a label which is why we use the activate label to set possible label key only when our conditions apply.

A propos conditions, I've used the and key from the ext.calendar-plis library of my tikz-ext package.

Code

\documentclass[tikz]{standalone}

\usetikzlibrary{ext.calendar-plus}% loads calendar library

% Fonts \RequirePackage{lmodern} \renewcommand*\familydefault{\sfdefault}

% Color \definecolor{definedcolor}{HTML}{00CC00}

% Calculation \ExplSyntaxOn \DeclareExpandableDocumentCommand{\eval}{m}{\int_eval:n {#1}} \ExplSyntaxOff

% Variables \newcommand{\currentyear}{\the\year} \newcommand{\nextyear}{\eval{\currentyear + 1}} \newcommand{\calbegindate}{\currentyear-10-01} \newcommand{\calenddate}{\nextyear-03-31}

\newcommand*{\practicalCourses}{ {Foo}/{\currentyear-10-09}/{\currentyear-11-10}, {Bar}/{\currentyear-11-06}/{\currentyear-11-17}}

\begin{document} \begin{tikzpicture}[ every day/.style={anchor = north}, collect label/.style={ collected labels/.initial={#1}, collect label/.style={ collected labels/.append={, ##1}}}, % %%% is basically a \def\keymacro#1/#2/#3\STOP{<…>} %%% with the /tikz/if key we can control both conditions %%% and the true/false options/code %%% PracticalCourse/.style args={#1/#2/#3}{ if={(and={workday, between=#2 and #3})[ days={fill=olive!30, activate label, collect label={#1}} ]} }, % %%% activate label sets the possible label style %%% that uses the argument of possible label %%% hence ##1 and not #1 ( = would be value to activate label) activate label/.style={ possible label/.style={ label={[text=black, text width = 3.1cm, xshift=3.5em, anchor=base west, font=\scriptsize]base west:##1}}, activate label/.style=% deactivate me }] \calendar[ dates = \calbegindate to \calenddate, name = cal, day yshift = 3em, day code = { \node[name = \pgfcalendarsuggestedname, every day, minimum height = .53cm, text width = 4.4cm, draw = gray, % %%% try the possible label with the content of /tikz/collected labels %%% needs to be expanded because the value os lost after the node %%% possible label/.try/.expand twice/.expand once= \pgfkeysvalueof{/tikz/collected labels} ] {\tikzdaytext}; \draw (-1.8cm, -.1ex) node [anchor = west, font=\footnotesize] {%w.}; }, execute before day scope={ \ifdate{day of month = 1} {% \pgftransformxshift{4.8cm}% \node [minimum height = .53cm, text width = 4.4cm, fill = definedcolor, text = white, draw = definedcolor, text centered] {\textbf{%mt\strut}}; }{}% \ifdate{workday} {% \tikzset{every day/.style = {fill = white}}% }{}% \ifdate{Saturday} {% \tikzset{every day/.style = {fill = definedcolor!10}}% }{}% \ifdate{Sunday} {% \tikzset{every day/.style = {fill = definedcolor!20}}% }{}% }, execute at begin day scope = {% \pgftransformyshift{-.53*\pgfcalendarcurrentday cm}% }, %%% actual test four courses PracticalCourse/.list/.expand once=\practicalCourses ]; \end{tikzpicture} \end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821