1

My goal is to have a year calendar with boxes instead of days that get filled according to the type of exercise I did that day. These exercise would be loaded from a .csv file in a format such as:

2020-01-01;2020-01-01;2020-01-03

2020-01-02; ...

where each column is a particular type of activity and I just need to add the date and the code will do the rest.

This is my work so far:

\documentclass{article}
\usepackage[a4paper, total={8.5in, 11in}]{geometry}
\usepackage{csvsimple}
\usepackage{tikz}
\usetikzlibrary{calendar}
\usepackage{pgfcalendar}

\begin{document}
\def\s{0.85} %size of box around day
\def\ss{6.3ex} %space between days
\def\ds{0.02}

\def\nexer{0.75} %1-1/number of exercise to be put into calendar

\def\cardio{red}
\def\str{blue}
\def\spo{green}
\def\oth{orange}
\begin{tikzpicture}[every calendar/.style={
                    week list, month label above centered, day xshift=\ss, day yshift=\ss,
                    day code = { 
                        \draw (-0.5*\s,0.5*\s) -- (0.5*\s,0.5*\s) -- (0.5*\s,-0.5*\s) -- (-0.5*\s,-0.5*\s) -- cycle;
                    }
                    }]
    \csvreader[no head, separator=semicolon]{cal.csv}{1=\dcar,2=\dstr,3=\dspo,4=\doth}{
    \calendar
        [dates=2020-01-01 to 2020-01-last]
        if (equals=\dcar) {\filldraw[\cardio] (-0.5*\s+\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,-0.5*\s+\s*\nexer+\ds) --  
            (-0.5*\s+\ds,-0.5*\s+\s*\nexer+\ds) -- cycle;}
        if (equals=\dstr) {\filldraw[\str,yshift=-5] (-0.5*\s+\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,-0.5*\s+\s*\nexer+\ds) --  
            (-0.5*\s+\ds,-0.5*\s+\s*\nexer+\ds) -- cycle;}
        if (equals=\dspo) {\filldraw[\spo,yshift=-10] (-0.5*\s+\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,-0.5*\s+\s*\nexer+\ds) --  
            (-0.5*\s+\ds,-0.5*\s+\s*\nexer+\ds) -- cycle;}
        if (equals=\doth) {\filldraw[\oth,yshift=-15] (-0.5*\s+\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,0.5*\s-\ds) --  
            (0.5*\s-\ds,-0.5*\s+\s*\nexer+\ds) --  
            (-0.5*\s+\ds,-0.5*\s+\s*\nexer+\ds) -- cycle;};}


\end{tikzpicture}
\end{document}

I have run into couple of problems, first of all this code won't work if there are different number of days for each activity, meaning the .csv columns are not the same length. Also csvreader creates this weird effect that the calendar is "doubled" because it draws over itself and I do not know how to get rid of it.

Any help appreciated.

EDIT1: How it looks right now Current state of calendar

5
  • Can you provide an example csv file and expected output?
    – Alan Xiang
    Nov 13, 2020 at 1:00
  • @AlanXiang I added the output so it should be clear how I would want it to look like. Example .csv file is in the beginning, number of columns corresponds to number of "activities/exercises" and each row is an entry but the problem is that in this system all exercises have to have the same amount of entries. Nov 13, 2020 at 13:27
  • In a CSV file, the number of columns is supposed to be fixed.
    – Alan Xiang
    Nov 13, 2020 at 15:09
  • @AlanXiang you probably wanted to write rows, in that cases I am looking for a solution that could do it differently, the .csv file can have different format, I just need it to be easy to add new entries. Nov 13, 2020 at 16:51
  • I am pretty sure the number of columns is fixed, and the number of rows is mutable. Anyways, I can come up with a solution if you prefer to write all dates in one row.
    – Alan Xiang
    Nov 14, 2020 at 2:24

1 Answer 1

2

I think what you want deviate from csv standard. Therefore, you need some custom implementation for your purposes.

enter image description here

\documentclass{article}
\usepackage[a4paper, total={8.5in, 11in}]{geometry}
\usepackage{csvsimple}
\usepackage{tikz}
\usetikzlibrary{calendar}
\usepackage{pgfcalendar}
\usepackage{expl3}

