Beispiel #1
0
 def getInitialLinkDataFields(self):
     """
     Returns a dict mapping fields to be pre-populated with the corresponding model and model-field
     """
     initial = {}
     for section in self.page:
         for field in section:
             ftype = field['field_type']
             if cf_cache.isLinkField(ftype):
                 field_name = 'question_%d' % field['id']
                 initial[field_name] = {
                     'model': cf_cache.modelForLinkField(ftype)
                 }
                 """
                 if 'combo' in link_fields[ftype]:
                     initial[field_name]['field']=[]
                     for f in link_fields[ftype]['combo']:
                         initial[field_name]['field'].append(link_fields[f]['model_field'])
                 else:
                 """
                 model_field = cf_cache.getLinkFieldData(
                     ftype)['model_field']
                 if cf_cache.isCompoundLinkField(
                         initial[field_name]['model'], model_field):
                     model_field = cf_cache.getCompoundLinkFields(
                         initial[field_name]['model'], model_field)
                 initial[field_name]['model_field'] = model_field
     return initial
Beispiel #2
0
 def get_field_name(self, field):
     """
     Returns the field name for this field.
     Returns the field name corresponding to the linked model for link fields.
     """
     if cf_cache.isLinkField(field.field_type):
         model = cf_cache.modelForLinkField(field.field_type)
         return 'link_'+model.__name__
         
     return "question_%d" % field.id
    def get_field_name(self, field):
        """
        Returns the field name for this field.
        Returns the field name corresponding to the linked model for link fields.
        """
        if cf_cache.isLinkField(field.field_type):
            model = cf_cache.modelForLinkField(field.field_type)
            return 'link_'+model.__name__

        return "question_%d" % field.id
Beispiel #4
0
 def removeLinkField(self, field):
     """
     Removes the FK-column corresponding to a link field if present.
     """
     if not cf_cache.isLinkField(field.field_type):
         return
     model_cls = cf_cache.modelForLinkField(field.field_type)
     if model_cls.__name__ in self.link_models_list:
         field_name = 'link_%s' % model_cls.__name__
         db.delete_column(self._tname, "%s_id" % field_name)
         self.link_models_list.remove(model_cls.__name__)
Beispiel #5
0
 def addLinkFieldColumn(self, field):
     """
     Checks if the FK-column corresponding to this link field is already present.
     If not, it adds in the column.
     """
     if not cf_cache.isLinkField(field.field_type):
         return    
     model_cls = cf_cache.modelForLinkField(field.field_type)
     if model_cls.__name__ not in self.link_models_list:
         # Add in the FK-column for this model
         field_name = self.get_field_name(field)
         db.add_column(self._tname, field_name, models.ForeignKey(model_cls, null=True, blank=True, on_delete=models.SET_NULL))
         self.link_models_list.append(model_cls.__name__)
 def removeLinkField(self, field):
     """
     Removes the FK-column corresponding to a link field if present.
     """
     if not cf_cache.isLinkField(field.field_type):
         return
     with connection.schema_editor() as schema_editor:
         model = self.createDynModel()
         link_model_cls = cf_cache.modelForLinkField(field.field_type)
         if link_model_cls.__name__ in self.link_models_list:
             field_name = 'link_%s' % link_model_cls.__name__
             schema_editor.remove_field(model, model._meta.get_field(field_name))
             self.link_models_list.remove(link_model_cls.__name__)
 def addLinkFieldColumn(self, field):
     """
     Checks if the FK-column corresponding to this link field is already present.
     If not, it adds in the column.
     """
     if not cf_cache.isLinkField(field.field_type):
         return
     with connection.schema_editor() as schema_editor:
         link_model_cls = cf_cache.modelForLinkField(field.field_type)
         if link_model_cls.__name__ not in self.link_models_list:
             # Add in the FK-column for this model
             model = self.createDynModel()
             field_name = self.get_field_name(field)
             schema_editor.add_field(model, model._meta.get_field(field_name))
             self.link_models_list.append(link_model_cls.__name__)
    def _getModelFieldList(self):
        """
        Returns a list of Model Field tuples given a list of field_types (from the metadata)
            - An entry would be like (field_name, field)
            - An 'id' field is automatically added, as is a 'user_id' field, based on whether the form
              is anonymous or not.
            - A field-name is of the form 'question_23', where '23' is the ID of the corresponding question
            - For custom fields like address, a field-name would be of the form 'question_23_zip'
        """
        link_models = []
        if not self.fields:
            self.fields = self._getFieldsForForm(self.form)

        self.field_list.append( ('id', models.AutoField(primary_key = True) ) )
        if not self.form.anonymous:
            self.field_list.append( ('user', self._getLinkModelField(ESPUser) ) )

        # Checking for only_fkey links
        if self.form.link_type != '-1':
            model_cls = cf_cache.only_fkey_models[self.form.link_type]
            self.field_list.append( ('link_%s' % model_cls.__name__, self._getLinkModelField(model_cls)) )

        # Check for linked fields-
        # Insert a foreign-key to the parent model for link fields
        # Insert a regular column for non-link fields
        for field_id, field in self.fields:
            if cf_cache.isLinkField(field):
                lm = cf_cache.modelForLinkField(field)
                if lm not in link_models: link_models.append(lm)
            else:
                new_field = self._getModelField(field)
                if new_field:
                    self.field_list.append( ('question_%s' % str(field_id), new_field) )

        for model in link_models:
            if model:
                new_field = self._getLinkModelField(model)
                self.field_list.append( ('link_%s' % model.__name__, new_field) )
                self.link_models_list.append(model.__name__)

        return self.field_list
