Merge lp:~stevanr/lava-dashboard/image-report-editor-frontend into lp:lava-dashboard

Proposed by Stevan Radaković
Status: Merged
Merged at revision: 422
Proposed branch: lp:~stevanr/lava-dashboard/image-report-editor-frontend
Merge into: lp:lava-dashboard
Diff against target: 1948 lines (+1720/-95)
16 files modified
dashboard_app/migrations/0031_auto__del_imagecharttestrun__add_imagecharttest__add_unique_imagechart.py (+388/-0)
dashboard_app/models.py (+134/-95)
dashboard_app/static/dashboard_app/css/image-charts.css (+82/-0)
dashboard_app/static/dashboard_app/js/image-report-editor.js (+221/-0)
dashboard_app/templates/dashboard_app/image_chart_filter_form.html (+109/-0)
dashboard_app/templates/dashboard_app/image_report_chart_detail.html (+86/-0)
dashboard_app/templates/dashboard_app/image_report_chart_form.html (+62/-0)
dashboard_app/templates/dashboard_app/image_report_detail.html (+80/-0)
dashboard_app/templates/dashboard_app/image_report_form.html (+45/-0)
dashboard_app/templates/dashboard_app/image_report_list.html (+37/-0)
dashboard_app/urls.py (+14/-0)
dashboard_app/views/filters/tables.py (+9/-0)
dashboard_app/views/filters/views.py (+20/-0)
dashboard_app/views/image_reports/__init__.py (+17/-0)
dashboard_app/views/image_reports/forms.py (+91/-0)
dashboard_app/views/image_reports/views.py (+325/-0)
To merge this branch: bzr merge lp:~stevanr/lava-dashboard/image-report-editor-frontend
Reviewer Review Type Date Requested Status
Antonio Terceiro Approve
Review via email: mp+185255@code.launchpad.net

Description of the change

To post a comment you must log in.
432. By Stevan Radaković

Fix ordering in aliases bug.

433. By Stevan Radaković

Small makeup changes.

434. By Stevan Radaković

Add comments to js file.

435. By Stevan Radaković

Remove empty options from select boxes.

436. By Stevan Radaković

Set redirect for image report chart add/edit view.

437. By Stevan Radaković

Chart type not editable when filters are present.

438. By Stevan Radaković

Fix aliases for test cases.

Revision history for this message
Antonio Terceiro (terceiro) wrote :

> === added file 'dashboard_app/migrations/0030_auto__add_imagecharttest__add_imagereport__add_imagecharttestcase__add.py'
> === removed file 'dashboard_app/migrations/0030_auto__add_imagecharttestcase__add_imagereport__add_imagecharttestrun__.py'

AFAICT you cannot remove migrations that were already added and were already
run, only add new migrations. I think you have to put the old one back, remove
the new one, and generate one that will record just the changes since the
previous.

Did you test upgrading from trunk code to your new version?

 review needs-fixing

review: Needs Fixing
439. By Stevan Radaković

Revert migration delete. Add new migration step.

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

Of course you can, the only thing you have to do is something like:
"lava-server manage migrate dashboard_app 0029"
before the migration.

I realize our scripts on staging and production are not that sophisticated, so I reverted the deleted migration and created new one based on that migration step and my changes to the model.

440. By Stevan Radaković

Change database yet again.

441. By Stevan Radaković

Better way to update tests/test cases within filter.

442. By Stevan Radaković

Remove item menu for now.

Revision history for this message
Antonio Terceiro (terceiro) wrote :

On Fri, Sep 13, 2013 at 10:19:41AM -0000, Stevan Radaković wrote:
> Of course you can, the only thing you have to do is something like:
> "lava-server manage migrate dashboard_app 0029" before the migration.

Sure, but that defeats the point of using migrations since it will not
require specific manual steps on a specific upgrade.

> I realize our scripts on staging and production are not that
> sophisticated, so I reverted the deleted migration and created new one
> based on that migration step and my changes to the model.

The point of migrations is to have your database evolve without manual
intervention other than "upgrade my database", and having this "upgrade
my datavase" action always to the right thing.

 review approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'dashboard_app/migrations/0031_auto__del_imagecharttestrun__add_imagecharttest__add_unique_imagechart.py'
