Merge lp:~arthur-she/lava-dashboard/fix-for-bug-1175597 into lp:lava-dashboard

Proposed by Arthur She
Status: Approved
Approved by: Stevan Radaković
Approved revision: 408
Proposed branch: lp:~arthur-she/lava-dashboard/fix-for-bug-1175597
Merge into: lp:lava-dashboard
Diff against target: 458 lines (+318/-13)
7 files modified
dashboard_app/filters.py (+7/-5)
dashboard_app/migrations/0030_auto__add_equation_to_testrunfilterattribute.py (+270/-0)
dashboard_app/models.py (+13/-2)
dashboard_app/templates/dashboard_app/filter_form.html (+9/-0)
dashboard_app/templates/dashboard_app/filter_preview.html (+1/-1)
dashboard_app/templates/dashboard_app/filter_summary.html (+3/-3)
dashboard_app/views/filters/forms.py (+15/-2)
To merge this branch: bzr merge lp:~arthur-she/lava-dashboard/fix-for-bug-1175597
Reviewer Review Type Date Requested Status
Stevan Radaković Approve
Review via email: mp+167494@code.launchpad.net

Description of the change

Add "Equation" field (<=, ==, >=) to make the filter more flexible.

To post a comment you must log in.
Revision history for this message
Stevan Radaković (stevanr) wrote :

Arthur, great work!

Couple of comments:

=== modified file 'dashboard_app/filters.py'
--- dashboard_app/filters.py 2013-01-09 00:15:10 +0000
+++ dashboard_app/filters.py 2013-06-05 10:06:54 +0000
@@ -365,13 +365,24 @@

     content_type_id = ContentType.objects.get_for_model(TestRun).id

- for (name, value) in filter_data['attributes']:
+ for (name, equation, value) in filter_data['attributes']:
         # We punch through the generic relation abstraction here for 100x
         # better performance.
- conditions.append(
- models.Q(id__in=NamedAttribute.objects.filter(
- name=name, value=value, content_type_id=content_type_id
- ).values('object_id')))
+ if equation == '>=':
+ conditions.append(
+ models.Q(id__in=NamedAttribute.objects.filter(
+ name=name, value__gte=value, content_type_id=content_type_id
+ ).values('object_id')))
+ elif equation == '<=':
+ conditions.append(
+ models.Q(id__in=NamedAttribute.objects.filter(
+ name=name, value__lte=value, content_type_id=content_type_id
+ ).values('object_id')))
+ else:
+ conditions.append(
+ models.Q(id__in=NamedAttribute.objects.filter(
+ name=name, value=value, content_type_id=content_type_id
+ ).values('object_id')))

I'd really like to see a more generic solution here... If we add more filter equations later, this code will have to be subjected to refactoring again and again. If we would have some kind of mapping like
equation_map = {"<=": "lte" ....}
we could use pyhon's argument packing, something like
kwargs = {
    '{0}__{1}'.format('value', 'lte'): value,
    '{0}__{1}'.format('value', 'gte'): value
}

NamedAttribute.objects.filter(**kwargs)

Second thing are the tests. I know the test coverage in dashboard is scarce, but do we want to keep it that way?

review: Needs Fixing (code)
Revision history for this message
Stevan Radaković (stevanr) wrote :

Sorry for the code paste, I hope it's clear what lines I'm referring to.

Revision history for this message
Stevan Radaković (stevanr) wrote :

One additional comment, we can circumvent the equation map altogether, if the db values for equation field would be defined as 'lte', 'gte'.. to start with. In the frontend we keep the '<=', '>=' of course...

Revision history for this message
Arthur She (arthur-she) wrote :

Thanks a lot, Stevan. It really helpful.
I'll modify it.

2013/6/5 Stevan Radaković <email address hidden>

> One additional comment, we can circumvent the equation map altogether, if
> the db values for equation field would be defined as 'lte', 'gte'.. to
> start with. In the frontend we keep the '<=', '>=' of course...
> --
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>

Revision history for this message
Arthur She (arthur-she) wrote :