Beispiel #9
0
 def _getModelFieldList(self):
     """
     Returns a list of Model Field tuples given a list of field_types (from the metadata)
         - An entry would be like (field_name, field)
         - An 'id' field is automatically added, as is a 'user_id' field, based on whether the form
           is anonymous or not.
         - A field-name is of the form 'question_23', where '23' is the ID of the corresponding question
         - For custom fields like address, a field-name would be of the form 'question_23_zip'
     """
     link_models = []
     if not self.fields:
         self.fields = self._getFieldsForForm(self.form)
     
     self.field_list.append( ('id', models.AutoField(primary_key = True) ) )
     if not self.form.anonymous:
         self.field_list.append( ('user', models.ForeignKey(ESPUser, null = True, blank = True, on_delete = models.SET_NULL) ) )
         
     # Checking for only_fkey links
     if self.form.link_type != '-1':
         model_cls = cf_cache.only_fkey_models[self.form.link_type]
         self.field_list.append( ('link_%s' % model_cls.__name__, models.ForeignKey(model_cls, null=True, blank=True, on_delete=models.SET_NULL)) )
         
     # Check for linked fields-
     # Insert a foreign-key to the parent model for link fields
     # Insert a regular column for non-link fields
     for field_id, field in self.fields:
         if cf_cache.isLinkField(field):
             lm = cf_cache.modelForLinkField(field)
             if lm not in link_models: link_models.append(lm)
         else:
             new_field = self._getModelField(field)
             if new_field:
                 self.field_list.append( ('question_%s' % str(field_id), new_field) )
     
     # Adding foreign key fields for link-field models
     for model in link_models:
         if model:
             self.field_list.append( ('link_%s' % model.__name__, models.ForeignKey(model, null=True, blank=True, on_delete=models.SET_NULL) ) )
             self.link_models_list.append(model.__name__)
                 
     return self.field_list
 def getInitialLinkDataFields(self):
     """
     Returns a dict mapping fields to be pre-populated with the corresponding model and model-field
     """
     initial = {}
     for section in self.page:
         for field in section:
             ftype = field['field_type']
             if cf_cache.isLinkField(ftype):
                 field_name = 'question_%d' % field['id']
                 initial[field_name] = {'model': cf_cache.modelForLinkField(ftype)}
                 """
                 if 'combo' in link_fields[ftype]:
                     initial[field_name]['field']=[]
                     for f in link_fields[ftype]['combo']:
                         initial[field_name]['field'].append(link_fields[f]['model_field'])
                 else:
                 """
                 model_field = cf_cache.getLinkFieldData(ftype)['model_field']
                 if cf_cache.isCompoundLinkField(initial[field_name]['model'], model_field):
                     model_field = cf_cache.getCompoundLinkFields(initial[field_name]['model'], model_field)
                 initial[field_name]['model_field'] = model_field
     return initial
Beispiel #11
0
    def getResponseData(self, form):
        """
        Returns the response data for this form, along with the questions
        """
        dmh = DMH(form=form)
        dyn = dmh.createDynModel()
        response_data = {'questions': [], 'answers': []}
        responses = dyn.objects.all().order_by('id').values()
        fields = Field.objects.filter(form=form).order_by('section__page__seq', 'section__seq', 'seq').values('id', 'field_type', 'label')
        
        # Let's first do a bit of introspection to figure out
        # what the linked models are, and what values need to be added to the
        # response data from these linked models.
        # And since we're already iterating over fields,
        # let's also set the questions in the process.
        add_fields = {}
        
        # Add in the user column if form is not anonymous
        if not form.anonymous:
            response_data['questions'].append(['user_id', 'User', 'fk'])
            
        # Add in the column for link fields, if any
        if form.link_type != "-1":
            only_fkey_model = cf_cache.only_fkey_models[form.link_type]
            response_data['questions'].append(["link_%s_id" % only_fkey_model.__name__, form.link_type, 'fk'])
        else:
            only_fkey_model = None      
        
        for field in fields:
            # I'll do a lot of merging here later
            qname = 'question_%d' % field['id']
            ftype = field['field_type']
            if cf_cache.isLinkField(ftype):
                # Let's grab the model first
                model = cf_cache.modelForLinkField(ftype)
                
                # Now let's see what fields need to be set
                add_fields[qname] = [model, cf_cache.getLinkFieldData(ftype)['model_field']]
                response_data['questions'].append([qname, field['label'], ftype])
                # Include this field only if it isn't a dummy field
            elif generic_fields[ftype]['typeMap'] is not DummyField:
                response_data['questions'].append([qname, field['label'], ftype])
            
        # Now let's set up the responses
        for response in responses:
            link_instances_cache={}
            
            # Add in user if form is not anonymous
            if not form.anonymous:
                response['user_id'] = unicode(response['user_id'])
                
            # Add in links
            if only_fkey_model is not None:
                if only_fkey_model.objects.filter(pk=response["link_%s_id" % only_fkey_model.__name__]).exists():
                    inst = only_fkey_model.objects.get(pk=response["link_%s_id" % only_fkey_model.__name__])
                else: inst = None    
                response["link_%s_id" % only_fkey_model.__name__] = unicode(inst)

            # Now, put in the additional fields in response
            for qname, data in add_fields.items():
                if data[0].__name__ not in link_instances_cache:
                    if data[0].objects.filter(pk=response["link_%s_id" % data[0].__name__]).exists():
                        link_instances_cache[data[0].__name__] = data[0].objects.get(pk=response["link_%s_id" % data[0].__name__])
                    else:
                        link_instances_cache[data[0].__name__] = None
                            
                if cf_cache.isCompoundLinkField(data[0], data[1]):
                    if link_instances_cache[data[0].__name__] is None:
                        response[qname] = []
                    else:    
                        response[qname] = [link_instances_cache[data[0].__name__].__dict__[x] for x in cf_cache.getCompoundLinkFields(data[0], data[1])]
                else:
                    if link_instances_cache[data[0].__name__] is None:
                        response[qname]=''
                    else:    
                        response[qname] = link_instances_cache[data[0].__name__].__dict__[data[1]]    
                
        # Add responses to response_data
        response_data['answers'].extend(responses)                                    
                    
        return response_data
