% ---------------------------------------------------------------------------- % the XSIM package - collections module % % eXercise Sheets IMproved % % ---------------------------------------------------------------------------- % Clemens Niederberger % Web: https://github.com/cgnieder/xsim % E-Mail: contact@mychemistry.eu % ---------------------------------------------------------------------------- % Copyright 2017--2020 Clemens Niederberger % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Clemens Niederberger. % ---------------------------------------------------------------------------- % If you have any ideas, questions, suggestions or bugs to report, please % feel free to contact me. % ---------------------------------------------------------------------------- \XSIMmodule{collections}{collect exercises and print collected exercises} \xsim_load_modules:n {base} \seq_new:N \g__xsim_collections_seq \bool_new:N \l____xsim_active_bool \msg_new:nnn {xsim} {collection-exists} { The~ collection~ `#1'~ you're~ trying~ to~ define~ \msg_line_context: \c_space_tl already~ exists. } \msg_new:nnn {xsim} {collection-active} { You~ can't~ print~ collection~ `#1'~ while~ it~ is~ still~ active~ \msg_line_context: } \msg_new:nnn {xsim} {unknown-collection} { You're~ trying~ to~ use~ the~ collection~ `#1'~ \msg_line_context: \c_space_tl but~ no~ collection~ with~ that~ name~ exists.~ You~ need~ to~ define~ it~ first! } \msg_new:nnn {xsim} {print-collection-choice} { The~ choice~ `#1'~ for~ the~ option~ `print-collection/print'~ does~ not~ exist.~ I'm~ using~ `exercises'~ instead~ \msg_line_context: } % ---------------------------------------------------------------------------- % #1: collection name \cs_new_protected:Npn \xsim_new_collection:n #1 { \xsim_if_collection_exist:nTF {#1} { \msg_error:nnn {xsim} {collection-exists} {#1} } { \xsim_verbose:n { Declaring~ new~ collection~ `#1'. } \xsim_attribute_set:nnn {collection} {#1} { \c_false_bool } \seq_gput_right:Nn \g__xsim_collections_seq {#1} \xsim_new_list:n {collection:#1} \seq_new:c {g__xsim_collection_#1_seq} } } % #1: collection name \prg_new_conditional:Npnn \xsim_if_collection_exist:n #1 {T,F,TF} { \seq_if_in:NnTF \g__xsim_collections_seq {#1} { \prg_return_true: } { \prg_return_false: } } \xsim_new_collection:n {all~ exercises} \cs_new_protected:Npn \xsim_foreach_collection:n #1 { \seq_map_inline:Nn \g__xsim_collections_seq {#1} } \cs_new:Npn \xsim_collection_map_break: { \seq_map_break: } % ---------------------------------------------------------------------------- % #1: collection name % #2: type \cs_new_protected:Npn \xsim_activate_collection:nn #1#2 { \xsim_if_collection_active:nTF {#1} { % TODO: error } { \xsim_verbose:n { Activating~ collection~ `#1'. } \xsim_attribute_set:nnn {collection} {#1} { \c_true_bool } \tl_if_blank:nF {#2} { \seq_gput_right:cn {g__xsim_collection_#1_seq} {#2} } } } \cs_new_protected:Npn \xsim_activate_collection:n #1 { \xsim_activate_collection:nn {#1} {} } % #1: collection name \cs_new_protected:Npn \xsim_deactivate_collection:n #1 { \xsim_if_collection_active:nTF {#1} { \xsim_verbose:n { Deactivating~ collection~ `#1'. } \xsim_attribute_set:nnn {collection} {#1} { \c_false_bool } % \seq_gclear:c {g__xsim_collection_#1_seq} } { % TODO: warning } } % start collection for an exercise type: % #1: collection name % #2: type \cs_new_protected:Npn \xsim_start_collection:nn #1#2 { \group_begin: \xsim_if_collection_exist:nF {#1} { \msg_error:nnn {xsim} {unknown-collection} {#1} } \xsim_activate_collection:nn {#1} {#2} \xsim_setup:x { \xsim_get_parameter:nn {#2} {exercise-env} /print = false } } \cs_generate_variant:Nn \xsim_setup:n {x} % start collection for all exercise types: % #1: collection name \cs_new_protected:Npn \xsim_start_collection:n #1 { \group_begin: \xsim_if_collection_exist:nF {#1} { \msg_error:nnn {xsim} {unknown-collection} {#1} } \xsim_activate_collection:n {#1} \xsim_foreach_exercise_type:n { \xsim_setup:x { \xsim_get_parameter:nn {##1} {exercise-env} /print = false } } } % stop collection: % #1: collection name \cs_new_protected:Npn \xsim_stop_collection:n #1 { \xsim_deactivate_collection:n {#1} \group_end: } % ---------------------------------------------------------------------------- % #1: collection name % #2: type % #3: id \cs_new_protected:Npn \xsim_add_to_collection:nnn #1#2#3 { \seq_if_empty:cTF {g__xsim_collection_#1_seq} { \__xsim_add_to_collection:nnn {#1} {#2} {#3} } { \seq_if_in:cnF {g__xsim_collection_#1_seq} {#2} { \__xsim_add_to_collection:nnn {#1} {#2} {#3} } } } % #1: collection name % #2: type % #3: id \cs_new_protected:Npn \__xsim_add_to_collection:nnn #1#2#3 { \xsim_verbose:n { Adding~ exercise~ type~ `#2'~ id~ `#3'~ to~ collection~ `#1'. } \xsim_add_to_list:nn {collection:#1} {#2-#3} } \cs_new:Npn \xsim_collection_count:n #1 { \xsim_list_count:n {collection:#1} } % #1: type % #2: id \cs_new_protected:Npn \xsim_update_collections:nn #1#2 { \xsim_if_insert_mode:F { \xsim_exercise_if_use:nnT {#1} {#2} { \xsim_add_to_collection:nnn {all~ exercises} {#1} {#2} } \xsim_foreach_collection:n { \xsim_exercise_if_use:nnTF {#1} {#2} { \xsim_if_collection_active:nT {##1} { \xsim_add_to_collection:nnn {##1} {#1} {#2} } } { \xsim_remove_from_list:nn {collection:##1} {#1-#2} } } } } \cs_generate_variant:Nn \xsim_update_collections:nn {nV} % #1: collection name \cs_new_protected:Npn \xsim_get_collections: { \xsim_foreach_collection:n { \xsim_foreach_list_entry:nn {collection:##1} { \__xsim_list_to_collection:nww {##1} ####1 \q_stop } } } \cs_new_protected:Npn \__xsim_list_to_collection:nww #1#2-#3 \q_stop { \xsim_add_to_collection:nnn {#1} {#2} {#3} } % ---------------------------------------------------------------------------- % #1: collection name % #2: type % #3: id \prg_new_conditional:Npnn \xsim_if_in_collection:nnn #1#2#3 {T,F,TF} { \xsim_if_in_list:nnTF {collection:#1} {#2-#3} { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \xsim_if_in_collection:nnnTF {V} \cs_generate_variant:Nn \xsim_if_in_collection:nnnT {x,nV} % ---------------------------------------------------------------------------- \bool_new:N \l__xsim_printcollection_headings_bool \tl_new:N \l__xsim_printcollection_headings_template_tl \tl_new:N \l__xsim_print_collection_choice_tl \keys_define:nn {xsim/print-collection} { headings .bool_set:N = \l__xsim_printcollection_headings_bool , headings .initial:n = false , headings-template .tl_set:N = \l__xsim_printcollection_headings_template_tl , headings-template .initial:n = collection , print .choice: , print/exercises .code:n = \tl_set:Nn \l__xsim_print_collection_choice_tl {exercise} , print/solutions .code:n = \tl_set:Nn \l__xsim_print_collection_choice_tl {solution} , print/both .code:n = \tl_set:Nn \l__xsim_print_collection_choice_tl {both} , print/unknown .code:n = \msg_warning:nnn {xsim} {print-collection-choice} {#1} \tl_set:Nn \l__xsim_print_collection_choice_tl {exercise} , print .initial:n = exercises } % #1: options % #2: collection name \cs_new_protected:Npn \xsim_print_collection:nn #1#2 { \xsim_if_collection_active:nTF {#2} { \msg_error:nnn {xsim} {collection-active} {#2} } { \xsim_verbose:x { Printing~ \l__xsim_print_collection_choice_tl \c_space_tl \str_if_eq:onT { \l__xsim_print_collection_choice_tl } {both} { exercises~ and~ solutions~ } of~ collection~ `#2' } \group_begin: \keys_set:nn {xsim/print-collection} {#1} \tl_set:Nn \ExerciseCollection {#2} \xsim_foreach_exercise_type:n { \tl_set:Nn \ExerciseType {##1} \bool_if:NT \l__xsim_printcollection_headings_bool { \bool_set_false:N \l__xsim_tmpa_bool \xsim_foreach_exercise_id:n { \xsim_if_in_collection:nnnT {#2} {##1} {####1} { \tl_if_eq:VnTF \l__xsim_print_collection_choice_tl {both} { \xsim_if_exercise_exist:nnT {##1} {####1} { \bool_set_true:N \l__xsim_tmpa_bool } } { \use:c { xsim_if_ \l__xsim_print_collection_choice_tl _exist:nnT } {##1} {####1} { \bool_set_true:N \l__xsim_tmpa_bool } } } } \bool_if:NT \l__xsim_tmpa_bool { \xsim_use_template:nV {heading} \l__xsim_printcollection_headings_template_tl } } \xsim_foreach_exercise_id:n { \xsim_if_in_collection:nnnT {#2} {##1} {####1} { \tl_if_eq:VnTF \l__xsim_print_collection_choice_tl {both} { \xsim_if_exercise_exist:nnT {##1} {####1} { \xsim_insert:nnnn {##1} {####1} {} {exercise} } \xsim_if_solution_exist:nnT {##1} {####1} { \xsim_insert:nnnn {##1} {####1} {} {solution} } } { \use:c { xsim_if_ \l__xsim_print_collection_choice_tl _exist:nnT } {##1} {####1} { \xsim_insert:nnnV {##1} {####1} {} \l__xsim_print_collection_choice_tl } } } } } \group_end: } } % ---------------------------------------------------------------------------- % #1: collection name \prg_new_conditional:Npnn \xsim_if_collection_active:n #1 {T,F,TF} { \xsim_attribute_if_set:nnTF {collection} {#1} { \bool_lazy_and:nnTF { !\str_if_eq_p:en {#1} {all~ exercises} } { \xsim_attribute_get:nn {collection} {#1} } { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } \prg_new_protected_conditional:Npnn \xsim_if_collections_active: {T,F,TF} { \bool_set_false:N \l____xsim_active_bool \xsim_foreach_collection:n { \xsim_if_collection_active:nT {##1} { \bool_set_true:N \l____xsim_active_bool \xsim_collection_map_break: } } \bool_if:NTF \l____xsim_active_bool { \prg_return_true: } { \prg_return_false: } } % ---------------------------------------------------------------------------- \xsim_at_begin_document:n { \xsim_get_collections: \xsim_activate_collection:n {all~ exercises} } \xsim_at_end_document:n { \xsim_deactivate_collection:n {all~ exercises} \xsim_foreach_collection:n { \xsim_update_list:n {collection:#1} } } % ---------------------------------------------------------------------------- \file_input_stop: