Never jumps into 2nd elif

Hi,

There must be a better way to do this, but this is what I have at the moment. The function never executes the logic in the 2nd elif statement.

I am trying to calculate the total score for a group of questions and then applying some different calculations to the total score.

I’m using else and elif based on the question_id but it doesn’t work. :frowning:

if q.id == 9 :
    questionnaire_score = []                   
    for answer in all_q_answered:

        # Section 1 Scoring 

        if answer.question_id == 133:
            do some calculations
            
            questionnaire_score.append(cfscore)

         # Section 2 Scoring 

        elif answer.question_id == 134 or 135 or 136 or 137 or 138 or 139:
            do some calcualtions

            questionnaire_score.append(cfscore)

         # Section 3 Scoring 

        elif answer.question_id == 140 or 141 or 142 or 143 or 144 or 145:
            do some calculations
            
            questionnaire_score.append(cfscore)
        else:
            pass
...

Why would it be doing this?

Thanks

found what i was doing wrong.

i needed to add answer.question == for every or operator.

Two issues:

  • First, are you sure the question_id values are what you think they are? You want to double and triple check this.

  • Second:

This is not a proper statement for what you’re trying to do. Python doesn’t work the way you’re thinking it does here.

To properly express this, you’d want to do something like:
elif answer.question_id in [134, 135, 136, 137, 138, 139]:

Or use the in operator as described in my previous response.

Thanks, Ken.

I’ve updated the elif and is working now.

I’ve another problem with this. I’m trying to calculate the total value for that group of questions, but I can’t figure out how I would do this.

I’m trying to use aggregate. but I’m not sure how I specific the filter to only include those questions.

elif answer.question_id in [134, 135, 136, 137, 138, 139]:
       total = ProjectQuestionnaireAnswer.objects.filter(response = q_response, answer__isnull=False,
                  question_id = 134  ... ).aggregate(
                  total_value=Sum('answer__choice_value')
               )