Beispiel #12
0
    def done(self, form_list, **kwargs):
        data = {}
        dyn = DMH(form=self.form)
        dynModel = dyn.createDynModel()
        fields = dict(dyn.fields)
        link_models_cache = {}
        
        # Plonking in user_id if the form is non-anonymous
        if not self.form.anonymous:
            data['user'] = self.curr_request.user
        
        # Populating data with the values that need to be inserted
        for form in form_list:
            for k,v in form.cleaned_data.items():
                # Check for only_fkey link models first
                if k.split('_')[1] in cf_cache.only_fkey_models:
                    data[k] = v
                    continue
                    
                field_id = int(k.split("_")[1])
                ftype = fields[field_id]

                # Now check for link fields
                if cf_cache.isLinkField(ftype):
                    model = cf_cache.modelForLinkField(ftype)
                    if model.__name__ not in link_models_cache:
                        link_models_cache[model.__name__] = {'model': model, 'data': {}}
                        pre_instance = self.form_handler.getInstanceForLinkField(k, model)
                        if pre_instance is not None:
                            link_models_cache[model.__name__]['instance'] = pre_instance
                        else:    
                            link_models_cache[model.__name__]['instance'] = getattr(model, 'cf_link_instance')(self.curr_request)
                    ftype_parts = ftype.split('_')
                    if len(ftype_parts) > 1 and cf_cache.isCompoundLinkField(model, '_'.join(ftype_parts[1:])):
                        #   Try to match a model field to the last part of the key we have.
                        partial_field_name = str(field_id).join(k.split(str(field_id))[1:]).lstrip('_')
                        target_fields = cf_cache.getCompoundLinkFields(model, '_'.join(ftype_parts[1:]))
                        for f in target_fields:
                            if f.endswith(partial_field_name):
                                model_field = f
                                break
                    else:
                        model_field = cf_cache.getLinkFieldData(ftype)['model_field']
                    link_models_cache[model.__name__]['data'].update({model_field: v}) 
                else:
                    data[k] = v
        
        # Create/update instances corresponding to link fields
        # Also, populate 'data' with foreign-keys that need to be inserted into the response table
        for k,v in link_models_cache.items():
            if v['instance'] is not None:
                # TODO-> the following update won't work for fk fields.
                v['instance'].__dict__.update(v['data'])
                v['instance'].save()
                curr_instance = v['instance']
            else:
                try:
                    new_instance = v['model'].objects.create(**v['data'])
                except:    
                    # show some error message
                    pass
            if v['instance'] is not None:
                data['link_%s' % v['model'].__name__] = v['instance']                
        
        # Saving response
        initial_keys = data.keys()
        for key in initial_keys:
            #   Check that we didn't already handle this value as a linked field
            if key.split('_')[0] in cf_cache.link_fields:
                del data[key]
            #   Check that this value didn't come from a dummy field
            if key.split('_')[0] == 'question' and generic_fields[fields[int(key.split('_')[1])]]['typeMap'] == DummyField:
                del data[key]
        dynModel.objects.create(**data)    
        return HttpResponseRedirect('/customforms/success/%d/' % self.form.id)