--- dashboard_app/migrations/0031_auto__del_imagecharttestrun__add_imagecharttest__add_unique_imagechart.py 1970-01-01 00:00:00 +0000
+++ dashboard_app/migrations/0031_auto__del_imagecharttestrun__add_imagecharttest__add_unique_imagechart.py 2013-09-13 13:13:49 +0000
@@ -0,0 +1,388 @@
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 # Deleting model 'ImageChartTestRun'
12 db.delete_table('dashboard_app_imagecharttestrun')
13
14 # Adding model 'ImageChartTest'
15 db.create_table('dashboard_app_imagecharttest', (
16 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
17 ('image_chart_filter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.ImageChartFilter'])),
18 ('test', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.Test'])),
19 ('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
20 ))
21 db.send_create_signal('dashboard_app', ['ImageChartTest'])
22
23 # Adding unique constraint on 'ImageChartTest', fields ['image_chart_filter', 'test']
24 db.create_unique('dashboard_app_imagecharttest', ['image_chart_filter_id', 'test_id'])
25
26 # Adding model 'ImageChartFilter'
27 db.create_table('dashboard_app_imagechartfilter', (
28 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
29 ('image_chart', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.ImageReportChart'])),
30 ('filter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.TestRunFilter'], null=True, on_delete=models.SET_NULL)),
31 ('representation', self.gf('django.db.models.fields.CharField')(default='lines', max_length=20)),
32 ))
33 db.send_create_signal('dashboard_app', ['ImageChartFilter'])
34
35 # Deleting field 'ImageChartTestCase.image_chart'
36 db.delete_column('dashboard_app_imagecharttestcase', 'image_chart_id')
37
38 # Adding field 'ImageChartTestCase.image_chart_filter'
39 db.add_column('dashboard_app_imagecharttestcase', 'image_chart_filter',
40 self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['dashboard_app.ImageChartFilter']),
41 keep_default=False)
42
43 # Adding unique constraint on 'ImageChartTestCase', fields ['image_chart_filter', 'test_case']
44 db.create_unique('dashboard_app_imagecharttestcase', ['image_chart_filter_id', 'test_case_id'])
45
46 # Adding field 'ImageReport.is_published'
47 db.add_column('dashboard_app_imagereport', 'is_published',
48 self.gf('django.db.models.fields.BooleanField')(default=False),
49 keep_default=False)
50
51 # Deleting field 'ImageReportChart.representation'
52 db.delete_column('dashboard_app_imagereportchart', 'representation')
53
54 # Adding field 'ImageReportChart.description'
55 db.add_column('dashboard_app_imagereportchart', 'description',
56 self.gf('django.db.models.fields.TextField')(null=True, blank=True),
57 keep_default=False)
58
59
60 def backwards(self, orm):
61 # Removing unique constraint on 'ImageChartTestCase', fields ['image_chart_filter', 'test_case']
62 db.delete_unique('dashboard_app_imagecharttestcase', ['image_chart_filter_id', 'test_case_id'])
63
64 # Removing unique constraint on 'ImageChartTest', fields ['image_chart_filter', 'test']
65 db.delete_unique('dashboard_app_imagecharttest', ['image_chart_filter_id', 'test_id'])
66
67 # Adding model 'ImageChartTestRun'
68 db.create_table('dashboard_app_imagecharttestrun', (
69 ('test_run', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.TestRun'])),
70 ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
71 ('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
72 ('image_chart', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.ImageReportChart'])),
73 ))
74 db.send_create_signal('dashboard_app', ['ImageChartTestRun'])
75
76 # Deleting model 'ImageChartTest'
77 db.delete_table('dashboard_app_imagecharttest')
78
79 # Deleting model 'ImageChartFilter'
80 db.delete_table('dashboard_app_imagechartfilter')
81
82 # Adding field 'ImageChartTestCase.image_chart'
83 db.add_column('dashboard_app_imagecharttestcase', 'image_chart',
84 self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['dashboard_app.ImageReportChart']),
85 keep_default=False)
86
87 # Deleting field 'ImageChartTestCase.image_chart_filter'
88 db.delete_column('dashboard_app_imagecharttestcase', 'image_chart_filter_id')
89
90 # Deleting field 'ImageReport.is_published'
91 db.delete_column('dashboard_app_imagereport', 'is_published')
92
93 # Adding field 'ImageReportChart.representation'
94 db.add_column('dashboard_app_imagereportchart', 'representation',
95 self.gf('django.db.models.fields.CharField')(default='pass/fail', max_length=20),
96 keep_default=False)
97
98 # Deleting field 'ImageReportChart.description'
99 db.delete_column('dashboard_app_imagereportchart', 'description')
100
101
102 models = {
103 'auth.group': {
104 'Meta': {'object_name': 'Group'},
105 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
106 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
107 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
108 },
109 'auth.permission': {
110 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
111 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
112 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
113 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
114 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
115 },
116 'auth.user': {
117 'Meta': {'object_name': 'User'},
118 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
119 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
120 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
121 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
122 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
123 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
124 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
125 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
126 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
127 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
128 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
129 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
130 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
131 },
132 'contenttypes.contenttype': {
133 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
134 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
135 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
136 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
137 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
138 },
139 'dashboard_app.attachment': {
140 'Meta': {'object_name': 'Attachment'},
141 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}),
142 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
143 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
144 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
145 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
146 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
147 'public_url': ('django.db.models.fields.URLField', [], {'max_length': '512', 'blank': 'True'})
148 },
149 'dashboard_app.bundle': {
150 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'},
151 '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}),
152 '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}),
153 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}),
154 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
155 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}),
156 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
157 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
158 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}),
159 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'})
160 },
161 'dashboard_app.bundledeserializationerror': {
162 'Meta': {'object_name': 'BundleDeserializationError'},
163 'bundle': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'deserialization_error'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.Bundle']"}),
164 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
165 'traceback': ('django.db.models.fields.TextField', [], {'max_length': '32768'})
166 },
167 'dashboard_app.bundlestream': {
168 'Meta': {'object_name': 'BundleStream'},
169 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
170 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
171 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
172 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
173 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
174 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
175 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
176 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
177 },
178 'dashboard_app.hardwaredevice': {
179 'Meta': {'object_name': 'HardwareDevice'},
180 'description': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
181 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
182 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
183 },
184 'dashboard_app.image': {
185 'Meta': {'object_name': 'Image'},
186 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['dashboard_app.TestRunFilter']"}),
187 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
188 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'})
189 },
190 'dashboard_app.imagechartfilter': {
191 'Meta': {'object_name': 'ImageChartFilter'},
192 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
193 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
194 'image_chart': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.ImageReportChart']"}),
195 'representation': ('django.db.models.fields.CharField', [], {'default': "'lines'", 'max_length': '20'})
196 },
197 'dashboard_app.imagecharttest': {
198 'Meta': {'unique_together': "(('image_chart_filter', 'test'),)", 'object_name': 'ImageChartTest'},
199 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
200 'image_chart_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.ImageChartFilter']"}),
201 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
202 'test': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.Test']"})
203 },
204 'dashboard_app.imagecharttestcase': {
205 'Meta': {'unique_together': "(('image_chart_filter', 'test_case'),)", 'object_name': 'ImageChartTestCase'},
206 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
207 'image_chart_filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.ImageChartFilter']"}),
208 'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
209 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestCase']"})
210 },
211 'dashboard_app.imagereport': {
212 'Meta': {'object_name': 'ImageReport'},
213 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
214 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
215 'is_published': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
216 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'})
217 },
218 'dashboard_app.imagereportchart': {
219 'Meta': {'object_name': 'ImageReportChart'},
220 'chart_type': ('django.db.models.fields.CharField', [], {'default': "'pass/fail'", 'max_length': '20'}),
221 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
222 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
223 'image_report': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dashboard_app.ImageReport']"}),
224 'is_data_table_visible': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
225 'is_interactive': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
226 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
227 'target_goal': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '5', 'blank': 'True'})
228 },
229 'dashboard_app.imageset': {
230 'Meta': {'object_name': 'ImageSet'},
231 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
232 'images': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.Image']", 'symmetrical': 'False'}),
233 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'})
234 },
235 'dashboard_app.launchpadbug': {
236 'Meta': {'object_name': 'LaunchpadBug'},
237 'bug_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}),
238 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
239 'test_runs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'launchpad_bugs'", 'symmetrical': 'False', 'to': "orm['dashboard_app.TestRun']"})
240 },
241 'dashboard_app.namedattribute': {
242 'Meta': {'unique_together': "(('object_id', 'name'),)", 'object_name': 'NamedAttribute'},
243 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
244 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
245 'name': ('django.db.models.fields.TextField', [], {}),
246 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
247 'value': ('django.db.models.fields.TextField', [], {})
248 },
249 'dashboard_app.pmqabundlestream': {
250 'Meta': {'object_name': 'PMQABundleStream'},
251 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.BundleStream']"}),
252 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
253 },
254 'dashboard_app.softwarepackage': {
255 'Meta': {'unique_together': "(('name', 'version'),)", 'object_name': 'SoftwarePackage'},
256 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
257 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
258 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
259 },
260 'dashboard_app.softwarepackagescratch': {
261 'Meta': {'object_name': 'SoftwarePackageScratch'},
262 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
263 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
264 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
265 },
266 'dashboard_app.softwaresource': {
267 'Meta': {'object_name': 'SoftwareSource'},
268 'branch_revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
269 'branch_url': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
270 'branch_vcs': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
271 'commit_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
272 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
273 'project_name': ('django.db.models.fields.CharField', [], {'max_length': '32'})
274 },
275 'dashboard_app.tag': {
276 'Meta': {'object_name': 'Tag'},
277 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
278 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '256'})
279 },
280 'dashboard_app.test': {
281 'Meta': {'object_name': 'Test'},
282 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
283 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
284 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'})
285 },
286 'dashboard_app.testcase': {
287 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'},
288 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
289 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
290 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}),
291 'test_case_id': ('django.db.models.fields.TextField', [], {}),
292 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'})
293 },
294 'dashboard_app.testdefinition': {
295 'Meta': {'object_name': 'TestDefinition'},
296 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
297 'description': ('django.db.models.fields.TextField', [], {}),
298 'environment': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
299 'format': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
300 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
301 'location': ('django.db.models.fields.CharField', [], {'default': "'LOCAL'", 'max_length': '64'}),
302 'mime_type': ('django.db.models.fields.CharField', [], {'default': "'text/plain'", 'max_length': '64'}),
303 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
304 'target_dev_types': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
305 'target_os': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
306 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
307 'version': ('django.db.models.fields.CharField', [], {'max_length': '256'})
308 },
309 'dashboard_app.testresult': {
310 'Meta': {'ordering': "('_order',)", 'object_name': 'TestResult'},
311 '_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
312 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
313 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
314 'lineno': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
315 'measurement': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '10', 'blank': 'True'}),
316 'message': ('django.db.models.fields.TextField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
317 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}),
318 'relative_index': ('django.db.models.fields.PositiveIntegerField', [], {}),
319 'result': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
320 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'test_results'", 'null': 'True', 'to': "orm['dashboard_app.TestCase']"}),
321 'test_run': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_results'", 'to': "orm['dashboard_app.TestRun']"}),
322 'timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
323 },
324 'dashboard_app.testrun': {
325 'Meta': {'ordering': "['-import_assigned_date']", 'object_name': 'TestRun'},
326 'analyzer_assigned_date': ('django.db.models.fields.DateTimeField', [], {}),
327 'analyzer_assigned_uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}),
328 'bundle': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Bundle']"}),
329 'devices': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.HardwareDevice']"}),
330 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
331 'import_assigned_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
332 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}),
333 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwarePackage']"}),
334 'sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwareSource']"}),
335 'sw_image_desc': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
336 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.Tag']"}),
337 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Test']"}),
338 'time_check_performed': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
339 },
340 'dashboard_app.testrundenormalization': {
341 'Meta': {'object_name': 'TestRunDenormalization'},
342 'count_fail': ('django.db.models.fields.PositiveIntegerField', [], {}),
343 'count_pass': ('django.db.models.fields.PositiveIntegerField', [], {}),
344 'count_skip': ('django.db.models.fields.PositiveIntegerField', [], {}),
345 'count_unknown': ('django.db.models.fields.PositiveIntegerField', [], {}),
346 'test_run': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'denormalization'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.TestRun']"})
347 },
348 'dashboard_app.testrunfilter': {
349 'Meta': {'unique_together': "(('owner', 'name'),)", 'object_name': 'TestRunFilter'},
350 'build_number_attribute': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
351 'bundle_streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.BundleStream']", 'symmetrical': 'False'}),
352 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
353 'name': ('django.db.models.fields.SlugField', [], {'max_length': '1024'}),
354 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
355 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
356 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['auth.User']"})
357 },
358 'dashboard_app.testrunfilterattribute': {
359 'Meta': {'object_name': 'TestRunFilterAttribute'},
360 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attributes'", 'to': "orm['dashboard_app.TestRunFilter']"}),
361 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
362 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
363 'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'})
364 },
365 'dashboard_app.testrunfiltersubscription': {
366 'Meta': {'unique_together': "(('user', 'filter'),)", 'object_name': 'TestRunFilterSubscription'},
367 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']"}),
368 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
369 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
370 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
371 },
372 'dashboard_app.testrunfiltertest': {
373 'Meta': {'object_name': 'TestRunFilterTest'},
374 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tests'", 'to': "orm['dashboard_app.TestRunFilter']"}),
375 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
376 'index': ('django.db.models.fields.PositiveIntegerField', [], {}),
377 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.Test']"})
378 },
379 'dashboard_app.testrunfiltertestcase': {
380 'Meta': {'object_name': 'TestRunFilterTestCase'},
381 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
382 'index': ('django.db.models.fields.PositiveIntegerField', [], {}),
383 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cases'", 'to': "orm['dashboard_app.TestRunFilterTest']"}),
384 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.TestCase']"})
385 }
386 }
387
388 complete_apps = ['dashboard_app']
0\ No newline at end of file389\ No newline at end of file
1390
=== modified file 'dashboard_app/models.py'
--- dashboard_app/models.py 2013-09-05 16:00:38 +0000
+++ dashboard_app/models.py 2013-09-13 13:13:49 +0000
@@ -1540,101 +1540,6 @@
1540 return self.name1540 return self.name
15411541
15421542
1543class ImageReport(models.Model):
1544
1545 name = models.SlugField(max_length=1024, unique=True)
1546
1547 description = models.TextField(blank=True, null=True)
1548
1549 def __unicode__(self):
1550 return self.name
1551
1552
1553# Chart types
1554CHART_TYPES = ((r'pass/fail', 'Pass/Fail'),
1555 (r'measurement', 'Measurement'))
1556# Chart representation
1557REPRESENTATION_TYPES = ((r'lines', 'Lines'),
1558 (r'bars', 'Bars'))
1559
1560
1561class ImageReportChart(models.Model):
1562
1563 name = models.CharField(max_length=100)
1564
1565 image_report = models.ForeignKey(
1566 ImageReport,
1567 default=None,
1568 null=False,
1569 on_delete=models.CASCADE)
1570
1571 test_runs = models.ManyToManyField(
1572 TestRun,
1573 through='ImageChartTestRun')
1574
1575 test_cases = models.ManyToManyField(
1576 TestCase,
1577 through='ImageChartTestCase')
1578
1579 chart_type = models.CharField(
1580 max_length=20,
1581 choices=CHART_TYPES,
1582 verbose_name='Chart type')
1583
1584 representation = models.CharField(
1585 max_length=20,
1586 choices=REPRESENTATION_TYPES,
1587 verbose_name='Representation type')
1588
1589 target_goal = models.DecimalField(
1590 blank = True,
1591 decimal_places = 5,
1592 max_digits = 10,
1593 null = True,
1594 verbose_name = 'Target goal')
1595
1596 is_interactive = models.BooleanField(
1597 default=False,
1598 verbose_name='Chart is interactive')
1599
1600 is_data_table_visible = models.BooleanField(
1601 default=False,
1602 verbose_name='Data table is visible')
1603
1604 def __unicode__(self):
1605 return self.name
1606
1607
1608class ImageChartTestRun(models.Model):
1609
1610 image_chart = models.ForeignKey(
1611 ImageReportChart,
1612 null=False,
1613 on_delete=models.CASCADE)
1614
1615 test_run = models.ForeignKey(
1616 TestRun,
1617 null=False,
1618 on_delete=models.CASCADE)
1619
1620 name = models.CharField(max_length=200)
1621
1622
1623class ImageChartTestCase(models.Model):
1624
1625 image_chart = models.ForeignKey(
1626 ImageReportChart,
1627 null=False,
1628 on_delete=models.CASCADE)
1629
1630 test_case = models.ForeignKey(
1631 TestCase,
1632 null=False,
1633 on_delete=models.CASCADE)
1634
1635 name = models.CharField(max_length=200)
1636
1637
1638class LaunchpadBug(models.Model):1543class LaunchpadBug(models.Model):
16391544
1640 bug_id = models.PositiveIntegerField(unique=True)1545 bug_id = models.PositiveIntegerField(unique=True)
@@ -1923,3 +1828,137 @@
1923class PMQABundleStream(models.Model):1828class PMQABundleStream(models.Model):
19241829
1925 bundle_stream = models.ForeignKey(BundleStream, related_name='+')1830 bundle_stream = models.ForeignKey(BundleStream, related_name='+')
1831
1832
1833class ImageReport(models.Model):
1834
1835 name = models.SlugField(max_length=1024, unique=True)
1836
1837 description = models.TextField(blank=True, null=True)
1838
1839 is_published = models.BooleanField(
1840 default=False,
1841 verbose_name='Published')
1842
1843 def __unicode__(self):
1844 return self.name
1845
1846 @models.permalink
1847 def get_absolute_url(self):
1848 return ("dashboard_app.views.image_reports.views.image_report_detail",
1849 (), dict(name=self.name))
1850
1851# Chart types
1852CHART_TYPES = ((r'pass/fail', 'Pass/Fail'),
1853 (r'measurement', 'Measurement'))
1854# Chart representation
1855REPRESENTATION_TYPES = ((r'lines', 'Lines'),
1856 (r'bars', 'Bars'))
1857
1858
1859class ImageReportChart(models.Model):
1860
1861 name = models.CharField(max_length=100)
1862
1863 description = models.TextField(blank=True, null=True)
1864
1865 image_report = models.ForeignKey(
1866 ImageReport,
1867 default=None,
1868 null=False,
1869 on_delete=models.CASCADE)
1870
1871 chart_type = models.CharField(
1872 max_length=20,
1873 choices=CHART_TYPES,
1874 verbose_name='Chart type',
1875 blank=False,
1876 default="pass/fail",
1877 )
1878
1879 target_goal = models.DecimalField(
1880 blank = True,
1881 decimal_places = 5,
1882 max_digits = 10,
1883 null = True,
1884 verbose_name = 'Target goal')
1885
1886 is_interactive = models.BooleanField(
1887 default=False,
1888 verbose_name='Chart is interactive')
1889
1890 is_data_table_visible = models.BooleanField(
1891 default=False,
1892 verbose_name='Data table is visible')
1893
1894 def __unicode__(self):
1895 return self.name
1896
1897 @models.permalink
1898 def get_absolute_url(self):
1899 return ("dashboard_app.views.image_reports.views.image_chart_detail",
1900 (), dict(id=self.id))
1901
1902
1903class ImageChartFilter(models.Model):
1904
1905 image_chart = models.ForeignKey(
1906 ImageReportChart,
1907 null=False,
1908 on_delete=models.CASCADE)
1909
1910 filter = models.ForeignKey(
1911 TestRunFilter,
1912 null=True,
1913 on_delete=models.SET_NULL)
1914
1915 representation = models.CharField(
1916 max_length=20,
1917 choices=REPRESENTATION_TYPES,
1918 verbose_name='Representation',
1919 blank=False,
1920 default="lines",
1921 )
1922
1923 @models.permalink
1924 def get_absolute_url(self):
1925 return (
1926 "dashboard_app.views.image_reports.views.image_chart_filter_edit",
1927 (), dict(id=self.id))
1928
1929
1930class ImageChartTest(models.Model):
1931
1932 class Meta:
1933 unique_together = ("image_chart_filter", "test")
1934
1935 image_chart_filter = models.ForeignKey(
1936 ImageChartFilter,
1937 null=False,
1938 on_delete=models.CASCADE)
1939
1940 test = models.ForeignKey(
1941 Test,
1942 null=False,
1943 on_delete=models.CASCADE)
1944
1945 name = models.CharField(max_length=200)
1946
1947
1948class ImageChartTestCase(models.Model):
1949
1950 class Meta:
1951 unique_together = ("image_chart_filter", "test_case")
1952
1953 image_chart_filter = models.ForeignKey(
1954 ImageChartFilter,
1955 null=False,
1956 on_delete=models.CASCADE)
1957
1958 test_case = models.ForeignKey(
1959 TestCase,
1960 null=False,
1961 on_delete=models.CASCADE)
1962
1963 name = models.CharField(max_length=200)
1964
19261965
=== added file 'dashboard_app/static/dashboard_app/css/image-charts.css'
--- dashboard_app/static/dashboard_app/css/image-charts.css 1970-01-01 00:00:00 +0000
+++ dashboard_app/static/dashboard_app/css/image-charts.css 2013-09-13 13:13:49 +0000
@@ -0,0 +1,82 @@
1@import url("../../admin/css/widgets.css");
2
3div.selector { clear: both; }
4div.selector span.helptext { display: none; }
5div.selector h2 { margin: 0; font-size: 11pt; }
6div.selector a { text-decoration: none; }
7div.selector select { height: 10em; }
8div.selector ul.selector-chooser { margin-top: 5.5em; }
9div.selector .selector-chosen select {
10 border: 1px solid rgb(204, 204, 204);
11 border-top: none;
12}
13
14.list-container {
15 border: 1px solid #000000;
16 clear: both;
17 margin: 10px 10px 10px 10px;
18 padding: 10px;
19 width: 50%;
20}
21
22.form-field {
23 margin-bottom: 5px;
24 vertical-align: top;
25}
26
27.form-field label {
28 vertical-align: top;
29 width: 100px;
30 display: inline-block;
31 margin-left: 10px;
32}
33
34.submit-button {
35 margin-top: 20px;
36 margin-left: 10px;
37}
38
39.filter-headline {
40 font-weight: bold;
41 font-size: 16px;
42}
43
44.filter-container {
45 margin-bottom: 10px;
46 clear: both;
47}
48
49.filter-title {
50 font-weight: bold;
51 font-size: 15px;
52 margin-bottom: 10px;
53}
54
55.chart-title {
56 font-weight: bold;
57 font-size: 15px;
58 margin-bottom: 10px;
59}
60
61.errors {
62 color: red;
63}
64
65.fields-container {
66 margin-left: 10px;
67}
68
69#filters_div {
70 margin: 10px 0 0 10px;
71 border: 1px solid #000000;
72 clear: both;
73 width: 75%;
74 padding: 5px 0 10px 10px;
75 overflow: auto;
76}
77
78#alias_container {
79 font-weight: bold;
80 float: left;
81 display: none;
82}
0\ No newline at end of file83\ No newline at end of file
184
=== added file 'dashboard_app/static/dashboard_app/images/ajax-progress.gif'
2Binary files dashboard_app/static/dashboard_app/images/ajax-progress.gif 1970-01-01 00:00:00 +0000 and dashboard_app/static/dashboard_app/images/ajax-progress.gif 2013-09-13 13:13:49 +0000 differ85Binary files dashboard_app/static/dashboard_app/images/ajax-progress.gif 1970-01-01 00:00:00 +0000 and dashboard_app/static/dashboard_app/images/ajax-progress.gif 2013-09-13 13:13:49 +0000 differ
=== added file 'dashboard_app/static/dashboard_app/js/image-report-editor.js'
--- dashboard_app/static/dashboard_app/js/image-report-editor.js 1970-01-01 00:00:00 +0000
+++ dashboard_app/static/dashboard_app/js/image-report-editor.js 2013-09-13 13:13:49 +0000
@@ -0,0 +1,221 @@
1select_filter = function() {
2 // Open the filter select dialog.
3 $('#filter_select_dialog').dialog('open');
4}
5
6filters_callback = function(id, name) {
7 // Function which will be called when a filter is selected from the dialog.
8
9 if ($('#id_chart_type').val() == "pass/fail") {
10 url = "/dashboard/filters/+get-tests-json";
11 } else {
12 url = "/dashboard/filters/+get-test-cases-json";
13 }
14
15 $.ajax({
16 url: url,
17 async: false,
18 data: {"id": id},
19 beforeSend: function () {
20 $('#filter-container').remove();
21 $('#filter_select_dialog').dialog('close');
22 $('#loading_dialog').dialog('open');
23 },
24 success: function (data) {
25 $('#loading_dialog').dialog('close');
26 $("#id_filter").val(id);
27 add_filter_container(data, name);
28 },
29 error: function(data, status, error) {
30 $('#loading_dialog').dialog('close');
31 alert('Filter could not be loaded, please try again.');
32 }
33 });
34}
35
36add_filter_container = function(data, title) {
37 // Adds elements which contain tests or test cases from the previously
38 // selected filter.
39
40 content = '<hr><div class="filter-title">' + title + '</div>';
41
42 if ($('#id_chart_type').val() == "pass/fail") {
43 test_label = "Tests";
44 } else {
45 test_label = "Test Cases";
46 }
47
48 content += '<div class="selector"><div class="selector-available"><h2>' +
49 'Select ' + test_label + '</h2>';
50
51 content += '<select id="available_tests" multiple class="filtered">';
52 for (i in data) {
53 if ($('#id_chart_type').val() == "pass/fail") {
54 content += '<option value="' + data[i].pk + '">' +
55 data[i].fields.test_id + '</option>';
56 } else {
57 content += '<option value="' + data[i].pk + '">' +
58 data[i].fields.test_case_id + '</option>';
59 }
60 }
61 content += '</select>';
62
63 content += '<a id="add_all_link" href="javascript: void(0)">' +
64 'Choose All</a>';
65 content += '</div>';
66
67 content += '<ul class="selector-chooser">' +
68 '<li><a href="javascript: void(0)" id="add_link"' +
69 'class="selector-add active"></a></li>' +
70 '<li><a href="javascript: void(0)" id="remove_link"' +
71 'class="selector-remove active"></a></li>' +
72 '</ul>';
73
74 content += '<div class="selector-chosen"><h2>' +
75 'Choosen ' + test_label + '</h2>';
76
77 content += '<select id="chosen_tests" onchange="toggle_alias()" multiple class="filtered"></select>';
78 content += '<a id="remove_all_link" href="javascript: void(0)">' +
79 'Remove All</a>';
80 content += '</div></div>';
81
82 content += '<div id="alias_container">Alias<br/>';
83 content += '<input type="text" onkeyup="copy_alias(this);" id="alias" />';
84 content += '</div>';
85
86 $('<div id="filter-container"></div>').html(
87 content).appendTo($('#filters_div'));
88
89 update_events();
90}
91
92update_events = function() {
93 // Add onclick events to the links controlling the select boxes.
94
95 $('#add_link').click(function() {
96 move_options('available_tests', 'chosen_tests');
97 });
98 $('#remove_link').click(function() {
99 move_options('chosen_tests', 'available_tests');
100 });
101 $('#add_all_link').click(function() {
102 $('#available_tests option').each(function() {
103 $(this).attr('selected', 'selected');
104 });
105 move_options('available_tests', 'chosen_tests');
106 });
107 $('#remove_all_link').click(function() {
108 $('#chosen_tests option').each(function() {
109 $(this).attr('selected', 'selected');
110 });
111 move_options('chosen_tests', 'available_tests');
112 });
113}
114
115move_options = function(from_element, to_element) {
116 var options = $("#" + from_element + " option:selected");
117 $("#" + to_element).append(options.clone());
118 $(options).remove();
119
120 update_aliases();
121 toggle_alias();
122}
123
124add_selected_options = function() {
125 // Adds options from chosen tests select box as hidden fields.
126
127 $('#chosen_tests option').each(function() {
128 if ($('#id_chart_type').val() == "pass/fail") {
129 field_name = "image_chart_tests";
130 } else {
131 field_name = "image_chart_test_cases";
132 }
133 $('<input type="hidden" name="' + field_name +
134 '" value="'+ $(this).val() + '" />').appendTo($('#add_filter_link'));
135 });
136}
137
138update_aliases = function() {
139 // Update hidden aliases inputs based on chosen tests.
140
141 $('#chosen_tests option').each(function() {
142 if ($('#alias_' + $(this).val()).length == 0) {
143 $('<input type="hidden" class="alias" data-sid="' + $(this).val() +
144 '" name="aliases" id="alias_' + $(this).val() +
145 '" />').appendTo($('#aliases_div'));
146 }
147 });
148 chosen_tests = $.map($('#chosen_tests option'), function(e) {
149 return e.value;
150 });
151 $('.alias').each(function(index, value) {
152 test_id = value.id.split('_')[1];
153
154 if (chosen_tests.indexOf(test_id) == -1) {
155 $('#alias_' + test_id).remove();
156 }
157 });
158}
159
160toggle_alias = function() {
161 // Show/hide alias input field.
162
163 if ($('#chosen_tests option:selected').length == 1) {
164 $('#alias_container').show();
165 test_id = $('#chosen_tests option:selected').val();
166 $('#alias').val($('#alias_' + test_id).val());
167 } else {
168 $('#alias_container').hide();
169 }
170}
171
172copy_alias = function(e) {
173 // Populate alias input based on the selected test.
174
175 if ($('#chosen_tests option:selected').length == 1) {
176 test_id = $('#chosen_tests option:selected').val();
177 $('#alias_' + test_id).val(e.value);
178 }
179}
180
181sort_aliases = function() {
182 // Pre submit function. Sort the aliases hidden inputs.
183
184 $('#aliases_div input').sort(function(a,b) {
185 return a.dataset.sid > b.dataset.sid;
186 }).appendTo('#aliases_div');
187}
188
189init_filter_dialog = function() {
190 // Setup the filter table dialog.
191
192 var filter_dialog = $('<div id="filter_select_dialog"></div>');
193 $('#all-filters_wrapper').wrapAll(filter_dialog);
194
195 $('#filter_select_dialog').dialog({
196 autoOpen: false,
197 title: 'Select Filter',
198 draggable: false,
199 height: 280,
200 width: 420,
201 modal: true,
202 resizable: false
203 });
204}
205
206init_loading_dialog = function() {
207 // Setup the loading image dialog.
208
209 $('#loading_dialog').dialog({
210 autoOpen: false,
211 title: '',
212 draggable: false,
213 height: 35,
214 width: 250,
215 modal: true,
216 resizable: false,
217 dialogClass: 'loading-dialog'
218 });
219
220 $('.loading-dialog div.ui-dialog-titlebar').hide();
221}
0222
=== added file 'dashboard_app/templates/dashboard_app/image_chart_filter_form.html'
--- dashboard_app/templates/dashboard_app/image_chart_filter_form.html 1970-01-01 00:00:00 +0000
+++ dashboard_app/templates/dashboard_app/image_chart_filter_form.html 2013-09-13 13:13:49 +0000
@@ -0,0 +1,109 @@
1{% extends "dashboard_app/_content.html" %}
2{% load i18n %}
3{% load django_tables2 %}
4
5{% block extrahead %}
6{{ block.super }}
7<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-charts.css"/>
8<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/image-report-editor.js"></script>
9
10{% endblock %}
11
12
13{% block content %}
14<h1>Image Chart Filter</h1>
15
16{% block content_form %}
17<form action="" method="post">{% csrf_token %}
18
19 {% if form.errors %}
20 <div class="errors">
21 <div>
22 {{ form.non_field_errors }}
23 <ul>
24 {% for field in form %}
25 {% if field.errors %}
26 <li>{{ field.label }}: {{ field.errors|striptags }}</li>
27 {% endif %}
28 {% endfor %}
29 </ul>
30 </div>
31 </div>
32 {% endif %}
33
34 <div id="filters_div">
35 <div id="add_filter_link">
36 <a href="#" onclick="select_filter()">Select filter</a>
37 {{ form.filter }}
38 {{ form.image_chart }}
39 <input type="hidden" id="id_chart_type" value="{{ image_chart.chart_type }}"/>
40 {{ form.image_chart_tests }}
41 {{ form.image_chart_test_cases }}
42 </div>
43
44 <div>
45 {{ form.representation.label_tag }}
46 {{ form.representation }}
47 </div>
48 </div>
49
50 <div id="aliases_div">
51 </div>
52
53 <div class="submit-button">
54 <input type="submit" value="Save" />
55 </div>
56</form>
57
58{% endblock content_form %}
59
60{% render_table filters_table %}
61
62<div id="loading_dialog">
63<img src="{{ STATIC_URL }}dashboard_app/images/ajax-progress.gif" alt="Loading..." />
64</div>
65
66<script type="text/javascript">
67 $().ready(function () {
68
69 init_filter_dialog();
70 init_loading_dialog();
71
72 $('form').submit(function() {
73 add_selected_options();
74 sort_aliases();
75 });
76
77 {% if form.filter.value %}
78 filters_callback('{{ instance.filter.id }}',
79 '{{ instance.filter.name }}');
80
81 if ($('#id_chart_type').val() == "pass/fail") {
82 {% for test in instance.imagecharttest_set.all %}
83 $('#available_tests option[value="{{ test.test_id }}"]').attr('selected', 'selected');
84 {% endfor %}
85 } else {
86 {% for test in instance.imagecharttestcase_set.all %}
87 $('#available_tests option[value="{{ test.test_case_id }}"]').attr('selected', 'selected');
88 {% endfor %}
89 }
90
91 move_options('available_tests', 'chosen_tests');
92
93 if ($('#id_chart_type').val() == "pass/fail") {
94 {% for test in instance.imagecharttest_set.all %}
95 $('#alias_{{ test.test_id }}').val('{{ test.name }}');
96 {% endfor %}
97 } else {
98 {% for test in instance.imagecharttestcase_set.all %}
99
100 $('#alias_{{ test.test_case_id }}').val('{{ test.name }}');
101 {% endfor %}
102 }
103
104 {% endif %}
105});
106
107</script>
108
109{% endblock %}
0110
=== added file 'dashboard_app/templates/dashboard_app/image_report_chart_detail.html'
--- dashboard_app/templates/dashboard_app/image_report_chart_detail.html 1970-01-01 00:00:00 +0000
+++ dashboard_app/templates/dashboard_app/image_report_chart_detail.html 2013-09-13 13:13:49 +0000
@@ -0,0 +1,86 @@
1{% extends "dashboard_app/_content.html" %}
2{% load i18n %}
3
4{% block extrahead %}
5{{ block.super }}
6<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-charts.css"/>
7{% endblock %}
8
9{% block content %}
10
11<h1>Image Chart {{ image_chart.name }}</h1>
12
13
14<div class="fields-container">
15 <div class="form-field">
16 <a href="{{ image_chart.get_absolute_url }}/+edit">Edit</a> this chart.
17 </div>
18 <div class="form-field">
19 Description: {{ image_chart.description }}
20 </div>
21 <div class="form-field">
22 Chart type: {{ image_chart.chart_type }}
23 </div>
24 <div class="form-field">
25 Data table visible: {{ image_chart.is_data_table_visible }}
26 </div>
27 <div class="form-field">
28 Target goal: {{ image_chart.target_goal|floatformat:"-2" }}
29 </div>
30</div>
31
32
33<h3>Filters</h3>
34
35<div class="fields-container">
36 <div id="add_filter_link">
37 <a href="{{ image_chart.get_absolute_url }}/+add-filter">Add filter</a>
38 </div>
39</div>
40
41<div class="list-container">
42 {% for chart_filter in image_chart.imagechartfilter_set.all %}
43 <div class="chart-title">
44 {{ chart_filter.filter.name }}&nbsp;&nbsp;&nbsp;&nbsp;
45 <a style="font-size: 13px;" href="{{ chart_filter.get_absolute_url }}">
46 edit
47 </a>&nbsp;
48 <a style="font-size: 13px;" href="{{ chart_filter.get_absolute_url }}/+delete">
49 remove
50 </a>
51 </div>
52 <div>
53 {% if image_chart.chart_type == "pass/fail" %}
54 Tests:&nbsp;
55 {% for chart_test in chart_filter.imagecharttest_set.all %}
56 {% if forloop.last %}
57 {{ chart_test.test.test_id }}
58 {% else %}
59 {{ chart_test.test.test_id }},&nbsp;
60 {% endif %}
61 {% endfor %}
62 {% else %}
63 Test Cases:&nbsp
64 {% for chart_test in chart_filter.imagecharttestcase_set.all %}
65 {% if forloop.last %}
66 {{ chart_test.test_case.test_case_id }}
67 {% else %}
68 {{ chart_test.test_case.test_case_id }},&nbsp;
69 {% endif %}
70 {% endfor %}
71 {% endif %}
72 </div>
73 <div>
74 Representation: {{ chart_filter.representation }}
75 </div>
76
77 <hr/>
78 {% empty %}
79 <div>
80 <li>No filters added yet.</li>
81 </div>
82 {% endfor %}
83</div>
84
85
86{% endblock %}
087
=== added file 'dashboard_app/templates/dashboard_app/image_report_chart_form.html'
--- dashboard_app/templates/dashboard_app/image_report_chart_form.html 1970-01-01 00:00:00 +0000
+++ dashboard_app/templates/dashboard_app/image_report_chart_form.html 2013-09-13 13:13:49 +0000
@@ -0,0 +1,62 @@
1{% extends "dashboard_app/_content.html" %}
2{% load i18n %}
3{% load django_tables2 %}
4
5{% block extrahead %}
6{{ block.super }}
7<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-charts.css"/>
8
9{% endblock %}
10
11
12{% block content %}
13<h1>Add Image Charts 2.0</h1>
14
15{% block content_form %}
16<form action="" method="post">{% csrf_token %}
17
18 {% if form.errors %}
19 <div class="errors">
20 <div>
21 {{ form.non_field_errors }}
22 <ul>
23 {% for field in form %}
24 {% if field.errors %}
25 <li>{{ field.label }}: {{ field.errors|striptags }}</li>
26 {% endif %}
27 {% endfor %}
28 </ul>
29 </div>
30 </div>
31 {% endif %}
32
33 <div class="form-field">
34 {{ form.name.label_tag }}
35 {{ form.name }}
36 <input type="hidden" id="id_image_report" name="image_report" value="{{ image_report_id }}"/>
37 </div>
38 <div class="form-field">
39 {{ form.description.label_tag }}
40 {{ form.description }}
41 </div>
42 <div class="form-field">
43 {{ form.chart_type.label_tag }}
44 {{ form.chart_type }}
45 </div>
46 <div class="form-field">
47 {{ form.is_data_table_visible.label_tag }}
48 {{ form.is_data_table_visible }}
49 </div>
50 <div class="form-field">
51 {{ form.target_goal.label_tag }}
52 {{ form.target_goal }}
53 </div>
54
55 <div class="submit-button">
56 <input type="submit" value="Save" />
57 </div>
58</form>
59
60{% endblock content_form %}
61
62{% endblock %}
063
=== added file 'dashboard_app/templates/dashboard_app/image_report_detail.html'
--- dashboard_app/templates/dashboard_app/image_report_detail.html 1970-01-01 00:00:00 +0000
+++ dashboard_app/templates/dashboard_app/image_report_detail.html 2013-09-13 13:13:49 +0000
@@ -0,0 +1,80 @@
1{% extends "dashboard_app/_content.html" %}
2{% load i18n %}
3
4{% block extrahead %}
5{{ block.super }}
6<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-charts.css"/>
7{% endblock %}
8
9{% block content %}
10
11<h1>Image Report {{ image_report.name }}</h1>
12
13<div class="fields-container">
14 <div class="form-field">
15 Status&#58;&nbsp;
16 {% if image_report.is_published %}
17 <span style="font-weight: bold; color: green;">
18 Published
19 </span>
20 {% else %}
21 <span style="font-weight: bold; color: orange;">
22 Not Published
23 </span>
24 {% endif %}
25 </div>
26 <div class="form-field">
27 Description&#58;&nbsp;{{ image_report.description }}
28 </div>
29 <div class="form-field">
30 <a href="{{ image_report.get_absolute_url }}/+edit">Edit</a> this image report.
31 </div>
32 <div class="form-field">
33 {% if image_report.is_published %}
34 <a href="{{ image_report.get_absolute_url }}/+unpublish">Unpublish</a> this image report.
35 {% else %}
36 <a href="{{ image_report.get_absolute_url }}/+publish">Publish</a> this image report.
37{% endif %}
38 </div>
39</div>
40
41<h3>Charts</h3>
42
43<div class="fields-container">
44 <a href="{% url dashboard_app.views.image_reports.views.image_chart_add %}?image_report_id={{ image_report.id }}">
45 Add new chart
46 </a>
47</div>
48
49<div class="list-container">
50 {% for image_chart in image_report.imagereportchart_set.all %}
51 <div class="chart-title">
52 {{ image_chart.name }}&nbsp;&nbsp;
53 <a style="font-size: 13px;" href="{{ image_chart.get_absolute_url }}">
54 details
55 </a>&nbsp;
56 <a style="font-size: 13px;" href="{{ image_chart.get_absolute_url }}">
57 preview
58 </a>
59 </div>
60 <div>
61 Description: {{ image_chart.description }}
62 </div>
63 <div>
64 Chart type: {{ image_chart.chart_type }}
65 </div>
66 <div>
67 Data table visible: {{ image_chart.is_data_table_visible }}
68 </div>
69 <div>
70 Target goal: {{ image_chart.target_goal|floatformat:"-2" }}
71 </div>
72 <hr/>
73 {% empty %}
74 <div>
75 <li>No charts added yet.</li>
76 </div>
77 {% endfor %}
78</div>
79
80{% endblock %}
081
=== added file 'dashboard_app/templates/dashboard_app/image_report_form.html'
--- dashboard_app/templates/dashboard_app/image_report_form.html 1970-01-01 00:00:00 +0000
+++ dashboard_app/templates/dashboard_app/image_report_form.html 2013-09-13 13:13:49 +0000
@@ -0,0 +1,45 @@
1{% extends "dashboard_app/_content.html" %}
2
3{% block extrahead %}
4{{ block.super }}
5<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-charts.css"/>
6{% endblock %}
7
8{% block content %}
9<h1>Image Reports 2.0</h1>
10
11{% block content_form %}
12<form action="" method="post">{% csrf_token %}
13
14 {% if form.errors %}
15 <div class="errors">
16 <div>
17 {{ form.non_field_errors }}
18 <ul>
19 {% for field in form %}
20 {% if field.errors %}
21 <li>{{ field.label }}: {{ field.errors|striptags }}</li>
22 {% endif %}
23 {% endfor %}
24 </ul>
25 </div>
26 </div>
27 {% endif %}
28
29<div class="form-field">
30 {{ form.name.label_tag }}
31 {{ form.name }}
32</div>
33<div class="form-field">
34 {{ form.description.label_tag }}
35 {{ form.description }}
36</div>
37
38<div class="submit-button">
39<input type="submit" value="Save" />
40</div>
41</form>
42
43{% endblock content_form %}
44
45{% endblock %}
046
=== added file 'dashboard_app/templates/dashboard_app/image_report_list.html'
--- dashboard_app/templates/dashboard_app/image_report_list.html 1970-01-01 00:00:00 +0000
+++ dashboard_app/templates/dashboard_app/image_report_list.html 2013-09-13 13:13:49 +0000
@@ -0,0 +1,37 @@
1{% extends "dashboard_app/_content.html" %}
2
3{% block extrahead %}
4{{ block.super }}
5<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-charts.css"/>
6{% endblock %}
7
8{% block content %}
9<h1>Image Reports 2.0</h1>
10
11<p style="margin-left: 10px;">
12 <a href="{% url dashboard_app.views.image_reports.views.image_report_add %}">
13 Add new Image Report
14 </a>
15</p>
16
17{% for image_report in image_reports %}
18<div class="list-container">
19 <div style="float: left;">
20 <a href="{{ image_report.get_absolute_url }}">{{ image_report.name }}</a>
21 &nbsp;&nbsp;
22 </div>
23 {% if image_report.is_published %}
24 <div style="font-weight: bold; float: right; color: green;">
25 Published
26 </div>
27 {% else %}
28 <div style="font-weight: bold; float: right; color: orange;">
29 Not Published
30 </div>
31 {% endif %}
32 <div style="clear: both;">
33 {{ image_report.description }}
34 </div>
35</div>
36{% endfor %}
37{% endblock %}
038
=== modified file 'dashboard_app/urls.py'
--- dashboard_app/urls.py 2013-09-05 11:27:15 +0000
+++ dashboard_app/urls.py 2013-09-13 13:13:49 +0000
@@ -33,6 +33,8 @@
33 url(r'^filters/\+add$', 'filters.views.filter_add'),33 url(r'^filters/\+add$', 'filters.views.filter_add'),
34 url(r'^filters/\+add-preview-json$', 'filters.views.filter_preview_json'),34 url(r'^filters/\+add-preview-json$', 'filters.views.filter_preview_json'),
35 url(r'^filters/\+add-cases-for-test-json$', 'filters.views.filter_add_cases_for_test_json'),35 url(r'^filters/\+add-cases-for-test-json$', 'filters.views.filter_add_cases_for_test_json'),
36 url(r'^filters/\+get-tests-json$', 'filters.views.get_tests_json'),
37 url(r'^filters/\+get-test-cases-json$', 'filters.views.get_test_cases_json'),
36 url(r'^filters/\+attribute-name-completion-json$', 'filters.views.filter_attr_name_completion_json'),38 url(r'^filters/\+attribute-name-completion-json$', 'filters.views.filter_attr_name_completion_json'),
37 url(r'^filters/\+attribute-value-completion-json$', 'filters.views.filter_attr_value_completion_json'),39 url(r'^filters/\+attribute-value-completion-json$', 'filters.views.filter_attr_value_completion_json'),
38 url(r'^filters/~(?P<username>[^/]+)/(?P<name>[a-zA-Z0-9-_]+)$', 'filters.views.filter_detail'),40 url(r'^filters/~(?P<username>[^/]+)/(?P<name>[a-zA-Z0-9-_]+)$', 'filters.views.filter_detail'),
@@ -63,6 +65,18 @@
63 url(r'^permalink/bundle/(?P<content_sha1>[0-9a-z]+)/$', 'redirect_to_bundle'),65 url(r'^permalink/bundle/(?P<content_sha1>[0-9a-z]+)/$', 'redirect_to_bundle'),
64 url(r'^permalink/bundle/(?P<content_sha1>[0-9a-z]+)/(?P<trailing>.*)$', 'redirect_to_bundle'),66 url(r'^permalink/bundle/(?P<content_sha1>[0-9a-z]+)/(?P<trailing>.*)$', 'redirect_to_bundle'),
65 url(r'^image-reports/$', 'images.image_report_list'),67 url(r'^image-reports/$', 'images.image_report_list'),
68 url(r'^image-charts/$', 'image_reports.views.image_report_list'),
69 url(r'^image-charts/(?P<name>[a-zA-Z0-9-_]+)$', 'image_reports.views.image_report_detail'),
70 url(r'^image-charts/\+add$', 'image_reports.views.image_report_add'),
71 url(r'^image-charts/(?P<name>[a-zA-Z0-9-_]+)/\+edit$', 'image_reports.views.image_report_edit'),
72 url(r'^image-charts/(?P<name>[a-zA-Z0-9-_]+)/\+publish$', 'image_reports.views.image_report_publish'),
73 url(r'^image-charts/(?P<name>[a-zA-Z0-9-_]+)/\+unpublish$', 'image_reports.views.image_report_unpublish'),
74 url(r'^image-chart/(?P<id>[a-zA-Z0-9-_]+)$', 'image_reports.views.image_chart_detail'),
75 url(r'^image-chart/\+add$', 'image_reports.views.image_chart_add'),
76 url(r'^image-chart/(?P<id>[a-zA-Z0-9-_]+)/\+edit$', 'image_reports.views.image_chart_edit'),
77 url(r'^image-chart/(?P<id>[a-zA-Z0-9-_]+)/\+add-filter$', 'image_reports.views.image_chart_filter_add'),
78 url(r'^image-chart-filter/(?P<id>[a-zA-Z0-9-_]+)$', 'image_reports.views.image_chart_filter_edit'),
79 url(r'^image-chart-filter/(?P<id>[a-zA-Z0-9-_]+)/\+delete$', 'image_reports.views.image_chart_filter_delete'),
66 url(r'^pmqa$', 'pmqa.pmqa_view'),80 url(r'^pmqa$', 'pmqa.pmqa_view'),
67 url(r'^pmqa(?P<pathname>/[a-zA-Z0-9/._-]+/)(?P<device_type>[a-zA-Z0-9-_]+)$', 'pmqa.pmqa_filter_view'),81 url(r'^pmqa(?P<pathname>/[a-zA-Z0-9/._-]+/)(?P<device_type>[a-zA-Z0-9-_]+)$', 'pmqa.pmqa_filter_view'),
68 url(r'^pmqa(?P<pathname>/[a-zA-Z0-9/._-]+/)(?P<device_type>[a-zA-Z0-9-_]+)/json$', 'pmqa.pmqa_filter_view_json'),82 url(r'^pmqa(?P<pathname>/[a-zA-Z0-9/._-]+/)(?P<device_type>[a-zA-Z0-9-_]+)/json$', 'pmqa.pmqa_filter_view_json'),
6983
=== modified file 'dashboard_app/views/filters/tables.py'
--- dashboard_app/views/filters/tables.py 2013-01-10 01:56:51 +0000
+++ dashboard_app/views/filters/tables.py 2013-09-13 13:13:49 +0000
@@ -109,6 +109,15 @@
109 return TestRunFilter.objects.filter(public=True)109 return TestRunFilter.objects.filter(public=True)
110110
111111
112class AllFiltersSimpleTable(DataTablesTable):
113
114 name = TemplateColumn('''
115 <a href="#" onclick="filters_callback('{{ record.id }}', '{{ record.name }}');">{{ record.name }}</a>
116 ''')
117
118 def get_queryset(self):
119 return TestRunFilter.objects.all()
120
112121
113class TestRunColumn(Column):122class TestRunColumn(Column):
114 def render(self, record):123 def render(self, record):
115124
=== modified file 'dashboard_app/views/filters/views.py'
--- dashboard_app/views/filters/views.py 2013-01-10 01:56:51 +0000
+++ dashboard_app/views/filters/views.py 2013-09-13 13:13:49 +0000
@@ -20,6 +20,7 @@
2020
21from django.contrib.auth.decorators import login_required21from django.contrib.auth.decorators import login_required
22from django.contrib.contenttypes.models import ContentType22from django.contrib.contenttypes.models import ContentType
23from django.core import serializers
23from django.core.exceptions import PermissionDenied, ValidationError24from django.core.exceptions import PermissionDenied, ValidationError
24from django.core.urlresolvers import reverse25from django.core.urlresolvers import reverse
25from django.http import HttpResponse, HttpResponseRedirect26from django.http import HttpResponse, HttpResponseRedirect
@@ -37,6 +38,7 @@
37 evaluate_filter,38 evaluate_filter,
38 )39 )
39from dashboard_app.models import (40from dashboard_app.models import (
41 Bundle,
40 NamedAttribute,42 NamedAttribute,
41 Test,43 Test,
42 TestCase,44 TestCase,
@@ -234,6 +236,24 @@
234 mimetype='application/json')236 mimetype='application/json')
235237
236238
239def get_tests_json(request):
240
241 tests = Test.objects.filter(
242 test_runs__bundle__bundle_stream__testrunfilter__id=request.GET['id']).distinct()
243
244 data = serializers.serialize('json', tests)
245 return HttpResponse(data, mimetype='application/json')
246
247
248def get_test_cases_json(request):
249
250 test_cases = TestCase.objects.filter(
251 test__test_runs__bundle__bundle_stream__testrunfilter__id=request.GET['id']).distinct()
252
253 data = serializers.serialize('json', test_cases)
254 return HttpResponse(data, mimetype='application/json')
255
256
237def filter_attr_name_completion_json(request):257def filter_attr_name_completion_json(request):
238 term = request.GET['term']258 term = request.GET['term']
239 content_type_id = ContentType.objects.get_for_model(TestRun).id259 content_type_id = ContentType.objects.get_for_model(TestRun).id
240260
=== added directory 'dashboard_app/views/image_reports'
=== added file 'dashboard_app/views/image_reports/__init__.py'
--- dashboard_app/views/image_reports/__init__.py 1970-01-01 00:00:00 +0000
+++ dashboard_app/views/image_reports/__init__.py 2013-09-13 13:13:49 +0000
@@ -0,0 +1,17 @@
1# Copyright (C) 2010-2013 Linaro Limited
2#
3# Author: Stevan Radakovic <stevan.radakovic@linaro.org>
4#
5# This file is part of Launch Control.
6#
7# Launch Control is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License version 3
9# as published by the Free Software Foundation
10#
11# Launch Control is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with Launch Control. If not, see <http://www.gnu.org/licenses/>.
018
=== added file 'dashboard_app/views/image_reports/forms.py'
--- dashboard_app/views/image_reports/forms.py 1970-01-01 00:00:00 +0000
+++ dashboard_app/views/image_reports/forms.py 2013-09-13 13:13:49 +0000
@@ -0,0 +1,91 @@
1# Copyright (C) 2010-2013 Linaro Limited
2#
3# Author: Stevan Radakovic <stevan.radakovic@linaro.org>
4#
5# This file is part of Launch Control.
6#
7# Launch Control is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License version 3
9# as published by the Free Software Foundation
10#
11# Launch Control is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with Launch Control. If not, see <http://www.gnu.org/licenses/>.
18
19from django import forms
20
21from dashboard_app.models import (
22 ImageReport,
23 ImageReportChart,
24 ImageChartFilter,
25 ImageChartTest,
26 ImageChartTestCase,
27 Test,
28 TestCase,
29)
30
31
32class ImageReportEditorForm(forms.ModelForm):
33 class Meta:
34 model = ImageReport
35 exclude = ('owner', 'is_published',)
36
37 def save(self, commit=True, **kwargs):
38 instance = super(ImageReportEditorForm,
39 self).save(commit=commit, **kwargs)
40 return instance
41
42 def is_valid(self):
43 return super(ImageReportEditorForm, self).is_valid()
44
45 def full_clean(self):
46 super(ImageReportEditorForm, self).full_clean()
47
48 def __init__(self, user, *args, **kwargs):
49 super(ImageReportEditorForm, self).__init__(*args, **kwargs)
50
51
52class ImageReportChartForm(forms.ModelForm):
53 class Meta:
54 model = ImageReportChart
55 widgets = {'image_report': forms.HiddenInput}
56
57 def __init__(self, user, *args, **kwargs):
58 super(ImageReportChartForm, self).__init__(*args, **kwargs)
59 if len(self.instance.imagechartfilter_set.all()) != 0:
60 self.fields['chart_type'].label = ""
61 self.fields['chart_type'].widget = forms.HiddenInput()
62
63 def save(self, commit=True, **kwargs):
64 instance = super(ImageReportChartForm,
65 self).save(commit=commit, **kwargs)
66 return instance
67
68
69class ImageChartFilterForm(forms.ModelForm):
70
71 image_chart_tests = forms.ModelMultipleChoiceField(
72 widget=forms.MultipleHiddenInput,
73 queryset=Test.objects.all().order_by("id"),
74 required=False)
75 image_chart_test_cases = forms.ModelMultipleChoiceField(
76 widget=forms.MultipleHiddenInput,
77 queryset=TestCase.objects.all().order_by("id"),
78 required=False)
79
80 class Meta:
81 model = ImageChartFilter
82 widgets = {'filter': forms.HiddenInput,
83 'image_chart': forms.HiddenInput,}
84
85 def __init__(self, user, *args, **kwargs):
86 super(ImageChartFilterForm, self).__init__(*args, **kwargs)
87
88 def save(self, commit=True, **kwargs):
89 instance = super(ImageChartFilterForm,
90 self).save(commit=commit, **kwargs)
91 return instance
092
=== added file 'dashboard_app/views/image_reports/views.py'
--- dashboard_app/views/image_reports/views.py 1970-01-01 00:00:00 +0000
+++ dashboard_app/views/image_reports/views.py 2013-09-13 13:13:49 +0000
@@ -0,0 +1,325 @@
1# Copyright (C) 2010-2013 Linaro Limited
2#
3# Author: Stevan Radakovic <stevan.radakovic@linaro.org>
4#
5# This file is part of Launch Control.
6#
7# Launch Control is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License version 3
9# as published by the Free Software Foundation
10#
11# Launch Control is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU Affero General Public License
17# along with Launch Control. If not, see <http://www.gnu.org/licenses/>.
18
19import json
20
21from django.contrib.auth.decorators import login_required
22from django.core.exceptions import PermissionDenied, ValidationError
23from django.http import HttpResponse, HttpResponseRedirect
24from django.shortcuts import render_to_response
25from django.template import RequestContext
26from django.utils.safestring import mark_safe
27
28from lava_server.bread_crumbs import (
29 BreadCrumb,
30 BreadCrumbTrail,
31)
32
33from dashboard_app.views import index
34
35from dashboard_app.views.image_reports.forms import (
36 ImageReportEditorForm,
37 ImageReportChartForm,
38 ImageChartFilterForm,
39 )
40
41from dashboard_app.models import (
42 ImageReport,
43 ImageReportChart,
44 ImageChartFilter,
45 ImageChartTest,
46 ImageChartTestCase,
47 Test,
48 TestCase,
49 TestRunFilter,
50 )
51
52from dashboard_app.views.filters.tables import AllFiltersSimpleTable
53
54
55
56@BreadCrumb("Image reports", parent=index)
57def image_report_list(request):
58
59 if request.user.is_authenticated():
60 image_reports = ImageReport.objects.all()
61 else:
62 image_reports = None
63
64 return render_to_response(
65 'dashboard_app/image_report_list.html', {
66 "image_reports": image_reports,
67 }, RequestContext(request)
68 )
69
70@BreadCrumb("Image report {name}", parent=image_report_list, needs=['name'])
71def image_report_detail(request, name):
72 image_report = ImageReport.objects.get(name=name)
73
74 return render_to_response(
75 'dashboard_app/image_report_detail.html', {
76 'image_report': image_report,
77 'bread_crumb_trail': BreadCrumbTrail.leading_to(
78 image_report_detail, name=name),
79 }, RequestContext(request)
80 )
81
82@BreadCrumb("Add new image report", parent=image_report_list)
83@login_required
84def image_report_add(request):
85 return image_report_form(
86 request,
87 BreadCrumbTrail.leading_to(image_report_add))
88
89@BreadCrumb("Update image report {name}", parent=image_report_list,
90 needs=['name'])
91@login_required
92def image_report_edit(request, name):
93 image_report = ImageReport.objects.get(name=name)
94 return image_report_form(
95 request,
96 BreadCrumbTrail.leading_to(image_report_edit,
97 name=name),
98 instance=image_report)
99
100@BreadCrumb("Publish image report {name}", parent=image_report_list,
101 needs=['name'])
102@login_required
103def image_report_publish(request, name):
104 image_report = ImageReport.objects.get(name=name)
105 image_report.is_published = True
106 image_report.save()
107
108 return render_to_response(
109 'dashboard_app/image_report_detail.html', {
110 'image_report': image_report,
111 'bread_crumb_trail': BreadCrumbTrail.leading_to(
112 image_report_detail, name=name),
113 }, RequestContext(request)
114 )
115
116@BreadCrumb("Unpublish image report {name}", parent=image_report_list,
117 needs=['name'])
118@login_required
119def image_report_unpublish(request, name):
120 image_report = ImageReport.objects.get(name=name)
121 image_report.is_published = False
122 image_report.save()
123
124 return render_to_response(
125 'dashboard_app/image_report_detail.html', {
126 'image_report': image_report,
127 'bread_crumb_trail': BreadCrumbTrail.leading_to(
128 image_report_detail, name=name),
129 }, RequestContext(request)
130 )
131
132def image_report_form(request, bread_crumb_trail, instance=None):
133
134 if request.method == 'POST':
135
136 form = ImageReportEditorForm(request.user, request.POST,
137 instance=instance)
138 if form.is_valid():
139 image_report = form.save()
140 return HttpResponseRedirect(image_report.get_absolute_url())
141
142 else:
143 form = ImageReportEditorForm(request.user, instance=instance)
144
145 return render_to_response(
146 'dashboard_app/image_report_form.html', {
147 'bread_crumb_trail': bread_crumb_trail,
148 'form': form,
149 }, RequestContext(request))
150
151@BreadCrumb("Image chart details", parent=image_report_list)
152def image_chart_detail(request, id):
153 image_chart = ImageReportChart.objects.get(id=id)
154
155 return render_to_response(
156 'dashboard_app/image_report_chart_detail.html', {
157 'image_chart': image_chart,
158 'bread_crumb_trail': BreadCrumbTrail.leading_to(
159 image_chart_detail, id=id),
160 }, RequestContext(request)
161 )
162
163@BreadCrumb("Add new image chart", parent=image_report_list)
164@login_required
165def image_chart_add(request):
166 return image_chart_form(
167 request,
168 BreadCrumbTrail.leading_to(image_chart_add))
169
170@BreadCrumb("Update image chart", parent=image_report_list)
171@login_required
172def image_chart_edit(request, id):
173 image_chart = ImageReportChart.objects.get(id=id)
174 return image_chart_form(
175 request,
176 BreadCrumbTrail.leading_to(image_chart_edit,
177 id=id),
178 instance=image_chart)
179
180def image_chart_form(request, bread_crumb_trail, instance=None):
181
182 if request.method == 'POST':
183
184 form = ImageReportChartForm(request.user, request.POST,
185 instance=instance)
186 if form.is_valid():
187 image_chart = form.save()
188 return HttpResponseRedirect(
189 image_chart.get_absolute_url())
190
191 else:
192 form = ImageReportChartForm(request.user, instance=instance)
193
194 if not instance:
195 image_report_id = request.GET.get('image_report_id', None)
196 else:
197 image_report_id = instance.image_report.id
198
199 filters_table = AllFiltersSimpleTable("all-filters", None)
200
201 return render_to_response(
202 'dashboard_app/image_report_chart_form.html', {
203 'bread_crumb_trail': bread_crumb_trail,
204 'form': form,
205 'filters_table': filters_table,
206 'image_report_id': image_report_id,
207 }, RequestContext(request))
208
209@BreadCrumb("Image chart add filter", parent=image_report_list)
210def image_chart_filter_add(request, id):
211 image_chart = ImageReportChart.objects.get(id=id)
212 return image_chart_filter_form(
213 request,
214 BreadCrumbTrail.leading_to(image_chart_filter_add),
215 chart_instance=image_chart)
216
217@BreadCrumb("Update image chart filter", parent=image_report_list)
218@login_required
219def image_chart_filter_edit(request, id):
220 image_chart_filter = ImageChartFilter.objects.get(id=id)
221 return image_chart_filter_form(
222 request,
223 BreadCrumbTrail.leading_to(image_chart_filter_edit, id=id),
224 instance=image_chart_filter)
225
226@BreadCrumb("Image chart add filter", parent=image_report_list)
227def image_chart_filter_delete(request, id):
228 image_chart_filter = ImageChartFilter.objects.get(id=id)
229 url = image_chart_filter.image_chart.get_absolute_url()
230 image_chart_filter.delete()
231 return HttpResponseRedirect(url)
232
233def image_chart_filter_form(request, bread_crumb_trail, chart_instance=None,
234 instance=None):
235
236 if instance:
237 chart_instance = instance.image_chart
238
239 if request.method == 'POST':
240
241 form = ImageChartFilterForm(request.user, request.POST,
242 instance=instance)
243
244 if form.is_valid():
245
246 chart_filter = form.save()
247 aliases = request.POST.getlist('aliases')
248
249
250 if chart_filter.image_chart.chart_type == 'pass/fail':
251
252 image_chart_tests = Test.objects.filter(
253 imagecharttest__image_chart_filter=chart_filter).order_by(
254 'id')
255
256 tests = form.cleaned_data['image_chart_tests']
257
258 for index, test in enumerate(tests):
259 if test in image_chart_tests:
260 chart_test = ImageChartTest.objects.get(
261 image_chart_filter=chart_filter, test=test)
262 chart_test.name = aliases[index]
263 chart_test.save()
264 else:
265 chart_test = ImageChartTest()
266 chart_test.image_chart_filter = chart_filter
267 chart_test.test = test
268 chart_test.name = aliases[index]
269 chart_test.save()
270
271 for index, chart_test in enumerate(image_chart_tests):
272 if chart_test not in tests:
273 ImageChartTest.objects.get(
274 image_chart_filter=chart_filter,
275 test=chart_test).delete()
276
277 return HttpResponseRedirect(
278 chart_filter.image_chart.get_absolute_url())
279
280 else:
281
282 image_chart_test_cases = TestCase.objects.filter(
283 imagecharttestcase__image_chart_filter=
284 chart_filter).order_by('id')
285
286 test_cases = form.cleaned_data['image_chart_test_cases']
287
288 for index, test_case in enumerate(test_cases):
289 if test_case in image_chart_test_cases:
290 chart_test_case = ImageChartTestCase.objects.get(
291 image_chart_filter=chart_filter,
292 test_case=test_case)
293 chart_test_case.name = aliases[index]
294 chart_test_case.save()
295 else:
296 chart_test_case = ImageChartTestCase()
297 chart_test_case.image_chart_filter = chart_filter
298 chart_test_case.test_case = test_case
299 chart_test_case.name = aliases[index]
300 chart_test_case.save()
301
302 for index, chart_test_case in enumerate(
303 image_chart_test_cases):
304 if chart_test_case not in test_cases:
305 ImageChartTestCase.objects.get(
306 image_chart_filter=chart_filter,
307 test_case=chart_test_case).delete()
308
309 return HttpResponseRedirect(
310 chart_filter.image_chart.get_absolute_url())
311
312 else:
313 form = ImageChartFilterForm(request.user, instance=instance,
314 initial={'image_chart': chart_instance})
315
316 filters_table = AllFiltersSimpleTable("all-filters", None)
317
318 return render_to_response(
319 'dashboard_app/image_chart_filter_form.html', {
320 'bread_crumb_trail': bread_crumb_trail,
321 'filters_table': filters_table,
322 'image_chart': chart_instance,
323 'instance': instance,
324 'form': form,
325 }, RequestContext(request))

Subscribers

People subscribed via source and target branches