i’ve tired answer.question_id__in=[134,135,136,137,138,139]).aggregate( but no luck.

class Choice(models.Model):
    question = models.ForeignKey(ProjectQuestionnaireQuestion, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200, null=True)
    choice_value = models.IntegerField(blank=True)
    choice_weight = models.DecimalField( max_digits=5, decimal_places=2,blank=True, null=True)

    def __str__(self):
        return str(self.choice_text)
class ProjectQuestionnaireAnswer(models.Model):
    YN_Choices = [
        ('Yes', 'Yes'),
        ('No', 'No'),
        ('Unknown', 'Unknown')
    ]
    question = models.ForeignKey(ProjectQuestionnaireQuestion, on_delete=models.CASCADE)
    answer = models.ForeignKey(Choice, on_delete=models.CASCADE,null=True, blank=True,)
    value = models.CharField(max_length=20, blank=True)
    notes = models.TextField(blank=True)
    response = models.ForeignKey(ProjectQuestionnaireResponse, on_delete=models.CASCADE)

Sorry if this isn’t making sense, but for this particular questionnaire I need to apply different calculations to a group of answers.

Thanks

Tom

Fix it! :slight_smile:

Needed to be

total = ProjectQuestionnaireAnswer.objects.filter(response = q_response, question_id__in=[134,135,136,137,138,139], answer__isnull=False,).aggregate(

so before the isnull=False filter

Hi Ken,

I’ve got another problem with this, and I’m not sure if I am coming at this the wrong way.

I am trying to apply a different calculation for a set of questions. This is based on the total score for a group and then applies a formula, but the problem I’m facing is that the scoring is being applied for every question within the group of questions, which is appending the total value for every question in the loop.

I start off with

            # START Scoring Calculator #
            all_q_answered = ProjectQuestionnaireAnswer.objects.filter(response = q_response, answer__isnull=False)

            # Scoring for Critical Fundamentals using specific formula #
            for answer in all_q_answered:

And then I move into the specific cals.

...

elif answer.question_id in [134, 135, 136, 137, 138, 139]:
    total = ProjectQuestionnaireAnswer.objects.filter(response = q_response, question_id__in=[134,135,136,137,138,139], answer__isnull=False,).aggregate(
        total_value=Sum('answer__choice_value')
    )
             
    base_perc = 10
    span_perc = 10
    min_value = 1
    span = 1
    increment = (min_value *-1 + total['total_value'] * -1)
    cfscore = (base_perc) + span_perc * increment / span
    print(cfscore)
    questionnaire_score.append(cfscore)

This is adding cfscore to questionnaire_score for each of the questions in [134, 135, 136, 137, 138, 139]

what I am trying to get is the total value of all the sections in this questionnaire into the questionnaire_score so that I can total up the overall score.

I was thinking that I could just do this elif answer.question_id in [134]: and then apply the aggregation, but this doesn’t seem right.

Is there a better way to do this?

Thanks

Unfortunately, it’s not clear to me from what you’ve written what you’re trying to calculate here.

What is your ultimate objective? (Overall objective, not just the goal of that inner section of code)

  • Is it one number or is it a set of numbers?
    • If it’s a set of numbers, what identifies the members of the set?

Its hard to explain.

For this particular questionnaire q.id 9 i need to apply a different formula to the scoring. The questions are numbers 133 to 150.

Questions are grouped together into sections, but this is just done on a set of questions, i.e. 134 - 139 would be section 2.

Each question in a section is given a value, but these vary from -2 to +2, but not every section has the same range of scores. So section 1 might only have a range of 0 - 1.

for each section, I need to Sum the total value of each question.
So Q 134 is worth 1, q 135 is 0 …

i then take the total value, apply my formula, and the outcome of the formula is added to questionnaire_score

So in the end, i should have a list (i think ‘[ ]’ is a list) of the total value for each section. I can then take the sum and work out an overall score for q.id 9

Does that explain what I’m trying to achieve?

We’re getting there.

So that I can provide some specific guidance and not just general ideas:

  • How many sections are there?

  • What are the specific formulas for each section? (Is it the same formula with just some constants changing, or are they really different formulas?)

In general, there’s no need to iterate over the questions. At worst, you should have one statement to calculate the values for each section. (This may be different depending upon the similarities of the formulas for the sections.)

Thanks, Ken.

There are 7 sections, but some sections just contain a single question.

The formula is always the same, but the values within the formula have different values based on the section.

So for x in section 1. x might be worth 5 but in section 2 x is worth 10.

and then the formula would be score * x / (y+z) *-1

doing this elif answer.question_id in [134]:
and then

   total = ProjectQuestionnaireAnswer.objects.filter(response = q_response, question_id__in=[134,135,136,137,138,139], answer__isnull=False,).aggregate(
        total_value=Sum('answer__choice_value')
    )

Does actually work, but I think it’s cheating. and since adding this, my app has slowed down, but that might be something unrelated.

Thanks

What my root questions is, is that I’m not following why you have a loop with these if statements in it, instead of having something like:

section_1_value = ...
section_2_value = ...
section_3_value = ...

Because it seems to me that depending upon what all_q_answered represents, you’re going to be recalculating the same value multiple times.

Ok. So all_q_answered is used to perform the calculation on all the questions outside of q.id = 9

It uses this

 @property
    def score(self):
        if not self.answer:
            return 0
        
        choice = Choice.objects.get(choice_text=self.answer, question = self.question)
        q = Choice.objects.filter(question = self.question).aggregate(
            max_choice=Max('choice_value'), 
            min_choice=Min('choice_value'), 
            choice_range=Min('choice_value') *-1 + Max('choice_value')
        )   
        perc = float(choice.choice_weight)  
...

and then within my view:

                    questionnaire_score = []
                    for answer in all_q_answered:
                        total_score = answer.score
                        questionnaire_score.append(total_score)

                    ovdec = sum(questionnaire_score)
                    ov = int(ovdec)

So i was just following the same process for questions in the specific questionnaire.

These works spot on for all the other questions, but i couldn’t do it for these questions as the scoring works differently.

Maybe it would be better to create the function within the model rather than the view - but i wasn’t sure this would work, cause i think the function within the model only works question by question?

I’ve just realized what you mean here. So i could just write statements for all sections, I’d just have to make sure it was for the particular project id so it was calculating only for that set of answers.

Correct.
That block of statements could be bounded by an if statement if you’re iterating over all project_id or if this is in a function (or view) being invoked for a single project_id.

[Edit: And, if you really wanted to get fancy - since the only difference between the sections is “the values within the forumla have different values”, you could set this up in a loop with a reference table of those calculation variables, and process all the sections within a loop.]

[Edit #2 and a side note - It’s really not a good idea to write code relying upon specific PK values. There should be some other “domain-specific” value that you can use to retrieve the PK as a query and then reference those retrieved values as appropriate.]

I like the sound of that.

Do you mean for the q.id =9 ? or do you mean everywhere where I have id == which makes sense cause if something was to change the ID then everything would break?

Thanks again for your help, Ken.

Yes, this. In the traditional relational database model, the id identifies a row, it has no domain-specific significance.

Hi Ken.

Is it possible to use aggregate to calculate in one statement all the scores so i can perform all the calculations I need as part of the specific scoring?

So continuing on from before, I need to perform different calculations based on the combined scores of different groups of questions.

I was doing this:

total = ProjectQuestionnaireAnswer.objects.filter(response = q_response, question_id__in=[134,135,136,137,138,139], answer__isnull=False,).aggregate(
                            total_value=Sum('answer__choice_value')
                        )

but was hoping that I could do all of this in 1 single aggregate? something like

q9_scores  = ProjectQuestionnaireAnswer.objects.filter(response = response, question__questionnaire_id = 9).aggregate(
    section_1 = sum(choice_value).filter(question_id = 133),
    section_2 = sum(choice_value).filter(question_id__in = [140,141,142),
    ...

)

When i try this i get this error: 'NoneType' object is not callable for section_1_score = Sum('choice_value').filter(question_id = 133)

Is this possible, or do I need to aggregate for each set of questions?

Thanks

Review the examples at Aggregation | Django documentation | Django to see how your syntax doesn’t match the appropriate syntax for your aggregations.

I’ve reviewed the examples, but not sure i’m am any closer.

I’ve switched to annotate which seems to work based on applying the filter after the annotate.

    q9_scores = ProjectQuestionnaireAnswer.objects.filter(response = response, question__questionnaire_id = 9).annotate(
        section_1_score = Sum('answer__choice_value')).filter(question_id = 133)

but unable to apply different filters based on the different questions.

Am I on the right track?