Beispiel #13
0
    def _getFields(self):
        """
        Sets self.fields and self.fieldsets for this page
        """
        model_fields_cache = {}
        for section in self.page:
            curr_fieldset = []
            curr_fieldset.extend([section[0]['section__title'], {'fields':[], 'classes':['section',]}])
            curr_fieldset[1]['description'] = section[0]['section__description']
            
            # Check for only_fkey models.
            # If any, insert the relevant field into the first section of the fist page
            if section[0]['section__seq'] == 0 and self.seq == 0:
                if self.form.link_type != '-1':
                    label = 'Please pick the %s you want to fill the form for' % self.form.link_type
                    link_cls = cf_cache.only_fkey_models[self.form.link_type]
                    if self.form.link_id == -1:
                        # User needs to be shown a list of instances from which to select
                        queryset = link_cls.objects.all()
                        widget = forms.Select()
                    else:
                        queryset = link_cls.objects.filter(pk=self.form.link_id)
                        widget = forms.HiddenInput()    
                    fld = forms.ModelChoiceField(queryset = queryset, label = label, initial = queryset[0], 
                                                widget = widget, required = True, empty_label = None)
                    self.fields.append(['link_%s' % link_cls.__name__, fld ])
                    curr_fieldset[1]['fields'].append('link_%s' % link_cls.__name__)        
            
            for field in section:
                field_name = 'question_%d' % field['id']
                field_attrs = {'label': field['label'], 'help_text': field['help_text'], 'required': field['required']}
                
                # Setting the 'name' attribute for combo fields
                """
                if field['field_type'] in self._combo_fields:
                    field_attrs['name']=field_name
                """    

                #   Extract form attributes for further use below
                other_attrs = []
                for attr_name in field['attributes']:
                    other_attrs.append({'attr_type': attr_name, 'value': field['attributes'][attr_name]})
                    
                    #   Create dynamic validators to check results if the correct answer has
                    #   been specified by the form author
                    if attr_name == 'correct_answer' and len(field['attributes'][attr_name].strip()) > 0:
                        if field['field_type'] in ['dropdown', 'radio']:
                            value_choices = field['attributes']['options'].split('|')
                            target_value = value_choices[int(field['attributes'][attr_name])]
                        elif field['field_type'] in ['checkboxes']:
                            value_choices = field['attributes']['options'].split('|')
                            target_value = [value_choices[int(index)] for index in field['attributes'][attr_name].split(',')]
                        else:
                            target_value = field['attributes'][attr_name]

                        field_attrs['validators'] = [matches_answer(target_value)]

                if other_attrs:
                    field_attrs.update(self._getAttrs(other_attrs))
                    
                # First, check for link fields
                if cf_cache.isLinkField(field['field_type']):
                    # Get all form fields for this model, if it hasn't already been done
                    link_model = cf_cache.modelForLinkField(field['field_type'])
                    if not link_model:
                        continue
                    if link_model.__name__ not in model_fields_cache:
                        model_fields_cache[link_model.__name__] = {}
                        model_fields_cache[link_model.__name__].update(fields_for_model(link_model, widgets=getattr(link_model, 'link_fields_widgets', None)))

                    model_field = cf_cache.getLinkFieldData(field['field_type'])['model_field']
                    
                    field_is_custom = False
                    if model_field in model_fields_cache[link_model.__name__]:
                        form_field = model_fields_cache[link_model.__name__][model_field]
                    else:
                        #   See if there's a custom field
                        if model_field in custom_fields:
                            form_field = cf_cache.getCustomFieldInstance(model_field, field_name)
                            field_is_custom = True
                        else:
                            raise Exception('Could not find linked field: %s' % model_field)

                    # TODO -> enforce "Required" constraint server-side as well, or trust the client-side code?
                    form_field.__dict__.update(field_attrs)
                    form_field.widget.attrs.update({'class': ''})
                    if form_field.required:
                        # Add a class 'required' to the widget
                        form_field.widget.attrs['class'] += 'required ' 
                        form_field.widget.is_required = True

                    if not field_is_custom:
                        # Add in other classes for validation
                        generic_type = cf_cache.getGenericType(form_field)
                        if 'widget_attrs' in self._field_types[generic_type] and 'class' in self._field_types[generic_type]['widget_attrs']:
                            form_field.widget.attrs['class'] += self._field_types[generic_type]['widget_attrs']['class']    

                    # Adding to field list
                    self.fields.append([field_name, form_field])
                    curr_fieldset[1]['fields'].append(field_name)
                    continue    
                
                # Generic field                                
                widget_attrs = {}    
                if 'attrs' in self._field_types[field['field_type']]:
                    field_attrs.update(self._field_types[field['field_type']]['attrs'])
                if 'widget_attrs' in self._field_types[field['field_type']]:
                    widget_attrs.update(self._field_types[field['field_type']]['widget_attrs'])
                typeMap = self._field_types[field['field_type']]['typeMap']
                
                # Setting classes required for front-end validation
                if field['required']:
                    widget_attrs['class'] += ' required'
                if 'min_value' in field_attrs:
                    widget_attrs['min'] = field_attrs['min_value']
                if 'max_value' in field_attrs:
                    widget_attrs['max'] = field_attrs['max_value']
                if 'min_length' in field_attrs:
                    widget_attrs['minlength'] = field_attrs['min_length']
                if 'max_length' in field_attrs:
                    widget_attrs['maxlength'] = field_attrs['max_length']
                if 'min_words' in field_attrs:
                    widget_attrs['minWords'] = field_attrs['min_words']
                if 'max_words' in field_attrs:
                    widget_attrs['maxWords'] = field_attrs['max_words']                    
            
                # For combo fields, classes need to be passed in to the field
                if field['field_type'] in self._combo_fields:
                    field_attrs.update(widget_attrs)
                    
                # Setting the queryset for a courses field
                if field['field_type'] == 'courses':
                    if self.form.link_type == 'program' or self.form.link_type == 'Program':
                        field_attrs['queryset'] = Program.objects.get(pk = self.form.link_id).classsubject_set.all()
                        
                # Initializing widget                
                if field_attrs['widget'] is not None:
                    try:
                        field_attrs['widget'] = field_attrs['widget'](attrs = widget_attrs)
                    except KeyError:
                        pass
                    
                self.fields.append([field_name, typeMap(**field_attrs) ])
                curr_fieldset[1]['fields'].append(field_name)            

            self.fieldsets.append(tuple(curr_fieldset))
    def getResponseData(self, form):
        """
        Returns the response data for this form, along with the questions
        """
        dmh = DMH(form=form)
        dyn = dmh.createDynModel()
        response_data = {'questions': [], 'answers': []}
        responses = dyn.objects.all().order_by('id').values()
        fields = Field.objects.filter(form=form).order_by('section__page__seq', 'section__seq', 'seq').values('id', 'field_type', 'label')

        # Let's first do a bit of introspection to figure out
        # what the linked models are, and what values need to be added to the
        # response data from these linked models.
        # And since we're already iterating over fields,
        # let's also set the questions in the process.
        add_fields = {}

        # Add in the user column if form is not anonymous
        if not form.anonymous:
            response_data['questions'].append(['user_id', 'User ID', 'fk'])
            response_data['questions'].append(['user_display', 'User', 'textField'])
            response_data['questions'].append(['user_email', 'User e-mail', 'textField'])
            response_data['questions'].append(['username', 'Username', 'textField'])

        # Add in the column for link fields, if any
        if form.link_type != "-1":
            only_fkey_model = cf_cache.only_fkey_models[form.link_type]
            response_data['questions'].append(["link_%s_id" % only_fkey_model.__name__, form.link_type, 'fk'])
        else:
            only_fkey_model = None

        for field in fields:
            # I'll do a lot of merging here later
            qname = 'question_%d' % field['id']
            ftype = field['field_type']
            if cf_cache.isLinkField(ftype):
                # Let's grab the model first
                model = cf_cache.modelForLinkField(ftype)

                # Now let's see what fields need to be set
                add_fields[qname] = [model, cf_cache.getLinkFieldData(ftype)['model_field']]
                response_data['questions'].append([qname, field['label'], ftype])
                # Include this field only if it isn't a dummy field
            elif generic_fields[ftype]['typeMap'] is not DummyField:
                response_data['questions'].append([qname, field['label'], ftype])

        users = ESPUser.objects.in_bulk(map(lambda response: response['user_id'], responses))

        # Now let's set up the responses
        for response in responses:
            link_instances_cache={}

            # Add in user if form is not anonymous
            if not form.anonymous:
                user = users[response['user_id']]
                response['user_id'] = unicode(response['user_id'])
                response['user_display'] = user.name()
                response['user_email'] = user.email
                response['username'] = user.username

            # Add in links
            if only_fkey_model is not None:
                if only_fkey_model.objects.filter(pk=response["link_%s_id" % only_fkey_model.__name__]).exists():
                    inst = only_fkey_model.objects.get(pk=response["link_%s_id" % only_fkey_model.__name__])
                else: inst = None
                response["link_%s_id" % only_fkey_model.__name__] = unicode(inst)

            # Now, put in the additional fields in response
            for qname, data in add_fields.items():
                if data[0].__name__ not in link_instances_cache:
                    if data[0].objects.filter(pk=response["link_%s_id" % data[0].__name__]).exists():
                        link_instances_cache[data[0].__name__] = data[0].objects.get(pk=response["link_%s_id" % data[0].__name__])
                    else:
                        link_instances_cache[data[0].__name__] = None

                if cf_cache.isCompoundLinkField(data[0], data[1]):
                    if link_instances_cache[data[0].__name__] is None:
                        response[qname] = []
                    else:
                        response[qname] = [link_instances_cache[data[0].__name__].__dict__[x] for x in cf_cache.getCompoundLinkFields(data[0], data[1])]
                else:
                    if link_instances_cache[data[0].__name__] is None:
                        response[qname]=''
                    else:
                        response[qname] = link_instances_cache[data[0].__name__].__dict__[data[1]]

        # Add responses to response_data
        response_data['answers'].extend(responses)

        return response_data
    def done(self, form_list, **kwargs):
        data = {}
        dyn = DMH(form=self.form)
        dynModel = dyn.createDynModel()
        fields = dict(dyn.fields)
        link_models_cache = {}

        # Plonking in user_id if the form is non-anonymous
        if not self.form.anonymous:
            data['user'] = self.curr_request.user

        # Populating data with the values that need to be inserted
        for form in form_list:
            for k,v in form.cleaned_data.items():
                # Check for only_fkey link models first
                if k.split('_')[1] in cf_cache.only_fkey_models:
                    data[k] = v
                    continue

                field_id = int(k.split("_")[1])
                ftype = fields[field_id]

                # Now check for link fields
                if cf_cache.isLinkField(ftype):
                    model = cf_cache.modelForLinkField(ftype)
                    if model.__name__ not in link_models_cache:
                        link_models_cache[model.__name__] = {'model': model, 'data': {}}
                        pre_instance = self.form_handler.getInstanceForLinkField(k, model)
                        if pre_instance is not None:
                            link_models_cache[model.__name__]['instance'] = pre_instance
                        else:
                            link_models_cache[model.__name__]['instance'] = getattr(model, 'cf_link_instance')(self.curr_request)
                    ftype_parts = ftype.split('_')
                    if len(ftype_parts) > 1 and cf_cache.isCompoundLinkField(model, '_'.join(ftype_parts[1:])):
                        #   Try to match a model field to the last part of the key we have.
                        partial_field_name = str(field_id).join(k.split(str(field_id))[1:]).lstrip('_')
                        target_fields = cf_cache.getCompoundLinkFields(model, '_'.join(ftype_parts[1:]))
                        for f in target_fields:
                            if f.endswith(partial_field_name):
                                model_field = f
                                break
                    else:
                        model_field = cf_cache.getLinkFieldData(ftype)['model_field']
                    link_models_cache[model.__name__]['data'].update({model_field: v})
                else:
                    data[k] = v

        # Create/update instances corresponding to link fields
        # Also, populate 'data' with foreign-keys that need to be inserted into the response table
        for k,v in link_models_cache.items():
            if v['instance'] is not None:
                # TODO-> the following update won't work for fk fields.
                v['instance'].__dict__.update(v['data'])
                v['instance'].save()
                curr_instance = v['instance']
            else:
                try:
                    new_instance = v['model'].objects.create(**v['data'])
                except:
                    # show some error message
                    pass
            if v['instance'] is not None:
                data['link_%s' % v['model'].__name__] = v['instance']

        # Saving response
        initial_keys = data.keys()
        for key in initial_keys:
            #   Check that we didn't already handle this value as a linked field
            if key.split('_')[0] in cf_cache.link_fields:
                del data[key]
            #   Check that this value didn't come from a dummy field
            if key.split('_')[0] == 'question' and generic_fields[fields[int(key.split('_')[1])]]['typeMap'] == DummyField:
                del data[key]
        dynModel.objects.create(**data)
        return HttpResponseRedirect('/customforms/success/%d/' % self.form.id)
    def _getFields(self):
        """
        Sets self.fields and self.fieldsets for this page
        """
        model_fields_cache = {}
        for section in self.page:
            curr_fieldset = []
            curr_fieldset.extend([section[0]['section__title'], {'fields':[], 'classes':['section',]}])
            curr_fieldset[1]['description'] = section[0]['section__description']

            # Check for only_fkey models.
            # If any, insert the relevant field into the first section of the fist page
            if section[0]['section__seq'] == 0 and self.seq == 0:
                if self.form.link_type != '-1':
                    label = 'Please pick the %s you want to fill the form for' % self.form.link_type
                    link_cls = cf_cache.only_fkey_models[self.form.link_type]
                    if self.form.link_id == -1:
                        # User needs to be shown a list of instances from which to select
                        queryset = link_cls.objects.all()
                        widget = forms.Select()
                    else:
                        queryset = link_cls.objects.filter(pk=self.form.link_id)
                        widget = forms.HiddenInput()
                    fld = forms.ModelChoiceField(queryset = queryset, label = label, initial = queryset[0],
                                                widget = widget, required = True, empty_label = None)
                    self.fields.append(['link_%s' % link_cls.__name__, fld ])
                    curr_fieldset[1]['fields'].append('link_%s' % link_cls.__name__)

            for field in section:
                field_name = 'question_%d' % field['id']
                field_attrs = {'label': field['label'], 'help_text': field['help_text'], 'required': field['required']}

                # Setting the 'name' attribute for combo fields
                """
                if field['field_type'] in self._combo_fields:
                    field_attrs['name']=field_name
                """

                #   Extract form attributes for further use below
                other_attrs = []
                for attr_name in field['attributes']:
                    other_attrs.append({'attr_type': attr_name, 'value': field['attributes'][attr_name]})

                    #   Create dynamic validators to check results if the correct answer has
                    #   been specified by the form author
                    if attr_name == 'correct_answer' and len(field['attributes'][attr_name].strip()) > 0:
                        if field['field_type'] in ['dropdown', 'radio']:
                            value_choices = field['attributes']['options'].split('|')
                            target_value = value_choices[int(field['attributes'][attr_name])]
                        elif field['field_type'] in ['checkboxes']:
                            value_choices = field['attributes']['options'].split('|')
                            target_value = [value_choices[int(index)] for index in field['attributes'][attr_name].split(',')]
                        else:
                            target_value = field['attributes'][attr_name]

                        field_attrs['validators'] = [matches_answer(target_value)]

                if other_attrs:
                    field_attrs.update(self._getAttrs(other_attrs))

                # First, check for link fields
                if cf_cache.isLinkField(field['field_type']):
                    # Get all form fields for this model, if it hasn't already been done
                    link_model = cf_cache.modelForLinkField(field['field_type'])
                    if not link_model:
                        continue
                    if link_model.__name__ not in model_fields_cache:
                        model_fields_cache[link_model.__name__] = {}
                        model_fields_cache[link_model.__name__].update(fields_for_model(link_model, widgets=getattr(link_model, 'link_fields_widgets', None)))

                    model_field = cf_cache.getLinkFieldData(field['field_type'])['model_field']

                    field_is_custom = False
                    if model_field in model_fields_cache[link_model.__name__]:
                        form_field = model_fields_cache[link_model.__name__][model_field]
                    else:
                        #   See if there's a custom field
                        if model_field in custom_fields:
                            form_field = cf_cache.getCustomFieldInstance(model_field, field_name)
                            field_is_custom = True
                        else:
                            raise Exception('Could not find linked field: %s' % model_field)

                    # TODO -> enforce "Required" constraint server-side as well, or trust the client-side code?
                    form_field.__dict__.update(field_attrs)
                    form_field.widget.attrs.update({'class': ''})
                    if form_field.required:
                        # Add a class 'required' to the widget
                        form_field.widget.attrs['class'] += 'required '
                        form_field.widget.is_required = True

                    if not field_is_custom:
                        # Add in other classes for validation
                        generic_type = cf_cache.getGenericType(form_field)
                        if 'widget_attrs' in self._field_types[generic_type] and 'class' in self._field_types[generic_type]['widget_attrs']:
                            form_field.widget.attrs['class'] += self._field_types[generic_type]['widget_attrs']['class']

                    # Adding to field list
                    self.fields.append([field_name, form_field])
                    curr_fieldset[1]['fields'].append(field_name)
                    continue

                # Generic field
                widget_attrs = {}
                if 'attrs' in self._field_types[field['field_type']]:
                    field_attrs.update(self._field_types[field['field_type']]['attrs'])
                if 'widget_attrs' in self._field_types[field['field_type']]:
                    widget_attrs.update(self._field_types[field['field_type']]['widget_attrs'])
                typeMap = self._field_types[field['field_type']]['typeMap']

                # Setting classes required for front-end validation
                if field['required']:
                    widget_attrs['class'] += ' required'
                if 'min_value' in field_attrs:
                    widget_attrs['min'] = field_attrs['min_value']
                if 'max_value' in field_attrs:
                    widget_attrs['max'] = field_attrs['max_value']
                if 'min_length' in field_attrs:
                    widget_attrs['minlength'] = field_attrs['min_length']
                if 'max_length' in field_attrs:
                    widget_attrs['maxlength'] = field_attrs['max_length']
                if 'min_words' in field_attrs:
                    widget_attrs['minWords'] = field_attrs['min_words']
                if 'max_words' in field_attrs:
                    widget_attrs['maxWords'] = field_attrs['max_words']

                # For combo fields, classes need to be passed in to the field
                if field['field_type'] in self._combo_fields:
                    field_attrs.update(widget_attrs)

                # Setting the queryset for a courses field
                if field['field_type'] == 'courses':
                    if self.form.link_type == 'program' or self.form.link_type == 'Program':
                        field_attrs['queryset'] = Program.objects.get(pk = self.form.link_id).classsubject_set.all()

                # Initializing widget
                if field_attrs['widget'] is not None:
                    try:
                        field_attrs['widget'] = field_attrs['widget'](attrs = widget_attrs)
                    except KeyError:
                        pass

                self.fields.append([field_name, typeMap(**field_attrs) ])
                curr_fieldset[1]['fields'].append(field_name)

            self.fieldsets.append(tuple(curr_fieldset))
