示例#1
0
class PdfForm(object):
    def __init__(self, xml_file, config_file=None):
        self.tree = etree.parse(xml_file)
        self.cur_form = None
        self.multiline = True
        self.header_box = 'box'
        self.all_same_page = False

        self.doc = None
        self.indent_stack = {}
        self.logic_parser = LogicParser()
        self.to_print = []
        self.print_selected_only = False

        if config_file != None:
            self.config = ConfigParser()
            self.config.read(config_file)
        self._indent = True
        self.print_const_name = None

    def revert_indent_val(self):
        self._indent = not self._indent

    def add_constraint_list(self, const_section):
        '''If  proceessed without running add_constraint_list
        then will print all forms and elements in the project.

        Arguments:
        const_section -- The section in the config_file to use along with
            the base seciton
        '''
        multiline = True
        if not self.config:
            raise ValueError("self.config is {config}: Please set the config"
                    " file before adding constraint.".format(config=self.config))

        sections = ['base', const_section]

        for sec in sections:
            if self.config.has_section(sec):
                for name, vals in self.config.items(sec):
                    if name == '__print_name':
                        #Flag for whether or not the form name should be
                        #printed at the top of the form
                        if vals == 'True':
                            self.print_const_name=const_section
                    elif name == '__multiline':
                        # If flase each element is printed on an individual line
                        # If true as many elements that can fit on a line are
                        # printed 
                        self.multiline = eval(vals)
                    elif name == '__all_same_page':
                        # Print all the forms together on one page
                        self.all_same_page = eval(vals)
                    elif name == '__header_box':
                        #The values to put in the header box
                        self.header_box = vals
                    elif name == '__regex_no_print':
                        # A list of regular expressions. If the regular
                        # expression match a field name it will not be printed
                        vals = vals.split(",")
                        self.logic_parser.add_no_print_regex(vals)
                    elif name == '__print_selected_only':
                        # Only print the values that are specified instead of
                        # all the options
                        self.print_selected_only = eval(vals)
                    elif name != '__forms':
                        # Add parsing Logic constriaints. You designate values
                        # in the const_* file and only print if conditions are
                        # met.
                        self.logic_parser.add_constraint(name, eval(vals))
            else:
                if sec != 'base':
                    raise ConstraintError(
                            '{section} is not in the constraint file.'.format(
                                section=sec), sec
                    )


    def _get_choices(self, choices):
        ''' Return a list of valid choices for the current question.

        Arguments:
        choices -- the string from REDCap data dictionary containing the
        choices.
        '''
        vals = choices.split("\\n")

        if len(vals) > 1:
            return map(lambda c: c.lstrip(' 0123456789').lstrip(',').strip(' '),
                       vals)
        else:
            vals = choices.split("|", 2)
            if len(vals) > 1:
                return vals
            elif len(vals) == 1:
                one = re.match("\s?[0-9.-]*\s?,\s?(.*)",vals[0]);
                if one:
                    return [one.group(1)]
        return None

    def _get_level(self, names):
        ''' Return the set of all the field names that the current field is
        branched under

        Arguments:
        names -- The branching logic string from the redcap export
        '''
        vals = re.split(' and | or ', names)
        indented_test = []
        if vals[0] != '':
            for v in vals:
                logic = v
                long_form = re.search(
                        '\[([a-z_0-9]*)\](\[[a-z_0-9\(\)]*\][0-9 \'=]*)', v)

                if long_form:
                    v = long_form.group(2)

                word = re.search('\[([a-z_0-9\(\)]*)\]([0-9 \'=]*)', v)
                if word != None:
                    val = word.group(2)
                    logic = word.group(1)
                    rm_num = re.search('([a-z_0-9]*)\(([0-9]*)\)', logic)
                    if rm_num != None:
                        #Pull out the number for checkboxes
                        logic = rm_num.group(1)
                        check_num = rm_num.group(2)
                indented_test.append(logic)
        return set(indented_test)

    def _indent_ques(self, logic, cur_field):
        ''' Indent the left margin as necessary for the field's level.

        Arguments:
        logic -- The set of fields that the current field is nested under
        cur_field -- The current field name.
        '''
        if self.indent_stack == {}:
            if len(logic) > 0:
                self.indent_stack[cur_field] = 1
                self.doc.start_indent()
                self.all_forms.start_indent()
        else:
            top = sorted(self.indent_stack.values()).pop()
            if len(logic) == 0:
                for k in self.indent_stack.keys():
                    self.doc.stop_indent()
                    self.all_forms.stop_indent()
                self.indent_stack = {}
                return {}
            high = 0
            for l in logic:
                if self.indent_stack.has_key(l):
                    if self.indent_stack[l] > high:
                        high = self.indent_stack[l]
            if high == 0:
                num = 0
                for k in self.indent_stack.keys():
                    if num > 0:
                        self.doc.stop_indent()
                        self.all_forms.stop_indent()
                    del self.indent_stack[k]
                    num += 1
                self.indent_stack[cur_field] = 1
            elif high == top:
                self.doc.start_indent()
                self.all_forms.start_indent()
                self.indent_stack[cur_field] = top + 1
            else:
                num = 0
                for k, v in self.indent_stack.items():
                    if v > high:
                        del self.indent_stack[k]
                        if num > 0:
                            self.doc.stop_indent()
                            self.all_forms.stop_indent()
                        num += 1
                self.indent_stack[cur_field] = high + 1

    def __reset_indent(self):
        if self.indent_stack != {}:
            top = sorted(self.indent_stack.values()).pop()
            for val in range(top):
                self.all_forms.stop_indent()

    def _create_form(self, const, form_name):
        if const != None:
            self.doc = RedcapForm("{constraint}_{form_name}.pdf"
                    .format(constraint=const, form_name=form_name),
                    self.multiline, self.header_box
            )
        else:
            self.doc = RedcapForm("{form_name}.pdf".format(
                form_name=form_name),self.multiline, self.header_box
            )
        if self.print_const_name != None:
            self.doc.print_const_name(self.print_const_name)
            self.all_forms.print_const_name(self.print_const_name)
        self.doc.setup()
        self.doc.form_name(form_name)


    def process(self, const):
        ''' Parse and create the RedcapForm associated with the REDcap XML data
        dictionary.
        '''
        self.all_forms = RedcapForm('ALL.pdf', self.multiline, self.header_box)
        self.all_forms.setup()

        try:
            iterator = self.tree.iter('item')
        except AttributeError:
            iterator = self.tree.findall('item')

        for item in iterator:
            form_name = item.findtext('form_name')
            if self.to_print == [] or form_name in self.to_print:
                name = form_name.replace('_', " ")
                prop_name = string.capwords(name)
                if self.cur_form == None:
                    self._create_form(const, item.findtext('form_name'))
                    self.all_forms.form_name(prop_name)
                    self.cur_form = item.findtext('form_name')

                elif not self.cur_form == item.findtext('form_name'):
                    self.doc.render()
                    self.__reset_indent()
                    self._create_form(const, item.findtext('form_name'))

                    self.indent_stack = {}
                    if self.print_const_name != None:
                        self.doc.print_const_name(self.print_const_name)
                        self.all_forms.print_const_name(self.print_const_name)
                    if not self.all_same_page:
                        if self.all_forms.form_name_current:
                            self.all_forms.remove_header_footer(
                                    self.all_forms.form_name_current)
                        self.all_forms.new_page()
                    self.all_forms.form_name(prop_name)

                    self.cur_form = item.findtext('form_name')

                redcap_types = {
                    'number': RedcapForm.number_element,
                    'integer': RedcapForm.integer_element,
                    'text': RedcapForm.text_element,
                    'checkbox': RedcapForm.check_box_element,
                    'yesno': RedcapForm.yesno_element,
                    'dropdown': RedcapForm.radio_element,
                    'date_mdy': RedcapForm.date_element,
                    'date' : RedcapForm.date_ymd,
                    'date_dmy' : RedcapForm.date_dmy,
                    'date_ymd' : RedcapForm.date_ymd,
                    'truefalse': RedcapForm.truefalse_element,
                    'notes': RedcapForm.note_element,
                    'descriptive': RedcapForm.descriptive_element,
                    'sql': RedcapForm.sql_element,
                    'radio': RedcapForm.radio_element,
                    'calc': RedcapForm.calculated_element,
                    'file' : RedcapForm.no_print,
                    'time' : RedcapForm.time,
                    'time_mm_ss' : RedcapForm.time_mm_ss,
                    'datetime_mdy' : RedcapForm.datetime_mdy,
                    'datetime_dmy' : RedcapForm.datetime_dmy,
                    'datetime_ymd' : RedcapForm.datetime_ymd,
                    'datetime_seconds_dmy' : RedcapForm.datetime_sec_dmy,
                    'datetime_seconds_mdy' : RedcapForm.datetime_sec_mdy,
                    'datetime_seconds_ymd' : RedcapForm.datetime_sec_ymd,
                    'slider' : RedcapForm.slider_element,
                }

                field_text = clean_html(item.findtext('field_label'))
                field_type = item.findtext('field_type')
                field_name = item.findtext('field_name')
                branching_logic = item.findtext('branching_logic')

                if field_type == 'text':
                    spec = item.findtext(
                            'text_validation_type_or_show_slider_number')
                    if spec != '':
                        field_type = spec
                choices = self._get_choices(item.findtext(
                        'select_choices_or_calculations'))
                if (branching_logic =='' or
                    self.logic_parser.evaluate(field_name, branching_logic)):
                    if self._indent != '':
                        logic_vals = self._get_level(branching_logic)
                        indent_stack = self._indent_ques(logic_vals,
                                                         field_name)
                    if item.findtext('section_header'):
                        self.doc.section_name(clean_html(item.findtext(
                                'section_header')))
                        self.all_forms.section_name(clean_html(item.findtext(
                                'section_header')))
                    if choices != None:
                        if self.print_selected_only:
                            if self.logic_parser.has_constraint(field_name):
                                valid_choices = (self.logic_parser
                                        .get_const_vals(field_name))
                                choice_list=[]
                                for n in valid_choices:
                                    choice_list.append(choices[n-1])
                                choices=choice_list
                        if redcap_types.has_key(field_type):
                            redcap_types[field_type](
                                    self.doc, field_text, choices)
                            redcap_types[field_type](
                                    self.all_forms, field_text, choices)
                        else:
                            redcap_types['text'](self.doc, field_text)
                            redcap_types['text'](self.all_forms, field_text)
                    else:
                        if redcap_types.has_key(field_type):
                            redcap_types[field_type](self.doc, field_text)
                            redcap_types[field_type](self.all_forms, field_text)
                        else:
                            redcap_types['text'](self.doc, field_text)
                            redcap_types['text'](self.all_forms, field_text)
        self.all_forms.render()
        self.doc.render()
