Merge lp:~stephenjohnson505/lava-dashboard/graphingenhancement into lp:lava-dashboard
- graphingenhancement
- Merge into trunk
Proposed by
Stephen Johnson
Status: | Needs review |
---|---|
Proposed branch: | lp:~stephenjohnson505/lava-dashboard/graphingenhancement |
Merge into: | lp:lava-dashboard |
Diff against target: |
3228 lines (+2914/-88) (has conflicts) 10 files modified
dashboard_app/admin.py (+11/-0) dashboard_app/migrations/0030_auto__add_goal__add_testresults.py (+344/-0) dashboard_app/models.py (+112/-0) dashboard_app/static/dashboard_app/js/jquery.flot.axislabels.js (+404/-75) dashboard_app/static/dashboard_app/js/jquery.flot.dashes.js (+231/-0) dashboard_app/static/dashboard_app/js/jquery.flot.resize.js (+60/-0) dashboard_app/static/dashboard_app/js/jquery.flot.threshold.js (+142/-0) dashboard_app/templates/dashboard_app/image-report.html (+1280/-0) dashboard_app/templates/dashboard_app/image-reports.html (+5/-3) dashboard_app/views/images.py (+325/-10) Text conflict in dashboard_app/templates/dashboard_app/image-report.html Text conflict in dashboard_app/views/images.py |
To merge this branch: | bzr merge lp:~stephenjohnson505/lava-dashboard/graphingenhancement |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Seth Beinhart | Pending | ||
Linaro Validation Team | Pending | ||
Review via email: mp+166139@code.launchpad.net |
Commit message
Description of the change
Added graphing functionality to the image-reports tab of the dashboard.
Graphs can be viewed based on pass/fail, or based on measurements over time. Measurements can be further changed to include a target value, a floor value, and a weight.
Furthermore, project goals can be added to an image-set, which give the table of dates, as well as a dashed target-line for the weighted graph.
An example can be seen:
https:/
To post a comment you must log in.
- 407. By Stephen Johnson
-
Added pretty colored graphs.
Unmerged revisions
- 407. By Stephen Johnson
-
Added pretty colored graphs.
- 406. By Stephen Johnson
-
Added migration file
- 405. By Stephen Johnson
-
Fixed row graph, ready for merge
- 404. By Stephen Johnson
-
Fixed single date bug
- 403. By Stephen Johnson
-
Fixed typo, added the right axislabels
- 402. By Stephen Johnson
-
added graphing
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'dashboard_app/admin.py' |
2 | --- dashboard_app/admin.py 2013-04-01 08:50:05 +0000 |
3 | +++ dashboard_app/admin.py 2013-07-17 17:43:24 +0000 |
4 | @@ -40,6 +40,8 @@ |
5 | SoftwarePackage, |
6 | SoftwareSource, |
7 | Tag, |
8 | + Goal, |
9 | + GoalDate, |
10 | Test, |
11 | TestCase, |
12 | TestResult, |
13 | @@ -154,6 +156,14 @@ |
14 | list_filter = ('test',) |
15 | |
16 | |
17 | +class GoalsAdmin(admin.ModelAdmin): |
18 | + class GoalDates(admin.TabularInline): |
19 | + model = GoalDate |
20 | + extra = 0 |
21 | + list_display = ('name',) |
22 | + inlines = [GoalDates] |
23 | + |
24 | + |
25 | class TestAdmin(admin.ModelAdmin): |
26 | pass |
27 | |
28 | @@ -219,6 +229,7 @@ |
29 | admin.site.register(SoftwarePackage, SoftwarePackageAdmin) |
30 | admin.site.register(SoftwareSource, SoftwareSourceAdmin) |
31 | admin.site.register(Test, TestAdmin) |
32 | +admin.site.register(Goal, GoalsAdmin) |
33 | admin.site.register(TestCase, TestCaseAdmin) |
34 | admin.site.register(TestResult, TestResultAdmin) |
35 | admin.site.register(TestRun, TestRunAdmin) |
36 | |
37 | === added file 'dashboard_app/migrations/0030_auto__add_goal__add_testresults.py' |
38 | --- dashboard_app/migrations/0030_auto__add_goal__add_testresults.py 1970-01-01 00:00:00 +0000 |
39 | +++ dashboard_app/migrations/0030_auto__add_goal__add_testresults.py 2013-07-17 17:43:24 +0000 |
40 | @@ -0,0 +1,344 @@ |
41 | +# -*- coding: utf-8 -*- |
42 | +import datetime |
43 | +from south.db import db |
44 | +from south.v2 import SchemaMigration |
45 | +from django.db import models |
46 | + |
47 | + |
48 | +class Migration(SchemaMigration): |
49 | + |
50 | + def forwards(self, orm): |
51 | + # Adding model 'GoalDate' |
52 | + db.create_table('dashboard_app_goaldate', ( |
53 | + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
54 | + ('goal', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.Goal'])), |
55 | + ('tag', self.gf('django.db.models.fields.CharField')(max_length=4)), |
56 | + ('startdate', self.gf('django.db.models.fields.DateField')()), |
57 | + ('enddate', self.gf('django.db.models.fields.DateField')()), |
58 | + ('value', self.gf('django.db.models.fields.FloatField')(default=0, max_length=64)), |
59 | + )) |
60 | + db.send_create_signal('dashboard_app', ['GoalDate']) |
61 | + |
62 | + # Adding model 'Goal' |
63 | + db.create_table('dashboard_app_goal', ( |
64 | + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
65 | + ('name', self.gf('django.db.models.fields.TextField')()), |
66 | + )) |
67 | + db.send_create_signal('dashboard_app', ['Goal']) |
68 | + |
69 | + # Adding field 'ImageSet.relatedDates' |
70 | + db.add_column('dashboard_app_imageset', 'relatedDates', |
71 | + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dashboard_app.Goal'], null=True, blank=True), |
72 | + keep_default=False) |
73 | + |
74 | + # Adding field 'TestCase.target' |
75 | + db.add_column('dashboard_app_testcase', 'target', |
76 | + self.gf('django.db.models.fields.FloatField')(default=0, max_length=64, null=True), |
77 | + keep_default=False) |
78 | + |
79 | + # Adding field 'TestCase.floor' |
80 | + db.add_column('dashboard_app_testcase', 'floor', |
81 | + self.gf('django.db.models.fields.FloatField')(default=0, max_length=64, null=True), |
82 | + keep_default=False) |
83 | + |
84 | + # Adding field 'TestCase.weight' |
85 | + db.add_column('dashboard_app_testcase', 'weight', |
86 | + self.gf('django.db.models.fields.FloatField')(default=1, max_length=64, null=True), |
87 | + keep_default=False) |
88 | + |
89 | + # Adding field 'TestCase.goal' |
90 | + db.add_column('dashboard_app_testcase', 'goal', |
91 | + self.gf('django.db.models.fields.FloatField')(default=0, max_length=64, null=True), |
92 | + keep_default=False) |
93 | + |
94 | + |
95 | + def backwards(self, orm): |
96 | + # Deleting model 'GoalDate' |
97 | + db.delete_table('dashboard_app_goaldate') |
98 | + |
99 | + # Deleting model 'Goal' |
100 | + db.delete_table('dashboard_app_goal') |
101 | + |
102 | + # Deleting field 'ImageSet.relatedDates' |
103 | + db.delete_column('dashboard_app_imageset', 'relatedDates_id') |
104 | + |
105 | + # Deleting field 'TestCase.target' |
106 | + db.delete_column('dashboard_app_testcase', 'target') |
107 | + |
108 | + # Deleting field 'TestCase.floor' |
109 | + db.delete_column('dashboard_app_testcase', 'floor') |
110 | + |
111 | + # Deleting field 'TestCase.weight' |
112 | + db.delete_column('dashboard_app_testcase', 'weight') |
113 | + |
114 | + # Deleting field 'TestCase.goal' |
115 | + db.delete_column('dashboard_app_testcase', 'goal') |
116 | + |
117 | + |
118 | + models = { |
119 | + 'auth.group': { |
120 | + 'Meta': {'object_name': 'Group'}, |
121 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
122 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
123 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
124 | + }, |
125 | + 'auth.permission': { |
126 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
127 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
128 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
129 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
130 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
131 | + }, |
132 | + 'auth.user': { |
133 | + 'Meta': {'object_name': 'User'}, |
134 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
135 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
136 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
137 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
138 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
139 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
140 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
141 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
142 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
143 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
144 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
145 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
146 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
147 | + }, |
148 | + 'contenttypes.contenttype': { |
149 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
150 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
151 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
152 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
153 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
154 | + }, |
155 | + 'dashboard_app.attachment': { |
156 | + 'Meta': {'object_name': 'Attachment'}, |
157 | + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True'}), |
158 | + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
159 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
160 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
161 | + 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}), |
162 | + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), |
163 | + 'public_url': ('django.db.models.fields.URLField', [], {'max_length': '512', 'blank': 'True'}) |
164 | + }, |
165 | + 'dashboard_app.bundle': { |
166 | + 'Meta': {'ordering': "['-uploaded_on']", 'object_name': 'Bundle'}, |
167 | + '_gz_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'gz_content'"}), |
168 | + '_raw_content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'db_column': "'content'"}), |
169 | + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'bundles'", 'to': "orm['dashboard_app.BundleStream']"}), |
170 | + 'content_filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
171 | + 'content_sha1': ('django.db.models.fields.CharField', [], {'max_length': '40', 'unique': 'True', 'null': 'True'}), |
172 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
173 | + 'is_deserialized': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
174 | + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'uploaded_bundles'", 'null': 'True', 'to': "orm['auth.User']"}), |
175 | + 'uploaded_on': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}) |
176 | + }, |
177 | + 'dashboard_app.bundledeserializationerror': { |
178 | + 'Meta': {'object_name': 'BundleDeserializationError'}, |
179 | + 'bundle': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'deserialization_error'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.Bundle']"}), |
180 | + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
181 | + 'traceback': ('django.db.models.fields.TextField', [], {'max_length': '32768'}) |
182 | + }, |
183 | + 'dashboard_app.bundlestream': { |
184 | + 'Meta': {'object_name': 'BundleStream'}, |
185 | + 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), |
186 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
187 | + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
188 | + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
189 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), |
190 | + 'pathname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}), |
191 | + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), |
192 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) |
193 | + }, |
194 | + 'dashboard_app.goal': { |
195 | + 'Meta': {'object_name': 'Goal'}, |
196 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
197 | + 'name': ('django.db.models.fields.TextField', [], {}) |
198 | + }, |
199 | + 'dashboard_app.goaldate': { |
200 | + 'Meta': {'object_name': 'GoalDate'}, |
201 | + 'enddate': ('django.db.models.fields.DateField', [], {}), |
202 | + 'goal': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.Goal']"}), |
203 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
204 | + 'startdate': ('django.db.models.fields.DateField', [], {}), |
205 | + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '4'}), |
206 | + 'value': ('django.db.models.fields.FloatField', [], {'default': '0', 'max_length': '64'}) |
207 | + }, |
208 | + 'dashboard_app.hardwaredevice': { |
209 | + 'Meta': {'object_name': 'HardwareDevice'}, |
210 | + 'description': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
211 | + 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), |
212 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) |
213 | + }, |
214 | + 'dashboard_app.image': { |
215 | + 'Meta': {'object_name': 'Image'}, |
216 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': "orm['dashboard_app.TestRunFilter']"}), |
217 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
218 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '1024'}) |
219 | + }, |
220 | + 'dashboard_app.imageset': { |
221 | + 'Meta': {'object_name': 'ImageSet'}, |
222 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
223 | + 'images': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.Image']", 'symmetrical': 'False'}), |
224 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}), |
225 | + 'relatedDates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.Goal']", 'null': 'True', 'blank': 'True'}) |
226 | + }, |
227 | + 'dashboard_app.launchpadbug': { |
228 | + 'Meta': {'object_name': 'LaunchpadBug'}, |
229 | + 'bug_id': ('django.db.models.fields.PositiveIntegerField', [], {'unique': 'True'}), |
230 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
231 | + 'test_runs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'launchpad_bugs'", 'symmetrical': 'False', 'to': "orm['dashboard_app.TestRun']"}) |
232 | + }, |
233 | + 'dashboard_app.namedattribute': { |
234 | + 'Meta': {'unique_together': "(('object_id', 'name'),)", 'object_name': 'NamedAttribute'}, |
235 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
236 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
237 | + 'name': ('django.db.models.fields.TextField', [], {}), |
238 | + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), |
239 | + 'value': ('django.db.models.fields.TextField', [], {}) |
240 | + }, |
241 | + 'dashboard_app.pmqabundlestream': { |
242 | + 'Meta': {'object_name': 'PMQABundleStream'}, |
243 | + 'bundle_stream': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.BundleStream']"}), |
244 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) |
245 | + }, |
246 | + 'dashboard_app.softwarepackage': { |
247 | + 'Meta': {'unique_together': "(('name', 'version'),)", 'object_name': 'SoftwarePackage'}, |
248 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
249 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
250 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'}) |
251 | + }, |
252 | + 'dashboard_app.softwarepackagescratch': { |
253 | + 'Meta': {'object_name': 'SoftwarePackageScratch'}, |
254 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
255 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
256 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '128'}) |
257 | + }, |
258 | + 'dashboard_app.softwaresource': { |
259 | + 'Meta': {'object_name': 'SoftwareSource'}, |
260 | + 'branch_revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
261 | + 'branch_url': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
262 | + 'branch_vcs': ('django.db.models.fields.CharField', [], {'max_length': '10'}), |
263 | + 'commit_timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), |
264 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
265 | + 'project_name': ('django.db.models.fields.CharField', [], {'max_length': '32'}) |
266 | + }, |
267 | + 'dashboard_app.tag': { |
268 | + 'Meta': {'object_name': 'Tag'}, |
269 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
270 | + 'name': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '256'}) |
271 | + }, |
272 | + 'dashboard_app.test': { |
273 | + 'Meta': {'object_name': 'Test'}, |
274 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
275 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), |
276 | + 'test_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '1024'}) |
277 | + }, |
278 | + 'dashboard_app.testcase': { |
279 | + 'Meta': {'unique_together': "(('test', 'test_case_id'),)", 'object_name': 'TestCase'}, |
280 | + 'floor': ('django.db.models.fields.FloatField', [], {'default': '0', 'max_length': '64', 'null': 'True'}), |
281 | + 'goal': ('django.db.models.fields.FloatField', [], {'default': '0', 'max_length': '64', 'null': 'True'}), |
282 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
283 | + 'name': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
284 | + 'target': ('django.db.models.fields.FloatField', [], {'default': '0', 'max_length': '64', 'null': 'True'}), |
285 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_cases'", 'to': "orm['dashboard_app.Test']"}), |
286 | + 'test_case_id': ('django.db.models.fields.TextField', [], {}), |
287 | + 'units': ('django.db.models.fields.TextField', [], {'blank': 'True'}), |
288 | + 'weight': ('django.db.models.fields.FloatField', [], {'default': '1', 'max_length': '64', 'null': 'True'}) |
289 | + }, |
290 | + 'dashboard_app.testdefinition': { |
291 | + 'Meta': {'object_name': 'TestDefinition'}, |
292 | + 'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), |
293 | + 'description': ('django.db.models.fields.TextField', [], {}), |
294 | + 'environment': ('django.db.models.fields.CharField', [], {'max_length': '256'}), |
295 | + 'format': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
296 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
297 | + 'location': ('django.db.models.fields.CharField', [], {'default': "'LOCAL'", 'max_length': '64'}), |
298 | + 'mime_type': ('django.db.models.fields.CharField', [], {'default': "'text/plain'", 'max_length': '64'}), |
299 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}), |
300 | + 'target_dev_types': ('django.db.models.fields.CharField', [], {'max_length': '512'}), |
301 | + 'target_os': ('django.db.models.fields.CharField', [], {'max_length': '512'}), |
302 | + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
303 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '256'}) |
304 | + }, |
305 | + 'dashboard_app.testresult': { |
306 | + 'Meta': {'ordering': "('_order',)", 'object_name': 'TestResult'}, |
307 | + '_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
308 | + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), |
309 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
310 | + 'lineno': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), |
311 | + 'measurement': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '20', 'decimal_places': '10', 'blank': 'True'}), |
312 | + 'message': ('django.db.models.fields.TextField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), |
313 | + 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), |
314 | + 'relative_index': ('django.db.models.fields.PositiveIntegerField', [], {}), |
315 | + 'result': ('django.db.models.fields.PositiveSmallIntegerField', [], {}), |
316 | + 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'test_results'", 'null': 'True', 'to': "orm['dashboard_app.TestCase']"}), |
317 | + 'test_run': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_results'", 'to': "orm['dashboard_app.TestRun']"}), |
318 | + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) |
319 | + }, |
320 | + 'dashboard_app.testrun': { |
321 | + 'Meta': {'ordering': "['-import_assigned_date']", 'object_name': 'TestRun'}, |
322 | + 'analyzer_assigned_date': ('django.db.models.fields.DateTimeField', [], {}), |
323 | + 'analyzer_assigned_uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), |
324 | + 'bundle': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Bundle']"}), |
325 | + 'devices': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.HardwareDevice']"}), |
326 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
327 | + 'import_assigned_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), |
328 | + 'microseconds': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), |
329 | + 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwarePackage']"}), |
330 | + 'sources': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.SoftwareSource']"}), |
331 | + 'sw_image_desc': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), |
332 | + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'test_runs'", 'blank': 'True', 'to': "orm['dashboard_app.Tag']"}), |
333 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'test_runs'", 'to': "orm['dashboard_app.Test']"}), |
334 | + 'time_check_performed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) |
335 | + }, |
336 | + 'dashboard_app.testrundenormalization': { |
337 | + 'Meta': {'object_name': 'TestRunDenormalization'}, |
338 | + 'count_fail': ('django.db.models.fields.PositiveIntegerField', [], {}), |
339 | + 'count_pass': ('django.db.models.fields.PositiveIntegerField', [], {}), |
340 | + 'count_skip': ('django.db.models.fields.PositiveIntegerField', [], {}), |
341 | + 'count_unknown': ('django.db.models.fields.PositiveIntegerField', [], {}), |
342 | + 'test_run': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'denormalization'", 'unique': 'True', 'primary_key': 'True', 'to': "orm['dashboard_app.TestRun']"}) |
343 | + }, |
344 | + 'dashboard_app.testrunfilter': { |
345 | + 'Meta': {'unique_together': "(('owner', 'name'),)", 'object_name': 'TestRunFilter'}, |
346 | + 'build_number_attribute': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), |
347 | + 'bundle_streams': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dashboard_app.BundleStream']", 'symmetrical': 'False'}), |
348 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
349 | + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '1024'}), |
350 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), |
351 | + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
352 | + 'uploaded_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['auth.User']"}) |
353 | + }, |
354 | + 'dashboard_app.testrunfilterattribute': { |
355 | + 'Meta': {'object_name': 'TestRunFilterAttribute'}, |
356 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attributes'", 'to': "orm['dashboard_app.TestRunFilter']"}), |
357 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
358 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), |
359 | + 'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'}) |
360 | + }, |
361 | + 'dashboard_app.testrunfiltersubscription': { |
362 | + 'Meta': {'unique_together': "(('user', 'filter'),)", 'object_name': 'TestRunFilterSubscription'}, |
363 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dashboard_app.TestRunFilter']"}), |
364 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
365 | + 'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}), |
366 | + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) |
367 | + }, |
368 | + 'dashboard_app.testrunfiltertest': { |
369 | + 'Meta': {'object_name': 'TestRunFilterTest'}, |
370 | + 'filter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tests'", 'to': "orm['dashboard_app.TestRunFilter']"}), |
371 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
372 | + 'index': ('django.db.models.fields.PositiveIntegerField', [], {}), |
373 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.Test']"}) |
374 | + }, |
375 | + 'dashboard_app.testrunfiltertestcase': { |
376 | + 'Meta': {'object_name': 'TestRunFilterTestCase'}, |
377 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
378 | + 'index': ('django.db.models.fields.PositiveIntegerField', [], {}), |
379 | + 'test': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'cases'", 'to': "orm['dashboard_app.TestRunFilterTest']"}), |
380 | + 'test_case': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['dashboard_app.TestCase']"}) |
381 | + } |
382 | + } |
383 | + |
384 | + complete_apps = ['dashboard_app'] |
385 | \ No newline at end of file |
386 | |
387 | === modified file 'dashboard_app/models.py' |
388 | --- dashboard_app/models.py 2013-04-01 08:50:05 +0000 |
389 | +++ dashboard_app/models.py 2013-07-17 17:43:24 +0000 |
390 | @@ -659,6 +659,45 @@ |
391 | result=TestResult.RESULT_FAIL).count() |
392 | |
393 | |
394 | +class Goal(models.Model): |
395 | + name = models.TextField( |
396 | + verbose_name = _("Goal Name"), |
397 | + help_text = (_("""The name for this series of goals"""))) |
398 | + |
399 | + def __unicode__(self): |
400 | + return self.name |
401 | + |
402 | + def return_dates(self): |
403 | + goaldate = GoalDate.objects.all() |
404 | + date = {} |
405 | + for name in goaldate: |
406 | + if (name.goal == self): |
407 | + date[name] = {'startdate':name.startdate.isoformat(),'enddate':name.enddate.isoformat(), 'value':name.value} |
408 | + return date |
409 | + |
410 | + |
411 | +class GoalDate(models.Model): |
412 | + goal = models.ForeignKey(Goal) |
413 | + |
414 | + tag = models.CharField( |
415 | + max_length = 4, |
416 | + help_text = (_("""1-4 character tag which distinguishes this goal on the graph"""))) |
417 | + |
418 | + startdate = models.DateField( |
419 | + verbose_name= _("Start Date")) |
420 | + |
421 | + enddate = models.DateField( |
422 | + verbose_name= _("End Date")) |
423 | + |
424 | + value = models.FloatField( |
425 | + verbose_name= _("Value"), |
426 | + default=0, |
427 | + max_length = 64, |
428 | + help_text = (_("""The goal % for this milestone."""))) |
429 | + |
430 | + def __unicode__(self): |
431 | + return self.tag |
432 | + |
433 | class TestCase(models.Model): |
434 | """ |
435 | Model for representing test cases. |
436 | @@ -687,6 +726,34 @@ |
437 | + _help_max_length(100)), |
438 | verbose_name = _("Units")) |
439 | |
440 | + target = models.FloatField( |
441 | + default=0, |
442 | + null = True, |
443 | + max_length = 64, |
444 | + help_text = (_("""The target value of the testcase, representing 100%.""")), |
445 | + verbose_name= _("Target")) |
446 | + |
447 | + floor = models.FloatField( |
448 | + default=0, |
449 | + max_length = 64, |
450 | + null = True, |
451 | + help_text = (_("""The lowest value of the testcase, representing 0%.""")), |
452 | + verbose_name= _("Floor")) |
453 | + |
454 | + weight = models.FloatField( |
455 | + default=1, |
456 | + null = True, |
457 | + max_length = 64, |
458 | + help_text = (_("""The weight of the testcase when graphed against others.""")), |
459 | + verbose_name= _("Weight")) |
460 | + |
461 | + goal = models.FloatField( |
462 | + default=0, |
463 | + null = True, |
464 | + max_length = 64, |
465 | + help_text = (_("""The goal % for a testcase.""")), |
466 | + verbose_name= _("Goal")) |
467 | + |
468 | class Meta: |
469 | unique_together = (('test', 'test_case_id')) |
470 | |
471 | @@ -1177,6 +1244,22 @@ |
472 | def test(self): |
473 | return self.test_run.test |
474 | |
475 | + @property |
476 | + def target(self): |
477 | + return self.test_case.target |
478 | + |
479 | + @property |
480 | + def weight(self): |
481 | + return self.test_case.weight |
482 | + |
483 | + @property |
484 | + def floor(self): |
485 | + return self.test_case.floor |
486 | + |
487 | + @property |
488 | + def goal(self): |
489 | + return self.test_case.goal |
490 | + |
491 | # Core attributes |
492 | |
493 | result = models.PositiveSmallIntegerField( |
494 | @@ -1517,6 +1600,8 @@ |
495 | |
496 | name = models.CharField(max_length=1024, unique=True) |
497 | |
498 | + relatedDates = models.ForeignKey(Goal, null=True, blank=True) |
499 | + |
500 | images = models.ManyToManyField(Image) |
501 | |
502 | def __unicode__(self): |
503 | @@ -1715,6 +1800,33 @@ |
504 | "dashboard_app.views.filters.views.filter_detail", |
505 | [self.owner.username, self.name]) |
506 | |
507 | + def get_testruns_impl(self, user, bundle_streams, attributes): |
508 | + accessible_bundle_streams = BundleStream.objects.accessible_by_principal( |
509 | + user) |
510 | + testruns = TestRun.objects.filter( |
511 | + models.Q(bundle__bundle_stream__in=accessible_bundle_streams), |
512 | + models.Q(bundle__bundle_stream__in=bundle_streams), |
513 | + ) |
514 | + |
515 | + for (name, value) in attributes: |
516 | + testruns = TestRun.objects.filter( |
517 | + id__in=testruns.values_list('id'), |
518 | + attributes__name=name, attributes__value=value) |
519 | + |
520 | + # if the filter doesn't specify a test, we still only return one |
521 | + # test run per bundle. the display code knows to do different |
522 | + # things in this case. |
523 | + testruns = TestRun.objects.filter( |
524 | + id__in=testruns.values_list('id'), |
525 | + test=Test.objects.get(test_id='lava')) |
526 | + |
527 | + return testruns |
528 | + |
529 | + def get_test_runs(self, user): |
530 | + return self.get_testruns_impl( |
531 | + user, |
532 | + self.bundle_streams.all(), |
533 | + self.attributes.values_list('name', 'value')) |
534 | |
535 | class TestRunFilterSubscription(models.Model): |
536 | |
537 | |
538 | === modified file 'dashboard_app/static/dashboard_app/js/jquery.flot.axislabels.js' (properties changed: -x to +x) |
539 | --- dashboard_app/static/dashboard_app/js/jquery.flot.axislabels.js 2011-05-05 01:22:07 +0000 |
540 | +++ dashboard_app/static/dashboard_app/js/jquery.flot.axislabels.js 2013-07-17 17:43:24 +0000 |
541 | @@ -1,87 +1,416 @@ |
542 | /* |
543 | -Flot plugin for labeling axis |
544 | - |
545 | - (xy)axis: { |
546 | - label: "label string", |
547 | - labelPos: "high" or "low" |
548 | - } |
549 | - |
550 | -This plugin allows you to label an axis without much fuss, by |
551 | -replacing one of the extreme ticks with the chosen label string. Set |
552 | -labelPos to "high" or "low" to replace respectively the maximum or the |
553 | -minimum value of the ticks. User set axis.tickFormatter are respected |
554 | -and multiple axes supported. |
555 | - |
556 | -Rui Pereira |
557 | -rui (dot) pereira (at) gmail (dot) com |
558 | +Axis Labels Plugin for flot. |
559 | +http://github.com/markrcote/flot-axislabels |
560 | + |
561 | +Original code is Copyright (c) 2010 Xuan Luo. |
562 | +Original code was released under the GPLv3 license by Xuan Luo, September 2010. |
563 | +Original code was rereleased under the MIT license by Xuan Luo, April 2012. |
564 | + |
565 | +Improvements by Mark Cote. |
566 | + |
567 | +Permission is hereby granted, free of charge, to any person obtaining |
568 | +a copy of this software and associated documentation files (the |
569 | +"Software"), to deal in the Software without restriction, including |
570 | +without limitation the rights to use, copy, modify, merge, publish, |
571 | +distribute, sublicense, and/or sell copies of the Software, and to |
572 | +permit persons to whom the Software is furnished to do so, subject to |
573 | +the following conditions: |
574 | + |
575 | +The above copyright notice and this permission notice shall be |
576 | +included in all copies or substantial portions of the Software. |
577 | + |
578 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
579 | +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
580 | +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
581 | +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
582 | +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
583 | +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
584 | +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
585 | + |
586 | */ |
587 | (function ($) { |
588 | - |
589 | - function labelAxis(val, axis){ |
590 | - var ticks, opts = axis.options; |
591 | - |
592 | - // generator |
593 | - var tmpopts = axis.n == 1? opts: (typeof opts.alignedTo != 'undefined')? opts.alignedTo.options: null; |
594 | - // first axis or some axis aligned wrt it |
595 | - if (tmpopts && (tmpopts.autoscaleMargin == null || |
596 | - (tmpopts.labelPos == 'high' && tmpopts.max != null) || |
597 | - (tmpopts.labelPos == 'low' && tmpopts.min != null))) |
598 | - // cut ticks not seen |
599 | - ticks = $.grep(axis.tickGenerator(axis), function(v){ |
600 | - return (v > axis.min && v < axis.max); |
601 | - }); |
602 | - // standard tick generator |
603 | - else ticks = axis.tickGenerator(axis); |
604 | - |
605 | - // formatter |
606 | - if ((opts.labelPos == 'high' && val == ticks[ticks.length-1]) || |
607 | - (opts.labelPos == 'low' && val == ticks[0])) |
608 | - return opts.label; |
609 | - else { |
610 | - // user set tickFormatter |
611 | - if ($.isFunction(opts.userFormatter)){ |
612 | - var tmp = opts.userFormatter; |
613 | - // avoid infinite loops |
614 | - opts.userFormatter = null; |
615 | - return tmp(val, axis); |
616 | + var options = { }; |
617 | + |
618 | + function canvasSupported() { |
619 | + return !!document.createElement('canvas').getContext; |
620 | + } |
621 | + |
622 | + function canvasTextSupported() { |
623 | + if (!canvasSupported()) { |
624 | + return false; |
625 | + } |
626 | + var dummy_canvas = document.createElement('canvas'); |
627 | + var context = dummy_canvas.getContext('2d'); |
628 | + return typeof context.fillText == 'function'; |
629 | + } |
630 | + |
631 | + function css3TransitionSupported() { |
632 | + var div = document.createElement('div'); |
633 | + return typeof div.style.MozTransition != 'undefined' // Gecko |
634 | + || typeof div.style.OTransition != 'undefined' // Opera |
635 | + || typeof div.style.webkitTransition != 'undefined' // WebKit |
636 | + || typeof div.style.transition != 'undefined'; |
637 | + } |
638 | + |
639 | + |
640 | + function AxisLabel(axisName, position, padding, plot, opts) { |
641 | + this.axisName = axisName; |
642 | + this.position = position; |
643 | + this.padding = padding; |
644 | + this.plot = plot; |
645 | + this.opts = opts; |
646 | + this.width = 0; |
647 | + this.height = 0; |
648 | + } |
649 | + |
650 | + |
651 | + CanvasAxisLabel.prototype = new AxisLabel(); |
652 | + CanvasAxisLabel.prototype.constructor = CanvasAxisLabel; |
653 | + function CanvasAxisLabel(axisName, position, padding, plot, opts) { |
654 | + AxisLabel.prototype.constructor.call(this, axisName, position, padding, |
655 | + plot, opts); |
656 | + } |
657 | + |
658 | + CanvasAxisLabel.prototype.calculateSize = function() { |
659 | + if (!this.opts.axisLabelFontSizePixels) |
660 | + this.opts.axisLabelFontSizePixels = 14; |
661 | + if (!this.opts.axisLabelFontFamily) |
662 | + this.opts.axisLabelFontFamily = 'sans-serif'; |
663 | + |
664 | + var textWidth = this.opts.axisLabelFontSizePixels + this.padding; |
665 | + var textHeight = this.opts.axisLabelFontSizePixels + this.padding; |
666 | + if (this.position == 'left' || this.position == 'right') { |
667 | + this.width = this.opts.axisLabelFontSizePixels + this.padding; |
668 | + this.height = 0; |
669 | + } else { |
670 | + this.width = 0; |
671 | + this.height = this.opts.axisLabelFontSizePixels + this.padding; |
672 | + } |
673 | + }; |
674 | + |
675 | + CanvasAxisLabel.prototype.draw = function(box) { |
676 | + var ctx = this.plot.getCanvas().getContext('2d'); |
677 | + ctx.save(); |
678 | + ctx.font = this.opts.axisLabelFontSizePixels + 'px ' + |
679 | + this.opts.axisLabelFontFamily; |
680 | + var width = ctx.measureText(this.opts.axisLabel).width; |
681 | + var height = this.opts.axisLabelFontSizePixels; |
682 | + var x, y, angle = 0; |
683 | + if (this.position == 'top') { |
684 | + x = box.left + box.width/2 - width/2; |
685 | + y = box.top + height*0.72; |
686 | + } else if (this.position == 'bottom') { |
687 | + x = box.left + box.width/2 - width/2; |
688 | + y = box.top + box.height - height*0.72; |
689 | + } else if (this.position == 'left') { |
690 | + x = box.left + height*0.72; |
691 | + y = box.height/2 + box.top + width/2; |
692 | + angle = -Math.PI/2; |
693 | + } else if (this.position == 'right') { |
694 | + x = box.left + box.width - height*0.72; |
695 | + y = box.height/2 + box.top - width/2; |
696 | + angle = Math.PI/2; |
697 | + } |
698 | + ctx.translate(x, y); |
699 | + ctx.rotate(angle); |
700 | + ctx.fillText(this.opts.axisLabel, 0, 0); |
701 | + ctx.restore(); |
702 | + }; |
703 | + |
704 | + |
705 | + HtmlAxisLabel.prototype = new AxisLabel(); |
706 | + HtmlAxisLabel.prototype.constructor = HtmlAxisLabel; |
707 | + function HtmlAxisLabel(axisName, position, padding, plot, opts) { |
708 | + AxisLabel.prototype.constructor.call(this, axisName, position, |
709 | + padding, plot, opts); |
710 | + } |
711 | + |
712 | + HtmlAxisLabel.prototype.calculateSize = function() { |
713 | + var elem = $('<div class="axisLabels" style="position:absolute;">' + |
714 | + this.opts.axisLabel + '</div>'); |
715 | + this.plot.getPlaceholder().append(elem); |
716 | + // store height and width of label itself, for use in draw() |
717 | + this.labelWidth = elem.outerWidth(true); |
718 | + this.labelHeight = elem.outerHeight(true); |
719 | + elem.remove(); |
720 | + |
721 | + this.width = this.height = 0; |
722 | + if (this.position == 'left' || this.position == 'right') { |
723 | + this.width = this.labelWidth + this.padding; |
724 | + } else { |
725 | + this.height = this.labelHeight + this.padding; |
726 | + } |
727 | + }; |
728 | + |
729 | + HtmlAxisLabel.prototype.draw = function(box) { |
730 | + this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove(); |
731 | + var elem = $('<div id="' + this.axisName + |
732 | + 'Label" " class="axisLabels" style="position:absolute;">' |
733 | + + this.opts.axisLabel + '</div>'); |
734 | + this.plot.getPlaceholder().append(elem); |
735 | + if (this.position == 'top') { |
736 | + elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 'px'); |
737 | + elem.css('top', box.top + 'px'); |
738 | + } else if (this.position == 'bottom') { |
739 | + elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 'px'); |
740 | + elem.css('top', box.top + box.height - this.labelHeight + 'px'); |
741 | + } else if (this.position == 'left') { |
742 | + elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 'px'); |
743 | + elem.css('left', box.left + 'px'); |
744 | + } else if (this.position == 'right') { |
745 | + elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 'px'); |
746 | + elem.css('left', box.left + box.width - this.labelWidth + 'px'); |
747 | + } |
748 | + }; |
749 | + |
750 | + |
751 | + CssTransformAxisLabel.prototype = new HtmlAxisLabel(); |
752 | + CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel; |
753 | + function CssTransformAxisLabel(axisName, position, padding, plot, opts) { |
754 | + HtmlAxisLabel.prototype.constructor.call(this, axisName, position, |
755 | + padding, plot, opts); |
756 | + } |
757 | + |
758 | + CssTransformAxisLabel.prototype.calculateSize = function() { |
759 | + HtmlAxisLabel.prototype.calculateSize.call(this); |
760 | + this.width = this.height = 0; |
761 | + if (this.position == 'left' || this.position == 'right') { |
762 | + this.width = this.labelHeight + this.padding; |
763 | + } else { |
764 | + this.height = this.labelHeight + this.padding; |
765 | + } |
766 | + }; |
767 | + |
768 | + CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) { |
769 | + var stransforms = { |
770 | + '-moz-transform': '', |
771 | + '-webkit-transform': '', |
772 | + '-o-transform': '', |
773 | + '-ms-transform': '' |
774 | + }; |
775 | + if (x != 0 || y != 0) { |
776 | + var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)'; |
777 | + stransforms['-moz-transform'] += stdTranslate; |
778 | + stransforms['-webkit-transform'] += stdTranslate; |
779 | + stransforms['-o-transform'] += stdTranslate; |
780 | + stransforms['-ms-transform'] += stdTranslate; |
781 | + } |
782 | + if (degrees != 0) { |
783 | + var rotation = degrees / 90; |
784 | + var stdRotate = ' rotate(' + degrees + 'deg)'; |
785 | + stransforms['-moz-transform'] += stdRotate; |
786 | + stransforms['-webkit-transform'] += stdRotate; |
787 | + stransforms['-o-transform'] += stdRotate; |
788 | + stransforms['-ms-transform'] += stdRotate; |
789 | + } |
790 | + var s = 'top: 0; left: 0; '; |
791 | + for (var prop in stransforms) { |
792 | + if (stransforms[prop]) { |
793 | + s += prop + ':' + stransforms[prop] + ';'; |
794 | + } |
795 | + } |
796 | + s += ';'; |
797 | + return s; |
798 | + }; |
799 | + |
800 | + CssTransformAxisLabel.prototype.calculateOffsets = function(box) { |
801 | + var offsets = { x: 0, y: 0, degrees: 0 }; |
802 | + if (this.position == 'bottom') { |
803 | + offsets.x = box.left + box.width/2 - this.labelWidth/2; |
804 | + offsets.y = box.top + box.height - this.labelHeight; |
805 | + } else if (this.position == 'top') { |
806 | + offsets.x = box.left + box.width/2 - this.labelWidth/2; |
807 | + offsets.y = box.top; |
808 | + } else if (this.position == 'left') { |
809 | + offsets.degrees = -90; |
810 | + offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2; |
811 | + offsets.y = box.height/2 + box.top; |
812 | + } else if (this.position == 'right') { |
813 | + offsets.degrees = 90; |
814 | + offsets.x = box.left + box.width - this.labelWidth/2 |
815 | + - this.labelHeight/2; |
816 | + offsets.y = box.height/2 + box.top; |
817 | + } |
818 | + return offsets; |
819 | + }; |
820 | + |
821 | + CssTransformAxisLabel.prototype.draw = function(box) { |
822 | + this.plot.getPlaceholder().find("." + this.axisName + "Label").remove(); |
823 | + var offsets = this.calculateOffsets(box); |
824 | + var elem = $('<div class="axisLabels ' + this.axisName + |
825 | + 'Label" style="position:absolute; ' + |
826 | + 'color: ' + this.opts.color + '; ' + |
827 | + this.transforms(offsets.degrees, offsets.x, offsets.y) + |
828 | + '">' + this.opts.axisLabel + '</div>'); |
829 | + this.plot.getPlaceholder().append(elem); |
830 | + }; |
831 | + |
832 | + |
833 | + IeTransformAxisLabel.prototype = new CssTransformAxisLabel(); |
834 | + IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel; |
835 | + function IeTransformAxisLabel(axisName, position, padding, plot, opts) { |
836 | + CssTransformAxisLabel.prototype.constructor.call(this, axisName, |
837 | + position, padding, |
838 | + plot, opts); |
839 | + this.requiresResize = false; |
840 | + } |
841 | + |
842 | + IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) { |
843 | + // I didn't feel like learning the crazy Matrix stuff, so this uses |
844 | + // a combination of the rotation transform and CSS positioning. |
845 | + var s = ''; |
846 | + if (degrees != 0) { |
847 | + var rotation = degrees/90; |
848 | + while (rotation < 0) { |
849 | + rotation += 4; |
850 | + } |
851 | + s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); '; |
852 | + // see below |
853 | + this.requiresResize = (this.position == 'right'); |
854 | + } |
855 | + if (x != 0) { |
856 | + s += 'left: ' + x + 'px; '; |
857 | + } |
858 | + if (y != 0) { |
859 | + s += 'top: ' + y + 'px; '; |
860 | + } |
861 | + return s; |
862 | + }; |
863 | + |
864 | + IeTransformAxisLabel.prototype.calculateOffsets = function(box) { |
865 | + var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call( |
866 | + this, box); |
867 | + // adjust some values to take into account differences between |
868 | + // CSS and IE rotations. |
869 | + if (this.position == 'top') { |
870 | + // FIXME: not sure why, but placing this exactly at the top causes |
871 | + // the top axis label to flip to the bottom... |
872 | + offsets.y = box.top + 1; |
873 | + } else if (this.position == 'left') { |
874 | + offsets.x = box.left; |
875 | + offsets.y = box.height/2 + box.top - this.labelWidth/2; |
876 | + } else if (this.position == 'right') { |
877 | + offsets.x = box.left + box.width - this.labelHeight; |
878 | + offsets.y = box.height/2 + box.top - this.labelWidth/2; |
879 | + } |
880 | + return offsets; |
881 | + }; |
882 | + |
883 | + IeTransformAxisLabel.prototype.draw = function(box) { |
884 | + CssTransformAxisLabel.prototype.draw.call(this, box); |
885 | + if (this.requiresResize) { |
886 | + var elem = this.plot.getPlaceholder().find("." + this.axisName + "Label"); |
887 | + // Since we used CSS positioning instead of transforms for |
888 | + // translating the element, and since the positioning is done |
889 | + // before any rotations, we have to reset the width and height |
890 | + // in case the browser wrapped the text (specifically for the |
891 | + // y2axis). |
892 | + elem.css('width', this.labelWidth); |
893 | + elem.css('height', this.labelHeight); |
894 | + } |
895 | + }; |
896 | + |
897 | + |
898 | + function init(plot) { |
899 | + // This is kind of a hack. There are no hooks in Flot between |
900 | + // the creation and measuring of the ticks (setTicks, measureTickLabels |
901 | + // in setupGrid() ) and the drawing of the ticks and plot box |
902 | + // (insertAxisLabels in setupGrid() ). |
903 | + // |
904 | + // Therefore, we use a trick where we run the draw routine twice: |
905 | + // the first time to get the tick measurements, so that we can change |
906 | + // them, and then have it draw it again. |
907 | + var secondPass = false; |
908 | + |
909 | + var axisLabels = {}; |
910 | + var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 }; |
911 | + |
912 | + var defaultPadding = 2; // padding between axis and tick labels |
913 | + plot.hooks.draw.push(function (plot, ctx) { |
914 | + var hasAxisLabels = false; |
915 | + if (!secondPass) { |
916 | + // MEASURE AND SET OPTIONS |
917 | + $.each(plot.getAxes(), function(axisName, axis) { |
918 | + var opts = axis.options // Flot 0.7 |
919 | + || plot.getOptions()[axisName]; // Flot 0.6 |
920 | + if (!opts || !opts.axisLabel || !axis.show) |
921 | + return; |
922 | + |
923 | + hasAxisLabels = true; |
924 | + var renderer = null; |
925 | + |
926 | + if (!opts.axisLabelUseHtml && |
927 | + navigator.appName == 'Microsoft Internet Explorer') { |
928 | + var ua = navigator.userAgent; |
929 | + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); |
930 | + if (re.exec(ua) != null) { |
931 | + rv = parseFloat(RegExp.$1); |
932 | + } |
933 | + if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { |
934 | + renderer = CssTransformAxisLabel; |
935 | + } else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { |
936 | + renderer = IeTransformAxisLabel; |
937 | + } else if (opts.axisLabelUseCanvas) { |
938 | + renderer = CanvasAxisLabel; |
939 | + } else { |
940 | + renderer = HtmlAxisLabel; |
941 | + } |
942 | + } else { |
943 | + if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) { |
944 | + renderer = HtmlAxisLabel; |
945 | + } else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) { |
946 | + renderer = CanvasAxisLabel; |
947 | + } else { |
948 | + renderer = CssTransformAxisLabel; |
949 | + } |
950 | + } |
951 | + |
952 | + var padding = opts.axisLabelPadding === undefined ? |
953 | + defaultPadding : opts.axisLabelPadding; |
954 | + |
955 | + axisLabels[axisName] = new renderer(axisName, |
956 | + axis.position, padding, |
957 | + plot, opts); |
958 | + |
959 | + // flot interprets axis.labelHeight and .labelWidth as |
960 | + // the height and width of the tick labels. We increase |
961 | + // these values to make room for the axis label and |
962 | + // padding. |
963 | + |
964 | + axisLabels[axisName].calculateSize(); |
965 | + |
966 | + // AxisLabel.height and .width are the size of the |
967 | + // axis label and padding. |
968 | + axis.labelHeight += axisLabels[axisName].height; |
969 | + axis.labelWidth += axisLabels[axisName].width; |
970 | + opts.labelHeight = axis.labelHeight; |
971 | + opts.labelWidth = axis.labelWidth; |
972 | + }); |
973 | + // if there are axis labels re-draw with new label widths and heights |
974 | + if (hasAxisLabels) { |
975 | + secondPass = true; |
976 | + plot.setupGrid(); |
977 | + plot.draw(); |
978 | + } |
979 | } else { |
980 | - // scientific notation for small values |
981 | - if ((axis.datamax != 0 && Math.abs(axis.datamax) < 1e-5) || |
982 | - (axis.datamin != 0 && Math.abs(axis.datamin) < 1e-5)) |
983 | - return val.toPrecision(2); |
984 | - else return val.toFixed(axis.tickDecimals); |
985 | - } |
986 | - } |
987 | - } |
988 | + // DRAW |
989 | + $.each(plot.getAxes(), function(axisName, axis) { |
990 | + var opts = axis.options // Flot 0.7 |
991 | + || plot.getOptions()[axisName]; // Flot 0.6 |
992 | + if (!opts || !opts.axisLabel || !axis.show) |
993 | + return; |
994 | |
995 | - function init(plot){ |
996 | - plot.hooks.processOptions.push(function(plot, options){ |
997 | - // separate X and Y |
998 | - $.each({x: options.xaxes, y: options.yaxes}, function(direction, axes){ |
999 | - // get only axes with labels |
1000 | - $.each($.grep(axes, function(v){ |
1001 | - return (typeof v.label != 'undefined' && v.label); |
1002 | - }), function(i, axis){ |
1003 | - if ($.isFunction(axis.tickFormatter)) |
1004 | - axis.userFormatter = axis.tickFormatter; |
1005 | - if (typeof axis.alignTicksWithAxis != 'undefined') |
1006 | - $.each(plot.getAxes(), function(k,v){ |
1007 | - if (v.n == axis.alignTicksWithAxis && v.direction == direction) |
1008 | - axis.alignedTo = v; |
1009 | - }); |
1010 | - axis.tickFormatter = labelAxis; |
1011 | + axisLabels[axisName].draw(axis.box); |
1012 | }); |
1013 | - }); |
1014 | + } |
1015 | }); |
1016 | } |
1017 | |
1018 | - var options = { xaxis: {label: null, labelPos: 'high'}, |
1019 | - yaxis: {label: null, labelPos: 'high'} }; |
1020 | |
1021 | $.plot.plugins.push({ |
1022 | - init: init, |
1023 | - options: options, |
1024 | - name: "axislabels", |
1025 | - version: "0.1" |
1026 | - }); |
1027 | + init: init, |
1028 | + options: options, |
1029 | + name: 'axisLabels', |
1030 | + version: '2.0b0' |
1031 | + }); |
1032 | })(jQuery); |
1033 | |
1034 | === added file 'dashboard_app/static/dashboard_app/js/jquery.flot.dashes.js' |
1035 | --- dashboard_app/static/dashboard_app/js/jquery.flot.dashes.js 1970-01-01 00:00:00 +0000 |
1036 | +++ dashboard_app/static/dashboard_app/js/jquery.flot.dashes.js 2013-07-17 17:43:24 +0000 |
1037 | @@ -0,0 +1,231 @@ |
1038 | +/* |
1039 | + * jQuery.flot.dashes |
1040 | + * |
1041 | + * options = { |
1042 | + * series: { |
1043 | + * dashes: { |
1044 | + * |
1045 | + * // show |
1046 | + * // default: false |
1047 | + * // Whether to show dashes for the series. |
1048 | + * show: <boolean>, |
1049 | + * |
1050 | + * // lineWidth |
1051 | + * // default: 2 |
1052 | + * // The width of the dashed line in pixels. |
1053 | + * lineWidth: <number>, |
1054 | + * |
1055 | + * // dashLength |
1056 | + * // default: 10 |
1057 | + * // Controls the length of the individual dashes and the amount of |
1058 | + * // space between them. |
1059 | + * // If this is a number, the dashes and spaces will have that length. |
1060 | + * // If this is an array, it is read as [ dashLength, spaceLength ] |
1061 | + * dashLength: <number> or <array[2]> |
1062 | + * } |
1063 | + * } |
1064 | + * } |
1065 | + */ |
1066 | +(function($){ |
1067 | + |
1068 | + function init(plot) { |
1069 | + |
1070 | + plot.hooks.drawSeries.push(function(plot, ctx, series) { |
1071 | + |
1072 | + if (!series.dashes.show) { |
1073 | + return; |
1074 | + } |
1075 | + |
1076 | + var plotOffset = plot.getPlotOffset(); |
1077 | + |
1078 | + function plotDashes(datapoints, xoffset, yoffset, axisx, axisy) { |
1079 | + |
1080 | + var points = datapoints.points, |
1081 | + ps = datapoints.pointsize, |
1082 | + prevx = null, |
1083 | + prevy = null, |
1084 | + dashRemainder = 0, |
1085 | + dashOn = true, |
1086 | + dashOnLength, |
1087 | + dashOffLength; |
1088 | + |
1089 | + if (series.dashes.dashLength[0]) { |
1090 | + dashOnLength = series.dashes.dashLength[0]; |
1091 | + if (series.dashes.dashLength[1]) { |
1092 | + dashOffLength = series.dashes.dashLength[1]; |
1093 | + } else { |
1094 | + dashOffLength = dashOnLength; |
1095 | + } |
1096 | + } else { |
1097 | + dashOffLength = dashOnLength = series.dashes.dashLength; |
1098 | + } |
1099 | + |
1100 | + ctx.beginPath(); |
1101 | + |
1102 | + for (var i = ps; i < points.length; i += ps) { |
1103 | + |
1104 | + var x1 = points[i - ps], |
1105 | + y1 = points[i - ps + 1], |
1106 | + x2 = points[i], |
1107 | + y2 = points[i + 1]; |
1108 | + |
1109 | + if (x1 == null || x2 == null) continue; |
1110 | + |
1111 | + // clip with ymin |
1112 | + if (y1 <= y2 && y1 < axisy.min) { |
1113 | + if (y2 < axisy.min) continue; // line segment is outside |
1114 | + // compute new intersection point |
1115 | + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; |
1116 | + y1 = axisy.min; |
1117 | + } else if (y2 <= y1 && y2 < axisy.min) { |
1118 | + if (y1 < axisy.min) continue; |
1119 | + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; |
1120 | + y2 = axisy.min; |
1121 | + } |
1122 | + |
1123 | + // clip with ymax |
1124 | + if (y1 >= y2 && y1 > axisy.max) { |
1125 | + if (y2 > axisy.max) continue; |
1126 | + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; |
1127 | + y1 = axisy.max; |
1128 | + } else if (y2 >= y1 && y2 > axisy.max) { |
1129 | + if (y1 > axisy.max) continue; |
1130 | + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; |
1131 | + y2 = axisy.max; |
1132 | + } |
1133 | + |
1134 | + // clip with xmin |
1135 | + if (x1 <= x2 && x1 < axisx.min) { |
1136 | + if (x2 < axisx.min) continue; |
1137 | + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; |
1138 | + x1 = axisx.min; |
1139 | + } else if (x2 <= x1 && x2 < axisx.min) { |
1140 | + if (x1 < axisx.min) continue; |
1141 | + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; |
1142 | + x2 = axisx.min; |
1143 | + } |
1144 | + |
1145 | + // clip with xmax |
1146 | + if (x1 >= x2 && x1 > axisx.max) { |
1147 | + if (x2 > axisx.max) continue; |
1148 | + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; |
1149 | + x1 = axisx.max; |
1150 | + } else if (x2 >= x1 && x2 > axisx.max) { |
1151 | + if (x1 > axisx.max) continue; |
1152 | + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; |
1153 | + x2 = axisx.max; |
1154 | + } |
1155 | + |
1156 | + if (x1 != prevx || y1 != prevy) { |
1157 | + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); |
1158 | + } |
1159 | + |
1160 | + var ax1 = axisx.p2c(x1) + xoffset, |
1161 | + ay1 = axisy.p2c(y1) + yoffset, |
1162 | + ax2 = axisx.p2c(x2) + xoffset, |
1163 | + ay2 = axisy.p2c(y2) + yoffset, |
1164 | + dashOffset; |
1165 | + |
1166 | + function lineSegmentOffset(segmentLength) { |
1167 | + |
1168 | + var c = Math.sqrt(Math.pow(ax2 - ax1, 2) + Math.pow(ay2 - ay1, 2)); |
1169 | + |
1170 | + if (c <= segmentLength) { |
1171 | + return { |
1172 | + deltaX: ax2 - ax1, |
1173 | + deltaY: ay2 - ay1, |
1174 | + distance: c, |
1175 | + remainder: segmentLength - c |
1176 | + } |
1177 | + } else { |
1178 | + var xsign = ax2 > ax1 ? 1 : -1, |
1179 | + ysign = ay2 > ay1 ? 1 : -1; |
1180 | + return { |
1181 | + deltaX: xsign * Math.sqrt(Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))), |
1182 | + deltaY: ysign * Math.sqrt(Math.pow(segmentLength, 2) - Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))), |
1183 | + distance: segmentLength, |
1184 | + remainder: 0 |
1185 | + }; |
1186 | + } |
1187 | + } |
1188 | + //-end lineSegmentOffset |
1189 | + |
1190 | + do { |
1191 | + |
1192 | + dashOffset = lineSegmentOffset( |
1193 | + dashRemainder > 0 ? dashRemainder : |
1194 | + dashOn ? dashOnLength : dashOffLength); |
1195 | + |
1196 | + if (dashOffset.deltaX != 0 || dashOffset.deltaY != 0) { |
1197 | + if (dashOn) { |
1198 | + ctx.lineTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY); |
1199 | + } else { |
1200 | + ctx.moveTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY); |
1201 | + } |
1202 | + } |
1203 | + |
1204 | + dashOn = !dashOn; |
1205 | + dashRemainder = dashOffset.remainder; |
1206 | + ax1 += dashOffset.deltaX; |
1207 | + ay1 += dashOffset.deltaY; |
1208 | + |
1209 | + } while (dashOffset.distance > 0); |
1210 | + |
1211 | + prevx = x2; |
1212 | + prevy = y2; |
1213 | + } |
1214 | + |
1215 | + ctx.stroke(); |
1216 | + } |
1217 | + //-end plotDashes |
1218 | + |
1219 | + ctx.save(); |
1220 | + ctx.translate(plotOffset.left, plotOffset.top); |
1221 | + ctx.lineJoin = 'round'; |
1222 | + |
1223 | + var lw = series.dashes.lineWidth, |
1224 | + sw = series.shadowSize; |
1225 | + |
1226 | + // FIXME: consider another form of shadow when filling is turned on |
1227 | + if (lw > 0 && sw > 0) { |
1228 | + // draw shadow as a thick and thin line with transparency |
1229 | + ctx.lineWidth = sw; |
1230 | + ctx.strokeStyle = "rgba(0,0,0,0.1)"; |
1231 | + // position shadow at angle from the mid of line |
1232 | + var angle = Math.PI/18; |
1233 | + plotDashes(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); |
1234 | + ctx.lineWidth = sw/2; |
1235 | + plotDashes(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); |
1236 | + } |
1237 | + |
1238 | + ctx.lineWidth = lw; |
1239 | + ctx.strokeStyle = series.color; |
1240 | + |
1241 | + if (lw > 0) { |
1242 | + plotDashes(series.datapoints, 0, 0, series.xaxis, series.yaxis); |
1243 | + } |
1244 | + |
1245 | + ctx.restore(); |
1246 | + |
1247 | + }); |
1248 | + //-end drawSeries hook |
1249 | + |
1250 | + } |
1251 | + //-end init |
1252 | + |
1253 | + $.plot.plugins.push({ |
1254 | + init: init, |
1255 | + options: { |
1256 | + series: { |
1257 | + dashes: { |
1258 | + show: false, |
1259 | + lineWidth: 2, |
1260 | + dashLength: 10 |
1261 | + } |
1262 | + } |
1263 | + }, |
1264 | + name: 'dashes', |
1265 | + version: '0.1b' |
1266 | + }); |
1267 | + |
1268 | +})(jQuery) |
1269 | \ No newline at end of file |
1270 | |
1271 | === added file 'dashboard_app/static/dashboard_app/js/jquery.flot.resize.js' |
1272 | --- dashboard_app/static/dashboard_app/js/jquery.flot.resize.js 1970-01-01 00:00:00 +0000 |
1273 | +++ dashboard_app/static/dashboard_app/js/jquery.flot.resize.js 2013-07-17 17:43:24 +0000 |
1274 | @@ -0,0 +1,60 @@ |
1275 | +/* |
1276 | +Flot plugin for automatically redrawing plots when the placeholder |
1277 | +size changes, e.g. on window resizes. |
1278 | + |
1279 | +It works by listening for changes on the placeholder div (through the |
1280 | +jQuery resize event plugin) - if the size changes, it will redraw the |
1281 | +plot. |
1282 | + |
1283 | +There are no options. If you need to disable the plugin for some |
1284 | +plots, you can just fix the size of their placeholders. |
1285 | +*/ |
1286 | + |
1287 | + |
1288 | +/* Inline dependency: |
1289 | + * jQuery resize event - v1.1 - 3/14/2010 |
1290 | + * http://benalman.com/projects/jquery-resize-plugin/ |
1291 | + * |
1292 | + * Copyright (c) 2010 "Cowboy" Ben Alman |
1293 | + * Dual licensed under the MIT and GPL licenses. |
1294 | + * http://benalman.com/about/license/ |
1295 | + */ |
1296 | +(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this); |
1297 | + |
1298 | + |
1299 | +(function ($) { |
1300 | + var options = { }; // no options |
1301 | + |
1302 | + function init(plot) { |
1303 | + function onResize() { |
1304 | + var placeholder = plot.getPlaceholder(); |
1305 | + |
1306 | + // somebody might have hidden us and we can't plot |
1307 | + // when we don't have the dimensions |
1308 | + if (placeholder.width() == 0 || placeholder.height() == 0) |
1309 | + return; |
1310 | + |
1311 | + plot.resize(); |
1312 | + plot.setupGrid(); |
1313 | + plot.draw(); |
1314 | + } |
1315 | + |
1316 | + function bindEvents(plot, eventHolder) { |
1317 | + plot.getPlaceholder().resize(onResize); |
1318 | + } |
1319 | + |
1320 | + function shutdown(plot, eventHolder) { |
1321 | + plot.getPlaceholder().unbind("resize", onResize); |
1322 | + } |
1323 | + |
1324 | + plot.hooks.bindEvents.push(bindEvents); |
1325 | + plot.hooks.shutdown.push(shutdown); |
1326 | + } |
1327 | + |
1328 | + $.plot.plugins.push({ |
1329 | + init: init, |
1330 | + options: options, |
1331 | + name: 'resize', |
1332 | + version: '1.0' |
1333 | + }); |
1334 | +})(jQuery); |
1335 | |
1336 | === added file 'dashboard_app/static/dashboard_app/js/jquery.flot.threshold.js' |
1337 | --- dashboard_app/static/dashboard_app/js/jquery.flot.threshold.js 1970-01-01 00:00:00 +0000 |
1338 | +++ dashboard_app/static/dashboard_app/js/jquery.flot.threshold.js 2013-07-17 17:43:24 +0000 |
1339 | @@ -0,0 +1,142 @@ |
1340 | +/* Flot plugin for thresholding data. |
1341 | + |
1342 | +Copyright (c) 2007-2013 IOLA and Ole Laursen. |
1343 | +Licensed under the MIT license. |
1344 | + |
1345 | +The plugin supports these options: |
1346 | + |
1347 | +series: { |
1348 | +threshold: { |
1349 | +below: number |
1350 | +color: colorspec |
1351 | +} |
1352 | +} |
1353 | + |
1354 | +It can also be applied to a single series, like this: |
1355 | + |
1356 | +$.plot( $("#placeholder"), [{ |
1357 | +data: [ ... ], |
1358 | +threshold: { ... } |
1359 | +}]) |
1360 | + |
1361 | +An array can be passed for multiple thresholding, like this: |
1362 | + |
1363 | +threshold: [{ |
1364 | +below: number1 |
1365 | +color: color1 |
1366 | +},{ |
1367 | +below: number2 |
1368 | +color: color2 |
1369 | +}] |
1370 | + |
1371 | +These multiple threshold objects can be passed in any order since they are |
1372 | +sorted by the processing function. |
1373 | + |
1374 | +The data points below "below" are drawn with the specified color. This makes |
1375 | +it easy to mark points below 0, e.g. for budget data. |
1376 | + |
1377 | +Internally, the plugin works by splitting the data into two series, above and |
1378 | +below the threshold. The extra series below the threshold will have its label |
1379 | +cleared and the special "originSeries" attribute set to the original series. |
1380 | +You may need to check for this in hover events. |
1381 | + |
1382 | +*/ |
1383 | + |
1384 | +(function ($) { |
1385 | + var options = { |
1386 | + series: { threshold: null } // or { below: number, color: color spec} |
1387 | + }; |
1388 | + |
1389 | + function init(plot) { |
1390 | + function thresholdData(plot, s, datapoints, below, color) { |
1391 | + var ps = datapoints.pointsize, i, x, y, p, prevp, |
1392 | + thresholded = $.extend({}, s); // note: shallow copy |
1393 | + |
1394 | + thresholded.datapoints = { points: [], pointsize: ps, format: datapoints.format }; |
1395 | + thresholded.label = null; |
1396 | + thresholded.color = color; |
1397 | + thresholded.threshold = null; |
1398 | + thresholded.originSeries = s; |
1399 | + thresholded.data = []; |
1400 | + |
1401 | + var origpoints = datapoints.points, |
1402 | + addCrossingPoints = s.lines.show; |
1403 | + |
1404 | + var threspoints = []; |
1405 | + var newpoints = []; |
1406 | + var m; |
1407 | + |
1408 | + for (i = 0; i < origpoints.length; i += ps) { |
1409 | + x = origpoints[i]; |
1410 | + y = origpoints[i + 1]; |
1411 | + |
1412 | + prevp = p; |
1413 | + if (y < below) |
1414 | + p = threspoints; |
1415 | + else |
1416 | + p = newpoints; |
1417 | + |
1418 | + if (addCrossingPoints && prevp != p && x != null |
1419 | + && i > 0 && origpoints[i - ps] != null) { |
1420 | + var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); |
1421 | + prevp.push(interx); |
1422 | + prevp.push(below); |
1423 | + for (m = 2; m < ps; ++m) |
1424 | + prevp.push(origpoints[i + m]); |
1425 | + |
1426 | + p.push(null); // start new segment |
1427 | + p.push(null); |
1428 | + for (m = 2; m < ps; ++m) |
1429 | + p.push(origpoints[i + m]); |
1430 | + p.push(interx); |
1431 | + p.push(below); |
1432 | + for (m = 2; m < ps; ++m) |
1433 | + p.push(origpoints[i + m]); |
1434 | + } |
1435 | + |
1436 | + p.push(x); |
1437 | + p.push(y); |
1438 | + for (m = 2; m < ps; ++m) |
1439 | + p.push(origpoints[i + m]); |
1440 | + } |
1441 | + |
1442 | + datapoints.points = newpoints; |
1443 | + thresholded.datapoints.points = threspoints; |
1444 | + |
1445 | + if (thresholded.datapoints.points.length > 0) { |
1446 | + var origIndex = $.inArray(s, plot.getData()); |
1447 | + // Insert newly-generated series right after original one (to prevent it from becoming top-most) |
1448 | + plot.getData().splice(origIndex + 1, 0, thresholded); |
1449 | + } |
1450 | + |
1451 | + // FIXME: there are probably some edge cases left in bars |
1452 | + } |
1453 | + |
1454 | + function processThresholds(plot, s, datapoints) { |
1455 | + if (!s.threshold) |
1456 | + return; |
1457 | + |
1458 | + if (s.threshold instanceof Array) { |
1459 | + s.threshold.sort(function(a, b) { |
1460 | + return a.below - b.below; |
1461 | + }); |
1462 | + |
1463 | + $(s.threshold).each(function(i, th) { |
1464 | + thresholdData(plot, s, datapoints, th.below, th.color); |
1465 | + }); |
1466 | + } |
1467 | + else { |
1468 | + thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); |
1469 | + } |
1470 | + } |
1471 | + |
1472 | + plot.hooks.processDatapoints.push(processThresholds); |
1473 | + } |
1474 | + |
1475 | + $.plot.plugins.push({ |
1476 | + init: init, |
1477 | + options: options, |
1478 | + name: 'threshold', |
1479 | + version: '1.2' |
1480 | + }); |
1481 | +})(jQuery); |
1482 | |
1483 | === modified file 'dashboard_app/templates/dashboard_app/image-report.html' |
1484 | --- dashboard_app/templates/dashboard_app/image-report.html 2013-07-01 15:49:15 +0000 |
1485 | +++ dashboard_app/templates/dashboard_app/image-report.html 2013-07-17 17:43:24 +0000 |
1486 | @@ -4,6 +4,7 @@ |
1487 | {{ block.super }} |
1488 | <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-report.css"/> |
1489 | <script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/image-report.js"></script> |
1490 | +<<<<<<< TREE |
1491 | <script src="{{ STATIC_URL }}dashboard_app/js/excanvas.min.js"></script> |
1492 | <script src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.min.js"></script> |
1493 | <script src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.dashes.min.js"></script> |
1494 | @@ -15,10 +16,23 @@ |
1495 | test_names = $.parseJSON($('<div/>').html("{{test_names}}").text()); |
1496 | columns = $.parseJSON($('<div/>').html("{{columns}}").text()); |
1497 | </script> |
1498 | +======= |
1499 | +<script type="text/javascript" src="{{ STATIC_URL }}lava-server/js/jquery.dataTables.min.js"></script> |
1500 | +<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.min.js"></script> |
1501 | +<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.stack.min.js"></script> |
1502 | +<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.resize.js"></script> |
1503 | +<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.dashes.js"></script> |
1504 | +<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.axislabels.js"></script> |
1505 | +<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.threshold.js"></script> |
1506 | +<!-- cp jquery.flot.resize.js /var/www/lava-server/static/dashboard_app/js/ --> |
1507 | +<!-- cp jquery.flot.dashes.js /var/www/lava-server/static/dashboard_app/js/ --> |
1508 | +<!-- cp jquery.flot.axislabels.js /var/www/lava-server/static/dashboard_app/js/ --> |
1509 | +>>>>>>> MERGE-SOURCE |
1510 | {% endblock %} |
1511 | |
1512 | {% block content %} |
1513 | <h1>Image Report: {{ image.name }}</h1> |
1514 | +<<<<<<< TREE |
1515 | |
1516 | |
1517 | <div id="outer-container"> |
1518 | @@ -83,6 +97,1272 @@ |
1519 | </div> |
1520 | |
1521 | |
1522 | +======= |
1523 | +<div style="display:inline-block;padding-left:20px;width:95%;"> |
1524 | +<div id="outer" style="width: 95%;"> |
1525 | + <div id="inner" style="width: 75%; margin: 0 auto; "> |
1526 | + <input type="radio" name=typeselect id="passfailcheck" onclick="exposeList()" unchecked></input>Pass/Fail Graph |
1527 | + <input type="radio" name=typeselect id="kpicheck" onclick="exposeList()" checked></input>Measurement Graph |
1528 | + <div id="kpiinfo" style="display:inline">( |
1529 | + <input type="checkbox" id="weightedcheck" onclick="exposeList()"></input>Weighted |
1530 | + <div id="weightedinfo" style="display:none"> |
1531 | + <input type="checkbox" id="weightedonly" onclick="draw()"></input>Only Goal |
1532 | + </div> |
1533 | +) |
1534 | + <div style=float:right> <input type="checkbox" id="adjustedcheck" onclick="draw()"></input>Graph Adjusted Values</div> |
1535 | + </div> |
1536 | + </div> |
1537 | +</div> |
1538 | + |
1539 | +<div id="error" style=color:#FF0000;> |
1540 | +</div> |
1541 | +<br> |
1542 | +<div id="placeholder" style="width:90%;height:250px;float:left;"> |
1543 | +</div> |
1544 | +<div id="legendtitle" style="width:9%;float:right;"><b>Legend</b></div> |
1545 | +<div id="labeler" style="height:180px;width:9%;float:right;overflow:auto;border:1px solid gray;"> |
1546 | +</div> |
1547 | +<div id="legendhelp" style="width:9%;float:right;border:1px solid gray;""><p style="font-size:11px"> -Solid lines are values<br> -Dashed lines are goals</p> |
1548 | +</div> |
1549 | +<br> |
1550 | +<select id="startDate" onchange="draw()" style="display:inline;width:15%"> |
1551 | + <option>Start Date</option> |
1552 | +</select> |
1553 | +<select id="endDate" onchange="draw()" style="display:inline;width:15%"> |
1554 | + <option>End Date</option> |
1555 | +</select> |
1556 | +<select id="startImage" onchange="draw()" style="display:none;width:15%"> |
1557 | + <option>Start Image</option> |
1558 | +</select> |
1559 | +<select id="endImage" onchange="draw()" style="display:none;width:15%"> |
1560 | + <option>End Image</option> |
1561 | +</select> |
1562 | +<br> |
1563 | +<input type="checkbox" id="imagecheck" style="float:left" onclick="changeType()"></input>Graph by Image |
1564 | +<br> |
1565 | +<input type="checkbox" id="cbChoices" style="float:left;" onclick="exposeList()"></input>Choose Tests: |
1566 | +<div id="goal_dates_label" style="float:right;width:15%"><div id="goal_dates_label" style="float:left"><b>Project Goals</b></div></div> |
1567 | +<br> |
1568 | +<div id="rows" style="height:120px;width:30%;overflow:auto;border:1px solid blue;display:none"> |
1569 | +</div> |
1570 | +<div id="testcases" style="height:120px;width:30%;overflow:auto;border:1px solid blue;float:left;display:none"> |
1571 | +</div> |
1572 | +<div id="goal_dates" style="height:160px;width:15%;overflow:auto;border:1px solid #ff4400;display:inline;float:right"> |
1573 | +</div> |
1574 | +<div id="kpi" style="height:120px;width:20%;overflow:auto;border:1px solid green;display:none"> |
1575 | + <b><label for="kpi" id="kpilabel" >KPI Data</label></b><br> |
1576 | + Target:    <input type="text" id="kpitarget" style="width:50px" readonly></input> |
1577 | + <br> |
1578 | + Floor:   <input type="text" id="kpifloor" style="width:50px" readonly></input> |
1579 | + <br> |
1580 | + Weight:   <input type="text" id="kpiweight" style="width:50px" readonly></input> |
1581 | + <br> |
1582 | + Goal(XX%) <input type="text" id="kpigoal" style="width:50px" readonly></input> |
1583 | +</div> |
1584 | +<br> |
1585 | +Link: <input id="permalink" type="text" style="width:75%"></input> |
1586 | +<br> |
1587 | +<input type="checkbox" id="cbHelp" style="float:left;" onclick="exposeHelp()"></input><b>Display Help</b><br> |
1588 | +<div id="help" style="height:80px;width:80%;overflow:auto;border:1px solid black;display:none"> |
1589 | + <b>Pass/Fail Graph:</b> Displays a Pass Fail graph for the current Image Report.<br> |
1590 | + <b> Choose Tests:</b> If this box is unchecked, the graph displays a cumulative graph. Otherwise you can select individual tests to graph.<br> |
1591 | + <b>KPI Graph:</b> Displays a graph based on the measurements of the selected test cases. These are multiple ways to display the information.<br> <u>Note</u>: If no options are checked, displays the measurement values of the testcases over time.<br> |
1592 | + <b> Weighted:</b> Displays a bar graph which aggregates the selected testcases into a percent. Calculated by averaging the adjusted value multiplied by the testcases weight.<br> |
1593 | + <b> Only Goal:</b> Removes the individual test lines to display only the weighted bar graph.<br> |
1594 | + <b> Graph Adjusted Values:</b> Displays the measurements as a percent, calculated using the range between the target and floor.<br> |
1595 | + <b>Graph By Image:</b> Allows you to graph using image names, instead of dates.<br> |
1596 | + <b>Link:</b> A URL link to this page with current settings. |
1597 | +</div> |
1598 | +<script type="text/javascript"> |
1599 | + |
1600 | +if (document.URL.indexOf("?") != -1) { |
1601 | + var baseurl=document.URL.slice(0,document.URL.lastIndexOf("?")); |
1602 | + var url_options = document.URL.slice(document.URL.lastIndexOf("?")+1,document.URL.length+1); |
1603 | +} |
1604 | +else { |
1605 | + var baseurl = document.URL; |
1606 | + var url_options = ''; |
1607 | +} |
1608 | + |
1609 | +function zip() { |
1610 | + var args = [].slice.call(arguments); |
1611 | + var shortest = args.length==0 ? [] : args.reduce(function(a,b){ |
1612 | + return a.length<b.length ? a : b |
1613 | + }); |
1614 | + |
1615 | + return shortest.map(function(_,i){ |
1616 | + return args.map(function(array){return array[i]}) |
1617 | + }); |
1618 | +}; |
1619 | + |
1620 | +function trim (str) { |
1621 | + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); |
1622 | +} |
1623 | + |
1624 | +function unzip(a) { |
1625 | + var b = []; |
1626 | + var c = []; |
1627 | + for (var i=0;i<a.length;i++) { |
1628 | + b.push(a[i][0]); |
1629 | + c.push(a[i][1]); |
1630 | + } |
1631 | + return [b,c]; |
1632 | +}; |
1633 | + |
1634 | +function doublesort(a, b, func) { |
1635 | + var c = zip(a,b); |
1636 | + c = c.sort(function(a,b) { if(a[0][2] > b[0][2]){return 1}else{return -1} } ); |
1637 | + return unzip(c); |
1638 | +} |
1639 | + |
1640 | +function pare(a, m) { |
1641 | + var b = new Array(m); |
1642 | + var n2 = a.length - 2; |
1643 | + var m2 = m - 2; |
1644 | + b[0] = a[0]; |
1645 | + rem = Math.floor(n2/m2) |
1646 | + for (var i=0;i<(n2/rem)+1;i++) { |
1647 | + b[i]=a[i*rem]; |
1648 | + } |
1649 | + b[b.length] = a[n2+1]; |
1650 | + diff = b[1][0] - b[0][0]; |
1651 | + for (var i=6;i<b.length;i++){ |
1652 | + try { |
1653 | + if (b[i][0] - b[i-1][0] != diff) { |
1654 | + b.splice(i,1); |
1655 | + i--; |
1656 | + } |
1657 | + } |
1658 | + catch(err){ |
1659 | + b.splice(i,1); |
1660 | + i--; |
1661 | + } |
1662 | + } |
1663 | + return b; |
1664 | +} |
1665 | + |
1666 | +function rainbow(numOfSteps, step) { |
1667 | + var r, g, b; |
1668 | + var h = step / numOfSteps; |
1669 | + var i = ~~(h * 5); |
1670 | + var f = h * 8 - i; |
1671 | + var q = 1 - f; |
1672 | + switch(i % 6){ |
1673 | + case 0: r = q, g = 0, b = 0; break; |
1674 | + case 1: r = 0, g = q, b = 0; break; |
1675 | + case 2: r = 0, g = q, b = 1; break; |
1676 | + case 3: r = 1, g = q, b = 0; break; |
1677 | + case 4: r = q, g = 1, b = 0; break; |
1678 | + case 5: r = 1, g = q, b = 0; break; |
1679 | + } |
1680 | + var c = "#" + ("00" + (~ ~(r * 255)).toString(16)).slice(-2) + ("00" + (~ ~(g * 255)).toString(16)).slice(-2) + ("00" + (~ ~(b * 255)).toString(16)).slice(-2); |
1681 | + return (c); |
1682 | +} |
1683 | + |
1684 | +function contains(a, obj, gen) { |
1685 | + for (var i = 0; i < a.length; i++) { |
1686 | + if (gen) { |
1687 | +} if (a[i].toString().substr(0,a[i].toString().lastIndexOf(',')) === obj.toString()) { |
1688 | + return true; |
1689 | + } |
1690 | + else { |
1691 | + if (a[i].toString() === obj.toString()) { |
1692 | + return true; |
1693 | + } |
1694 | + } |
1695 | + } |
1696 | + return false; |
1697 | +}; |
1698 | + |
1699 | +function allimages(start, end) { |
1700 | + var images= []; |
1701 | + var counter=[] |
1702 | + {% for iimage in pfreport.images %} |
1703 | + images.push([{{forloop.counter0}},'{{iimage}}']); |
1704 | + {% endfor %} |
1705 | + tstart=start; |
1706 | + tend=end; |
1707 | + if (tstart == 0) { |
1708 | + tstart = 1; |
1709 | + } |
1710 | + if (tend == 0) { |
1711 | + tend = images.length; |
1712 | + } |
1713 | + if (tstart > tend) { |
1714 | + tend = images.length; |
1715 | + tstart = 1; |
1716 | + var div = document.getElementById("error"); |
1717 | + div.textContent = "Invalid image Range: Start > End"; |
1718 | + } |
1719 | + else { |
1720 | + var div = document.getElementById("error"); |
1721 | + div.textContent = ""; |
1722 | + } |
1723 | + images = images.slice(tstart-1,tend); |
1724 | + return [images,tstart-1,tend]; |
1725 | +}; |
1726 | + |
1727 | +function alldates(start, end) { |
1728 | + var ddates = []; |
1729 | + var counter=[]; |
1730 | + count = 0; |
1731 | + {% for ddate in pfreport.ddates %} |
1732 | + ddates.push([count,'{{ddate}}']); |
1733 | + count++; |
1734 | + {% endfor %} |
1735 | +// This pushes the goalddates to the dates, worth it? |
1736 | +/* |
1737 | + {% for tag, value in goaldates.items %} |
1738 | + ddates.push([count, '{{value.startdate}}']) |
1739 | + ddates.push([count, '{{value.enddate}}']) |
1740 | + count++ |
1741 | + {% endfor %} |
1742 | +*/ |
1743 | + tstart=start; |
1744 | + tend=end; |
1745 | + if (tstart == 0) { |
1746 | + tstart = 1; |
1747 | + } |
1748 | + if (tend == 0) { |
1749 | + tend = ddates.length; |
1750 | + } |
1751 | + if (tstart > tend) { |
1752 | + tend = ddates.length; |
1753 | + tstart = 1; |
1754 | + var div = document.getElementById("error"); |
1755 | + div.textContent = "Invalid date Range: Start > End"; |
1756 | + } |
1757 | + else { |
1758 | + var div = document.getElementById("error"); |
1759 | + div.textContent = ""; |
1760 | + } |
1761 | + ddates = ddates.slice(tstart-1,tend); |
1762 | + return [ddates,tstart-1,tend]; |
1763 | +}; |
1764 | + |
1765 | +function rownames() { |
1766 | + var rrow=[]; |
1767 | + {% for row in test_run_names %} |
1768 | + rrow.push(['{{row}}']); |
1769 | + {% endfor %} |
1770 | + return rrow; |
1771 | +}; |
1772 | + |
1773 | +function casenames() { |
1774 | + var rrow=[]; |
1775 | + {% for row, value in kpireport.items %} |
1776 | + rrow.push(['{{row}}', '{{value.units}}']); |
1777 | + {% endfor %} |
1778 | + return rrow; |
1779 | +}; |
1780 | + |
1781 | +function goalaxis(tstart, tend, datelocation) { |
1782 | + if (document.getElementById('imagecheck').checked == true) { |
1783 | + tstart = -1; |
1784 | + tend = -1; |
1785 | + for (var i=0; i<datelocation.length; i++){ |
1786 | + if (datelocation[i].length > 0) { |
1787 | + if (tstart == -1) { |
1788 | + tstart = datelocation[i][1]; |
1789 | + } |
1790 | + tend = datelocation[i][1]; |
1791 | + } |
1792 | + } |
1793 | + } |
1794 | + var alldate = alldates(tstart, tend)[0]; |
1795 | + var ret_array = []; |
1796 | + var goal_dates = []; |
1797 | + {% for tag, value in goaldates.items %} |
1798 | + goal_dates.push(['{{tag}}', '{{value.startdate}}', '{{value.enddate}}']); |
1799 | + ret_array.push(['{{tag}}',[], '{{value.value}}']); |
1800 | + {% endfor %} |
1801 | + ret = doublesort(goal_dates, ret_array, function(a,b) { return a[2] > b[2] } ) |
1802 | + goal_dates = ret[0]; |
1803 | + ret_array = ret[1]; |
1804 | + for (var all=0;all<alldate.length;all++) { |
1805 | + for (var gl=0;gl<goal_dates.length;gl++) { |
1806 | + // if the date falls within the tag range... |
1807 | + // push the location and the tag |
1808 | + if (alldate[all][1] >= goal_dates[gl][1] && alldate[all][1] <= goal_dates[gl][2]) { |
1809 | + if (document.getElementById('imagecheck').checked == true) { |
1810 | + ret_array[gl][1].push([all, ret_array[gl][2]]); |
1811 | + } |
1812 | + else { |
1813 | + ret_array[gl][1].push([alldate[all][0], ret_array[gl][2]]); |
1814 | + } |
1815 | + } |
1816 | + } |
1817 | + } |
1818 | + |
1819 | + // Make the dates overlap a little |
1820 | + var premx =-100; |
1821 | + var mxtag=1; |
1822 | + var around = false |
1823 | + for (var tag=0;tag<ret_array.length;tag++) { |
1824 | + var mn=9999999; |
1825 | + var mx =-100; |
1826 | + if (ret_array[tag][1].length > 0) { |
1827 | + for (var date=0;date<ret_array[tag][1].length;date++) { |
1828 | + if (ret_array[tag][1][date][0] < mn) { |
1829 | + mn =ret_array[tag][1][date][0]; |
1830 | + } |
1831 | + if (ret_array[tag][1][date][0] > mx) { |
1832 | + mx =ret_array[tag][1][date][0]; |
1833 | + } |
1834 | + } |
1835 | + if (mn != premx && around == true) { |
1836 | + ret_array[tag][1].push([mn-1, ret_array[tag][2]]); |
1837 | + } |
1838 | + |
1839 | + premx = mx; |
1840 | + mxtag = tag; |
1841 | + around = true |
1842 | + } |
1843 | + } |
1844 | + ret_array[mxtag][1].push([premx+1, ret_array[mxtag][2]]); |
1845 | + return ret_array; |
1846 | +} |
1847 | + |
1848 | +function allgraph(ddates) { |
1849 | + image = ddates[0]; |
1850 | + images = []; |
1851 | + for (var i=0;i<image.length;i++){ |
1852 | + images.push(image[i][1]); |
1853 | + } |
1854 | + tstart = ddates[1]; |
1855 | + tend = ddates[2]; |
1856 | + axis = []; |
1857 | + if (document.getElementById('imagecheck').checked == true) { |
1858 | + var treportpass = {{pfreport.dpass}}; |
1859 | + var reportpass = []; |
1860 | + imagelist = {{pfreport.imagelist|safe}}; |
1861 | + count = 0; |
1862 | + for (var i=0;i<treportpass.length;i++) { |
1863 | + if (contains(images, imagelist[i], false) && (imagelist[i] != '')) { |
1864 | + reportpass[count] = treportpass[i][1]; |
1865 | + axis.push([count, imagelist[i]]); |
1866 | + count++; |
1867 | + } |
1868 | + } |
1869 | + var zipper = []; |
1870 | + for (var c=0; c<reportpass.length;c++) { |
1871 | + zipper.push(c); |
1872 | + } |
1873 | + reportpass = zip(zipper, reportpass); |
1874 | + var treportfail = {{pfreport.dfail}}; |
1875 | + var reportfail = []; |
1876 | + count = 0; |
1877 | + for (var i=0;i<treportfail.length;i++) { |
1878 | + if (contains(images, imagelist[i], false) && (imagelist[i] != '')) { |
1879 | + reportfail[count] = treportfail[i][1]; |
1880 | + count++; |
1881 | + } |
1882 | + } |
1883 | + reportfail = zip(zipper, reportfail); |
1884 | + } |
1885 | + else { |
1886 | + reportpass = {{pfreport.dpass}}; |
1887 | + reportpass = reportpass.slice(tstart, tend); |
1888 | + reportfail = {{pfreport.dfail}}; |
1889 | + reportfail = reportfail.slice(tstart, tend); |
1890 | + axis = ddates[0]; |
1891 | + } |
1892 | + var reporttotalfails = 0; |
1893 | + for (var i=0; i < reportfail.length; i++) { |
1894 | + reporttotalfails = reporttotalfails + reportfail[i][1]; |
1895 | + } |
1896 | + var reporttotalpasses = 0; |
1897 | + for (var i=0; i < reportpass.length; i++) { |
1898 | + reporttotalpasses = reporttotalpasses + reportpass[i][1]; |
1899 | + } |
1900 | + total = reporttotalpasses+reporttotalfails; |
1901 | + var reportpercentpasses = parseInt((reporttotalpasses / total) *100); |
1902 | + var reportpercentfails = parseInt((reporttotalfails / total) *100); |
1903 | + var show = (reportfail.length == 1); |
1904 | + var ddata = [ |
1905 | + { |
1906 | + 'label': '<br><big><b>Fail: ' + reporttotalfails + '</b><br>' + reportpercentfails + '% failed.</big><br>', |
1907 | + 'data': reportfail, |
1908 | + 'color': '#FF0000', |
1909 | + 'lines': { show: true }, |
1910 | + 'points': { show: show }, |
1911 | + }, |
1912 | + { |
1913 | + 'label': '<br><big><b>Pass: ' + reporttotalpasses + '</b><br>' + reportpercentpasses + '% passed.</big><br>', |
1914 | + 'data': reportpass, |
1915 | + 'color': '#00FF00', |
1916 | + 'lines': { show: true }, |
1917 | + 'points': { show: show }, |
1918 | + }, |
1919 | + ]; |
1920 | + return [ddata, axis]; |
1921 | +}; |
1922 | + |
1923 | +function adjustvalues(measures, i, target, flr, wgt) { |
1924 | + var rvrs = flr > target; // little numbers are better |
1925 | + var range = Math.abs(target-flr) |
1926 | + var measures_copy = [] |
1927 | + for (var i=0;i<measures.length;i++) |
1928 | + measures_copy[i] = measures[i]; |
1929 | + for (var j=0;j<measures.length;j++) { |
1930 | + if (measures[j] == null) { |
1931 | + measures_copy[j]=null; |
1932 | + } |
1933 | + else if (rvrs == true && measures[j] > flr) { |
1934 | + measures_copy[j] = 0; |
1935 | + } |
1936 | + else if (rvrs == true && measures[j] < target) { |
1937 | + measures_copy[j] = 100; |
1938 | + } |
1939 | + else if (rvrs == false && measures[j] > target) { |
1940 | + measures_copy[j] = 100; |
1941 | + } |
1942 | + else if (rvrs == false && measures[j] < flr) { |
1943 | + measures_copy[j] = 0; |
1944 | + } |
1945 | + else { |
1946 | + adjustednumber = Math.abs(measures[j]-flr); |
1947 | + measures_copy[j]=Math.ceil((adjustednumber/range)*100); |
1948 | + } |
1949 | + } |
1950 | + return measures_copy; |
1951 | +} |
1952 | + |
1953 | +function averagevals(measures){ |
1954 | + function average_val(arr, start, end, startval, endval) { |
1955 | + if ((start != end) && (start != end-1)) { |
1956 | + if (startval == null) { |
1957 | + for (var i=start+1; i < end; i++){ |
1958 | + arr[i] = null; |
1959 | + } |
1960 | + } |
1961 | + else { |
1962 | + var points = end - start; |
1963 | + var range = endval - startval; |
1964 | + var change = range / points; |
1965 | + var count = 0; |
1966 | + for (var i=start+1; i < end; i++){ |
1967 | + count++; |
1968 | + if (i == 0) { |
1969 | + arr[i] = parseFloat(startval)+change; |
1970 | + } |
1971 | + else { |
1972 | + arr[i] = parseFloat(arr[i-1])+change; |
1973 | + } |
1974 | + } |
1975 | + } |
1976 | + } |
1977 | + return arr; |
1978 | + }; |
1979 | + |
1980 | + var startval = 0; // value at start |
1981 | + var endval = 0; // value at end |
1982 | + var start = -1; // location |
1983 | + var end = -1; // location |
1984 | + for (var num = 0; num < measures.length; num++) { |
1985 | + if (measures[num] == -1) { |
1986 | + if (num == measures.length-1){ |
1987 | + end = num+1; |
1988 | + startval = null; |
1989 | + measures = average_val(measures, start, end, startval, endval); |
1990 | + } |
1991 | + } |
1992 | + else { //value != -1 |
1993 | + // first num you find, make all -1 before = val |
1994 | + if (startval == 0 && endval == 0) { |
1995 | + startval = null; |
1996 | + } |
1997 | + end = num; |
1998 | + endval = parseFloat(measures[num]); |
1999 | + measures = average_val(measures, start, end, startval, endval); |
2000 | + start = num; |
2001 | + startval = parseFloat(measures[num]); |
2002 | + } |
2003 | + } |
2004 | + return measures; |
2005 | +} |
2006 | + |
2007 | +function kpigraph(pts, tests) { |
2008 | + var tstart = -1; |
2009 | + var tend = -1; |
2010 | + var ddata = []; |
2011 | + var weighteddata = []; |
2012 | + var sumweights = []; |
2013 | + var image = pts[0]; |
2014 | + var images = []; |
2015 | + var label = []; |
2016 | + var dates = []; |
2017 | + for (var i=0;i<image.length;i++){ |
2018 | + images.push(image[i][1]); |
2019 | + } |
2020 | + adjusted = document.getElementById("adjustedcheck").checked; |
2021 | + weighted = document.getElementById("weightedcheck").checked; |
2022 | + var i=-1; |
2023 | + var axis = []; |
2024 | + var needaxis = true; |
2025 | + var init = false; |
2026 | + {% for test,value in kpireport.items %} |
2027 | + i++; |
2028 | + var tgt; |
2029 | + var gl; |
2030 | + var wgt; |
2031 | + var flr; |
2032 | + if ('{{value.target}}'=='None') tgt = 0; |
2033 | + else tgt= {{value.target}}; |
2034 | + if ('{{value.goal}}'=='None') gl = 0; |
2035 | + else gl= {{value.goal}}; |
2036 | + if ('{{value.weight}}'=='None') wgt = 0; |
2037 | + else wgt= {{value.weight}}; |
2038 | + if ('{{value.flr}}'=='None') flr = 0; |
2039 | + else flr= {{value.flr}}; |
2040 | + |
2041 | + glo_casedata[i][0] = tgt; |
2042 | + glo_casedata[i][1] = flr; |
2043 | + glo_casedata[i][2] = wgt; |
2044 | + glo_casedata[i][3] = gl; |
2045 | + var measures =[]; |
2046 | + if (axis.length > 0) { |
2047 | + needaxis=false; |
2048 | + } |
2049 | + // its one of the selected test cases! |
2050 | + if (contains(tests,"{{test}}", true)) { |
2051 | + if (document.getElementById('imagecheck').checked == true) { |
2052 | + imagelist = {{pfreport.imagelist|safe}}; |
2053 | + var tmeasures= new Array{{value.measurement|safe}}; |
2054 | + count = 0; |
2055 | + for (var k=0;k<tmeasures.length;k++) { |
2056 | + if (contains(images, imagelist[k], false) && (imagelist[k] != '')) { |
2057 | + measures[count] = tmeasures[k]; |
2058 | + if (needaxis == true) { |
2059 | + tstart = 0; |
2060 | + tend = count+1; |
2061 | + axis.push([count, imagelist[k]]); |
2062 | + } |
2063 | + count++; |
2064 | + dates.push([count, k]); |
2065 | + } |
2066 | + else { |
2067 | + dates.push([]); |
2068 | + } |
2069 | + } |
2070 | + } |
2071 | + else { |
2072 | + // Grab the measurements::Add the (0,#,1,#) with a zip |
2073 | + measures = new Array{{value.measurement|safe}}; |
2074 | + for (var q=0;q<measures.length;q++){ |
2075 | + dates.push(true); |
2076 | + } |
2077 | + axis = pts[0]; |
2078 | + tstart = pts[1]; |
2079 | + tend = pts[2]; |
2080 | + } |
2081 | + measures = averagevals(measures); |
2082 | + if (weighteddata.length == 0) { |
2083 | + for (var k=0; k<measures.length;k++) { |
2084 | + weighteddata.push(0); |
2085 | + sumweights.push(0); |
2086 | + } |
2087 | + } |
2088 | + // If they want to % adjusted values |
2089 | + goalmeasures = adjustvalues(measures, i, tgt, flr, wgt); |
2090 | + if (adjusted == true) { |
2091 | + measures = adjustvalues(measures, i, tgt, flr, wgt); |
2092 | + } |
2093 | + for (var k=0; k<measures.length;k++) { |
2094 | + if (wgt != 0) { |
2095 | + if (goalmeasures[k] == null) { |
2096 | + // ignore it, don't update sumweights |
2097 | + } |
2098 | + else { |
2099 | + weighteddata[k] += goalmeasures[k]*wgt; |
2100 | + sumweights[k] += wgt; |
2101 | + } |
2102 | + } |
2103 | + } |
2104 | + //Lets save the data to graph the weighted graph |
2105 | + |
2106 | + var zipper = []; |
2107 | + for (var c=0; c<measures.length;c++) { |
2108 | + zipper.push(c); |
2109 | + } |
2110 | + measures = zip(zipper, measures); |
2111 | + measures = measures.slice(tstart, tend); |
2112 | + goalmeasures = zip(zipper, goalmeasures); |
2113 | + goalmeasures = goalmeasures.slice(tstart, tend); |
2114 | + var color = rainbow(5, i); |
2115 | + len = 0 |
2116 | + for (var m=0;m<measures.length;m++){ |
2117 | + if (measures[m][1] != null) { |
2118 | + len++; |
2119 | + } |
2120 | + } |
2121 | + var show = (len <= 1); |
2122 | + ddata.push( |
2123 | + { |
2124 | + 'label': "{{test}}", |
2125 | + 'data': measures, |
2126 | + 'color': color, |
2127 | + 'lines': { show: true }, |
2128 | + 'points': { show: show }, |
2129 | + } |
2130 | + ); |
2131 | + label.push(false); |
2132 | + // Add the dashed goal line |
2133 | + if (adjusted == true && weighted == false) { |
2134 | + var kpitarget = []; |
2135 | + var count = -1; |
2136 | + for (var j=measures[0][0];j<=measures[measures.length-1][0];j++) { |
2137 | + count++; |
2138 | + if (gl != 0) { |
2139 | + if (measures[count][1] == null) { |
2140 | + kpitarget.push([j, null]); |
2141 | + } |
2142 | + else { |
2143 | + kpitarget.push([j, gl]); |
2144 | + } |
2145 | + } |
2146 | + } |
2147 | + |
2148 | + ddata.push( |
2149 | + { |
2150 | + 'data': kpitarget, |
2151 | + 'color': color, |
2152 | + 'dashes': {show: true, }, |
2153 | + 'opacity': .9, |
2154 | + 'points': {show: false }, |
2155 | + } |
2156 | + ); |
2157 | + label.push(false); |
2158 | + } |
2159 | + } |
2160 | + {% endfor %} |
2161 | + |
2162 | + //check if they want the single weighted graph |
2163 | + if (weighted == true) { |
2164 | + if (document.getElementById('weightedonly').checked == true) { |
2165 | + ddata = []; |
2166 | + label = []; |
2167 | + } |
2168 | + for (var wt = 0; wt < weighteddata.length; wt++) { |
2169 | + weighteddata[wt]= ((weighteddata[wt]/100)/sumweights[wt])*100; |
2170 | + } |
2171 | + if (weighteddata.length > 0 && '{{goalname}}' != 'None') { |
2172 | + goaltarget = goalaxis(tstart, tend, dates); |
2173 | + for (var gl=0;gl<goaltarget.length;gl++) { |
2174 | + if (goaltarget[gl][1].length > 0) { |
2175 | + currentdates = goaltarget[gl][1] |
2176 | + ret = unzip(goaltarget[gl][1]); |
2177 | + ret[0] = ret[0].sort(function(a,b){ if(a > b){return 1}else{return -1} } ); |
2178 | + goaltarget[gl][1] = zip(ret[0], ret[1]); |
2179 | + ddata.push({ |
2180 | + 'label': goaltarget[gl][0], |
2181 | + 'data': goaltarget[gl][1], |
2182 | + //CAN YOU MAKE THIS OPACQUE? |
2183 | + 'color': "#000000", |
2184 | + 'dashes': {show: true, lineWidth: 2.5, dashLength: 5}, |
2185 | + 'points': {show: false }, |
2186 | + 'yaxis': 2, |
2187 | + }); |
2188 | + label.push(true); |
2189 | + targetvalue = parseInt(currentdates[0][1]) |
2190 | + mn = 9999 |
2191 | + mx = -1 |
2192 | + for (var val = 0; val<currentdates.length;val++) { |
2193 | + if (currentdates[val][0] < mn) mn = currentdates[val][0] |
2194 | + if (currentdates[val][0] > mx) mx = currentdates[val][0] |
2195 | + } |
2196 | + var goalvalues = weighteddata.slice(mn, mx) |
2197 | + var zipper = []; |
2198 | + for (var c=mn; c<mx;c++) { |
2199 | + zipper.push(c); |
2200 | + } |
2201 | + goalvalues = zip(zipper, goalvalues); |
2202 | + ddata.push( |
2203 | + { |
2204 | + 'data': goalvalues, |
2205 | + 'color': "#555555", |
2206 | + 'bars': { show: true, barWidth: 0.8, align: "center", fillColor: { colors: [ { opacity: 0.5 }, { opacity: 0.2 } ] }}, |
2207 | + 'points': { show: false }, |
2208 | + 'yaxis': 2, |
2209 | + 'threshold': [{below: targetvalue-5, color:'#990000'},{below: 101, color:'#009900'}, {below:targetvalue, color:'#999900'}], |
2210 | + } |
2211 | + ); |
2212 | + } |
2213 | + } |
2214 | + } |
2215 | + else { // NO DATES GIVEN, JUST MAKE IT BLACK |
2216 | + var zipper = []; |
2217 | + for (var c=0; c<weighteddata.length;c++) { |
2218 | + zipper.push(c); |
2219 | + } |
2220 | + weighteddata = zip(zipper, weighteddata); |
2221 | + weighteddata = weighteddata.slice(tstart, tend); |
2222 | + ddata.push( |
2223 | + { |
2224 | + 'data': weighteddata, |
2225 | + 'color': "#555555", |
2226 | + 'bars': { show: true, barWidth: 0.8, align: "center", fillColor: { colors: [ { opacity: 0.4 }, { opacity: 0.1 } ] }}, |
2227 | + 'points': { show: false }, |
2228 | + 'yaxis': 2, |
2229 | + } |
2230 | + ); |
2231 | + label.push(false); |
2232 | + } |
2233 | + } |
2234 | + return [ddata,axis,label]; |
2235 | +}; |
2236 | + |
2237 | +function rowgraph(ddates, selectedrows){ |
2238 | + var dates = ddates[0]; |
2239 | + images = []; |
2240 | + for (var i=0;i<dates.length;i++){ |
2241 | + images.push(dates[i][1]); |
2242 | + } |
2243 | + var tstart = ddates[1]; |
2244 | + var tend = ddates[2]; |
2245 | + //Slice dictionary as needed..? |
2246 | + var ddata = []; |
2247 | + var axis = []; |
2248 | + var needaxis = true; |
2249 | + totalrows = rownames().length; |
2250 | + var selectnames = []; |
2251 | + for (var i=0; i<selectedrows.length;i++) { |
2252 | + selectnames.push(selectedrows[i][0]); |
2253 | + } |
2254 | + var m=-1; |
2255 | + var n=-1; |
2256 | + {% for value, row in rowreport.items %} |
2257 | + var passes =[]; |
2258 | + var fails =[]; |
2259 | + if (axis.length > 0) { |
2260 | + needaxis=false; |
2261 | + } |
2262 | + if (contains(selectnames, '{{value}}', true)) { |
2263 | + m++; |
2264 | + n++; |
2265 | + // graph this row |
2266 | + if (document.getElementById('imagecheck').checked == true) { |
2267 | + imagelist = {{pfreport.imagelist|safe}}; |
2268 | + |
2269 | + var tpasses=new Array({{row.pass}}); |
2270 | + var tfails=new Array({{row.fail}}); |
2271 | + tpasses = tpasses[0]; |
2272 | + tfails = tfails[0]; |
2273 | + |
2274 | + count = 0; |
2275 | + for (var i=0;i<tpasses.length;i++) { |
2276 | + if (contains(images, imagelist[i], false) && (imagelist[i] != '')) { |
2277 | + passes[count] = tpasses[i][1]; |
2278 | + if (needaxis == true) { |
2279 | + axis.push([count, imagelist[i]]); |
2280 | + } |
2281 | + count++; |
2282 | + } |
2283 | + } |
2284 | + var zipper = []; |
2285 | + for (var c=0; c<passes.length;c++) { |
2286 | + zipper.push(c); |
2287 | + } |
2288 | + passes = zip(zipper, passes); |
2289 | + |
2290 | + count = 0; |
2291 | + for (var i=0;i<tfails.length;i++) { |
2292 | + if (contains(images, imagelist[i], false) && (imagelist[i] != '')) { |
2293 | + fails[count] = tfails[i][1]; |
2294 | + count++; |
2295 | + } |
2296 | + } |
2297 | + fails = zip(zipper, fails); |
2298 | + } |
2299 | + else { |
2300 | + passes=new Array({{row.pass}}); |
2301 | + fails=new Array({{row.fail}}); |
2302 | + passes = passes[0]; |
2303 | + fails = fails[0]; |
2304 | + axis = ddates[0]; |
2305 | + fails = fails.slice(tstart, tend); |
2306 | + passes = passes.slice(tstart, tend); |
2307 | + } |
2308 | + //ddata it up |
2309 | + var failcolor = ''; |
2310 | + var passcolor = ''; |
2311 | + m = n%8; |
2312 | + if (m<4){ |
2313 | + failcolor = '#'+(15-m).toString(16)+(15-m).toString(16) |
2314 | + +(m*4).toString(16)+(m*4).toString(16) |
2315 | + +'00'; |
2316 | + passcolor = '#00' |
2317 | + +(15-m).toString(16)+(15-m).toString(16) |
2318 | + +(m*4).toString(16)+(m*4).toString(16); |
2319 | + } |
2320 | + else { |
2321 | + m = n%4; |
2322 | + failcolor = '#'+(15-m).toString(16)+(15-m).toString(16) |
2323 | + +'00' |
2324 | + +(m*4).toString(16)+(m*4).toString(16); |
2325 | + passcolor = '#'+(m*4).toString(16)+(m*4).toString(16) |
2326 | + +(15-m).toString(16)+(15-m).toString(16) |
2327 | + +'00'; |
2328 | + } |
2329 | + var show = (fails.length == 1); |
2330 | + ddata.push( |
2331 | + { |
2332 | + 'label': '{{value}}'+' Fails', |
2333 | + 'data': fails, |
2334 | + 'color': failcolor, |
2335 | + 'lines': { show: true }, |
2336 | + 'points': { show: show }, |
2337 | + } |
2338 | + ); |
2339 | + ddata.push( |
2340 | + { |
2341 | + 'label': '{{value}}'+' Passes', |
2342 | + 'data': passes, |
2343 | + 'color': passcolor, |
2344 | + 'lines': { show: true }, |
2345 | + 'points': { show: show }, |
2346 | + } |
2347 | + ); |
2348 | + } |
2349 | + {% endfor %} |
2350 | + return [ddata,axis]; |
2351 | +}; |
2352 | + |
2353 | +function exposeList() { |
2354 | + var status = document.getElementById('cbChoices').checked; |
2355 | + var passstatus = document.getElementById('passfailcheck').checked; |
2356 | + if (passstatus == true) { |
2357 | + document.getElementById('testcases').style.display = 'none'; |
2358 | + document.getElementById('kpi').style.display = 'none'; |
2359 | + document.getElementById('cbChoices').style.display = "block"; |
2360 | + document.getElementById('kpiinfo').style.display = "none"; |
2361 | + if (status == true) { |
2362 | + document.getElementById('rows').style.display = "block"; |
2363 | + } |
2364 | + else { |
2365 | + document.getElementById('rows').style.display = 'none'; |
2366 | + } |
2367 | + } |
2368 | + else { |
2369 | + document.getElementById('rows').style.display = 'none'; |
2370 | + document.getElementById('testcases').style.display = "block"; |
2371 | + document.getElementById('cbChoices').style.display = "none"; |
2372 | + document.getElementById('kpi').style.display = 'block'; |
2373 | + document.getElementById('kpiinfo').style.display = "inline"; |
2374 | + if (document.getElementById('weightedcheck').checked == true) { |
2375 | + document.getElementById('weightedinfo').style.display = 'inline'; |
2376 | + } |
2377 | + else { |
2378 | + document.getElementById('weightedinfo').style.display = 'none'; |
2379 | + } |
2380 | + } |
2381 | + draw(); |
2382 | +} |
2383 | + |
2384 | +function exposeHelp() { |
2385 | + var status = document.getElementById('cbHelp').checked; |
2386 | + if (status == true) { |
2387 | + document.getElementById('help').style.display = 'block'; |
2388 | + } |
2389 | + else { |
2390 | + document.getElementById('help').style.display = 'none'; |
2391 | + } |
2392 | +} |
2393 | + |
2394 | +function changeType() { |
2395 | + var status = document.getElementById('imagecheck').checked; |
2396 | + if (status == true) { |
2397 | + document.getElementById('startDate').style.display = 'none'; |
2398 | + document.getElementById('endDate').style.display = 'none'; |
2399 | + document.getElementById('startImage').style.display = 'inline'; |
2400 | + document.getElementById('endImage').style.display = 'inline'; |
2401 | + } |
2402 | + else { |
2403 | + document.getElementById('startDate').style.display = 'inline'; |
2404 | + document.getElementById('endDate').style.display = 'inline'; |
2405 | + document.getElementById('startImage').style.display = 'none'; |
2406 | + document.getElementById('endImage').style.display = 'none'; |
2407 | + } |
2408 | + draw(); |
2409 | +} |
2410 | + |
2411 | +function loadtext() { |
2412 | + glo_selectedkpi=this.id.match(/\d+/); |
2413 | + document.getElementById('kpilabel').innerHTML = this.value; |
2414 | + document.getElementById('kpitarget').value = glo_casedata[glo_selectedkpi][0]; |
2415 | + document.getElementById('kpifloor').value = glo_casedata[glo_selectedkpi][1]; |
2416 | + document.getElementById('kpiweight').value = glo_casedata[glo_selectedkpi][2]; |
2417 | + document.getElementById('kpigoal').value = glo_casedata[glo_selectedkpi][3]; |
2418 | + draw(); |
2419 | +} |
2420 | + |
2421 | +function updateURL(casename, rowname) { |
2422 | + var url = baseurl; |
2423 | + url += '?sets={{setname}}&var='; |
2424 | + for (var i=0;i<=6;i++){ |
2425 | + if ($("input").get(i).checked == true){ |
2426 | + url += i+","; |
2427 | + } |
2428 | + } |
2429 | + url += '&kpis='; |
2430 | + for (var i=0; i < casename.length; i++) { |
2431 | + if (document.getElementById("case"+i) != null) { |
2432 | + if (document.getElementById("case"+i).checked == true) { |
2433 | + url += casename[i][0]+","; |
2434 | + } |
2435 | + } |
2436 | + } |
2437 | + url += '&rows='; |
2438 | + for (var i=0; i < rowname.length; i++) { |
2439 | + if (document.getElementById("row"+i) != null) { |
2440 | + if (document.getElementById("row"+i).checked == true) { |
2441 | + url += rowname[i][0]+","; |
2442 | + } |
2443 | + } |
2444 | + } |
2445 | + url += '&select='; |
2446 | + for (var i=0;i<=3;i++){ |
2447 | + url += $("select").get(i).selectedIndex+"," |
2448 | + } |
2449 | + $("#permalink")[0].value = url; |
2450 | + |
2451 | +} |
2452 | + |
2453 | +function draw() { |
2454 | + var ddata = []; |
2455 | + var val = []; |
2456 | + var labels = []; |
2457 | + if (document.getElementById('imagecheck').checked == true) { |
2458 | + var startd=document.getElementById("startImage"); |
2459 | + var endd=document.getElementById("endImage"); |
2460 | + var start = startd.options[startd.selectedIndex].index; |
2461 | + var end = endd.options[endd.selectedIndex].index; |
2462 | + var axis = allimages(start,end); |
2463 | + } |
2464 | + else { |
2465 | + var startd=document.getElementById("startDate"); |
2466 | + var endd=document.getElementById("endDate"); |
2467 | + var start = startd.options[startd.selectedIndex].index; |
2468 | + var end = endd.options[endd.selectedIndex].index; |
2469 | + var axis = alldates(start,end); |
2470 | + } |
2471 | + var rowstatus = document.getElementById('cbChoices').checked; |
2472 | + var passfailstatus = document.getElementById('passfailcheck').checked; |
2473 | + var selectedrows = []; |
2474 | + var kpi = false; |
2475 | + var casename = casenames(); |
2476 | + var rowname = rownames(); |
2477 | + if (passfailstatus == false) { |
2478 | + for (var i=0; i < casename.length; i++) { |
2479 | + if (document.getElementById("case"+i) != null) { |
2480 | + if (document.getElementById("case"+i).checked == true) { |
2481 | + selectedrows.push([casename[i][0], i]); |
2482 | + } |
2483 | + } |
2484 | + } |
2485 | + val = kpigraph(axis, selectedrows); |
2486 | + ddata = val[0]; |
2487 | + axis = val[1]; |
2488 | + labels = val[2]; |
2489 | + } |
2490 | + else { |
2491 | + if (rowstatus == true) { |
2492 | + selectedrows = []; |
2493 | + for (var i=0; i < rowname.length; i++) { |
2494 | + if (document.getElementById("row"+i).checked == true) { |
2495 | + selectedrows.push([rowname[i], i]); |
2496 | + } |
2497 | + } |
2498 | + if (selectedrows.length != 0) { |
2499 | + val = rowgraph(axis, selectedrows); |
2500 | + ddata = val[0]; |
2501 | + axis = val[1]; |
2502 | + } |
2503 | + else { |
2504 | + val = allgraph(axis); |
2505 | + ddata = val[0]; |
2506 | + axis = val[1]; |
2507 | + } |
2508 | + } |
2509 | + else { |
2510 | + val = allgraph(axis); |
2511 | + ddata = val[0]; |
2512 | + axis = val[1]; |
2513 | + } |
2514 | + } |
2515 | + updateURL(casename,rowname); |
2516 | + // Make the dates not look terrible.. |
2517 | + var goal = 10; |
2518 | + axispoints = axis; |
2519 | + if (axispoints.length > goal) { |
2520 | + axispoints = pare(axispoints, goal); |
2521 | + } |
2522 | + |
2523 | + if (document.getElementById('adjustedcheck').checked == true && document.getElementById('kpicheck').checked == true) { |
2524 | + p = $.plot($("#placeholder"), ddata, { |
2525 | + xaxis: { |
2526 | + ticks: axispoints, |
2527 | + }, |
2528 | + yaxis: { |
2529 | + min: 0, |
2530 | + max: 100, |
2531 | + autoscaleMargin: false, |
2532 | + axisLabel: 'Percent', |
2533 | + }, |
2534 | + y2axis: { |
2535 | + min: 0, |
2536 | + max: 100, |
2537 | + autoscaleMargin: false, |
2538 | + axisLabel: 'Weighted Percent', |
2539 | + }, |
2540 | + legend:{ |
2541 | + container: $("#labeler") |
2542 | + }, |
2543 | + }); |
2544 | + } |
2545 | + else if (document.getElementById('kpicheck').checked == true){ |
2546 | + p = $.plot($("#placeholder"), ddata, { |
2547 | + xaxis: { |
2548 | + ticks: axispoints, |
2549 | + }, |
2550 | + yaxis: { |
2551 | + axisLabel: 'Units', |
2552 | + }, |
2553 | + y2axis: { |
2554 | + min: 0, |
2555 | + max: 100, |
2556 | + autoscaleMargin: false, |
2557 | + axisLabel: 'Weighted Percent', |
2558 | + }, |
2559 | + legend:{ |
2560 | + container: $("#labeler") |
2561 | + }, |
2562 | + }); |
2563 | + } |
2564 | + else { |
2565 | + p = $.plot($("#placeholder"), ddata, { |
2566 | + xaxis: { |
2567 | + ticks: axispoints, |
2568 | + }, |
2569 | + yaxis: { |
2570 | + axisLabel: 'Units', |
2571 | + }, |
2572 | + y2axis: { |
2573 | + min: 0, |
2574 | + max: 100, |
2575 | + autoscaleMargin: false, |
2576 | + axisLabel: 'Weighted Percent', |
2577 | + }, |
2578 | + legend:{ |
2579 | + container: $("#labeler") |
2580 | + }, |
2581 | + }); |
2582 | + } |
2583 | + // Add the labels to the weighted graph |
2584 | + var count = -1; |
2585 | + if (p.getData().length > 0) { |
2586 | + $.each(p.getData(), function(i, row){ |
2587 | + if (row.label) { |
2588 | + count++; |
2589 | + if (labels[count] == true) { |
2590 | + tlabel = row.label; |
2591 | + row.label = ''; |
2592 | + if (row.data.length > 1) { |
2593 | + var o = p.pointOffset({x: row.data[0][0], y: row.data[0][1], yaxis: 2}); |
2594 | + $('<div class="data-point-label">' +'<b><font size=3>'+ tlabel+ '</font></b>' + '</div>').css( { |
2595 | + position: 'absolute', |
2596 | + left: o.left + 2, |
2597 | + top: o.top - 23, |
2598 | + display: 'none', |
2599 | + escapeHTML: 'false', |
2600 | + border: '1.5px solid #aaaaaa', |
2601 | + background: '#aaccee', |
2602 | + opacity: '0.9', |
2603 | + }).appendTo(p.getPlaceholder()).fadeIn(0); |
2604 | + } |
2605 | + } |
2606 | + } |
2607 | + }); |
2608 | + p.setupGrid(); |
2609 | + } |
2610 | +}; |
2611 | + |
2612 | +var glo_selectedkpi = 0; |
2613 | +var glo_casedata = []; |
2614 | + |
2615 | +$(window).resize(function() { |
2616 | + draw(); |
2617 | +}); |
2618 | + |
2619 | +$(document).ready(function(){ |
2620 | + var select = document.getElementById("startDate"); |
2621 | + var select2 = document.getElementById("endDate"); |
2622 | + var options = alldates(0,0)[0]; |
2623 | +// url_options |
2624 | + for(var i = 0; i < options.length; i++) { |
2625 | + var opt = options[i][1]; |
2626 | + var el = document.createElement("option"); |
2627 | + var el2 = document.createElement("option"); |
2628 | + el.textContent = opt; |
2629 | + el.value = opt; |
2630 | + el2.textContent = opt; |
2631 | + el2.value = opt; |
2632 | + select.appendChild(el); |
2633 | + select2.appendChild(el2); |
2634 | + } |
2635 | + |
2636 | + select = document.getElementById("startImage"); |
2637 | + select2 = document.getElementById("endImage"); |
2638 | + options = allimages(0,0)[0]; |
2639 | + for(var i = 0; i < options.length; i++) { |
2640 | + var opt = options[i][1]; |
2641 | + var el = document.createElement("option"); |
2642 | + var el2 = document.createElement("option"); |
2643 | + el.textContent = opt; |
2644 | + el.value = opt; |
2645 | + el2.textContent = opt; |
2646 | + el2.value = opt; |
2647 | + select.appendChild(el); |
2648 | + select2.appendChild(el2); |
2649 | + } |
2650 | + select = document.getElementById("rows"); |
2651 | + options = rownames(); |
2652 | + rows=url_options.split("&rows=")[1]; |
2653 | + if (rows != null) { |
2654 | + if (rows.indexOf("&") != -1) {rows=rows.substr(0,rows.indexOf("&")).split(",");} |
2655 | + else {rows = rows.split(",");} |
2656 | + } |
2657 | + else { |
2658 | + rows = []; |
2659 | + } |
2660 | + for (var r=0;r<rows.length;r++) {rows[r] = trim(rows[r]);} |
2661 | + for(var i = 0; i < options.length; i++) { |
2662 | + var opt = options[i]; |
2663 | + var el = document.createElement("input"); |
2664 | + el.type = "checkbox"; |
2665 | + el.onclick=draw; |
2666 | + el.id = "row"+i; |
2667 | + el.name = "row"+i; |
2668 | + var label = document.createElement("label"); |
2669 | + label.htmlFor = opt; |
2670 | + if (contains(rows, opt, false)) {el.checked = true;} |
2671 | + else {el.checked = false;} |
2672 | + label.appendChild(document.createTextNode(opt)); |
2673 | + var br = document.createElement("br"); |
2674 | + label.appendChild(br); |
2675 | + select.appendChild(el); |
2676 | + select.appendChild(label); |
2677 | + } |
2678 | + |
2679 | + select = document.getElementById("testcases"); |
2680 | + options = casenames(); |
2681 | + rows=url_options.split("&kpis=")[1]; |
2682 | + if (rows != null) { |
2683 | + if (rows.indexOf("&") != -1) {rows=rows.substr(0,rows.indexOf("&")).split(",");} |
2684 | + else {rows = rows.split(",");} |
2685 | + } |
2686 | + else { |
2687 | + rows = []; |
2688 | + } |
2689 | + for (var r=0;r<rows.length;r++) {rows[r] = trim(rows[r]);} |
2690 | + for(var i = 0; i < options.length; i++) { |
2691 | + glo_casedata.push([0, 0, 1, 100]); |
2692 | + } |
2693 | + for(var i = 0; i < options.length; i++) { |
2694 | + glo_selectedkpi=i; |
2695 | + var opt = options[i][0]; |
2696 | + var units = options[i][1]; |
2697 | + var el = document.createElement("input"); |
2698 | + el.type = "checkbox"; |
2699 | + el.onclick=loadtext; |
2700 | + el.id = "case"+i; |
2701 | + el.name = "case"+i; |
2702 | + el.value = opt |
2703 | + if (contains(rows, opt, false)) {el.checked = true;} |
2704 | + else {el.checked = false;} |
2705 | + var label = document.createElement("input"); |
2706 | + label.type = "button"; |
2707 | + label.value = opt; |
2708 | + label.id = "button"+i; |
2709 | + label.name = "button"+i; |
2710 | + label.onclick= loadtext; |
2711 | + var unit = document.createElement("input"); |
2712 | + unit.type = "button"; |
2713 | + unit.onclick = draw; |
2714 | + unit.value = units; |
2715 | + unit.id = "unit"+i; |
2716 | + label.name = "unit"+i; |
2717 | + var br = document.createElement("br"); |
2718 | + select.appendChild(el); |
2719 | + select.appendChild(label); |
2720 | + select.appendChild(unit); |
2721 | + select.appendChild(br); |
2722 | + label.click(); |
2723 | + } |
2724 | + |
2725 | + rows=url_options.split("&var=")[1]; |
2726 | + if (rows != null) { |
2727 | + if (rows.indexOf("&") != -1) {rows=rows.substr(0,rows.indexOf("&")).split(",");} |
2728 | + else {rows = rows.split(",");} |
2729 | + } |
2730 | + else { |
2731 | + rows = [1,2]; |
2732 | + } |
2733 | + for (var i=0;i<rows.length;i++){ |
2734 | + if (rows[i] != ''){ |
2735 | + $("input").get(parseInt(rows[i])).checked = true |
2736 | + } |
2737 | + } |
2738 | + |
2739 | + rows=url_options.split("&select=")[1]; |
2740 | + var sdata = 0; |
2741 | + var simage = 0; |
2742 | + if (rows != null) { |
2743 | + if (rows.indexOf("&") != -1) {rows=rows.substr(0,rows.indexOf("&")).split(",");} |
2744 | + else {rows = rows.split(",");} |
2745 | + } |
2746 | + else { |
2747 | + if ($("select").get(0).length>7){ sdata = 7;} |
2748 | + else{ sdata=$("select").get(0).length-1;} |
2749 | + if ($("select").get(2).length>7){ simage = 7;} |
2750 | + else{ simage=$("select").get(2).length-1;} |
2751 | + rows = [$("select").get(0).length-sdata,$("select").get(1).length-1,$("select").get(2).length-simage,$("select").get(3).length-1]; |
2752 | + } |
2753 | + for (var i=0;i<Math.min(rows.length, 4);i++){ |
2754 | + $("select").get(i).selectedIndex = parseInt(rows[i]); |
2755 | + } |
2756 | + changeType(); |
2757 | + exposeList(); |
2758 | + goal_dates = []; |
2759 | + {% for tag, value in goaldates.items %} |
2760 | + goal_dates.push(['{{tag}}', '{{value.startdate}}', '{{value.enddate}}', '{{value.value}}']); |
2761 | + {% endfor %} |
2762 | + goal_dates = goal_dates.sort(function(a,b) { if(a[1] > b[1]){return 1}else{return -1} } ); |
2763 | + goalElement = document.getElementById("goal_dates"); |
2764 | + if (goal_dates.length > 0) { |
2765 | + goalElement.style.display = 'inline'; |
2766 | + document.getElementById('goal_dates_label').style.display = 'inline'; |
2767 | + inputString = '<table border="0"><tr><td><b>Tag </b></td><td><b>Target Date </b></td><td><b>Goal</b></td></tr>'; |
2768 | + for (var tag=0;tag<goal_dates.length;tag++){ |
2769 | + inputString += '<tr><td>'+goal_dates[tag][0]+'</td>'; |
2770 | + inputString += '<td>'+goal_dates[tag][1]+'</td>'; |
2771 | + inputString += '<td>'+goal_dates[tag][3]+'%</td></tr>'; |
2772 | + } |
2773 | + inputString += '</table>'; |
2774 | + goalElement.innerHTML = inputString; |
2775 | + } |
2776 | + else { |
2777 | + goalElement.style.display = 'none'; |
2778 | + document.getElementById('goal_dates_label').style.display = 'none'; |
2779 | + } |
2780 | + glo_selectedkpi=0; |
2781 | + draw(); |
2782 | +}); |
2783 | + |
2784 | +</script> |
2785 | + |
2786 | +<h2>Detailed results:</h2> |
2787 | +>>>>>>> MERGE-SOURCE |
2788 | <table id="outer-table"> |
2789 | <tr> |
2790 | <td> |
2791 | |
2792 | === modified file 'dashboard_app/templates/dashboard_app/image-reports.html' |
2793 | --- dashboard_app/templates/dashboard_app/image-reports.html 2012-07-18 05:20:11 +0000 |
2794 | +++ dashboard_app/templates/dashboard_app/image-reports.html 2013-07-17 17:43:24 +0000 |
2795 | @@ -9,10 +9,12 @@ |
2796 | {% for image in imageset.images %} |
2797 | <li> |
2798 | {% if image.bundle_count %} |
2799 | - <a href="{{ image.link }}">{{ image.name }}</a> |
2800 | - ({{ image.bundle_count }} results) |
2801 | + <a href="{{ image.link }}">{{ image.imagename }}</a> |
2802 | + ({{ image.bundle_count }} results) |
2803 | + (<font color="Blue">{{image.grandtotal}} Totalcases;</font> <font color="green"> {{ image.passes }} Passes; </font><font color="red">{{image.fails}} Fails</font>) |
2804 | {% else %} |
2805 | - {{ image.name }} ({{ image.bundle_count }} results) |
2806 | + {{ imagename }} ({{ image.bundle_count }} results) |
2807 | + (<font color="Blue">{{image.grandtotal}} Totalcases;</font> <font color="green"> {{ image.passes }} Passes; </font><font color="red">{{image.fails}} Fails</font>) |
2808 | {% endif %} |
2809 | </li> |
2810 | {% endfor %} |
2811 | |
2812 | === modified file 'dashboard_app/views/images.py' |
2813 | --- dashboard_app/views/images.py 2013-07-03 11:42:45 +0000 |
2814 | +++ dashboard_app/views/images.py 2013-07-17 17:43:24 +0000 |
2815 | @@ -16,11 +16,14 @@ |
2816 | # You should have received a copy of the GNU Affero General Public License |
2817 | # along with Launch Control. If not, see <http://www.gnu.org/licenses/>. |
2818 | |
2819 | - |
2820 | +import json |
2821 | +import re |
2822 | +from django.db import models |
2823 | from django.http import HttpResponseRedirect |
2824 | from django.shortcuts import get_object_or_404, render_to_response |
2825 | from django.template import RequestContext |
2826 | from django.views.decorators.http import require_POST |
2827 | +from datetime import date |
2828 | |
2829 | from lava_server.bread_crumbs import ( |
2830 | BreadCrumb, |
2831 | @@ -29,32 +32,302 @@ |
2832 | |
2833 | from dashboard_app.filters import evaluate_filter |
2834 | from dashboard_app.models import ( |
2835 | + Bundle, |
2836 | LaunchpadBug, |
2837 | Image, |
2838 | ImageSet, |
2839 | TestRun, |
2840 | ) |
2841 | from dashboard_app.views import index |
2842 | +<<<<<<< TREE |
2843 | import json |
2844 | +======= |
2845 | + |
2846 | +def pfdict_insert(dic, match, tag): |
2847 | + if tag not in dic: |
2848 | + dic[tag] = {'pass': 0, 'fail': 0, 'test_names': [], 'image': '', 'allpass':0, 'allfail':0} |
2849 | + dic[tag]['pass'] += match['count_pass'] |
2850 | + dic[tag]['fail'] += match['count_fail'] |
2851 | + dic[tag]['test_names']=match['test_names'] |
2852 | + dic[tag]['allpass']=match['allpass'] |
2853 | + dic[tag]['allfail']=match['allfail'] |
2854 | + if (str(match['image']) != ''): |
2855 | + dic[tag]['image'] = match['image'] |
2856 | + return dic |
2857 | + |
2858 | +def kpidict_insert(dic, aMatch, tag): |
2859 | + for match in aMatch: |
2860 | + testname = match |
2861 | + if testname not in dic: |
2862 | + dic[testname] = {'date': [],'measurement': [], 'units': '', |
2863 | + 'target': '', 'floor': '', 'goal': '', 'weight': ''} |
2864 | + dic[testname]['date'].append(tag) |
2865 | + if (aMatch[match]['measurement']==None): |
2866 | + dic[testname]['measurement'].append(-1) |
2867 | + else: |
2868 | + dic[testname]['measurement'].append(str(aMatch[match]['measurement'])) |
2869 | + if (dic[testname]['units'] == ''): |
2870 | + dic[testname]['units']=aMatch[match]['units'] |
2871 | + dic[testname]['target']=aMatch[match]['target'] |
2872 | + dic[testname]['floor']=aMatch[match]['floor'] |
2873 | + dic[testname]['goal']=aMatch[match]['goal'] |
2874 | + dic[testname]['weight']=aMatch[match]['weight'] |
2875 | + return dic |
2876 | + |
2877 | +def rowdict_insert(dic, rowMatch, tag): |
2878 | + for match in rowMatch: |
2879 | + if tag not in dic: |
2880 | + dic[tag] = {} |
2881 | + if match not in dic[tag]: |
2882 | + dic[tag][match] = {'pass':0,'fail':0} |
2883 | + for testname in dic[tag]: |
2884 | + try: |
2885 | + dic[tag][testname]['pass'] = rowMatch[testname]['pass'] |
2886 | + except KeyError: |
2887 | + dic[tag][testname]['pass'] = 0 |
2888 | + try: |
2889 | + dic[tag][testname]['fail'] = rowMatch[testname]['fail'] |
2890 | + except KeyError: |
2891 | + dic[tag][testname]['fail'] = 0 |
2892 | + return dic |
2893 | + |
2894 | + |
2895 | +def generate_dicts(allmatches, getcases): |
2896 | + # Lets only go through the database once.. |
2897 | + kpidict = {} |
2898 | + detaildict = {} |
2899 | + rowdict = {} |
2900 | + dates = [] |
2901 | + images = [] |
2902 | + tagdic = {} |
2903 | + for matches in allmatches: |
2904 | + tag = "" |
2905 | + for match in matches.test_runs: |
2906 | + #Only keep latest test for same image |
2907 | + if (tag == ""): |
2908 | + tag = match.analyzer_assigned_date |
2909 | + tag = date(tag.year,tag.month,tag.day) |
2910 | + #tag = matches.tag |
2911 | + if tag not in dates: |
2912 | + dates.append(tag) |
2913 | + if tag not in tagdic: |
2914 | + tagdic[tag]=[] |
2915 | + tagdic[tag].append(match) |
2916 | + for tagname in tagdic: |
2917 | + aMatch = {'count_pass': 0,'count_fail': 0, 'test_names': [], 'image': '', 'allpass': 0, 'allfail': 0} |
2918 | + rowMatch = {} |
2919 | + for ddate in tagdic[tagname]: |
2920 | + aMatch['count_pass'] += ddate.denormalization.count_pass |
2921 | + aMatch['count_fail'] += ddate.denormalization.count_fail |
2922 | + aMatch['test_names'].append(ddate) |
2923 | + aMatch['image'] = ddate.sw_image_desc |
2924 | + aMatch['allpass'] = aMatch['count_pass'] |
2925 | + aMatch['allfail'] = aMatch['count_fail'] |
2926 | + aMatch['count_pass'] = float(aMatch['count_pass']) / float(len(tagdic[tagname])) |
2927 | + aMatch['count_fail'] = float(aMatch['count_fail']) / float(len(tagdic[tagname])) |
2928 | + detaildict = pfdict_insert(detaildict, aMatch, tagname) |
2929 | + |
2930 | + if (match.sw_image_desc not in images) and not (match.sw_image_desc == ''): |
2931 | + images.append(match.sw_image_desc) |
2932 | + if (getcases == 1): |
2933 | + aMatch = {} |
2934 | + rowMatch = {} |
2935 | + for ddate in tagdic[tagname]: |
2936 | + if (ddate.test.test_id not in rowMatch): |
2937 | + rowMatch[ddate.test.test_id] = {'pass':0,'fail':0,'count':0} |
2938 | + denorm = ddate.denormalization |
2939 | + rowMatch[ddate.test.test_id]['fail'] += denorm.count_fail |
2940 | + rowMatch[ddate.test.test_id]['pass'] += denorm.count_pass |
2941 | + if (denorm.count_fail == 0 and denorm.count_pass == 0): |
2942 | + pass |
2943 | + else: |
2944 | + rowMatch[ddate.test.test_id]['count'] += 1 |
2945 | + testname = ddate.test # <-- TESTNAME |
2946 | + if (str(testname) != 'lava'): |
2947 | + # Only take measuresments with a . in them, current lava implementation |
2948 | + # measurements are stored as floats |
2949 | + for test_case in ddate.test_results.filter(measurement__icontains='.'): |
2950 | + test_case_name = test_case.test_case |
2951 | + if (test_case.test_case not in aMatch): |
2952 | + aMatch[test_case_name] = {'date': '','measurement': None, 'units': '','target': '', |
2953 | + 'floor': '', 'goal': '', 'weight': '', 'count': 0} |
2954 | + if (aMatch[test_case_name]['measurement'] == None and test_case.measurement != None): |
2955 | + aMatch[test_case_name]['measurement'] = 0 |
2956 | + if (test_case.measurement == None): |
2957 | + test_case.measurement = 0 |
2958 | + if (aMatch[test_case_name] != None): |
2959 | + aMatch[test_case_name]['measurement'] += test_case.measurement |
2960 | + aMatch[test_case_name]['count'] += 1 |
2961 | + aMatch[test_case_name]['date'] = tagname |
2962 | + aMatch[test_case_name]['units'] = test_case.units |
2963 | + aMatch[test_case_name]['target']=test_case.target |
2964 | + aMatch[test_case_name]['floor']=test_case.floor |
2965 | + aMatch[test_case_name]['goal']=test_case.goal |
2966 | + aMatch[test_case_name]['weight']=test_case.weight |
2967 | + for test in aMatch: |
2968 | + if (aMatch[test]['count'] == 0): |
2969 | + aMatch[test]['measurement'] = 0 |
2970 | + else: |
2971 | + aMatch[test]['measurement'] = aMatch[test]['measurement'] / aMatch[test]['count'] |
2972 | + kpidict = kpidict_insert(kpidict, aMatch, tagname) |
2973 | + for test in rowMatch: |
2974 | + if (rowMatch[test]['count'] == 0): |
2975 | + rowMatch[test]['pass'] = 0 |
2976 | + rowMatch[test]['fail'] = 0 |
2977 | + else: |
2978 | + rowMatch[test]['pass'] = float(rowMatch[test]['pass']) / float(rowMatch[test]['count']) |
2979 | + rowMatch[test]['fail'] = float(rowMatch[test]['fail']) / float(rowMatch[test]['count']) |
2980 | + rowdict = rowdict_insert(rowdict, rowMatch, tagname) |
2981 | + return kpidict, detaildict, rowdict, dates |
2982 | + |
2983 | +def report_for_kpigraph(counters, dates, images): |
2984 | + temp = counters.copy() |
2985 | + for key in counters: |
2986 | + found = 0; |
2987 | + for i in range(0, len(counters[key]['measurement'])): |
2988 | + if (counters[key]['measurement'][i] != -1): |
2989 | + found = 1 |
2990 | + break |
2991 | + if (found == 0): |
2992 | + temp.pop(key) |
2993 | + else: |
2994 | + for curdate in dates: |
2995 | + if curdate not in temp[key]['date']: |
2996 | + temp[key]['date'].append(curdate) |
2997 | + temp[key]['measurement'].append(-1) |
2998 | + # So now we have a dictionary of the following format.. |
2999 | + # caseid: {date:[] measurement:[], units:''}} |
3000 | + |
3001 | + cases = [] |
3002 | + dates = [] |
3003 | + measures = [] |
3004 | + returndict = {} |
3005 | + for key in temp: |
3006 | + returndict[key] = {'date': [],'measurement': [], 'units': '', |
3007 | + 'target': '', 'flr': '', 'goal': '', 'weight': ''} |
3008 | + dates = temp[key]['date'] |
3009 | + measures = temp[key]['measurement'] |
3010 | + if (len(dates)>1): |
3011 | + dates, measures = zip(*sorted(zip(dates, measures))) |
3012 | + else: |
3013 | + measures = str(measures).replace('[','(').replace(']',')') |
3014 | + dates = str(dates).replace('[','(').replace(']',')') |
3015 | + returndict[key]['date'] = dates |
3016 | + returndict[key]['measurement'] = measures |
3017 | + returndict[key]['units'] = temp[key]['units'] |
3018 | + returndict[key]['target'] = temp[key]['target'] |
3019 | + returndict[key]['flr'] = temp[key]['floor'] |
3020 | + returndict[key]['goal'] = temp[key]['goal'] |
3021 | + returndict[key]['weight'] = temp[key]['weight'] |
3022 | + return returndict |
3023 | + |
3024 | +def report_for_detailgraph(detaildict): |
3025 | + report = {'ddates': [], 'dfail': [], 'dpass': [], 'imagelist': [], 'totalfails': 0, |
3026 | + 'totalpasses': 0, 'percentfails': 0, 'percentpasses': 0, |
3027 | + 'grandtotal': 0, 'images': [], 'unfail': [], 'unpass': []} |
3028 | + taglist = [] |
3029 | + sortable = 0 |
3030 | + for tag in detaildict: |
3031 | + taglist.append(tag) |
3032 | + taglist = sorted(taglist) |
3033 | + count = 0 |
3034 | + for tag in taglist: |
3035 | + fail_count = detaildict[tag]['fail'] |
3036 | + pass_count = detaildict[tag]['pass'] |
3037 | + image = detaildict[tag]['image'] |
3038 | + if (image not in report['images']) and not (image == ''): |
3039 | + report['images'].append(image) |
3040 | + report['imagelist'].append(str(detaildict[tag]['image'])) |
3041 | + report['ddates'].append(unicode(tag)) |
3042 | + report['dfail'].append([count, fail_count]) |
3043 | + report['dpass'].append([count, pass_count]) |
3044 | + report['unfail'].append([count, detaildict[tag]['allfail']]) |
3045 | + report['unpass'].append([count, detaildict[tag]['allpass']]) |
3046 | + report['totalfails'] += detaildict[tag]['allfail'] |
3047 | + report['totalpasses'] += detaildict[tag]['allpass'] |
3048 | + report['grandtotal'] += detaildict[tag]['allfail']+detaildict[tag]['allpass'] |
3049 | + count += 1 |
3050 | + if report['grandtotal'] == 0: |
3051 | + report['percentfails'] = 0 |
3052 | + report['percentpasses'] = 0 |
3053 | + else: |
3054 | + report['percentfails'] = report['totalfails'] * 100 / report['grandtotal'] |
3055 | + report['percentpasses'] = report['totalpasses'] * 100 / report['grandtotal'] |
3056 | + report['dfail'] = json.dumps(report['dfail']) |
3057 | + report['dpass'] = json.dumps(report['dpass']) |
3058 | + return report |
3059 | + |
3060 | +def report_for_row(rowdict): |
3061 | + tags = {} |
3062 | + counters = {} |
3063 | + count = 0 |
3064 | + sortedtests = [] |
3065 | + sorteddates = [] |
3066 | + lava = False |
3067 | + for dates in rowdict: |
3068 | + sorteddates.append(dates) |
3069 | + for rows in rowdict[dates]: |
3070 | + if rows not in sortedtests: |
3071 | + if rows!='lava': |
3072 | + sortedtests.append(rows) |
3073 | + else: |
3074 | + lava = True |
3075 | + sorteddates = sorted(sorteddates) |
3076 | + sortedtests = sorted(sortedtests) |
3077 | + if (lava): |
3078 | + sortedtests.insert(0, 'lava') |
3079 | + count = 0 |
3080 | + for dates in sorteddates: |
3081 | + for testname in rowdict[dates]: |
3082 | + if (testname not in counters): |
3083 | + counters[testname] = {'pass':[],'fail':[]} |
3084 | + counters[testname]['pass'].append([count, rowdict[dates][testname]['pass']]) |
3085 | + counters[testname]['fail'].append([count, rowdict[dates][testname]['fail']]) |
3086 | + count += 1 |
3087 | + return counters |
3088 | + |
3089 | +def generate_reports(allmatches, getcases): |
3090 | + kpidict, detaildict, rowdict, dates = generate_dicts(allmatches, getcases) |
3091 | + detailreport = report_for_detailgraph(detaildict) |
3092 | + rowreport = report_for_row(rowdict) |
3093 | + if (getcases == 1): |
3094 | + kpireport = report_for_kpigraph(kpidict, dates, detailreport['images']) |
3095 | + else: |
3096 | + kpireport = {} |
3097 | + return kpireport, detailreport, rowreport |
3098 | +>>>>>>> MERGE-SOURCE |
3099 | |
3100 | @BreadCrumb("Image Reports", parent=index) |
3101 | def image_report_list(request): |
3102 | + kpireport = {} |
3103 | + pfreport = {} |
3104 | + testcases = [] |
3105 | imagesets = ImageSet.objects.filter() |
3106 | imagesets_data = [] |
3107 | for imageset in imagesets: |
3108 | images_data = [] |
3109 | + bundle_ids = set() |
3110 | + count = 0 |
3111 | for image in imageset.images.all(): |
3112 | # Migration hack: Image.filter cannot be auto populated, so ignore |
3113 | # images that have not been migrated to filters for now. |
3114 | if image.filter: |
3115 | filter_data = image.filter.as_data() |
3116 | + matches = evaluate_filter(request.user, filter_data, prefetch_related=['launchpad_bugs']) |
3117 | + kpireport, pfreport, rowreport = generate_reports(matches, 0) |
3118 | + filter_data = image.filter.as_data() |
3119 | image_data = { |
3120 | - 'name': image.name, |
3121 | - 'bundle_count': evaluate_filter(request.user, filter_data).count(), |
3122 | - 'link': image.name, |
3123 | + 'name': image.name+"?"+"sets="+imageset.name, |
3124 | + 'imagename': image.name, |
3125 | + 'imageset': imageset.name, |
3126 | + 'bundle_count': matches.count(), |
3127 | + 'link': image.name+"?"+"sets="+imageset.name, |
3128 | + 'passes': pfreport['totalpasses'], |
3129 | + 'grandtotal': pfreport['grandtotal'], |
3130 | + 'fails': pfreport['grandtotal']-pfreport['totalpasses'], |
3131 | } |
3132 | images_data.append(image_data) |
3133 | - images_data.sort(key=lambda d:d['name']) |
3134 | + #Quick hack |
3135 | imageset_data = { |
3136 | 'name': imageset.name, |
3137 | 'images': images_data, |
3138 | @@ -70,13 +343,36 @@ |
3139 | |
3140 | @BreadCrumb("{name}", parent=image_report_list, needs=['name']) |
3141 | def image_report_detail(request, name): |
3142 | - |
3143 | + setname = request.GET.get("sets", None) |
3144 | + currentset = None |
3145 | + if (setname != None): |
3146 | + imagesets = ImageSet.objects.filter() |
3147 | + for imageset in imagesets: |
3148 | + if (setname == imageset.name): |
3149 | + currentset = imageset |
3150 | + break |
3151 | + else: |
3152 | + goaldates = {} |
3153 | + currentset = None |
3154 | + if (currentset != None): |
3155 | + if (currentset.relatedDates != None): |
3156 | + goaldates = currentset.relatedDates.return_dates() |
3157 | + currentset = currentset.relatedDates.name |
3158 | + else: |
3159 | + goaldates = {} |
3160 | + currentset = None |
3161 | + else: |
3162 | + goaldates = {} |
3163 | image = Image.objects.get(name=name) |
3164 | filter_data = image.filter.as_data() |
3165 | +<<<<<<< TREE |
3166 | matches = evaluate_filter(request.user, filter_data, prefetch_related=['launchpad_bugs', 'test_results'])[:50] |
3167 | |
3168 | +======= |
3169 | + matches = evaluate_filter(request.user, filter_data, prefetch_related=['launchpad_bugs']) |
3170 | + kpireport, pfreport, rowreport = generate_reports(matches, 1) |
3171 | +>>>>>>> MERGE-SOURCE |
3172 | build_number_to_cols = {} |
3173 | - |
3174 | test_run_names = set() |
3175 | |
3176 | for match in matches: |
3177 | @@ -86,7 +382,7 @@ |
3178 | if denorm.count_fail == 0: |
3179 | cls = 'present pass' |
3180 | else: |
3181 | - cls = 'present fail' |
3182 | + cls = 'present fail' |
3183 | bug_ids = sorted([b.bug_id for b in test_run.launchpad_bugs.all()]) |
3184 | |
3185 | measurements = [{'measurement': str(item.measurement)} |
3186 | @@ -121,6 +417,7 @@ |
3187 | |
3188 | table_data = {} |
3189 | |
3190 | + table_data_dict = {} |
3191 | for test_run_name in test_run_names: |
3192 | row_data = [] |
3193 | for col in cols: |
3194 | @@ -131,16 +428,34 @@ |
3195 | cls='missing', |
3196 | ) |
3197 | row_data.append(test_run_data) |
3198 | +<<<<<<< TREE |
3199 | table_data[test_run_name] = row_data |
3200 | +======= |
3201 | + table_data.append(row_data) |
3202 | + table_data_dict[test_run_name] = row_data |
3203 | + |
3204 | + #Lets see what we can do with table_data_dict |
3205 | +>>>>>>> MERGE-SOURCE |
3206 | |
3207 | return render_to_response( |
3208 | "dashboard_app/image-report.html", { |
3209 | - 'bread_crumb_trail': BreadCrumbTrail.leading_to( |
3210 | - image_report_detail, name=image.name), |
3211 | + 'bread_crumb_trail': BreadCrumbTrail.leading_to(image_report_detail, name=image.name), |
3212 | 'image': image, |
3213 | +<<<<<<< TREE |
3214 | 'chart_data': json.dumps(table_data), |
3215 | 'test_names': json.dumps(test_run_names), |
3216 | 'columns': json.dumps(cols), |
3217 | +======= |
3218 | + 'cols': cols, |
3219 | + 'table_data': table_data, |
3220 | + 'test_run_names': test_run_names, |
3221 | + 'pfreport': pfreport, |
3222 | + 'rowreport': rowreport, |
3223 | + 'kpireport': kpireport, |
3224 | + 'goaldates': goaldates, |
3225 | + 'goalname': currentset, |
3226 | + 'setname': setname, |
3227 | +>>>>>>> MERGE-SOURCE |
3228 | }, RequestContext(request)) |
3229 | |
3230 |