Beispiel #17
0
def onModify(request):
    """
    Handles form modifications
    """
    if request.is_ajax():
        if request.method == 'POST':
            metadata = json.loads(request.body)
            try:
                form = Form.objects.get(id=int(metadata['form_id']))
            except:
                raise ESPError('Form %s not found' % metadata['form_id'],
                               log=False)
            dmh = DMH(form=form)
            link_models_list = [
            ]  # Stores a cache of link models that should not be removed

            # Populating the old fields list
            dmh._getModelFieldList()

            # NOT updating 'anonymous'
            form.__dict__.update(title=metadata['title'],
                                 description=metadata['desc'],
                                 perms=metadata['perms'],
                                 success_message=metadata['success_message'],
                                 success_url=metadata['success_url'])

            form.save()

            # Check if only_fkey links have changed
            if form.link_type != metadata['link_type']:
                dmh.change_only_fkey(form, form.link_type,
                                     metadata['link_type'], link_id)

            curr_keys = {'pages': [], 'sections': [], 'fields': []}
            old_pages = Page.objects.filter(form=form)
            old_sections = Section.objects.filter(page__in=old_pages)
            old_fields = Field.objects.filter(form=form)
            for page in metadata['pages']:
                curr_page = get_new_or_altered_obj(Page,
                                                   page['parent_id'],
                                                   form=form,
                                                   seq=int(page['seq']))
                curr_keys['pages'].append(curr_page.id)
                for section in page['sections']:
                    curr_sect = get_new_or_altered_obj(
                        Section,
                        section['data']['parent_id'],
                        page=curr_page,
                        title=section['data']['question_text'],
                        description=section['data']['help_text'],
                        seq=int(section['data']['seq']))
                    curr_keys['sections'].append(curr_sect.id)
                    for field in section['fields']:
                        (curr_field, old_field,
                         field_created) = get_or_create_altered_obj(
                             Field,
                             field['data']['parent_id'],
                             form=form,
                             section=curr_sect,
                             field_type=field['data']['field_type'],
                             seq=int(field['data']['seq']),
                             label=field['data']['question_text'],
                             help_text=field['data']['help_text'],
                             required=field['data']['required'])
                        if field_created:
                            # Check for link field
                            if cf_cache.isLinkField(curr_field.field_type):
                                dmh.addLinkFieldColumn(curr_field)
                            else:
                                dmh.addField(curr_field)
                        elif not cf_cache.isLinkField(curr_field.field_type):
                            dmh.updateField(curr_field, old_field)

                        # Store a reference to the linked model so that we don't drop it from the table.
                        if cf_cache.isLinkField(curr_field.field_type):
                            model_cls = cf_cache.modelForLinkField(
                                curr_field.field_type)
                            if model_cls.__name__ not in link_models_list:
                                link_models_list.append(model_cls.__name__)

                        for atype, aval in field['data']['attrs'].items():
                            curr_field.set_attribute(atype, aval)
                        curr_keys['fields'].append(curr_field.id)

            del_fields = old_fields.exclude(id__in=curr_keys['fields'])
            for df in del_fields:
                # Check for link fields
                if cf_cache.isLinkField(df.field_type):
                    model_cls = cf_cache.modelForLinkField(df.field_type)
                    if model_cls.__name__ not in link_models_list:
                        # This column needs to be dropped
                        dmh.removeLinkField(df)
                else:
                    dmh.removeField(df)
            del_fields.delete()

            old_sections.exclude(id__in=curr_keys['sections']).delete()
            old_pages.exclude(id__in=curr_keys['pages']).delete()

            return HttpResponse('OK')