class PdfForm(object):
    def __init__(self, xml_file, config_file=None):
        self.tree = etree.parse(xml_file)
        self.cur_form = None

        self.doc = None
        self.indent_stack = {}
        self.logic_parser = LogicParser()
        self.to_print = []

        if config_file != None:
            self.config = ConfigParser()
            self.config.read(config_file)
        self._indent = True
        self.print_const_name=None

    def revert_indent_val(self):
        self._indent = not self._indent

    def add_constraint_list(self, const_section):
        '''If  proceessed without running add_constraint_list
        then will print all forms and elements in the project.

        Arguments:
        const_section -- The section in the config_file to use along with
            the base seciton
        '''
        if not self.config:
            raise ValueError("self.config is %(self.config)s: Please set the config file before adding constraint." %({'config': self.config, 
                                                }))
        sections = ['base', const_section]
        for sec in sections:
            if self.config.has_section(sec):
                for name, vals in self.config.items(sec):
                    if name == '__print_name':
                        if vals == 'True':
                            self.print_const_name=const_section
                    elif name != '__forms':
                        self.logic_parser.add_constraint(name, eval(vals))
            else:
                if sec != 'base':
                    raise ConstraintError('%(sec)s is not in the constraint file.', sec);

    def _get_choices(self, choices):
        ''' Return a list of valid choices for the current question.

        Arguments:
        choices -- the string from REDCap data dictionary containing the
        choices.
        '''
        vals = choices.split("\\n")
        if len(vals) > 1:
            return map(lambda c: c.lstrip(' 0123456789').lstrip(',').strip(' '), vals)
        else:
            vals = choices.split("|", 2)
            if len(vals) > 1:
                return vals
            elif len(vals) == 1:
                one = re.match("\s?[0-9.-]*\s?,\s?(.*)",vals[0]);
                if one:
                    return [one.group(1)] 
        return None

    def _get_level(self, names):
        ''' Return the set of all the field names that the current field is
        branched under

        Arguments:
        names -- The branching logic string from the redcap export
        '''
        vals = re.split(' and | or ', names)
        indented_test = []
        if vals[0] != '':
            for v in vals:
                logic = v
                long_form = re.search('\[([a-z_0-9]*)\](\[[a-z_0-9\(\)]*\][0-9 \'=]*)', v)
                if long_form:
                    v = long_form.group(2)
                word = re.search('\[([a-z_0-9\(\)]*)\]([0-9 \'=]*)', v)
                if word != None:
                    val = word.group(2)
                    logic = word.group(1)
                    rm_num = re.search('([a-z_0-9]*)\(([0-9]*)\)', logic)
                    if rm_num != None:
                        #Pull out the number for checkboxes
                        logic = rm_num.group(1)
                        check_num = rm_num.group(2)
                indented_test.append(logic)
        return set(indented_test)

    def _indent_ques(self, logic, cur_field):
        ''' Indent the left margin as necessary for the field's level.

        Arguments:
        logic -- The set of fields that the current field is nested under
        cur_field -- The current field name.
        '''
        if self.indent_stack == {}:
            if len(logic) > 0:
                self.indent_stack[cur_field] = 1
                self.doc.start_indent()
        else:
            top = sorted(self.indent_stack.values()).pop()
            if len(logic) == 0:
                for k in self.indent_stack.keys():
                    self.doc.stop_indent()
                self.indent_stack={}
                return {}
            high = 0
            for l in logic:
                if self.indent_stack.has_key(l):
                    if self.indent_stack[l] > high:
                        high = self.indent_stack[l]
            if high == 0:
                num = 0
                for k in self.indent_stack.keys():
                    if num > 0:
                        self.doc.stop_indent()
                    del self.indent_stack[k]
                    num+=1
                self.indent_stack[cur_field] = 1
            elif high == top:
                self.doc.start_indent()
                self.indent_stack[cur_field] = top + 1
            else:
                num = 0
                for k, v in self.indent_stack.items():
                    if v > high:
                        del self.indent_stack[k]
                        if num > 0:
                            self.doc.stop_indent()
                        num+=1
                self.indent_stack[cur_field] = high + 1

    def process(self,const):
        ''' Parse and create the RedcapForm associated with the REDcap XML data
        dictionary.
        '''
        for item in self.tree.iter('item'):
            form_name = item.findtext('form_name')
            if self.to_print == [] or form_name in self.to_print:
                name = form_name.replace('_', " ")
                prop_name = string.capwords(name)
                if self.cur_form == None:
                    if const != None:
                        self.doc = RedcapForm(const + '_' + item.findtext('form_name')+".pdf")
                    else:
                        self.doc = RedcapForm(item.findtext('form_name')+".pdf")
                    if self.print_const_name != None:
                        self.doc.print_const_name(self.print_const_name)
                    self.doc.setup()
                    self.doc.form_name(prop_name)
                    self.cur_form = item.findtext('form_name')
                elif not self.cur_form == item.findtext('form_name'):
                    self.doc.render()
                    if const != None:
                        self.doc = RedcapForm(const + '_' + item.findtext('form_name')+".pdf")
                    else:
                        self.doc = RedcapForm(item.findtext('form_name')+".pdf")
                    self.indent_stack = {}
                    if self.print_const_name != None:
                        self.doc.print_const_name(self.print_const_name)
                    self.doc.setup()
                    self.doc.form_name(prop_name)
                    self.cur_form = item.findtext('form_name')

                redcap_types = {
                    'number': RedcapForm.number_element,
                    'integer': RedcapForm.integer_element,
                    'text': RedcapForm.text_element,
                    'checkbox': RedcapForm.check_box_element,
                    'yesno': RedcapForm.yesno_element,
                    'dropdown': RedcapForm.radio_element,
                    'date_mdy': RedcapForm.date_element,
                    'date' : RedcapForm.date_ymd,
                    'date_dmy' : RedcapForm.date_dmy,
                    'date_ymd' : RedcapForm.date_ymd,
                    'truefalse': RedcapForm.truefalse_element,
                    'notes': RedcapForm.note_element,
                    'descriptive': RedcapForm.descriptive_element,
                    'sql': RedcapForm.sql_element,
                    'radio': RedcapForm.radio_element,
                    'calc': RedcapForm.number_element,
                    'file' : RedcapForm.no_print,
                    'time' : RedcapForm.time,
                    'time_mm_ss' : RedcapForm.time_mm_ss,
                    'datetime_mdy' : RedcapForm.datetime_mdy,
                    'datetime_dmy' : RedcapForm.datetime_dmy,
                    'datetime_ymd' : RedcapForm.datetime_ymd,
                    'datetime_seconds_dmy' : RedcapForm.datetime_sec_dmy,
                    'datetime_seconds_mdy' : RedcapForm.datetime_sec_mdy,
                    'datetime_seconds_ymd' : RedcapForm.datetime_sec_ymd,
                    'slider' : RedcapForm.slider_element,
                }

                field_text = clean_html(item.findtext('field_label'))
                field_type = item.findtext('field_type')
                field_name = item.findtext('field_name')
                branching_logic = item.findtext('branching_logic')

                if field_type == 'text':
                    spec = item.findtext('text_validation_type_or_show_slider_number')
                    if spec != '':
                        field_type = spec
                choices = self._get_choices(item.findtext('select_choices_or_calculations'))
                if branching_logic =='' or self.logic_parser.evaluate(field_name, branching_logic):
                    if self._indent != '':
                        logic_vals = self._get_level(branching_logic)
                        indent_stack = self._indent_ques(logic_vals, field_name)
                    if item.findtext('section_header'):
                        self.doc.section_name(clean_html(item.findtext('section_header')))
                    if choices != None:
                        if redcap_types.has_key(field_type):
                            redcap_types[field_type](self.doc, field_text, choices)
                        else:
                            redcap_types['text'](self.doc, field_text)
                    else:
                        if redcap_types.has_key(field_type):
                            redcap_types[field_type](self.doc, field_text)
                        else:
                            redcap_types['text'](self.doc, field_text)
        return self.doc.render()