% sample csv file
\begin{filecontents*}{cal.csv}
2020-01-01;2020-01-10;2020-01-03
2020-01-02;2020-01-10;2020-01-05;2020-01-25
2020-01-06;2020-01-16;2020-01-03
2020-01-08;2020-01-09;2020-01-23
\end{filecontents*}

\newlength{\daywidth}
\setlength{\daywidth}{5.0ex} %size of box around day
\newlength{\dayinterval}
\setlength{\dayinterval}{6.3ex} %space between days
\newlength{\dayitemheight}
\setlength{\dayitemheight}{0.8ex}
\newlength{\dayiteminterval}
\setlength{\dayiteminterval}{0.2ex}

\ExplSyntaxOn

\seq_new:N \g_doc_dates_seq

\cs_set:Npn \doc_read_file:n #1 {
  \ior_open:Nn \g_tmpa_ior {#1}
  \seq_gclear:N \g_doc_dates_seq
  \ior_str_map_inline:Nn \g_tmpa_ior {
    \str_set:Nx \l_tmpa_str {\tl_trim_spaces:n {##1}}
    \str_if_empty:NF \l_tmpa_str {
      \seq_gput_right:NV \g_doc_dates_seq \l_tmpa_str
    }
  }
  \ior_close:N \g_tmpa_ior
}


\cs_set:Npn \doc_draw_command:nnn #1#2#3 {
  \node[#1,#2] at (#3.north) {};
}

\cs_generate_variant:Nn \regex_split:nnN {nVN}
\cs_generate_variant:Nn \doc_draw_command:nnn {xnx}

\newcommand{\drawfilerow}[3]{
  \iow_term:x {show: \fp_eval:n {-(#2 + 0.5) * (1.0ex)}pt}
  \int_compare:nT {1 <= #2 <= \seq_count:N \g_doc_dates_seq} {
    \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \g_doc_dates_seq {#2}}
    \regex_split:nVN {;} \l_tmpa_tl \l_tmpa_seq
    \seq_map_inline:Nn \l_tmpa_seq {
      \str_set:Nx \l_tmpa_str {\tl_trim_spaces:n {##1}}
      \str_if_empty:NF \l_tmpa_str {
        \doc_draw_command:xnx {
          minimum~width=0.98\daywidth,
          minimum~height=\dayitemheight,
          yshift=\fp_eval:n {-(#2 - 1) * (\dayitemheight + \dayiteminterval) -0.2pt}pt,
          anchor=north,
          inner~sep=0pt,
          outer~sep=0pt
        } {fill=#3} {#1-\str_use:N \l_tmpa_str};
      }
    }
  }
}

\newcommand{\readcalendarfile}[1]{
  \doc_read_file:n {#1}
}

\ExplSyntaxOff

\begin{document}

% read "csv" file
\readcalendarfile{cal.csv}

\begin{tikzpicture}[every calendar/.style={
                    week list, 
                    month label above centered, 
                    day xshift=\dayinterval, 
                    day yshift=\dayinterval,
                    day code = { 
                        \node[minimum width=\daywidth, 
                        minimum height=\daywidth,
                        name=\pgfcalendarsuggestedname,
                        draw=black] {};
                    }
                    }]
    \calendar (mycal) [dates=2020-01-01 to 2020-01-last];
    % use contents from "csv" file
    \drawfilerow{mycal}{1}{red};
    \drawfilerow{mycal}{2}{blue};
    \drawfilerow{mycal}{3}{green};
    \drawfilerow{mycal}{4}{orange};
\end{tikzpicture}


\end{document}
2
  • it is perfect! I am not skilled enough with TikZ to understand the \newcommands that you had to implement, can you recommend some resources on this topic? I have browsed before through the TikZ manual which is great but I can see that you also use Regex but lot of the syntax just goes over my head. Nov 15, 2020 at 21:07
  • 1
    Most of the programming is done with LaTeX3, here is an tutorial that I wrote. As a matter of fact, very little of this code has connection to TikZ. For TikZ/pgf, it is too complicated to study systematically. I would suggest learning from specific examples when you need them.
    – Alan Xiang
    Nov 15, 2020 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.