mathmaker  0.4(alpha)
mathmaker_dev/sheet/exercise/X_Structure.py
00001 # -*- coding: utf-8 -*-
00002 
00003 # Mathmaker creates automatically maths exercises sheets
00004 # with their answers
00005 # Copyright 2006-2014 Nicolas Hainaux <nico_h@users.sourceforge.net>
00006 
00007 # This file is part of Mathmaker.
00008 
00009 # Mathmaker is free software; you can redistribute it and/or modify
00010 # it under the terms of the GNU General Public License as published by
00011 # the Free Software Foundation; either version 3 of the License, or
00012 # any later version.
00013 
00014 # Mathmaker is distributed in the hope that it will be useful,
00015 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 # GNU General Public License for more details.
00018 
00019 # You should have received a copy of the GNU General Public License
00020 # along with Mathmaker; if not, write to the Free Software
00021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00022 
00023 from lib import *
00024 from lib.common import default
00025 import question
00026 # ------------------------------------------------------------------------------
00027 # --------------------------------------------------------------------------
00028 # ------------------------------------------------------------------------------
00029 ##
00030 # @class X_Structure
00031 # @brief Mother class of all exercises objects. Not instanciable.
00032 # This class suggests two default methods which are also in the exercise.Model
00033 # class : write_text and write_answer. In a new exercise, they can either be
00034 # kept untouched (then it would be wise to delete them from the new exercise)
00035 # or rewritten.
00036 class X_Structure(object):
00037 
00038 
00039 
00040 
00041 
00042     # --------------------------------------------------------------------------
00043     ##
00044     #   @brief /!\ Must be redefined. Constructor.
00045     #   @warning Exception NotInstanciableObject.
00046     #   @param embedded_machine The machine to be used
00047     #   @param **options Any options
00048     def __init__(self, embedded_machine,
00049                  x_kind, AVAILABLE_X_KIND_VALUES, X_LAYOUTS,
00050                  X_LAYOUT_UNIT, **options):
00051         try:
00052             self.derived
00053         except AttributeError:
00054             raise error.NotInstanciableObject(self)
00055 
00056         self.machine = embedded_machine.clone(embedded_machine.language_code)
00057         self.machine.set_redirect_output_to_str(True)
00058 
00059         self.questions_list = list()
00060 
00061         # OPTIONS -------------------------------------------------------------
00062         # It is necessary to define an options field to pass the
00063         # possibly modified value to the child class
00064         self.options = options
00065 
00066         try:
00067             AVAILABLE_X_KIND_VALUES[x_kind]
00068         except KeyError:
00069             raise error.OutOfRangeArgument(x_kind, str(AVAILABLE_X_KIND_VALUES))
00070 
00071         x_subkind = 'default'
00072         if 'x_subkind' in options:
00073             x_subkind = options['x_subkind']
00074             # let's remove this option from the options
00075             # since we re-use it recursively
00076             temp_options = dict()
00077             for key in options:
00078                 if key != 'x_subkind':
00079                     temp_options[key] = options[key]
00080             self.options = temp_options
00081 
00082         if not x_subkind in AVAILABLE_X_KIND_VALUES[x_kind]:
00083             raise error.OutOfRangeArgument(x_subkind,
00084                                            str(AVAILABLE_X_KIND_VALUES[x_kind]))
00085 
00086         self.x_kind = x_kind
00087         self.x_subkind = x_subkind
00088 
00089         # Start number
00090         self.start_number = 0
00091         if 'start_number' in options:
00092             if not is_.an_integer(options['start_number']):
00093                 raise error.UncompatibleType(options['start_number'],
00094                                              "integer")
00095             if not (options['start_number'] >= 1):
00096                 raise error.OutOfRangeArgument(options['start_number'],
00097                                                "should be >= 1")
00098 
00099             self.start_number = options['start_number']
00100 
00101         # Number of questions
00102         self.q_nb = default.NUMBER_OF_QUESTIONS
00103         if 'number_of_questions' in options:
00104             if not is_.an_integer(options['number_of_questions']):
00105                 raise error.UncompatibleType(options['number_of_questions'],
00106                                              "integer")
00107             if not (options['number_of_questions'] >= 1):
00108                 raise error.OutOfRangeArgument(options['number_of_questions'],
00109                                                "should be >= 1")
00110 
00111             self.q_nb = options['number_of_questions']
00112 
00113 
00114         self.x_layout_unit = X_LAYOUT_UNIT
00115 
00116         if (self.x_kind, self.x_subkind) in X_LAYOUTS:
00117             self.x_layout = X_LAYOUTS[(self.x_kind, self.x_subkind)]
00118 
00119         else:
00120             self.x_layout = X_LAYOUTS['default']
00121 
00122 
00123         # END OF OPTIONS ------------------------------------------------------
00124 
00125 
00126 
00127 
00128 
00129 
00130 
00131     # --------------------------------------------------------------------------
00132     ##
00133     #   @brief Writes the text of the exercise|answer to the output.
00134     def to_str(self, ex_or_answers):
00135         M = self.machine
00136         layout = self.x_layout[ex_or_answers]
00137 
00138         result = ""
00139         if self.text[ex_or_answers] != "":
00140             result += self.text[ex_or_answers]
00141             result += M.write_new_line()
00142 
00143         q_n = 0
00144 
00145         for k in xrange(len(layout)/2):
00146             if layout[2*k] is None:
00147                 how_many = layout[2*k+1]
00148                 if layout[2*k+1] == 'all_left' or layout[2*k+1] == 'all':
00149                     how_many = len(self.questions_list) - q_n
00150                 for i in xrange(how_many):
00151                     result += self.questions_list[q_n].to_str(ex_or_answers)
00152                     if ex_or_answers == 'ans':
00153                         result += M.write_new_line(check=result[len(result)-2:])
00154                     q_n += 1
00155             else:
00156                 nb_of_cols = len(layout[2*k]) - 1
00157                 col_widths = layout[2*k][1:len(layout[2*k])]
00158                 nb_of_lines = layout[2*k][0]
00159                 if nb_of_lines == '?':
00160                     nb_of_lines = len(self.questions_list) / nb_of_cols + \
00161                         (0 if not len(self.questions_list) % nb_of_cols else 1)
00162                 content = []
00163                 for i in xrange(nb_of_lines):
00164                     for j in xrange(nb_of_cols):
00165                         if layout[2*k+1] == 'all':
00166                             nb_of_q_in_this_cell = 1
00167                         else:
00168                             nb_of_q_in_this_cell = layout[2*k+1][i*nb_of_cols+j]
00169                         cell_content = ""
00170                         for n in xrange(nb_of_q_in_this_cell):
00171                             empty_cell = False
00172                             if q_n >= len(self.questions_list):
00173                                 cell_content += " "
00174                                 empty_cell = True
00175                             else:
00176                                 cell_content += self.questions_list[q_n].\
00177                                                            to_str(ex_or_answers)
00178                             if ex_or_answers == 'ans' and not empty_cell:
00179                                 cell_content += M.write_new_line(\
00180                                        check=cell_content[len(cell_content)-2:])
00181                             q_n += 1
00182                         content += [cell_content]
00183 
00184                 result += M.write_layout((nb_of_lines, nb_of_cols),
00185                                           col_widths,
00186                                           content)
00187 
00188         return result