Beispiel #18
0
def onModify(request):
    """
    Handles form modifications
    """
    if request.is_ajax():
        if request.method == 'POST':
            metadata = json.loads(request.body)
            try:
                form = Form.objects.get(id=int(metadata['form_id']))
            except:
                raise ESPError('Form %s not found' % metadata['form_id'], log=False)
            dmh = DMH(form=form)
            link_models_list = []     # Stores a cache of link models that should not be removed

            # Populating the old fields list
            dmh._getModelFieldList()

            # NOT updating 'anonymous'
            form.__dict__.update(title=metadata['title'], description=metadata['desc'], perms=metadata['perms'],
                success_message=metadata['success_message'], success_url=metadata['success_url']
                )

            form.save()

            # Check if only_fkey links have changed
            if form.link_type != metadata['link_type']:
                dmh.change_only_fkey(form, form.link_type, metadata['link_type'], link_id)

            curr_keys = {'pages': [], 'sections': [], 'fields': []}
            old_pages = Page.objects.filter(form=form)
            old_sections = Section.objects.filter(page__in=old_pages)
            old_fields = Field.objects.filter(form=form)
            for page in metadata['pages']:
                curr_page = get_new_or_altered_obj(Page, page['parent_id'], form=form, seq=int(page['seq']))
                curr_keys['pages'].append(curr_page.id)
                for section in page['sections']:
                    curr_sect = get_new_or_altered_obj(Section, section['data']['parent_id'],
                                page=curr_page, title=section['data']['question_text'],
                                description=section['data']['help_text'], seq=int(section['data']['seq'])
                                )
                    curr_keys['sections'].append(curr_sect.id)
                    for field in section['fields']:
                        (curr_field, old_field, field_created) = get_or_create_altered_obj(Field, field['data']['parent_id'],
                                                    form=form, section=curr_sect, field_type=field['data']['field_type'],
                                                    seq=int(field['data']['seq']), label=field['data']['question_text'],
                                                    help_text=field['data']['help_text'], required=field['data']['required']
                                                    )
                        if field_created:
                            # Check for link field
                            if cf_cache.isLinkField(curr_field.field_type):
                                dmh.addLinkFieldColumn(curr_field)
                            else: dmh.addField(curr_field)
                        elif not cf_cache.isLinkField(curr_field.field_type):
                            dmh.updateField(curr_field, old_field)

                        # Store a reference to the linked model so that we don't drop it from the table.
                        if cf_cache.isLinkField(curr_field.field_type):
                            model_cls = cf_cache.modelForLinkField(curr_field.field_type)
                            if model_cls.__name__ not in link_models_list: link_models_list.append(model_cls.__name__)

                        for atype, aval in field['data']['attrs'].items():
                            curr_field.set_attribute(atype, aval)
                        curr_keys['fields'].append(curr_field.id)

            del_fields = old_fields.exclude(id__in=curr_keys['fields'])
            for df in del_fields:
                # Check for link fields
                if cf_cache.isLinkField(df.field_type):
                    model_cls = cf_cache.modelForLinkField(df.field_type)
                    if model_cls.__name__ not in link_models_list:
                        # This column needs to be dropped
                        dmh.removeLinkField(df)
                else:
                    dmh.removeField(df)
            del_fields.delete()

            old_sections.exclude(id__in=curr_keys['sections']).delete()
            old_pages.exclude(id__in=curr_keys['pages']).delete()

            return HttpResponse('OK')