The following defines a question
environment that will check its key=value input with the keys date
and points
and compare those to some filter list you can define.
The filter list accepts the keys include
and exclude
. If you use include
without a value the initial behaviour is to include all questions, if you use exclude
without a value all questions are excluded.
Both include
and exclude
accept a value as well, the value can contain points
and date
. You can use these keys arbitrary often, which will add a rule to the filter list. And both can be used with either =
(just those matching), >=
(those with equal or more points/that date or later), and <=
(those with equal or less points/that date or earlier). As a special key both include
and exclude
accept all
, which will clear the filter list and include/exclude all questions.
If you use include
with a value before you used include
without a value or exclude
(with or without), this means that all questions are excluded except those matching the rules in the given value. The same is true for exclude
(all included except those matching the rules).
So for instance
\questionFilter{exclude={points<=2, date<=2019-12-31}}
means that all questions are included except those giving two or less points, or those being older than 2020-01-01.
You can also change filter rules mid-document, if you want to.
The question
environment itself will gobble its contents if its points
and/or date
don't match the filter rules. The gobbling is done by typesetting its contents into a box that isn't output.
You'll most likely want to tailor the code in \question@start
and \question@end
to your needs to get the output you want.
For new questions the points
default to 0, and the date
defaults to 0000-00-00.
The result of all of this is, that it doesn't matter whether a question
is in a file or in your main document, since the exclusion is done on an environment basis.
\documentclass[]{article}
\usepackage{expkv-def}
\makeatletter
% initial values of internals
\let\question@\relax
\newcommand*\question@check@{}
% internals for the filter definition
\newcommand\question@check[1]
{%
\edef\question@check@
{%
\unexpanded\expandafter{\question@check@#1}%
{\unexpanded\expandafter{\question@action}}%
}%
}
\newcommand*\question@datetonum[1]
{\numexpr\expandafter\question@datetonum@#1\relax}
\def\question@datetonum@#1-#2-{31*(12*#1+#2)+}
\newcommand*\question@check@points[4]
{\ifnum\question@points#1#2 #3#4\fi}
\newcommand*\question@check@date[4]
{\ifnum\question@datetonum\question@date#1\question@datetonum{#2} #3#4\fi}
% key=value setup for the filter
\ekvdefinekeys{qh23570filter-internal}
{
code date = \question@check{\question@check@date={#1}{}}
,code date >= \question@check{\question@check@date<{#1}\else}
,code date <= \question@check{\question@check@date>{#1}\else}
,code points = \question@check{\question@check@points={#1}{}}
,code points >= \question@check{\question@check@points<{#1}\else}
,code points <= \question@check{\question@check@points>{#1}\else}
,noval all = \let\question@check@\@empty\question@action
}
\ekvdefinekeys{qh23570filter}
{
noval include = \let\question@check@\@empty\let\question@\question@start
,code include =
\ifx\question@\relax\let\question@\question@remove\fi
\def\question@action{\let\question@\question@start}%
\questionFilter@{#1}%
,noval exclude = \let\question@check@\@empty\let\question@\question@remove
,code exclude =
\ifx\question@\relax\let\question@\question@start\fi
\def\question@action{\let\question@\question@remove}
\questionFilter@{#1}%
}
\ekvletkv{qh23570filter-internal}{date>} {qh23570filter-internal}{date >}
\ekvletkv{qh23570filter-internal}{date<} {qh23570filter-internal}{date <}
\ekvletkv{qh23570filter-internal}{points>}{qh23570filter-internal}{points >}
\ekvletkv{qh23570filter-internal}{points<}{qh23570filter-internal}{points <}
\ekvsetdef\questionFilter{qh23570filter}
\ekvsetdef\questionFilter@{qh23570filter-internal}
% key=value setup for the environment
\ekvdefinekeys{qh23570question}
{
store points = \question@points
,initial points = 0
,store date = \question@date
,initial date = 0000-00-00
}
\ekvsetdef\question@kv{qh23570question}
\newenvironment{question}[1][]
{%
\ifx\question@\relax
\PackageError{question}{Initial behaviour unset. Defaulting to include}{}%
\global\let\question@\question@start
\fi
\question@kv{#1}%
\question@check@
\question@
}
{\question@end}
\newcommand*\question@remove
{%
\setbox0\vbox\bgroup
\edef\question@end{\unexpanded\expandafter{\question@end\egroup}}%
\question@start
}
% TODO: Add the start and end code you need for questions that are included here
\newcommand*\question@start{\par\bigskip\section{Question}}
\newcommand*\question@end{}
\makeatother
% just to create many questions with less effort
\newcommand\testquestion[2]
{%
\begin{question}[points=#1, date=#2]
This is a question giving #1 points and created on #2.
\end{question}
}
% set the rules which questions are used
\questionFilter
{
include={points >= 2, points <= 0}
,exclude={points = 3, date <= 2020-10-09}
}
\begin{document}
\begin{question}[points=15, date=2021-12-04]
Does this answer your question?
\end{question}
% just to create many questions with less effort
\testquestion{0}{2020-10-08}
\testquestion{1}{2020-10-08}
\testquestion{2}{2020-10-08}
\testquestion{3}{2020-10-08}
\testquestion{4}{2020-10-08}
\testquestion{0}{2020-10-09}
\testquestion{1}{2020-10-09}
\testquestion{2}{2020-10-09}
\testquestion{3}{2020-10-09}
\testquestion{4}{2020-10-09}
\testquestion{0}{2020-10-10}
\testquestion{1}{2020-10-10}
\testquestion{2}{2020-10-10}
\testquestion{3}{2020-10-10}
\testquestion{4}{2020-10-10}
\end{document}

\begin{question}[point=3,date=2020-1-12] ...\end{question}
. This way thequestion
environment can just evaluate its key=value list forpoint
ordate
and act accordingly.