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