Merge lp:~stylesen/lava-dashboard/support-test-definitions into lp:lava-dashboard
- support-test-definitions
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 401 |
Proposed branch: | lp:~stylesen/lava-dashboard/support-test-definitions |
Merge into: | lp:lava-dashboard |
Diff against target: |
690 lines (+557/-0) 10 files modified
dashboard_app/admin.py (+5/-0) dashboard_app/extension.py (+1/-0) dashboard_app/helpers.py (+46/-0) dashboard_app/migrations/0029_auto__add_testdefinition.py (+281/-0) dashboard_app/models.py (+76/-0) dashboard_app/templates/dashboard_app/add_test_definition.html (+25/-0) dashboard_app/templates/dashboard_app/test_definition.html (+23/-0) dashboard_app/urls.py (+3/-0) dashboard_app/views/__init__.py (+51/-0) dashboard_app/xmlrpc.py (+46/-0) |
To merge this branch: | bzr merge lp:~stylesen/lava-dashboard/support-test-definitions |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Antonio Terceiro | Approve | ||
Zygmunt Krynicki (community) | Needs Fixing | ||
Linaro Validation Team | Pending | ||
Linaro Validation Team | Pending | ||
Senthil Kumaran S | Pending | ||
Review via email: mp+145137@code.launchpad.net |
Commit message
Description of the change
This code adds the following:
1) New model for representing test definitions in database.
2) Basic UI to list and add test definition (will be enhanced next month).
3) XML-RPC api to get test definitions list.
Zygmunt Krynicki (zyga) wrote : | # |
Zygmunt Krynicki (zyga) wrote : | # |
715 + if os == device == environment == None:
716 + for testdef in TestDefinition.
717 + testdefs[
Comparison to None should be done with 'is'
Zygmunt Krynicki (zyga) wrote : | # |
this code:
395 + testdef_name = models.CharField(
396 + max_length = 128,
397 + verbose_name = _("Name"),
398 + help_text = _help_max_
combined with this code:
703 + if os:
704 + for testdef in TestDefinition.
705 + target_
706 + testdefs[
707 + if device:
708 + for testdef in TestDefinition.
709 + target_
710 + testdefs[
711 + if environment:
712 + for testdef in TestDefinition.
713 + testdef_
714 + testdefs[
Is broken,
you need either to make testdef_name unique() or use the real key
Andy Doan (doanac) wrote : | # |
On 01/28/2013 03:35 AM, Senthil Kumaran S wrote:
> === modified file 'dashboard_
> +class TestDefinition(
A general comment for all fields. Many of the "max_length" fields don't
match their "_help_max_length" value. I suspect that's a copy/paste
error you made?
> + testdef_name = models.CharField(
> + max_length = 128,
> + verbose_name = _("Name"),
> + help_text = _help_max_
> +
> + version = models.CharField(
> + max_length=256,
> + verbose_name = _("Version"),
> + help_text = _help_max_
> + testdef_format = models.CharField(
> + max_length = 128,
> + verbose_name = _("Format"),
> + help_text = _help_max_
> + url = models.CharField(
> + verbose_name = _(u"URL"),
> + max_length = 512,
> + blank = False,
> + help_text = _help_max_
> + target_os = models.CharField(
> + max_length = 512,
> + verbose_name = _("Operating Systems"),
> + help_text = _help_max_
Shouldn't this be an array?
> + target_dev_types = models.CharField(
> + max_length = 512,
> + verbose_name = _("Device types"),
> + help_text = _help_max_
Shouldn't this be an array?
Andy Doan (doanac) wrote : | # |
On 01/28/2013 03:35 AM, Senthil Kumaran S wrote:
> === added file 'dashboard_
> +{% block extrahead %}
> +<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}lava-
> +<script type="text/
> +{% endblock %}
> +{% block content %}
> + <script type="text/
> + </script>
> + <script type="text/
> + </script>
I don't think you need this here, because its already in the "extrahead"
block.
> + {% load django_tables2 %}
I forget, but this might not be the proper place for this.
> + {% render_table testdefinition_
> +{% endblock %}
Andy Doan (doanac) wrote : | # |
On 01/28/2013 03:35 AM, Senthil Kumaran S wrote:
> === modified file 'dashboard_
> +class TestDefinitionT
> + testdef_name = Column()
> + testdef_location = Column()
> + description = Column()
> + def get_queryset(self):
> + return TestDefinition.
> +
> +
Trying to visualize this in my head... We aren't showing all the test
definition fields like os and device-types. Is this okay - ie should we
just add a drill-down view that shows a single test definition?
Andy Doan (doanac) wrote : | # |
On 01/28/2013 03:35 AM, Senthil Kumaran S wrote:
> === modified file 'dashboard_
> + testdefs = {}
> + if os:
> + for testdef in TestDefinition.
> + target_
> + testdefs[
> + if device:
> + for testdef in TestDefinition.
> + target_
> + testdefs[
> + if environment:
> + for testdef in TestDefinition.
> + testdef_
> + testdefs[
> + if os == device == environment == None:
> + for testdef in TestDefinition.
> + testdefs[
> + return testdefs
> +
I think this can be simplified like:
testdefs = {}
tds = TestDefinition.
if os:
tds = tds.filter(
if device:
tds = tds.filter(
if environment:
tds = tds.filter(
for testdef in tds:
return testdefs
Senthil Kumaran S (stylesen) wrote : | # |
On Monday 28 January 2013 11:03 PM, Andy Doan wrote:
> Shouldn't this be an array?
>
>> + target_dev_types = models.CharField(
>> + max_length = 512,
>> + verbose_name = _("Device types"),
>> + help_text = _help_max_
>
> Shouldn't this be an array?
Since they are not on separate columns, it gives me better control on
filtering, hence I made those as strings.
Thank You.
--
Senthil Kumaran S
http://
http://
Senthil Kumaran S (stylesen) wrote : | # |
On Monday 28 January 2013 11:10 PM, Andy Doan wrote:
>> +class TestDefinitionT
>> + testdef_name = Column()
>> + testdef_location = Column()
>> + description = Column()
>> + def get_queryset(self):
>> + return TestDefinition.
>> +
>> +
>
> Trying to visualize this in my head... We aren't showing all the test
> definition fields like os and device-types. Is this okay - ie should we
> just add a drill-down view that shows a single test definition?
Should I take it up along with the test case management UI improvements
BP? ;)
Thank You.
--
Senthil Kumaran S
http://
http://
- 397. By Senthil Kumaran S
-
Implement all review comments from zyga and doanac.
- 398. By Senthil Kumaran S
-
Make the testdef name column bigger in models. Also fix a typo in referring
a dictionary key.
Senthil Kumaran S (stylesen) wrote : | # |
Hi zyga and doanac,
I implemented all the review comments made by you and pushed the latest code at r398. I request you to go through the code once again and let me know if I can merge it to trunk.
Andy Doan (doanac) wrote : | # |
On 01/29, Senthil Kumaran S wrote:
> Should I take it up along with the test case management UI improvements
> BP? ;)
That's fine with me.
Andy Doan (doanac) wrote : | # |
On 01/28, Senthil Kumaran S wrote:
> On Monday 28 January 2013 11:03 PM, Andy Doan wrote:
> > Shouldn't this be an array?
> >
> >> + target_dev_types = models.CharField(
> >> + max_length = 512,
> >> + verbose_name = _("Device types"),
> >> + help_text = _help_max_
> >
> > Shouldn't this be an array?
>
> Since they are not on separate columns, it gives me better control on
> filtering, hence I made those as strings.
I don't think this approach will play very nice when we start to make
try and query the database. ie - you'll be mixing SQL and some type of
regular expession.
For device types, we have an easy solution - these are defined in the
lava-scheduler. I'm not sure the answer for operating systems. Michael
mentioned postgres has an array type. Not sure if that would work here.
Worst, case we create an OperatingSystem's table and pre-populate it in
our south migration with Android, Ubuntu, OpenEmbedded
Zygmunt Krynicki (zyga) wrote : | # |
17 + pass
Remove this, it's useless
60 + testdef_meta = {'testdef_name': c_test_id,
61 + 'version': version_info,
62 + 'description': c_testdef_
63 + 'testdef_format': c_testdef_
64 + 'testdef_location': c_testdef_
65 + 'url': c_testdef_
66 + 'testdef_
67 + c_testdef_
68 + 'target_os': c_testdef_
69 + 'target_dev_types': c_testdef_
70 + }
This is not PEP-8ish.
I don't care much but I found tools like flake8 invaluable for finding actual issues but stuff like this gets just like noise.
+ 'description': c_testdef_
63 + 'testdef_format': c_testdef_
64 + 'testdef_location': c_testdef_
65 + 'url': c_testdef_
66 + 'testdef_
67 + c_testdef_
68 + 'target_os': c_testdef_
69 + 'target_dev_types': c_testdef_
There is zero validation against a schema so I could upload something without any of those to bork the upload.
72 + s_testdef = TestDefinition.
I'd use get_or_create and use the second return argument and use it to update the stuff if needed. This is not critical thought.
429 +
430 + url = models.CharField(
431 + verbose_name = _(u"URL"),
432 + max_length = 1024,
433 + blank = False,
434 + help_text = _help_max_
What should be the value of url if for LOCAL test definitions? Blank prevents this from being empty (at django validation level)
683 + `get_test_
I'm not entirely sure, correct me if I'm wrong, but XML-RPC does not support optional arguments, does it?
692 + The type of operating system the retrieved test definitions should
693 + apply to.
694 +
695 + `device`: string
696 + The type of device the retrieved test definitions should apply to.
697 +
698 + `environment`: string
699 + The type of test environment the retrieved test definitions should
700 + apply to.
None of the docstrings describe that a substring search is being performed.
710 + if os:
711 + tds = tds.filter(
712 +
713 + if device:
714 + tds = tds.filter(
715 +
716 + if environment:
717 + tds = tds.filter(
You may want to check django docs on how subsequent .filter() are applied. Basically document if you want OR or AND behavior and make sure you really do what you want.
719 + for testdef in tds:
720 + testdefs[
This is probably wasting a lot of SQL. You can implement the same with .values() and that just pick testdef_name and url and return those for each record. It's not much but you are really pulling in all of the data to get two fields out.
Thanks for fixing the model testdef_name uniqueness.
- 399. By Senthil Kumaran S
-
Evolve bundle format to 1.6
- 400. By Senthil Kumaran S
-
Take care of zyga's review comments.
- 401. By Senthil Kumaran S
-
Bring up-to-date with trunk.
Zygmunt Krynicki (zyga) wrote : | # |
76 + s_testdef, testdef_created = TestDefinition.
77 + **testdef_meta)
78 +
79 + if testdef_created:
80 + s_testdef.save()
81 + else:
82 + # Do not try to update testdef_name since it is unique, hence
83 + # pop it from the dictionary.
84 + testdef_
85 + TestDefinition.
86 + testdef_
I think this is wrong. Basically testdef_created will always be false unless you pass in the exact same data twice. The idea behind get_or_create() is to pass the primary key / some unique key only. Otherwise you'll keep trying to add rows that will create uniqueness failures at the db level.
Also, this shows that either I totally don't know what I'm talking about or you have no tests at all.
I see no reaction (discussion, code) to URL + local problem, xml-rpc api docs, implementation or anything else? Did I miss something?
Senthil Kumaran S (stylesen) wrote : | # |
On Friday 01 February 2013 03:46 AM, Zygmunt Krynicki wrote:
> 76 + s_testdef, testdef_created = TestDefinition.
> 77 + **testdef_meta)
> 78 +
> 79 + if testdef_created:
> 80 + s_testdef.save()
> 81 + else:
> 82 + # Do not try to update testdef_name since it is unique, hence
> 83 + # pop it from the dictionary.
> 84 + testdef_
> 85 + TestDefinition.
> 86 + testdef_
>
> I think this is wrong. Basically testdef_created will always be false unless you pass in the exact same data twice. The idea behind get_or_create() is to pass the primary key / some unique key only. Otherwise you'll keep trying to add rows that will create uniqueness failures at the db level.
I hope I am doing the right thing here. I try to create an object, if it
fails due to primary key constraint, I will update the same object with
new values for different columns.
> Also, this shows that either I totally don't know what I'm talking about or you have no tests at all.
I tested it many times and it works fine for me.
> I see no reaction (discussion, code) to URL + local problem, xml-rpc api docs, implementation or anything else? Did I miss something?
Yesterday I was busy with some other BP, so I did not comment on this. I
shall let you know once I have addressed all your concerns.
Regarding the URL + local problem - local will be added via the web UI
manually by the user, when he/she does that, the local URL will be
calculated based on the server name and the path within the server where
the testdef contents will be stored. This is work in progress, but in
order to give way for this implementation, I made the field not blank.
Thank You.
--
Senthil Kumaran S
http://
http://
- 402. By Senthil Kumaran S
-
Bring up-to-date with trunk.
Michael Hudson-Doyle (mwhudson) wrote : | # |
Senthil Kumaran S <email address hidden> writes:
> You have been requested to review the proposed merge of lp:~stylesen/lava-dashboard/support-test-definitions into lp:lava-dashboard.
>
> For more details, see:
> https:/
>
> This code adds the following:
>
> 1) New model for representing test definitions in database.
> 2) Basic UI to list and add test definition (will be enhanced next month).
> 3) XML-RPC api to get test definitions list.
>
>
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -47,6 +47,7 @@
> TestRunFilter,
> TestRunFilterAt
> TestRunFilterSu
> + TestDefinition,
> )
>
>
> @@ -203,6 +204,9 @@
> save_as = True
>
>
> +class TestDefinitionA
> + list_display = ('testdef_name', )
I'm fairly sure that ('testdef_name', 'version') would be a more useful
choice here. Although... the whole issue of testdef versions is a best
messed up as I rant about below.
> admin.site.
> admin.site.
> admin.site.
> @@ -221,3 +225,4 @@
> admin.site.
> admin.site.
> admin.site.
> +admin.
>
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -49,6 +49,7 @@
> subm.append(
> if not settings.
> subm.append(
> + subm.append(
It seems most other menu items use the plural. So this should probably
be "Test Definitions".
> return menu
>
>
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -742,6 +742,56 @@
> self._import_
>
>
> +class BundleFormatImp
> + """
> + IFormatImporter subclass capable of loading "Dashboard Bundle Format 1.6"
> + """
> +
> + def _import_
> + """
> + Import dashboard_
> + based on a client-side description of a TestRun metadata.
> + """
> + from dashboard_
> +
> + if c_testdef_
> + version_info = c_testdef_
> + else:
> + version_info = c_testdef_
> +
> + testdef_meta = {
> + 'testdef_name': c_test_id,
> + 've...
- 403. By Senthil Kumaran S
-
Bring up-to-date with trunk.
- 404. By Senthil Kumaran S
-
Make simple changes as suggested by mwhudson.
- 405. By Senthil Kumaran S
-
Chnage the insertion logic for test definition which was previously wrong.
- 406. By Senthil Kumaran S
-
Use @BreadCrumb properly and avoid including title block in template.
- 407. By Senthil Kumaran S
-
Use form.as_table to display add test definition form.
- 408. By Senthil Kumaran S
-
Remove explicitly refering to feild names in add test definition form.
- 409. By Senthil Kumaran S
-
Change to plural naming convention for test definitions.
Antonio Terceiro (terceiro) wrote : | # |
Hello Senthil,
Follows my commments.
review needs-fixing
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -47,6 +47,7 @@
> TestRunFilter,
> TestRunFilterAt
> TestRunFilterSu
> + TestDefinitions,
> )
I don't understand why you changed the class name to the plural.
I saw Michael's comments about plural, but I think he meant to just use
plural in the manu label, not in the class name. :-)
> @@ -203,6 +204,9 @@
> save_as = True
>
>
> +class TestDefinitionA
> + list_display = ('testdef_name', 'version')
> +
> admin.site.
> admin.site.
> admin.site.
> @@ -221,3 +225,4 @@
> admin.site.
> admin.site.
> admin.site.
> +admin.
>
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -49,6 +49,7 @@
> subm.append(
> if not settings.
> subm.append(
> + subm.append(
>
> return menu
>
>
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -8,6 +8,7 @@
> import time
>
> from django.
> +from django.
> from django.db import connection, transaction, IntegrityError
> from linaro_
> from linaro_
> @@ -742,6 +743,51 @@
> self._import_
>
>
> +class BundleFormatImp
> + """
> + IFormatImporter subclass capable of loading "Dashboard Bundle Format 1.6"
> + """
> +
> + def _import_
> + """
> + Import dashboard_
> + based on a client-side description of a TestRun metadata.
> + """
> + from dashboard_
> +
> + testdef_meta = {
> + 'testdef_name': c_test_id,
> + 'version': c_testdef_
> + 'description': c_testdef_
> + 'testdef_format': c_testdef_
> + 'testdef_location': c_testdef_
> + 'url': c_testdef_
> + 'testdef_
> + ...
- 410. By Senthil Kumaran S
-
Remove plural class name for Test Definition.
Suggested by: terceiro
- 411. By Senthil Kumaran S
-
Fix testdef_ prefix in model member naming.
- 412. By Senthil Kumaran S
-
Change the database updation logic to make minimal queries. Also push the
script for testdefinition table migration which was missed in previous
commits.
Senthil Kumaran S (stylesen) wrote : | # |
Hi Antonio,
> Follows my commments.
>
> review needs-fixing
I fixed all the stuff pointed by you in your review comments. Would request you to go through the new difference and approve this branch.
--
Senthil Kumaran
http://
http://
- 413. By Senthil Kumaran S
-
Get the object first to properly update test definition metadata, else
the logic is flawed.
Antonio Terceiro (terceiro) wrote : | # |
Almost there. :)
review needs-fixing
> === modified file 'dashboard_
> --- dashboard_
> +++ dashboard_
> @@ -43,6 +43,7 @@
> DataView,
> Test,
> TestRunFilter,
> + TestDefinition,
> )
>
>
> @@ -436,7 +437,7 @@
> """
> test_names = []
> if device_type:
> - for test in Test.objects.
> + for test in Testobjects.filter(
typo
--
Antonio Terceiro
Software Engineer - Linaro
http://
- 414. By Senthil Kumaran S
-
Fix a typo that was accidental.
Senthil Kumaran S (stylesen) wrote : | # |
Hi Antonio,
On Wed, 2013-04-03 at 23:43 +0000, Antonio Terceiro wrote:
> > === modified file 'dashboard_
> > --- dashboard_
> > +++ dashboard_
> > @@ -43,6 +43,7 @@
> > DataView,
> > Test,
> > TestRunFilter,
> > + TestDefinition,
> > )
> >
> >
> > @@ -436,7 +437,7 @@
> > """
> > test_names = []
> > if device_type:
> > - for test in Test.objects.
> > + for test in Testobjects.filter(
>
> typo
Thanks for catching it. This is unrelated to my change, but I hope I did
some formatting tweaks which touched this line and by mistake I made
that typo. Anyways, I corrected it in r414.
Thank You.
--
Senthil Kumaran
http://
http://
Antonio Terceiro (terceiro) wrote : | # |
I think we are done with this one.
Preview Diff
1 | === modified file 'dashboard_app/admin.py' |
2 | --- dashboard_app/admin.py 2013-01-11 16:25:54 +0000 |
3 | +++ dashboard_app/admin.py 2013-04-04 08:19:22 +0000 |
4 | @@ -47,6 +47,7 @@ |
5 | TestRunFilter, |
6 | TestRunFilterAttribute, |
7 | TestRunFilterSubscription, |
8 | + TestDefinition, |
9 | ) |
10 | |
11 | |
12 | @@ -203,6 +204,9 @@ |
13 | save_as = True |
14 | |
15 | |
16 | +class TestDefinitionAdmin(admin.ModelAdmin): |
17 | + list_display = ('name', 'version') |
18 | + |
19 | admin.site.register(Attachment) |
20 | admin.site.register(Bundle, BundleAdmin) |
21 | admin.site.register(BundleDeserializationError, BundleDeserializationErrorAdmin) |
22 | @@ -221,3 +225,4 @@ |
23 | admin.site.register(TestRunFilter, TestRunFilterAdmin) |
24 | admin.site.register(TestRunFilterSubscription) |
25 | admin.site.register(Tag) |
26 | +admin.site.register(TestDefinition, TestDefinitionAdmin) |
27 | |
28 | === modified file 'dashboard_app/extension.py' |
29 | --- dashboard_app/extension.py 2013-01-31 02:52:51 +0000 |
30 | +++ dashboard_app/extension.py 2013-04-04 08:19:22 +0000 |
31 | @@ -49,6 +49,7 @@ |
32 | subm.append(Menu("Data Views", reverse("dashboard_app.views.data_view_list"))) |
33 | if not settings.DATAREPORTS_HIDE: |
34 | subm.append(Menu("Reports", reverse("dashboard_app.views.report_list"))) |
35 | + subm.append(Menu("Test Definitions", reverse("dashboard_app.views.test_definition"))) |
36 | |
37 | return menu |
38 | |
39 | |
40 | === modified file 'dashboard_app/helpers.py' |
41 | --- dashboard_app/helpers.py 2012-11-16 01:01:22 +0000 |
42 | +++ dashboard_app/helpers.py 2013-04-04 08:19:22 +0000 |
43 | @@ -8,6 +8,7 @@ |
44 | import time |
45 | |
46 | from django.core.files.base import ContentFile |
47 | +from django.core.exceptions import ObjectDoesNotExist |
48 | from django.db import connection, transaction, IntegrityError |
49 | from linaro_dashboard_bundle.errors import DocumentFormatError |
50 | from linaro_dashboard_bundle.evolution import DocumentEvolution |
51 | @@ -742,6 +743,50 @@ |
52 | self._import_test_result_attachments(c_test_result, s_test_result) |
53 | |
54 | |
55 | +class BundleFormatImporter_1_6(BundleFormatImporter_1_5): |
56 | + """ |
57 | + IFormatImporter subclass capable of loading "Dashboard Bundle Format 1.6" |
58 | + """ |
59 | + |
60 | + def _import_testdef(self, c_test_id, c_testdef_metadata): |
61 | + """ |
62 | + Import dashboard_app.models.TestDefinition into the database |
63 | + based on a client-side description of a TestRun metadata. |
64 | + """ |
65 | + from dashboard_app.models import TestDefinition |
66 | + |
67 | + testdef_meta = { |
68 | + 'name': c_test_id, |
69 | + 'version': c_testdef_metadata.get("version"), |
70 | + 'description': c_testdef_metadata.get("description"), |
71 | + 'format': c_testdef_metadata.get("format"), |
72 | + 'location': c_testdef_metadata.get("location"), |
73 | + 'url': c_testdef_metadata.get("url"), |
74 | + 'environment': c_testdef_metadata.get("environment"), |
75 | + 'target_os': c_testdef_metadata.get("os"), |
76 | + 'target_dev_types': c_testdef_metadata.get("devices"), |
77 | + } |
78 | + |
79 | + try: |
80 | + s_testdef = TestDefinition.objects.get(name=c_test_id) |
81 | + # Do not try to update name since it is unique, hence |
82 | + # pop it from the dictionary. |
83 | + testdef_meta.pop('name', None) |
84 | + TestDefinition.objects.filter(name=c_test_id).update( |
85 | + **testdef_meta) |
86 | + except ObjectDoesNotExist: |
87 | + s_testdef = TestDefinition.objects.create(**testdef_meta) |
88 | + s_testdef.save() |
89 | + |
90 | + def _import_test_results(self, c_test_run, s_test_run): |
91 | + from dashboard_app.models import TestResult |
92 | + super(BundleFormatImporter_1_6, self)._import_test_results(c_test_run, |
93 | + s_test_run) |
94 | + if c_test_run.get("testdef_metadata"): |
95 | + self._import_testdef(c_test_run["test_id"], |
96 | + c_test_run["testdef_metadata"]) |
97 | + |
98 | + |
99 | class BundleDeserializer(object): |
100 | """ |
101 | Helper class for de-serializing JSON bundle content into database models |
102 | @@ -755,6 +800,7 @@ |
103 | "Dashboard Bundle Format 1.3": BundleFormatImporter_1_3, |
104 | "Dashboard Bundle Format 1.4": BundleFormatImporter_1_4, |
105 | "Dashboard Bundle Format 1.5": BundleFormatImporter_1_5, |
106 | + "Dashboard Bundle Format 1.6": BundleFormatImporter_1_6, |
107 | } |
108 | |
109 | def deserialize(self, s_bundle, prefer_evolution): |
110 | |
111 | === added file 'dashboard_app/migrations/0029_auto__add_testdefinition.py' |
112 | --- dashboard_app/migrations/0029_auto__add_testdefinition.py 1970-01-01 00:00:00 +0000 |
113 | +++ dashboard_app/migrations/0029_auto__add_testdefinition.py 2013-04-04 08:19:22 +0000 |
114 | @@ -0,0 +1,281 @@ |
115 | +# -*- coding: utf-8 -*- |
116 | +import datetime |
117 | +from south.db import db |
118 | +from south.v2 import SchemaMigration |
119 | +from django.db import models |
120 | + |
121 | + |
122 | +class Migration(SchemaMigration): |
123 | + |
124 | + def forwards(self, orm): |
125 | + # Adding model 'TestDefinition' |
126 | + db.create_table('dashboard_app_testdefinition', ( |
127 | + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
128 | + ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=512)), |
129 | + ('version', self.gf('django.db.models.fields.CharField')(max_length=256)), |
130 | + ('description', self.gf('django.db.models.fields.TextField')()), |
131 | + ('format', self.gf('django.db.models.fields.CharField')(max_length=128)), |
132 | + ('location', self.gf('django.db.models.fields.CharField')(default='LOCAL', max_length=64)), |
133 | + ('url', self.gf('django.db.models.fields.CharField')(max_length=1024)), |
134 | + ('environment', self.gf('django.db.models.fields.CharField')(max_length=256)), |
135 | + ('target_os', self.gf('django.db.models.fields.CharField')(max_length=512)), |
136 | + ('target_dev_types', self.gf('django.db.models.fields.CharField')(max_length=512)), |
137 | + ('content', self.gf('django.db.models.fields.files.FileField')(max_length=100, null=True, blank=True)), |
138 | + ('mime_type', self.gf('django.db.models.fields.CharField')(default='text/plain', max_length=64)), |
139 | + )) |
140 | + db.send_create_signal('dashboard_app', ['TestDefinition']) |
141 | + |
142 | + |
143 | + def backwards(self, orm): |
144 | + # Deleting model 'TestDefinition' |
145 | + db.delete_table('dashboard_app_testdefinition') |
146 | + |
147 | + |
148 | + models = { |
149 | + 'auth.group': { |
150 | + 'Meta': {'object_name': 'Group'}, |
151 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
152 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
153 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
154 | + }, |
155 | + 'auth.permission': { |
156 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
157 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
158 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
159 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
160 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
161 | + }, |
162 | + 'auth.user': { |
163 | + 'Meta': {'object_name': 'User'}, |
164 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
165 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
166 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
167 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
168 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
169 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
170 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
171 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
172 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
173 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
174 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
175 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
176 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
177 | + }, |
178 | + 'contenttypes.contenttype': { |
179 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
180 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
181 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
182 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
183 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
184 | + }, |
185 | + 'dashboard_app.attachment': { |
186 | + 'Meta': {'object_name': 'Attachment'}, |
187 | + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), |
188 | + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
189 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
190 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
191 | + 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
192 | + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), |
193 | + 'public_url': ('django.db.models.fields.URLField', [], {'max_length': '512', 'blank': 'True'}) |
194 | + }, |
195 | + 'dashboard_app.bundle': { |
196 | + 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'}, |
197 | + '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}), |
198 | + '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}), |
199 | + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), |
200 | + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
201 | + 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}), |
202 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
203 | + 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
204 | + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}), |
205 | + 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
206 | + }, |
207 | + 'dashboard_app.bundledeserializationerror': { |
208 | + 'Meta': {'object_name': 'BundleDeserializationError'}, |
209 | + 'bundle': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'deserialization_error'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.Bundle']"}), |
210 | + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
211 | + 'traceback': ('django.db.models.fields.TextField', [], {'max_length': '32768'}) |
212 | + }, |
213 | + 'dashboard_app.bundlestream': { |
214 | + 'Meta': {'object_name': 'BundleStream'}, |
215 | + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), |
216 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
217 | + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
218 | + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
219 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), |
220 | + 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), |
221 | + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), |
222 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
223 | + }, |
224 | + 'dashboard_app.hardwaredevice': { |
225 | + 'Meta': {'object_name': 'HardwareDevice'}, |
226 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
227 | + 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
228 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) |
229 | + }, |
230 | + 'dashboard_app.image': { |
231 | + 'Meta': {'object_name': 'Image'}, |
232 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['dashboard_app.TestRunFilter']"}), |
233 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
234 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'}) |
235 | + }, |
236 | + 'dashboard_app.imageset': { |
237 | + 'Meta': {'object_name': 'ImageSet'}, |
238 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
239 | + 'images': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.Image']", 'symmetrical': 'False'}), |
240 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) |
241 | + }, |
242 | + 'dashboard_app.launchpadbug': { |
243 | + 'Meta': {'object_name': 'LaunchpadBug'}, |
244 | + 'bug_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}), |
245 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
246 | + 'test_runs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'launchpad_bugs'", 'symmetrical': 'False', 'to': "orm['dashboard_app.TestRun']"}) |
247 | + }, |
248 | + 'dashboard_app.namedattribute': { |
249 | + 'Meta': {'unique_together': "(('object_id', 'name'),)", 'object_name': 'NamedAttribute'}, |
250 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
251 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
252 | + 'name': ('django.db.models.fields.TextField', [], {}), |
253 | + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), |
254 | + 'value': ('django.db.models.fields.TextField', [], {}) |
255 | + }, |
256 | + 'dashboard_app.pmqabundlestream': { |
257 | + 'Meta': {'object_name': 'PMQABundleStream'}, |
258 | + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.BundleStream']"}), |
259 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) |
260 | + }, |
261 | + 'dashboard_app.softwarepackage': { |
262 | + 'Meta': {'unique_together': "(('name', 'version'),)", 'object_name': 'SoftwarePackage'}, |
263 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
264 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
265 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'}) |
266 | + }, |
267 | + 'dashboard_app.softwarepackagescratch': { |
268 | + 'Meta': {'object_name': 'SoftwarePackageScratch'}, |
269 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
270 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
271 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'}) |
272 | + }, |
273 | + 'dashboard_app.softwaresource': { |
274 | + 'Meta': {'object_name': 'SoftwareSource'}, |
275 | + 'branch_revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
276 | + 'branch_url': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
277 | + 'branch_vcs': ('django.db.models.fields.CharField', [], {'max_length': '10'}), |
278 | + 'commit_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
279 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
280 | + 'project_name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) |
281 | + }, |
282 | + 'dashboard_app.tag': { |
283 | + 'Meta': {'object_name': 'Tag'}, |
284 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
285 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '256'}) |
286 | + }, |
287 | + 'dashboard_app.test': { |
288 | + 'Meta': {'object_name': 'Test'}, |
289 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
290 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), |
291 | + 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) |
292 | + }, |
293 | + 'dashboard_app.testcase': { |
294 | + 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'}, |
295 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
296 | + 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
297 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}), |
298 | + 'test_case_id': ('django.db.models.fields.TextField', [], {}), |
299 | + 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'}) |
300 | + }, |
301 | + 'dashboard_app.testdefinition': { |
302 | + 'Meta': {'object_name': 'TestDefinition'}, |
303 | + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), |
304 | + 'description': ('django.db.models.fields.TextField', [], {}), |
305 | + 'environment': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
306 | + 'format': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
307 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
308 | + 'location': ('django.db.models.fields.CharField', [], {'default': "'LOCAL'", 'max_length': '64'}), |
309 | + 'mime_type': ('django.db.models.fields.CharField', [], {'default': "'text/plain'", 'max_length': '64'}), |
310 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}), |
311 | + 'target_dev_types': ('django.db.models.fields.CharField', [], {'max_length': '512'}), |
312 | + 'target_os': ('django.db.models.fields.CharField', [], {'max_length': '512'}), |
313 | + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
314 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '256'}) |
315 | + }, |
316 | + 'dashboard_app.testresult': { |
317 | + 'Meta': {'ordering': "('_order',)", 'object_name': 'TestResult'}, |
318 | + '_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
319 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), |
320 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
321 | + 'lineno': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), |
322 | + 'measurement': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '10', 'blank': 'True'}), |
323 | + 'message': ('django.db.models.fields.TextField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), |
324 | + 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), |
325 | + 'relative_index': ('django.db.models.fields.PositiveIntegerField', [], {}), |
326 | + 'result': ('django.db.models.fields.PositiveSmallIntegerField', [], {}), |
327 | + 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'test_results'", 'null': 'True', 'to': "orm['dashboard_app.TestCase']"}), |
328 | + 'test_run': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_results'", 'to': "orm['dashboard_app.TestRun']"}), |
329 | + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) |
330 | + }, |
331 | + 'dashboard_app.testrun': { |
332 | + 'Meta': {'ordering': "['-import_assigned_date']", 'object_name': 'TestRun'}, |
333 | + 'analyzer_assigned_date': ('django.db.models.fields.DateTimeField', [], {}), |
334 | + 'analyzer_assigned_uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), |
335 | + 'bundle': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Bundle']"}), |
336 | + 'devices': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.HardwareDevice']"}), |
337 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
338 | + 'import_assigned_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
339 | + 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), |
340 | + 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwarePackage']"}), |
341 | + 'sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwareSource']"}), |
342 | + 'sw_image_desc': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), |
343 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.Tag']"}), |
344 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Test']"}), |
345 | + 'time_check_performed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) |
346 | + }, |
347 | + 'dashboard_app.testrundenormalization': { |
348 | + 'Meta': {'object_name': 'TestRunDenormalization'}, |
349 | + 'count_fail': ('django.db.models.fields.PositiveIntegerField', [], {}), |
350 | + 'count_pass': ('django.db.models.fields.PositiveIntegerField', [], {}), |
351 | + 'count_skip': ('django.db.models.fields.PositiveIntegerField', [], {}), |
352 | + 'count_unknown': ('django.db.models.fields.PositiveIntegerField', [], {}), |
353 | + 'test_run': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'denormalization'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.TestRun']"}) |
354 | + }, |
355 | + 'dashboard_app.testrunfilter': { |
356 | + 'Meta': {'unique_together': "(('owner', 'name'),)", 'object_name': 'TestRunFilter'}, |
357 | + 'build_number_attribute': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), |
358 | + 'bundle_streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.BundleStream']", 'symmetrical': 'False'}), |
359 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
360 | + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '1024'}), |
361 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
362 | + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
363 | + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['auth.User']"}) |
364 | + }, |
365 | + 'dashboard_app.testrunfilterattribute': { |
366 | + 'Meta': {'object_name': 'TestRunFilterAttribute'}, |
367 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attributes'", 'to': "orm['dashboard_app.TestRunFilter']"}), |
368 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
369 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
370 | + 'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) |
371 | + }, |
372 | + 'dashboard_app.testrunfiltersubscription': { |
373 | + 'Meta': {'unique_together': "(('user', 'filter'),)", 'object_name': 'TestRunFilterSubscription'}, |
374 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']"}), |
375 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
376 | + 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
377 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) |
378 | + }, |
379 | + 'dashboard_app.testrunfiltertest': { |
380 | + 'Meta': {'object_name': 'TestRunFilterTest'}, |
381 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tests'", 'to': "orm['dashboard_app.TestRunFilter']"}), |
382 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
383 | + 'index': ('django.db.models.fields.PositiveIntegerField', [], {}), |
384 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.Test']"}) |
385 | + }, |
386 | + 'dashboard_app.testrunfiltertestcase': { |
387 | + 'Meta': {'object_name': 'TestRunFilterTestCase'}, |
388 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
389 | + 'index': ('django.db.models.fields.PositiveIntegerField', [], {}), |
390 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cases'", 'to': "orm['dashboard_app.TestRunFilterTest']"}), |
391 | + 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.TestCase']"}) |
392 | + } |
393 | + } |
394 | + |
395 | + complete_apps = ['dashboard_app'] |
396 | \ No newline at end of file |
397 | |
398 | === modified file 'dashboard_app/models.py' |
399 | --- dashboard_app/models.py 2013-02-07 22:30:46 +0000 |
400 | +++ dashboard_app/models.py 2013-04-04 08:19:22 +0000 |
401 | @@ -701,6 +701,82 @@ |
402 | return self.test_results.filter(result=TestResult.RESULT_FAIL).count() |
403 | |
404 | |
405 | +class TestDefinition(models.Model): |
406 | + """ |
407 | + Model for representing test definitions. |
408 | + |
409 | + Test Definition are in YAML format. |
410 | + """ |
411 | + LOCATION_CHOICES = ( |
412 | + ('LOCAL', 'Local'), |
413 | + ('URL', 'URL'), |
414 | + ('GIT', 'GIT Repo'), |
415 | + ('BZR', 'BZR Repo'), |
416 | + ) |
417 | + |
418 | + name = models.CharField( |
419 | + max_length = 512, |
420 | + verbose_name = _("Name"), |
421 | + unique = True, |
422 | + help_text = _help_max_length(512)) |
423 | + |
424 | + version = models.CharField( |
425 | + max_length=256, |
426 | + verbose_name = _("Version"), |
427 | + help_text = _help_max_length(256)) |
428 | + |
429 | + description = models.TextField( |
430 | + verbose_name = _("Description")) |
431 | + |
432 | + format = models.CharField( |
433 | + max_length = 128, |
434 | + verbose_name = _("Format"), |
435 | + help_text = _help_max_length(128)) |
436 | + |
437 | + location = models.CharField( |
438 | + max_length = 64, |
439 | + verbose_name = _("Location"), |
440 | + choices = LOCATION_CHOICES, |
441 | + default = 'LOCAL') |
442 | + |
443 | + url = models.CharField( |
444 | + verbose_name = _(u"URL"), |
445 | + max_length = 1024, |
446 | + blank = False, |
447 | + help_text = _help_max_length(1024)) |
448 | + |
449 | + environment = models.CharField( |
450 | + max_length = 256, |
451 | + verbose_name = _("Environment"), |
452 | + help_text = _help_max_length(256)) |
453 | + |
454 | + target_os = models.CharField( |
455 | + max_length = 512, |
456 | + verbose_name = _("Operating Systems"), |
457 | + help_text = _help_max_length(512)) |
458 | + |
459 | + target_dev_types = models.CharField( |
460 | + max_length = 512, |
461 | + verbose_name = _("Device types"), |
462 | + help_text = _help_max_length(512)) |
463 | + |
464 | + content = models.FileField( |
465 | + verbose_name = _(u"Upload Test Definition"), |
466 | + help_text = _(u"Test definition file"), |
467 | + upload_to = 'testdef', |
468 | + blank = True, |
469 | + null = True) |
470 | + |
471 | + mime_type = models.CharField( |
472 | + verbose_name = _(u"MIME type"), |
473 | + default = 'text/plain', |
474 | + max_length = 64, |
475 | + help_text = _help_max_length(64)) |
476 | + |
477 | + def __unicode__(self): |
478 | + return self.name |
479 | + |
480 | + |
481 | class SoftwareSource(models.Model): |
482 | """ |
483 | Model for representing source reference of a particular project |
484 | |
485 | === added file 'dashboard_app/templates/dashboard_app/add_test_definition.html' |
486 | --- dashboard_app/templates/dashboard_app/add_test_definition.html 1970-01-01 00:00:00 +0000 |
487 | +++ dashboard_app/templates/dashboard_app/add_test_definition.html 2013-04-04 08:19:22 +0000 |
488 | @@ -0,0 +1,25 @@ |
489 | +{% extends "dashboard_app/_content_with_sidebar.html" %} |
490 | +{% load i18n %} |
491 | +{% load stylize %} |
492 | + |
493 | +{% block extrahead %} |
494 | +<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}lava-server/css/demo_table_jui.css"/> |
495 | +{% endblock %} |
496 | + |
497 | +{% block sidebar %} |
498 | +<h3>Actions</h3> |
499 | +<ul> |
500 | + <li><a href="{% url dashboard_app.views.test_definition %}"> |
501 | + List test definitions</a></li> |
502 | + <li><a href="{% url dashboard_app.views.add_test_definition %}"> |
503 | + Add test definition</a></li> |
504 | +</ul> |
505 | +{% endblock %} |
506 | + |
507 | +{% block content %} |
508 | +<form method="post" action=""> |
509 | + {% csrf_token %} |
510 | + <table>{{ form.as_table }}</table> |
511 | + <input type="submit" value="Save"/> |
512 | +</form> |
513 | +{% endblock %} |
514 | |
515 | === added file 'dashboard_app/templates/dashboard_app/test_definition.html' |
516 | --- dashboard_app/templates/dashboard_app/test_definition.html 1970-01-01 00:00:00 +0000 |
517 | +++ dashboard_app/templates/dashboard_app/test_definition.html 2013-04-04 08:19:22 +0000 |
518 | @@ -0,0 +1,23 @@ |
519 | +{% extends "dashboard_app/_content_with_sidebar.html" %} |
520 | +{% load i18n %} |
521 | +{% load stylize %} |
522 | +{% load django_tables2 %} |
523 | + |
524 | +{% block extrahead %} |
525 | +<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}lava-server/css/demo_table_jui.css"/> |
526 | +<script type="text/javascript" src="{{ STATIC_URL }}lava-server/js/jquery.dataTables.min.js"></script> |
527 | +{% endblock %} |
528 | + |
529 | +{% block sidebar %} |
530 | +<h3>Actions</h3> |
531 | +<ul> |
532 | + <li><a href="{% url dashboard_app.views.test_definition %}"> |
533 | + List test definitions</a></li> |
534 | + <li><a href="{% url dashboard_app.views.add_test_definition %}"> |
535 | + Add test definition</a></li> |
536 | +</ul> |
537 | +{% endblock %} |
538 | + |
539 | +{% block content %} |
540 | + {% render_table testdefinition_table %} |
541 | +{% endblock %} |
542 | |
543 | === modified file 'dashboard_app/urls.py' |
544 | --- dashboard_app/urls.py 2013-01-11 17:07:32 +0000 |
545 | +++ dashboard_app/urls.py 2013-04-04 08:19:22 +0000 |
546 | @@ -70,4 +70,7 @@ |
547 | url(r'^image-reports/(?P<name>[A-Za-z0-9_-]+)$', 'images.image_report_detail'), |
548 | url(r'^api/link-bug-to-testrun', 'images.link_bug_to_testrun'), |
549 | url(r'^api/unlink-bug-and-testrun', 'images.unlink_bug_and_testrun'), |
550 | + url(r'^test-definition/add_test_definition', 'add_test_definition'), |
551 | + url(r'^test-definition/$', 'test_definition'), |
552 | + url(r'^testdefinition_table_json$', 'testdefinition_table_json'), |
553 | ) |
554 | |
555 | === modified file 'dashboard_app/views/__init__.py' |
556 | --- dashboard_app/views/__init__.py 2013-01-11 16:25:54 +0000 |
557 | +++ dashboard_app/views/__init__.py 2013-04-04 08:19:22 +0000 |
558 | @@ -40,6 +40,8 @@ |
559 | from django.template import RequestContext, loader |
560 | from django.utils.safestring import mark_safe |
561 | from django.views.generic.list_detail import object_list, object_detail |
562 | +from django.forms import ModelForm |
563 | +from django import forms |
564 | |
565 | from django_tables2 import Attrs, Column, TemplateColumn |
566 | |
567 | @@ -60,6 +62,7 @@ |
568 | Test, |
569 | TestResult, |
570 | TestRun, |
571 | + TestDefinition, |
572 | ) |
573 | |
574 | |
575 | @@ -673,3 +676,51 @@ |
576 | request.user, |
577 | content_sha1=content_sha1) |
578 | return redirect_to(request, bundle, trailing) |
579 | + |
580 | + |
581 | +class TestDefinitionTable(DataTablesTable): |
582 | + name = Column() |
583 | + version = Column() |
584 | + location = Column() |
585 | + description = Column() |
586 | + def get_queryset(self): |
587 | + return TestDefinition.objects.all() |
588 | + |
589 | + |
590 | +def testdefinition_table_json(request): |
591 | + return TestDefinitionTable.json(request) |
592 | + |
593 | + |
594 | +@BreadCrumb("Test Definitions", parent=index) |
595 | +def test_definition(request): |
596 | + return render_to_response( |
597 | + "dashboard_app/test_definition.html", { |
598 | + 'bread_crumb_trail': BreadCrumbTrail.leading_to(test_definition), |
599 | + "testdefinition_table": TestDefinitionTable( |
600 | + 'testdeflist', |
601 | + reverse(testdefinition_table_json)) |
602 | + }, RequestContext(request)) |
603 | + |
604 | + |
605 | +class AddTestDefForm(ModelForm): |
606 | + class Meta: |
607 | + model = TestDefinition |
608 | + fields = ('name', 'version', 'description', 'format', 'location', |
609 | + 'url', 'environment', 'target_os', 'target_dev_types', |
610 | + 'content', 'mime_type') |
611 | + |
612 | +@BreadCrumb("Add Test Definition", parent=index) |
613 | +def add_test_definition(request): |
614 | + if request.method == 'POST': |
615 | + form = AddTestDefForm(request.POST) |
616 | + if form.is_valid(): |
617 | + form.save() |
618 | + return HttpResponseRedirect('/dashboard/test-definition/') |
619 | + else: |
620 | + form = AddTestDefForm() |
621 | + return render_to_response( |
622 | + "dashboard_app/add_test_definition.html", { |
623 | + 'bread_crumb_trail': BreadCrumbTrail.leading_to( |
624 | + add_test_definition), |
625 | + "form": form, |
626 | + }, RequestContext(request)) |
627 | |
628 | === modified file 'dashboard_app/xmlrpc.py' |
629 | --- dashboard_app/xmlrpc.py 2013-01-15 00:30:25 +0000 |
630 | +++ dashboard_app/xmlrpc.py 2013-04-04 08:19:22 +0000 |
631 | @@ -43,6 +43,7 @@ |
632 | DataView, |
633 | Test, |
634 | TestRunFilter, |
635 | + TestDefinition, |
636 | ) |
637 | |
638 | |
639 | @@ -882,6 +883,51 @@ |
640 | matches = matches[:100] |
641 | return [match.serializable() for match in matches] |
642 | |
643 | + @xml_rpc_signature('str') |
644 | + def get_test_definitions(self, os=None, device=None, environment=None): |
645 | + """ |
646 | + Name |
647 | + ---- |
648 | + `get_test_definitions` ([`os`[, `device`[, `environment`]]]) |
649 | + |
650 | + Description |
651 | + ----------- |
652 | + Get the name and url of all the test definitions. |
653 | + |
654 | + Arguments |
655 | + --------- |
656 | + `os`: string |
657 | + The type of operating system the retrieved test definitions should |
658 | + apply to. |
659 | + |
660 | + `device`: string |
661 | + The type of device the retrieved test definitions should apply to. |
662 | + |
663 | + `environment`: string |
664 | + The type of test environment the retrieved test definitions should |
665 | + apply to. |
666 | + |
667 | + Return value |
668 | + ------------ |
669 | + This function returns an XML-RPC structure of test definition name and |
670 | + URL where the test definition exists. |
671 | + """ |
672 | + testdefs = {} |
673 | + tds = TestDefinition.objects.all() |
674 | + |
675 | + if os: |
676 | + tds = tds.filter(target_os__contains=os) |
677 | + |
678 | + if device: |
679 | + tds = tds.filter(target_dev_types__contains=device) |
680 | + |
681 | + if environment: |
682 | + tds = tds.filter(environment__contains=environment) |
683 | + |
684 | + for testdef in tds: |
685 | + testdefs[testdef.name] = testdef.url |
686 | + return testdefs |
687 | + |
688 | # Mapper used by the legacy URL |
689 | legacy_mapper = Mapper() |
690 | legacy_mapper.register_introspection_methods() |
405 + description = models.CharField( length( 256))
406 + max_length = 256,
407 + verbose_name = _("Description"),
408 + help_text = _help_max_
Don't you want to make that a TextField so that length limit is no longer imposed?