Hi Stevan,
I know the idea, if we can make it more generic it should be better.
As you mentioned, we can use a dictionary to map the equation "equation_map
= {"<=": "lte", ">=": "gte", ""==": ""}"
and use argument packing, but I still don't have idea how to associate them
together. Could you talk more..

And regarding the tests, Tyler suggested me to do such modification. Do you
have any other idea to approach it?

Thanks a lot.
Arthur

2013/6/5 Stevan Radaković <email address hidden>

> Review: Needs Fixing code
>
> Arthur, great work!
>
> Couple of comments:
>
> === modified file 'dashboard_app/filters.py'
> --- dashboard_app/filters.py 2013-01-09 00:15:10 +0000
> +++ dashboard_app/filters.py 2013-06-05 10:06:54 +0000
> @@ -365,13 +365,24 @@
>
> content_type_id = ContentType.objects.get_for_model(TestRun).id
>
> - for (name, value) in filter_data['attributes']:
> + for (name, equation, value) in filter_data['attributes']:
> # We punch through the generic relation abstraction here for 100x
> # better performance.
> - conditions.append(
> - models.Q(id__in=NamedAttribute.objects.filter(
> - name=name, value=value, content_type_id=content_type_id
> - ).values('object_id')))
> + if equation == '>=':
> + conditions.append(
> + models.Q(id__in=NamedAttribute.objects.filter(
> + name=name, value__gte=value,
> content_type_id=content_type_id
> + ).values('object_id')))
> + elif equation == '<=':
> + conditions.append(
> + models.Q(id__in=NamedAttribute.objects.filter(
> + name=name, value__lte=value,
> content_type_id=content_type_id
> + ).values('object_id')))
> + else:
> + conditions.append(
> + models.Q(id__in=NamedAttribute.objects.filter(
> + name=name, value=value,
> content_type_id=content_type_id
> + ).values('object_id')))
>
> I'd really like to see a more generic solution here... If we add more
> filter equations later, this code will have to be subjected to refactoring
> again and again. If we would have some kind of mapping like
> equation_map = {"<=": "lte" ....}
> we could use pyhon's argument packing, something like
> kwargs = {
> '{0}__{1}'.format('value', 'lte'): value,
> '{0}__{1}'.format('value', 'gte'): value
> }
>
> NamedAttribute.objects.filter(**kwargs)
>
>
> Second thing are the tests. I know the test coverage in dashboard is
> scarce, but do we want to keep it that way?
> --
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>

Revision history for this message
Arthur She (arthur-she) wrote :

Stevan, do you mean, use
    equation = models.CharField(
            choices=(
                ('lte', '<='),
                ('eq', '=='),
                ('gte', '>=')),
            max_length=3, default='==')
instead of
    equation = models.CharField(
            choices=(
                ('<=', '<='),
                ('==', '=='),
                ('>=', '>=')),
            max_length=3, default='==')
in the class TestRunFilterAttribute

2013/6/5 Stevan Radaković <email address hidden>

> One additional comment, we can circumvent the equation map altogether, if
> the db values for equation field would be defined as 'lte', 'gte'.. to
> start with. In the frontend we keep the '<=', '>=' of course...
> --
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>

Revision history for this message
Stevan Radaković (stevanr) wrote :

Exactly like that!

On 06/06/2013 11:07 AM, Arthur She wrote:
> Stevan, do you mean, use
> equation = models.CharField(
> choices=(
> ('lte', '<='),
> ('eq', '=='),
> ('gte', '>=')),
> max_length=3, default='==')
> instead of
> equation = models.CharField(
> choices=(
> ('<=', '<='),
> ('==', '=='),
> ('>=', '>=')),
> max_length=3, default='==')
> in the class TestRunFilterAttribute
>
>
>
>
>
> 2013/6/5 Stevan Radaković <email address hidden>
>
>> One additional comment, we can circumvent the equation map altogether, if
>> the db values for equation field would be defined as 'lte', 'gte'.. to
>> start with. In the frontend we keep the '<=', '>=' of course...
>> --
>>
>> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
>> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>>

--
Stevan Radaković | LAVA Engineer
Linaro.org <www.linaro.org> │ Open source software for ARM SoCs

Revision history for this message
Stevan Radaković (stevanr) wrote :

> Hi Stevan,
> I know the idea, if we can make it more generic it should be better.
> As you mentioned, we can use a dictionary to map the equation "equation_map
> = {"<=": "lte", ">=": "gte", ""==": ""}"
> and use argument packing, but I still don't have idea how to associate them
> together. Could you talk more..
>
> And regarding the tests, Tyler suggested me to do such modification. Do you
> have any other idea to approach it?
>
> Thanks a lot.
> Arthur
>

You can take a look at the lp:linaro-ci-dashboard it's also a django application and has a lot of good example for model tests. If you need more info about it please feel free to ping me on irc
>
> 2013/6/5 Stevan Radaković <email address hidden>
>
> > Review: Needs Fixing code
> >
> > Arthur, great work!
> >
> > Couple of comments:
> >
> > === modified file 'dashboard_app/filters.py'
> > --- dashboard_app/filters.py 2013-01-09 00:15:10 +0000
> > +++ dashboard_app/filters.py 2013-06-05 10:06:54 +0000
> > @@ -365,13 +365,24 @@
> >
> > content_type_id = ContentType.objects.get_for_model(TestRun).id
> >
> > - for (name, value) in filter_data['attributes']:
> > + for (name, equation, value) in filter_data['attributes']:
> > # We punch through the generic relation abstraction here for 100x
> > # better performance.
> > - conditions.append(
> > - models.Q(id__in=NamedAttribute.objects.filter(
> > - name=name, value=value, content_type_id=content_type_id
> > - ).values('object_id')))
> > + if equation == '>=':
> > + conditions.append(
> > + models.Q(id__in=NamedAttribute.objects.filter(
> > + name=name, value__gte=value,
> > content_type_id=content_type_id
> > + ).values('object_id')))
> > + elif equation == '<=':
> > + conditions.append(
> > + models.Q(id__in=NamedAttribute.objects.filter(
> > + name=name, value__lte=value,
> > content_type_id=content_type_id
> > + ).values('object_id')))
> > + else:
> > + conditions.append(
> > + models.Q(id__in=NamedAttribute.objects.filter(
> > + name=name, value=value,
> > content_type_id=content_type_id
> > + ).values('object_id')))
> >
> > I'd really like to see a more generic solution here... If we add more
> > filter equations later, this code will have to be subjected to refactoring
> > again and again. If we would have some kind of mapping like
> > equation_map = {"<=": "lte" ....}
> > we could use pyhon's argument packing, something like
> > kwargs = {
> > '{0}__{1}'.format('value', 'lte'): value,
> > '{0}__{1}'.format('value', 'gte'): value
> > }
> >
> > NamedAttribute.objects.filter(**kwargs)
> >
> >
> > Second thing are the tests. I know the test coverage in dashboard is
> > scarce, but do we want to keep it that way?
> > --
> >
> > https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-
> bug-1175597/+merge/167494
> > You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
> >

Revision history for this message
Arthur She (arthur-she) wrote :
Download full text (3.5 KiB)

Thanks a lot, Stevan. :)

2013/6/6 Stevan Radaković <email address hidden>

> > Hi Stevan,
> > I know the idea, if we can make it more generic it should be better.
> > As you mentioned, we can use a dictionary to map the equation
> "equation_map
> > = {"<=": "lte", ">=": "gte", ""==": ""}"
> > and use argument packing, but I still don't have idea how to associate
> them
> > together. Could you talk more..
> >
> > And regarding the tests, Tyler suggested me to do such modification. Do
> you
> > have any other idea to approach it?
> >
> > Thanks a lot.
> > Arthur
> >
>
> You can take a look at the lp:linaro-ci-dashboard it's also a django
> application and has a lot of good example for model tests. If you need more
> info about it please feel free to ping me on irc
> >
> > 2013/6/5 Stevan Radaković <email address hidden>
> >
> > > Review: Needs Fixing code
> > >
> > > Arthur, great work!
> > >
> > > Couple of comments:
> > >
> > > === modified file 'dashboard_app/filters.py'
> > > --- dashboard_app/filters.py 2013-01-09 00:15:10 +0000
> > > +++ dashboard_app/filters.py 2013-06-05 10:06:54 +0000
> > > @@ -365,13 +365,24 @@
> > >
> > > content_type_id = ContentType.objects.get_for_model(TestRun).id
> > >
> > > - for (name, value) in filter_data['attributes']:
> > > + for (name, equation, value) in filter_data['attributes']:
> > > # We punch through the generic relation abstraction here for
> 100x
> > > # better performance.
> > > - conditions.append(
> > > - models.Q(id__in=NamedAttribute.objects.filter(
> > > - name=name, value=value,
> content_type_id=content_type_id
> > > - ).values('object_id')))
> > > + if equation == '>=':
> > > + conditions.append(
> > > + models.Q(id__in=NamedAttribute.objects.filter(
> > > + name=name, value__gte=value,
> > > content_type_id=content_type_id
> > > + ).values('object_id')))
> > > + elif equation == '<=':
> > > + conditions.append(
> > > + models.Q(id__in=NamedAttribute.objects.filter(
> > > + name=name, value__lte=value,
> > > content_type_id=content_type_id
> > > + ).values('object_id')))
> > > + else:
> > > + conditions.append(
> > > + models.Q(id__in=NamedAttribute.objects.filter(
> > > + name=name, value=value,
> > > content_type_id=content_type_id
> > > + ).values('object_id')))
> > >
> > > I'd really like to see a more generic solution here... If we add more
> > > filter equations later, this code will have to be subjected to
> refactoring
> > > again and again. If we would have some kind of mapping like
> > > equation_map = {"<=": "lte" ....}
> > > we could use pyhon's argument packing, something like
> > > kwargs = {
> > > '{0}__{1}'.format('value', 'lte'): value,
> > > '{0}__{1}'.format('value', 'gte'): value
> > > }
> > >
> > > NamedAttribute.objects.filter(**kwargs)
> > >
> > >
> > > Second thing are the tests. I know the test coverage in dashboard is
> > > scarce, but do we want t...

Read more...

Revision history for this message
Stevan Radaković (stevanr) wrote :

Hey Arthur,

so instead of all the if/else business, code would look something like this (maybe not 100% accurate didn't try it in the codebase:)

kwargs = {
    name: name,
    '{0}__{1}'.format('value', equation): value,
    content_type_id=content_type_id
}

conditions.append(models.Q(id__in=NamedAttribute.objects.filter(**kwargs).values('object_id')))

I may be missing some quotes but you'll figure it out :)

Revision history for this message
Arthur She (arthur-she) wrote :

Thanks a lot for your code, Stevan.
I think I might misunderstand your meaning for the "tests".
I'll check the code again...

2013/6/7 Stevan Radaković <email address hidden>

> Hey Arthur,
>
> so instead of all the if/else business, code would look something like
> this (maybe not 100% accurate didn't try it in the codebase:)
>
> kwargs = {
> name: name,
> '{0}__{1}'.format('value', equation): value,
> content_type_id=content_type_id
> }
>
>
> conditions.append(models.Q(id__in=NamedAttribute.objects.filter(**kwargs).values('object_id')))
>
> I may be missing some quotes but you'll figure it out :)
> --
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>

406. By Arthur She

Modify it to make it more generic.

modified:
  dashboard_app/filters.py
  dashboard_app/migrations/0030_auto__add_equation_to_testrunfilterattribute.py
  dashboard_app/models.py
  dashboard_app/views/filters/forms.py

407. By Arthur She

use display value instead of db value

modified:
  dashboard_app/models.py
  dashboard_app/templates/dashboard_app/filter_summary.html

Revision history for this message
Stevan Radaković (stevanr) wrote :

I've also noticed we're missing the "Attributes 'filter_name' <= 'value' " sting on the filter preview page, please add this as well.

408. By Arthur She

Show attributes on filter preview page.

modified:
  dashboard_app/templates/dashboard_app/filter_preview.html
  dashboard_app/views/filters/forms.py

Revision history for this message
Arthur She (arthur-she) wrote :

Thanks a lot for your help, I've modified it and pushed to the server.

2013/6/18 Stevan Radaković <email address hidden>

> I've also noticed we're missing the "Attributes 'filter_name' <= 'value' "
> sting on the filter preview page, please add this as well.
> --
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>

Revision history for this message
Stevan Radaković (stevanr) wrote :

Ok this looks like good to go now.
Thanks Arthur.
Approve +1.

review: Approve
Revision history for this message
Arthur She (arthur-she) wrote :

Thanks, Stevan :)

2013/6/24 Stevan Radaković <email address hidden>

> The proposal to merge lp:~arthur-she/lava-dashboard/fix-for-bug-1175597
> into lp:lava-dashboard has been updated.
>
> Status: Needs review => Approved
>
> For more details, see:
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> --
>
> https://code.launchpad.net/~arthur-she/lava-dashboard/fix-for-bug-1175597/+merge/167494
> You are the owner of lp:~arthur-she/lava-dashboard/fix-for-bug-1175597.
>

Unmerged revisions

408. By Arthur She

Show attributes on filter preview page.

modified:
  dashboard_app/templates/dashboard_app/filter_preview.html
  dashboard_app/views/filters/forms.py

407. By Arthur She

use display value instead of db value

modified:
  dashboard_app/models.py
  dashboard_app/templates/dashboard_app/filter_summary.html

406. By Arthur She

Modify it to make it more generic.

modified:
  dashboard_app/filters.py
  dashboard_app/migrations/0030_auto__add_equation_to_testrunfilterattribute.py
  dashboard_app/models.py
  dashboard_app/views/filters/forms.py

405. By Arthur She

fix for bug #1175597

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'dashboard_app/filters.py'
--- dashboard_app/filters.py 2013-01-09 00:15:10 +0000
+++ dashboard_app/filters.py 2013-06-19 08:00:38 +0000
@@ -365,13 +365,15 @@
365365
366 content_type_id = ContentType.objects.get_for_model(TestRun).id366 content_type_id = ContentType.objects.get_for_model(TestRun).id
367367
368 for (name, value) in filter_data['attributes']:368 for (name, equation, value) in filter_data['attributes']:
369 # We punch through the generic relation abstraction here for 100x369 # We punch through the generic relation abstraction here for 100x
370 # better performance.370 # better performance.
371 conditions.append(371 kwargs = {
372 models.Q(id__in=NamedAttribute.objects.filter(372 'name': name,
373 name=name, value=value, content_type_id=content_type_id373 '{0}__{1}'.format('value', equation): value,
374 ).values('object_id')))374 'content_type_id': content_type_id
375 }
376 conditions.append(models.Q(id__in=NamedAttribute.objects.filter(**kwargs).values('object_id')))
375377
376 test_condition = None378 test_condition = None
377 for test in filter_data.get('tests', []):379 for test in filter_data.get('tests', []):
378380
=== added file 'dashboard_app/migrations/0030_auto__add_equation_to_testrunfilterattribute.py'
--- dashboard_app/migrations/0030_auto__add_equation_to_testrunfilterattribute.py 1970-01-01 00:00:00 +0000
+++ dashboard_app/migrations/0030_auto__add_equation_to_testrunfilterattribute.py 2013-06-19 08:00:38 +0000
@@ -0,0 +1,270 @@
1# -*- coding: utf-8 -*-
2import datetime
3from south.db import db
4from south.v2 import SchemaMigration
5from django.db import models
6
7
8class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Adding field 'TestRunFilterAttribute.equation'
12 db.add_column('dashboard_app_testrunfilterattribute', 'equation',
13 self.gf('django.db.models.fields.CharField')(default='exact', max_length=6),
14 keep_default=False)
15
16
17 def backwards(self, orm):
18 # Deleting field 'TestRunFilterAttribute.equation'
19 db.delete_column('dashboard_app_testrunfilterattribute', 'equation')
20
21
22 models = {
23 'auth.group': {
24 'Meta': {'object_name': 'Group'},
25 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
26 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
27 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
28 },
29 'auth.permission': {
30 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
31 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
32 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
33 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
34 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
35 },
36 'auth.user': {
37 'Meta': {'object_name': 'User'},
38 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
39 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
40 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
41 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
42 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
44 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
45 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
46 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
47 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
48 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
49 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
50 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
51 },
52 'contenttypes.contenttype': {
53 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
54 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
55 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
57 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
58 },
59 'dashboard_app.attachment': {
60 'Meta': {'object_name': 'Attachment'},
61 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
62 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
63 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
64 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
66 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
67 'public_url': ('django.db.models.fields.URLField', [], {'max_length': '512', 'blank': 'True'})
68 },
69 'dashboard_app.bundle': {
70 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'},
71 '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}),
72 '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}),
73 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}),
74 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
75 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}),
76 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
77 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
78 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}),
79 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
80 },
81 'dashboard_app.bundledeserializationerror': {
82 'Meta': {'object_name': 'BundleDeserializationError'},
83 'bundle': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'deserialization_error'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.Bundle']"}),
84 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
85 'traceback': ('django.db.models.fields.TextField', [], {'max_length': '32768'})
86 },
87 'dashboard_app.bundlestream': {
88 'Meta': {'object_name': 'BundleStream'},
89 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
90 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
91 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
92 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
93 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
94 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
95 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
96 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
97 },
98 'dashboard_app.hardwaredevice': {
99 'Meta': {'object_name': 'HardwareDevice'},
100 'description': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
101 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
102 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
103 },
104 'dashboard_app.image': {
105 'Meta': {'object_name': 'Image'},
106 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['dashboard_app.TestRunFilter']"}),
107 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
108 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'})
109 },
110 'dashboard_app.imageset': {
111 'Meta': {'object_name': 'ImageSet'},
112 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
113 'images': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.Image']", 'symmetrical': 'False'}),
114 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'})
115 },
116 'dashboard_app.launchpadbug': {
117 'Meta': {'object_name': 'LaunchpadBug'},
118 'bug_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}),
119 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
120 'test_runs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'launchpad_bugs'", 'symmetrical': 'False', 'to': "orm['dashboard_app.TestRun']"})
121 },
122 'dashboard_app.namedattribute': {
123 'Meta': {'unique_together': "(('object_id', 'name'),)", 'object_name': 'NamedAttribute'},
124 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
125 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126 'name': ('django.db.models.fields.TextField', [], {}),
127 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
128 'value': ('django.db.models.fields.TextField', [], {})
129 },
130 'dashboard_app.pmqabundlestream': {
131 'Meta': {'object_name': 'PMQABundleStream'},
132 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.BundleStream']"}),
133 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
134 },
135 'dashboard_app.softwarepackage': {
136 'Meta': {'unique_together': "(('name', 'version'),)", 'object_name': 'SoftwarePackage'},
137 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
138 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
139 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
140 },
141 'dashboard_app.softwarepackagescratch': {
142 'Meta': {'object_name': 'SoftwarePackageScratch'},
143 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
144 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
145 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
146 },
147 'dashboard_app.softwaresource': {
148 'Meta': {'object_name': 'SoftwareSource'},
149 'branch_revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
150 'branch_url': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
151 'branch_vcs': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
152 'commit_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
153 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
154 'project_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
155 },
156 'dashboard_app.tag': {
157 'Meta': {'object_name': 'Tag'},
158 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '256'})
160 },
161 'dashboard_app.test': {
162 'Meta': {'object_name': 'Test'},
163 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
164 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
165 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'})
166 },
167 'dashboard_app.testcase': {
168 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'},
169 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
170 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
171 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}),
172 'test_case_id': ('django.db.models.fields.TextField', [], {}),
173 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'})
174 },
175 'dashboard_app.testdefinition': {
176 'Meta': {'object_name': 'TestDefinition'},
177 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
178 'description': ('django.db.models.fields.TextField', [], {}),
179 'environment': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
180 'format': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
181 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
182 'location': ('django.db.models.fields.CharField', [], {'default': "'LOCAL'", 'max_length': '64'}),
183 'mime_type': ('django.db.models.fields.CharField', [], {'default': "'text/plain'", 'max_length': '64'}),
184 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
185 'target_dev_types': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
186 'target_os': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
187 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
188 'version': ('django.db.models.fields.CharField', [], {'max_length': '256'})
189 },
190 'dashboard_app.testresult': {
191 'Meta': {'ordering': "('_order',)", 'object_name': 'TestResult'},
192 '_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
193 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
194 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
195 'lineno': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
196 'measurement': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '10', 'blank': 'True'}),
197 'message': ('django.db.models.fields.TextField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
198 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}),
199 'relative_index': ('django.db.models.fields.PositiveIntegerField', [], {}),
200 'result': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
201 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'test_results'", 'null': 'True', 'to': "orm['dashboard_app.TestCase']"}),
202 'test_run': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_results'", 'to': "orm['dashboard_app.TestRun']"}),
203 'timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
204 },
205 'dashboard_app.testrun': {
206 'Meta': {'ordering': "['-import_assigned_date']", 'object_name': 'TestRun'},
207 'analyzer_assigned_date': ('django.db.models.fields.DateTimeField', [], {}),
208 'analyzer_assigned_uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}),
209 'bundle': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Bundle']"}),
210 'devices': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.HardwareDevice']"}),
211 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
212 'import_assigned_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
213 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}),
214 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwarePackage']"}),
215 'sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwareSource']"}),
216 'sw_image_desc': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
217 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.Tag']"}),
218 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Test']"}),
219 'time_check_performed': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
220 },
221 'dashboard_app.testrundenormalization': {
222 'Meta': {'object_name': 'TestRunDenormalization'},
223 'count_fail': ('django.db.models.fields.PositiveIntegerField', [], {}),
224 'count_pass': ('django.db.models.fields.PositiveIntegerField', [], {}),
225 'count_skip': ('django.db.models.fields.PositiveIntegerField', [], {}),
226 'count_unknown': ('django.db.models.fields.PositiveIntegerField', [], {}),
227 'test_run': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'denormalization'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.TestRun']"})
228 },
229 'dashboard_app.testrunfilter': {
230 'Meta': {'unique_together': "(('owner', 'name'),)", 'object_name': 'TestRunFilter'},
231 'build_number_attribute': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
232 'bundle_streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.BundleStream']", 'symmetrical': 'False'}),
233 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
234 'name': ('django.db.models.fields.SlugField', [], {'max_length': '1024'}),
235 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
236 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
237 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['auth.User']"})
238 },
239 'dashboard_app.testrunfilterattribute': {
240 'Meta': {'object_name': 'TestRunFilterAttribute'},
241 'equation': ('django.db.models.fields.CharField', [], {'default': "'exact'", 'max_length': '6'}),
242 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attributes'", 'to': "orm['dashboard_app.TestRunFilter']"}),
243 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
244 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
245 'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'})
246 },
247 'dashboard_app.testrunfiltersubscription': {
248 'Meta': {'unique_together': "(('user', 'filter'),)", 'object_name': 'TestRunFilterSubscription'},
249 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']"}),
250 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
251 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
252 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
253 },
254 'dashboard_app.testrunfiltertest': {
255 'Meta': {'object_name': 'TestRunFilterTest'},
256 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tests'", 'to': "orm['dashboard_app.TestRunFilter']"}),
257 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
258 'index': ('django.db.models.fields.PositiveIntegerField', [], {}),
259 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.Test']"})
260 },
261 'dashboard_app.testrunfiltertestcase': {
262 'Meta': {'object_name': 'TestRunFilterTestCase'},
263 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
264 'index': ('django.db.models.fields.PositiveIntegerField', [], {}),
265 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cases'", 'to': "orm['dashboard_app.TestRunFilterTest']"}),
266 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.TestCase']"})
267 }
268 }
269
270 complete_apps = ['dashboard_app']
0\ No newline at end of file271\ No newline at end of file
1272
=== modified file 'dashboard_app/models.py'
--- dashboard_app/models.py 2013-04-01 08:50:05 +0000
+++ dashboard_app/models.py 2013-06-19 08:00:38 +0000
@@ -1566,11 +1566,17 @@
15661566
1567 name = models.CharField(max_length=1024)1567 name = models.CharField(max_length=1024)
1568 value = models.CharField(max_length=1024)1568 value = models.CharField(max_length=1024)
1569 equation = models.CharField(
1570 choices=(
1571 ('lte', '<='),
1572 ('exact', '=='),
1573 ('gte', '>=')),
1574 max_length=6, default='exact')
15691575
1570 filter = models.ForeignKey("TestRunFilter", related_name="attributes")1576 filter = models.ForeignKey("TestRunFilter", related_name="attributes")
15711577
1572 def __unicode__(self):1578 def __unicode__(self):
1573 return '%s = %s' % (self.name, self.value)1579 return '%s %s %s' % (self.name, self.get_equation_display(), self.value)
15741580
15751581
1576class TestRunFilterTest(models.Model):1582class TestRunFilterTest(models.Model):
@@ -1633,9 +1639,14 @@
1633 'test': trftest.test,1639 'test': trftest.test,
1634 'test_cases': [trftestcase.test_case for trftestcase in trftest.cases.all().select_related('test_case')],1640 'test_cases': [trftestcase.test_case for trftestcase in trftest.cases.all().select_related('test_case')],
1635 })1641 })
1642 att = []
1643 for a in self.attributes.all():
1644 b = (a.name, a.get_equation_display(), a.value)
1645 att.append(b)
1636 return {1646 return {
1637 'bundle_streams': self.bundle_streams.all(),1647 'bundle_streams': self.bundle_streams.all(),
1638 'attributes': self.attributes.all().values_list('name', 'value'),1648 'attributes': self.attributes.all().values_list('name', 'equation', 'value'),
1649 'attr_display': att,
1639 'tests': tests,1650 'tests': tests,
1640 'build_number_attribute': self.build_number_attribute,1651 'build_number_attribute': self.build_number_attribute,
1641 'uploaded_by': self.uploaded_by,1652 'uploaded_by': self.uploaded_by,
16421653
=== modified file 'dashboard_app/templates/dashboard_app/filter_form.html'
--- dashboard_app/templates/dashboard_app/filter_form.html 2012-09-19 22:56:03 +0000
+++ dashboard_app/templates/dashboard_app/filter_form.html 2013-06-19 08:00:38 +0000
@@ -42,6 +42,9 @@
42 Name42 Name
43 </th>43 </th>
44 <th>44 <th>
45 Equation
46 </th>
47 <th>
45 Value48 Value
46 </th>49 </th>
47 </tr>50 </tr>
@@ -54,6 +57,9 @@
54 {{ form.name }}57 {{ form.name }}
55 </td>58 </td>
56 <td class="value">59 <td class="value">
60 {{ form.equation }}
61 </td>
62 <td class="value">
57 {{ form.value }}63 {{ form.value }}
58 </td>64 </td>
59 <td>65 <td>
@@ -70,6 +76,9 @@
70 <td class="name">76 <td class="name">
71 {{ form.name }}77 {{ form.name }}
72 </td>78 </td>
79 <td class="equation">
80 {{ form.equation }}
81 </td>
73 <td class="value">82 <td class="value">
74 {{ form.value }}83 {{ form.value }}
75 </td>84 </td>
7685
=== modified file 'dashboard_app/templates/dashboard_app/filter_preview.html'
--- dashboard_app/templates/dashboard_app/filter_preview.html 2013-01-09 00:05:14 +0000
+++ dashboard_app/templates/dashboard_app/filter_preview.html 2013-06-19 08:00:38 +0000
@@ -14,7 +14,7 @@
14<h1>Previewing new filter &ldquo;{{ form.name.value }}&rdquo;</h1>14<h1>Previewing new filter &ldquo;{{ form.name.value }}&rdquo;</h1>
15{% endif %}15{% endif %}
1616
17{% include "dashboard_app/filter_summary.html" with summary_data=filter.as_data %}17{% include "dashboard_app/filter_summary.html" with filter_data=form.as_data %}
1818
19<p>19<p>
20 These are the results matched by your filter.20 These are the results matched by your filter.
2121
=== modified file 'dashboard_app/templates/dashboard_app/filter_summary.html'
--- dashboard_app/templates/dashboard_app/filter_summary.html 2012-12-12 23:49:01 +0000
+++ dashboard_app/templates/dashboard_app/filter_summary.html 2013-06-19 08:00:38 +0000
@@ -9,14 +9,14 @@
9 {% endfor %}9 {% endfor %}
10 </td>10 </td>
11 </tr>11 </tr>
12{% if filter_data.attributes %}12{% if filter_data.attr_display %}
13 <tr>13 <tr>
14 <th>14 <th>
15 Attributes15 Attributes
16 </th>16 </th>
17 <td>17 <td>
18 {% for a in filter_data.attributes %}18 {% for a in filter_data.attr_display %}
19 {{ a.0 }} == {{ a.1 }} <br />19 {{ a.0 }} {{ a.1 }} {{ a.2 }} <br />
20 {% endfor %}20 {% endfor %}
21 </td>21 </td>
22 </tr>22 </tr>
2323
=== modified file 'dashboard_app/views/filters/forms.py'
--- dashboard_app/views/filters/forms.py 2013-01-09 00:14:58 +0000
+++ dashboard_app/views/filters/forms.py 2013-06-19 08:00:38 +0000
@@ -63,6 +63,11 @@
63class AttributesForm(forms.Form):63class AttributesForm(forms.Form):
64 name = forms.CharField(max_length=1024)64 name = forms.CharField(max_length=1024)
65 value = forms.CharField(max_length=1024)65 value = forms.CharField(max_length=1024)
66 equation = forms.ChoiceField(
67 choices=[
68 ('lte', '<='),
69 ('exact', '=='),
70 ('gte', '>=')])
6671
67AttributesFormSet = formset_factory(AttributesForm, extra=0)72AttributesFormSet = formset_factory(AttributesForm, extra=0)
6873
@@ -170,7 +175,7 @@
170 if commit:175 if commit:
171 instance.attributes.all().delete()176 instance.attributes.all().delete()
172 for a in self.attributes_formset.cleaned_data:177 for a in self.attributes_formset.cleaned_data:
173 instance.attributes.create(name=a['name'], value=a['value'])178 instance.attributes.create(name=a['name'], equation=a['equation'], value=a['value'])
174 instance.tests.all().delete()179 instance.tests.all().delete()
175 for i, test_form in enumerate(self.tests_formset.forms):180 for i, test_form in enumerate(self.tests_formset.forms):
176 trf_test = instance.tests.create(181 trf_test = instance.tests.create(
@@ -201,8 +206,15 @@
201 tc_form.cleaned_data['test_case']206 tc_form.cleaned_data['test_case']
202 for tc_form in form.test_case_formset]207 for tc_form in form.test_case_formset]
203 })208 })
209 att = []
210 for form in self.attributes_formset.forms:
211 att.append((
212 form.cleaned_data['name'],
213 dict(form.fields['equation'].choices)[form.cleaned_data['equation']],
214 form.cleaned_data['value']))
215 data['attr_display'] = att
204 data['attributes'] = [216 data['attributes'] = [
205 (d['name'], d['value']) for d in self.attributes_formset.cleaned_data]217 (d['name'], d['equation'], d['value']) for d in self.attributes_formset.cleaned_data]
206 data['tests'] = tests218 data['tests'] = tests
207 data['uploaded_by'] = None219 data['uploaded_by'] = None
208 return data220 return data
@@ -218,6 +230,7 @@
218 for attr in self.instance.attributes.all():230 for attr in self.instance.attributes.all():
219 initial.append({231 initial.append({
220 'name': attr.name,232 'name': attr.name,
233 'equation': attr.equation,
221 'value': attr.value,234 'value': attr.value,
222 })235 })
223 attr_set_args['initial'] = initial236 attr_set_args['initial'] = initial

Subscribers

People subscribed via source and target branches