It is possible to achieve that using the rdfref package. It is not available on CTAN, so it needs to be installed manually to the $TEXMFHOME/tex/latex/rdfref
directory.
I would modify your TeX file a bit:
\documentclass{article}
\usepackage{attendees}
\begin{document}
\maketableofattendees{Attendees}
\maketableofpolls{Polls}
\joins[20:00]{Adam}
\joins[20:00]{Bob}
\topic{First Topic}
Adam Speaks about a great topic and does a long speech.
\poll{First Poll}{2}{0}
% First Poll: \attendeecount People Present. Yes: 2, No: 0
\joins[20:15]{Charlie}
\topic{Second Topic}
Bob speaks about the second topic.
\poll{Second Poll}{1}{2}%: \attendeecount People Present. Yes: 1, No :2
\leaves[20:30]{Bob}
\topic{Third Topic}
Adam speaks about the third topic.
\poll{Third Poll}{1}{1}%: \attendeecount People Present. Yes: 1, No :1
\end{document}
It uses an attendees
custom package, it contains definitions for commands used in the document. I've added some new commands, namely \maketableofpolls
which lists the used polls and topics, \topic
which is used instead of the \section
, and \poll
which records poll name, votes and prints them.
Here is the attendees.sty
package:
\ProvidesPackage{attendees}
\RequirePackage{rdfref-user,rdfref-query}
\newcommand\maketableofpolls[1]{%
\section*{#1}%
\Bind{?topic}{rdf:type}{at:topic}{%
\noindent\textbf{\GetValProperty{?topic}{at:topicName}}\dotfill\GetValProperty{?topic}{doc:pageNo}\par
\Bind{?poll}{at:pollTopic}{?topic}{%
\noindent \quad\GetValProperty{?poll}{at:pollName}. Yes: \GetValProperty{?poll}{at:pollYes}, no: \GetValProperty{?poll}{at:pollNo}\dotfill\GetValProperty{?poll}{doc:pageNo}\par
}
}
}
% \newcommand\at@printpoll
\newcommand\maketableofattendees[1]{%
\section*{#1}%
\Bind{?name}{rdf:type}{at:attendee}{%
% \GetVal prints the variable name, \GetValProperty extracts property from the name
% Set the default left time
\def\at@left{end}%
% use the real at:left if it has been set
\IfProperty{\GetVal{?name}}{at:left}{\def\at@left{\GetValProperty{?name}{at:left}}}{}%
\at@printname{\GetValProperty{?name}{at:name}}{\GetValProperty{?name}{at:joined}}{\at@left}%
}%
}
\newcommand\at@printname[3]{\noindent\textbf{#1}~(From #2 to #3)\par}
\newcommand\topic[1]{%
\section{#1}%
\BlankNode%
\AddProperty{rdf:type}{at:topic}%
\AddPropertyEx{doc:pageNo}{\thepage}% save the page number
\AddProperty{at:topicName}{#1}%
\edef\CurrentTopic{\CurrentObject}% we want to reference the current topic in the poll
}
\newcommand\joins[2][]{%
\WithObject{#2}{%
\AddProperty{rdf:type}{at:attendee}%
\AddProperty{at:name}{#2}%
\AddProperty{at:joined}{#1}%
\AddProperty{is:active}{yes}
}%
}
\newcommand\leaves[2][]{%
\WithObject{#2}{%
\AddProperty{at:left}{#1}%
\AddProperty{is:active}{} % remove the is:active property, so it doesn't pollute next \poll
}%
}
\newcounter{poll@count}
\newcommand\poll[3]{%
\setcounter{poll@count}{0}%
\BlankNode%
\AddProperty{rdf:type}{at:poll}%
\AddPropertyEx{doc:pageNo}{\thepage}%
\AddProperty{at:pollName}{#1}%%
\AddPropertyEx{at:pollTopic}{\CurrentTopic}%
\AddProperty{at:pollYes}{#2}%
\AddProperty{at:pollNo}{#3}%
% process all attendands%
\Bind{?name}{rdf:type}{at:attendee}{%%
% execute the code only on active attendands
\IfProperty{\GetVal{?name}}{is:active}{\stepcounter{poll@count}}{}
}%
\par
\medskip%
\noindent\textbf{#1}: \arabic{poll@count} People Present. Yes: #2, No: #3\par
}%
\AtBeginDocument{%
% reset attendands
\Bind{?name}{rdf:type}{at:attendee}{\SetProperty{\GetVal{?name}}{is:active}{}}
}
\endinput
Quite a lot of stuff happens here. rdfref
provides commands that can set properties to objects. These properties are saved to the aux file, so they are available from the beginning of the document in the next compilation. This means that the document needs two compilations each time an object changes!
There are two types of objects, named and anonymous. In our example, named objects are people that can join and leave the meeting. The following command:
\newcommand\leaves[2][]{%
\WithObject{#2}{%
\AddProperty{at:left}{#1}%
\SetProperty{#2}{is:active}{} % remove the is:active property, so it doesn't pollute next \poll
}%
}
will set the time person left and makes the person inactive, so it doesn't count in the next poll. The \WithObject
command sets the person name as a current object identifier and properties are saved to this object.
Two commands for property handling are used. The first one is \AddProperty
, it sets a property of a current object and saves it to the aux
file, which means that this property will be restored next time the document is compiled and it will be available from the beginning. It enables us to make a list of attendands and list of polls, for example. Other kind of properties is just temporary, like in this case, where we just need to disable this person in the next poll count. We can use the \SetProperty
command. It needs an explicit object identifier as the first parameter.
In contrast, each poll creates anonymous object:
\newcommand\poll[3]{%
\setcounter{poll@count}{0}%
\BlankNode%
\AddProperty{rdf:type}{at:poll}%
\AddPropertyEx{doc:pageNo}{\thepage}%
...
The \BlankNode
creates an anonymous object and subsequent \AddProperty
commands set properties for that. \AddPropertyEx
can be used to save the expanded value of a macro, such as the page number as in our case.
The properties can be queried using the \Bind
command:
\newcommand\maketableofpolls[1]{%
\section*{#1}%
\Bind{?topic}{rdf:type}{at:topic}{%
\noindent\textbf{\GetValProperty{?topic}{at:topicName}}\dotfill\GetValProperty{?topic}{doc:pageNo}\par
\Bind{?poll}{at:pollTopic}{?topic}{%
\noindent \quad\GetValProperty{?poll}{at:pollName}. Yes: \GetValProperty{?poll}{at:pollYes}, no: \GetValProperty{?poll}{at:pollNo}\dotfill\GetValProperty{?poll}{doc:pageNo}\par
}
}
}
It takes four parameters. The first three are the query and the last one is code that is executed on matched properties. In this case, we are first looking for all topics. For each object, we set rdf:type
property, at:topic
for topics, at:poll
for polls and at:attendee
for attendees. So we need to search for all objects with rdf:type
set to at:topic
:
\Bind{?topic}{rdf:type}{at:topic}{%
The ?topic
parameter is a variable that will contain the topic object identifier in the executed code in the fourth parameter. Here we can get various parameters of the object using the \GetValProperty
command:
\noindent\textbf{\GetValProperty{?topic}{at:topicName}}\dotfill\GetValProperty{?topic}{doc:pageNo}\par
In this case he topic name and the page number where it starts are used.
\Bind
commands can be nested. To get all polls for a topic, we can use the following command which is nested in the previous \Bind
:
\Bind{?poll}{at:pollTopic}{?topic}{%
Here two variables are used. The ?topic
is set from the parent bind, so it contains the current topic object. So only ?poll
is searched in this command.
Another use of \Bind
in in the \poll
command, where it is used for counting of the current number of attendees:
\Bind{?name}{rdf:type}{at:attendee}{%%
% execute the code only on active attendands
\IfProperty{\GetVal{?name}}{is:active}{\stepcounter{poll@count}}{}
}%
The \IfProperty
command tests if object has a property. \GetVal
gets the object identifier from the bind variable.
Because all properties are set to the auxilary file, they have the same values as at the end of the document. It is thus necessary to set all attendees as inactive at the document start and enable them only in the \join
command:
\AtBeginDocument{%
% reset attendands
\Bind{?name}{rdf:type}{at:attendee}{\SetProperty{\GetVal{?name}}{is:active}{}}
}
The \SetProperty
directly sets the property to an object. If the value is empty, it removes the property.
This is the result:
