Merge lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-models into lp:ubuntu-ci-services-itself
- ts-ticket-models
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Chris Johnston |
Approved revision: | 26 |
Merged at revision: | 25 |
Proposed branch: | lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-models |
Merge into: | lp:ubuntu-ci-services-itself |
Diff against target: |
563 lines (+470/-6) 12 files modified
ticket_system/people/__init__.py (+0/-1) ticket_system/people/admin.py (+0/-1) ticket_system/people/api.py (+0/-1) ticket_system/people/models.py (+0/-1) ticket_system/people/tests.py (+0/-1) ticket_system/ticket/__init__.py (+14/-0) ticket_system/ticket/admin.py (+45/-0) ticket_system/ticket/migrations/0001_initial.py (+117/-0) ticket_system/ticket/models.py (+162/-0) ticket_system/ticket/tests.py (+131/-0) ticket_system/ticket_system/local_tests.py (+0/-1) ticket_system/ticket_system/settings.py (+1/-0) |
To merge this branch: | bzr merge lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-models |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andy Doan (community) | Approve | ||
Chris Johnston (community) | Needs Resubmitting | ||
Review via email:
|
Commit message
Add ticket related models to the ticket system
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andy Doan (doanac) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chris Johnston (cjohnston) wrote : | # |
On Tue, Dec 10, 2013 at 4:09 PM, Andy Doan <email address hidden>wrote:
> > + assignee = models.
>
> I assume we want subtickets to be able to have different owners than the
> main ticket?
>
>
Yes. If there are multiple people working on different parts of a ticket.
This is probably more for the future, but no sense in not adding it now.
> + sourcepackageupload = models.
>
> > +class Artifact(
>
> > + ticket_component = models.
> null=True)
> > + sourcepackageupload = models.ForeignKey(
> > + SourcePackageUp
> > + verbose_
>
> This feels like we might not have a relation defined properly. It seems
> like an Artifact shouldn't need the "ticket_component" attribute. I
> think a SubTicket has a SourcePackageUpload which has artifacts. So
> adding this ticket_component here feels like something isn't properly
> normalized. I'm not sure the intent but it seems like it should be one
> of these options:
>
> = 1 ======
> You add to SourcePackageUp
> subticket = models.
>
> Remove the the "ticket_component" attribute from Artifact.
>
> The drawback here, is that a SubTicket could have more than
> SourcePackageUp
>
> = 2 =====
>
> You don't need a SourcePackage upload class. You just make
> "source_package" and "source_
> SubTicket. Then Arfifact just has a foreign key to SubTicket.
>
> = 3 =====
>
> This is already correct, and I'm missing something.
>
An artifact can be two different things. When you upload a source package
at the initial time of creating a ticket, it is an artifact (it needs to be
stored somewhere, and we need to be able to tell other parts of the CI
engine where to look for the source package). As the system goes through
and does things, there (I assume) will be output files (logs and such,
maybe videos when using autopilot, etc). These things don't belong to a
package upload, they belong to a ticket. So, your artifact can either be
the source package or an output artifact from the CI engine.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andy Doan (doanac) wrote : | # |
On 12/10/2013 03:18 PM, Chris Johnston wrote:
> An artifact can be two different things. When you upload a source package
> at the initial time of creating a ticket, it is an artifact (it needs to be
> stored somewhere, and we need to be able to tell other parts of the CI
> engine where to look for the source package). As the system goes through
> and does things, there (I assume) will be output files (logs and such,
> maybe videos when using autopilot, etc). These things don't belong to a
> package upload, they belong to a ticket. So, your artifact can either be
> the source package or an output artifact from the CI engine.
So I think this means you don't want/need the
Artifact.
If you wanted to know the source package uploads for a subticket you
could still get them with:
subticket = <something>
uploads = Artifact.
ticket_
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chris Johnston (cjohnston) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chris Johnston (cjohnston) wrote : | # |
Made changes to the choices fields and made them Integers
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andy Doan (doanac) wrote : | # |
The "COMPLETE = 001" feels odd. We've got a set of increasing values except for this one value? Seems like COMPLETE in this case would be the biggest value? Also, be careful with padding things with 0's. Thats actual octal 1 which just happens to be 1. But if you did "010" you'd actually have 8 in base 10. Maybe reversing the order of the constants would make the COMPLETE thing nice where COMPLETE = 0 and WAITING = 500.
264 +WAITING = 000
265 +PACKAGE_BUILDING = 100
268 +PACKAGE_PUBLISHING = 400
269 +COMPLETE = 001
270 +PKG_BUILD_WAITING = 110
Last note is just styling - you can take it or leave it. Having all these constants defined outside the Ticket class means modules will have to do an akward import like:
import ticket_
models.
Where if they instead were in the Ticket class you could do:
from ticket_
Ticket.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andy Doan (doanac) wrote : | # |
Now to understand SubTicket.
1) why do we not use underscores here?
2) To confirm our relationships:
a) a SubTicket _has_ a SourcePackageUpload
b) a SourcePackageUpload can have many Artifacts
is this correct?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Francis Ginther (fginther) wrote : | # |
> 2) To confirm our relationships:
>
> a) a SubTicket _has_ a SourcePackageUpload
> b) a SourcePackageUpload can have many Artifacts
>
> is this correct?
That is correct. A ticket corresponds to a build request, which could contain one or more source packages. A subticket would be associated with each source package. A source package contains multiple files to upload (a .dsc, a orig.tar.gz, etc).
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andy Doan (doanac) wrote : | # |
On 12/10/2013 10:25 PM, Francis Ginther wrote:
>> 2) To confirm our relationships:
>>
>> a) a SubTicket _has_ a SourcePackageUpload
>> b) a SourcePackageUpload can have many Artifacts
>>
>> is this correct?
>
> That is correct. A ticket corresponds to a build request, which could contain one or more source packages. A subticket would be associated with each source package. A source package contains multiple files to upload (a .dsc, a orig.tar.gz, etc).
Awesome. I'm +1 for the model in general now. Still have my concerns
about the status codes.
- 24. By Chris Johnston
-
change constants per review
- 25. By Chris Johnston
-
Fix tests
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Chris Johnston (cjohnston) : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Ursula Junque (ursinha) wrote : | # |
Hey Chris, only a few notes:
322 + (COMPLETED, "COMPLETED"),
I think this label should be "Completed".
456 +def create_
457 + description="this is my ticket description",
458 + bug_id='12345', owner='Chris Johnston',
459 + current_
460 + status=220, base_image='17'):
Can we use the constants here (and other places that refer to it) instead of the integer, like Ticket.
I think it looks good for now. If we detect these workflow steps/statuses aren't precise enough we can easily add more of them.
- 26. By Chris Johnston
-
Test changes per review
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Andy Doan (doanac) : | # |
Preview Diff
1 | === modified file 'ticket_system/people/__init__.py' |
2 | --- ticket_system/people/__init__.py 2013-12-09 17:43:12 +0000 |
3 | +++ ticket_system/people/__init__.py 2013-12-11 18:03:47 +0000 |
4 | @@ -1,4 +1,3 @@ |
5 | -# Houston |
6 | # Ubuntu Continuous Integration Engine |
7 | # Copyright 2013 Canonical Ltd. |
8 | |
9 | |
10 | === modified file 'ticket_system/people/admin.py' |
11 | --- ticket_system/people/admin.py 2013-12-09 17:43:12 +0000 |
12 | +++ ticket_system/people/admin.py 2013-12-11 18:03:47 +0000 |
13 | @@ -1,4 +1,3 @@ |
14 | -# Houston |
15 | # Ubuntu Continuous Integration Engine |
16 | # Copyright 2013 Canonical Ltd. |
17 | |
18 | |
19 | === modified file 'ticket_system/people/api.py' |
20 | --- ticket_system/people/api.py 2013-12-09 17:43:12 +0000 |
21 | +++ ticket_system/people/api.py 2013-12-11 18:03:47 +0000 |
22 | @@ -1,4 +1,3 @@ |
23 | -# Houston |
24 | # Ubuntu Continuous Integration Engine |
25 | # Copyright 2013 Canonical Ltd. |
26 | |
27 | |
28 | === modified file 'ticket_system/people/models.py' |
29 | --- ticket_system/people/models.py 2013-12-09 18:57:54 +0000 |
30 | +++ ticket_system/people/models.py 2013-12-11 18:03:47 +0000 |
31 | @@ -1,4 +1,3 @@ |
32 | -# Houston |
33 | # Ubuntu Continuous Integration Engine |
34 | # Copyright 2013 Canonical Ltd. |
35 | |
36 | |
37 | === modified file 'ticket_system/people/tests.py' |
38 | --- ticket_system/people/tests.py 2013-12-10 18:34:08 +0000 |
39 | +++ ticket_system/people/tests.py 2013-12-11 18:03:47 +0000 |
40 | @@ -1,4 +1,3 @@ |
41 | -# Houston |
42 | # Ubuntu Continuous Integration Engine |
43 | # Copyright 2013 Canonical Ltd. |
44 | |
45 | |
46 | === added directory 'ticket_system/ticket' |
47 | === added file 'ticket_system/ticket/__init__.py' |
48 | --- ticket_system/ticket/__init__.py 1970-01-01 00:00:00 +0000 |
49 | +++ ticket_system/ticket/__init__.py 2013-12-11 18:03:47 +0000 |
50 | @@ -0,0 +1,14 @@ |
51 | +# Ubuntu Continuous Integration Engine |
52 | +# Copyright 2013 Canonical Ltd. |
53 | + |
54 | +# This program is free software: you can redistribute it and/or modify it |
55 | +# under the terms of the GNU Affero General Public License version 3, as |
56 | +# published by the Free Software Foundation. |
57 | + |
58 | +# This program is distributed in the hope that it will be useful, but |
59 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
60 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
61 | +# PURPOSE. See the GNU Affero General Public License for more details. |
62 | + |
63 | +# You should have received a copy of the GNU Affero General Public License |
64 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
65 | |
66 | === added file 'ticket_system/ticket/admin.py' |
67 | --- ticket_system/ticket/admin.py 1970-01-01 00:00:00 +0000 |
68 | +++ ticket_system/ticket/admin.py 2013-12-11 18:03:47 +0000 |
69 | @@ -0,0 +1,45 @@ |
70 | +# Ubuntu Continuous Integration Engine |
71 | +# Copyright 2013 Canonical Ltd. |
72 | + |
73 | +# This program is free software: you can redistribute it and/or modify it |
74 | +# under the terms of the GNU Affero General Public License version 3, as |
75 | +# published by the Free Software Foundation. |
76 | + |
77 | +# This program is distributed in the hope that it will be useful, but |
78 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
79 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
80 | +# PURPOSE. See the GNU Affero General Public License for more details. |
81 | + |
82 | +# You should have received a copy of the GNU Affero General Public License |
83 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
84 | + |
85 | +from django.contrib import admin |
86 | +from ticket.models import Ticket, SubTicket, SourcePackageUpload, Artifact |
87 | + |
88 | + |
89 | +class TicketAdmin(admin.ModelAdmin): |
90 | + list_filter = ['current_workflow_step', 'status'] |
91 | + search_fields = ['title', 'bug_id'] |
92 | + list_display = ('title', 'current_workflow_step', 'status', 'base_image') |
93 | + |
94 | + |
95 | +class SubTicketAdmin(admin.ModelAdmin): |
96 | + list_filter = ['status'] |
97 | + search_fields = ['ticket', 'sourcepackageupload'] |
98 | + list_display = ('ticket', 'status', 'assignee', 'sourcepackageupload') |
99 | + |
100 | + |
101 | +class SourcePackageUploadAdmin(admin.ModelAdmin): |
102 | + search_fields = ['sourcepackage'] |
103 | + list_display = ('sourcepackage', 'version') |
104 | + |
105 | + |
106 | +class ArtifactAdmin(admin.ModelAdmin): |
107 | + list_filter = ['type'] |
108 | + search_fields = ['name'] |
109 | + list_display = ('name', 'ticket_component', 'type') |
110 | + |
111 | +admin.site.register(Ticket, TicketAdmin) |
112 | +admin.site.register(SubTicket, SubTicketAdmin) |
113 | +admin.site.register(SourcePackageUpload, SourcePackageUploadAdmin) |
114 | +admin.site.register(Artifact, ArtifactAdmin) |
115 | |
116 | === added directory 'ticket_system/ticket/migrations' |
117 | === added file 'ticket_system/ticket/migrations/0001_initial.py' |
118 | --- ticket_system/ticket/migrations/0001_initial.py 1970-01-01 00:00:00 +0000 |
119 | +++ ticket_system/ticket/migrations/0001_initial.py 2013-12-11 18:03:47 +0000 |
120 | @@ -0,0 +1,117 @@ |
121 | +# -*- coding: utf-8 -*- |
122 | +import datetime |
123 | +from south.db import db |
124 | +from south.v2 import SchemaMigration |
125 | +from django.db import models |
126 | + |
127 | + |
128 | +class Migration(SchemaMigration): |
129 | + |
130 | + def forwards(self, orm): |
131 | + # Adding model 'Ticket' |
132 | + db.create_table('ticket', ( |
133 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
134 | + ('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['people.Person'])), |
135 | + ('title', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
136 | + ('description', self.gf('django.db.models.fields.TextField')()), |
137 | + ('bug_id', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), |
138 | + ('current_workflow_step', self.gf('django.db.models.fields.IntegerField')(default=0, max_length=4096)), |
139 | + ('status', self.gf('django.db.models.fields.IntegerField')(default=0, max_length=4096)), |
140 | + ('base_image', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
141 | + )) |
142 | + db.send_create_signal(u'ticket', ['Ticket']) |
143 | + |
144 | + # Adding model 'SourcePackageUpload' |
145 | + db.create_table('sourcepackageupload', ( |
146 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
147 | + ('sourcepackage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['project.SourcePackage'])), |
148 | + ('version', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
149 | + )) |
150 | + db.send_create_signal(u'ticket', ['SourcePackageUpload']) |
151 | + |
152 | + # Adding model 'SubTicket' |
153 | + db.create_table('subticket', ( |
154 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
155 | + ('current_workflow_step', self.gf('django.db.models.fields.IntegerField')(default=0, max_length=4096)), |
156 | + ('status', self.gf('django.db.models.fields.IntegerField')(default=0, max_length=4096)), |
157 | + ('ticket', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.Ticket'])), |
158 | + ('assignee', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['people.Person'])), |
159 | + ('source_package_upload', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.SourcePackageUpload'])), |
160 | + )) |
161 | + db.send_create_signal(u'ticket', ['SubTicket']) |
162 | + |
163 | + # Adding model 'Artifact' |
164 | + db.create_table('artifact', ( |
165 | + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
166 | + ('type', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
167 | + ('ticket_component', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['ticket.SubTicket'], null=True, blank=True)), |
168 | + ('reference', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
169 | + ('name', self.gf('django.db.models.fields.CharField')(max_length=4096)), |
170 | + )) |
171 | + db.send_create_signal(u'ticket', ['Artifact']) |
172 | + |
173 | + |
174 | + def backwards(self, orm): |
175 | + # Deleting model 'Ticket' |
176 | + db.delete_table('ticket') |
177 | + |
178 | + # Deleting model 'SourcePackageUpload' |
179 | + db.delete_table('sourcepackageupload') |
180 | + |
181 | + # Deleting model 'SubTicket' |
182 | + db.delete_table('subticket') |
183 | + |
184 | + # Deleting model 'Artifact' |
185 | + db.delete_table('artifact') |
186 | + |
187 | + |
188 | + models = { |
189 | + u'people.person': { |
190 | + 'Meta': {'object_name': 'Person', 'db_table': "'person'"}, |
191 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '4096'}), |
192 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
193 | + 'is_team': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
194 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
195 | + }, |
196 | + u'project.sourcepackage': { |
197 | + 'Meta': {'object_name': 'SourcePackage', 'db_table': "'sourcepackage'"}, |
198 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
199 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
200 | + }, |
201 | + u'ticket.artifact': { |
202 | + 'Meta': {'object_name': 'Artifact', 'db_table': "'artifact'"}, |
203 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
204 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
205 | + 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
206 | + 'ticket_component': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']", 'null': 'True', 'blank': 'True'}), |
207 | + 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
208 | + }, |
209 | + u'ticket.sourcepackageupload': { |
210 | + 'Meta': {'object_name': 'SourcePackageUpload', 'db_table': "'sourcepackageupload'"}, |
211 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
212 | + 'sourcepackage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['project.SourcePackage']"}), |
213 | + 'version': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
214 | + }, |
215 | + u'ticket.subticket': { |
216 | + 'Meta': {'object_name': 'SubTicket', 'db_table': "'subticket'"}, |
217 | + 'assignee': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['people.Person']"}), |
218 | + 'current_workflow_step': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}), |
219 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
220 | + 'source_package_upload': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SourcePackageUpload']"}), |
221 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}), |
222 | + 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']"}) |
223 | + }, |
224 | + u'ticket.ticket': { |
225 | + 'Meta': {'object_name': 'Ticket', 'db_table': "'ticket'"}, |
226 | + 'base_image': ('django.db.models.fields.CharField', [], {'max_length': '4096'}), |
227 | + 'bug_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), |
228 | + 'current_workflow_step': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}), |
229 | + 'description': ('django.db.models.fields.TextField', [], {}), |
230 | + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
231 | + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['people.Person']"}), |
232 | + 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}), |
233 | + 'title': ('django.db.models.fields.CharField', [], {'max_length': '4096'}) |
234 | + } |
235 | + } |
236 | + |
237 | + complete_apps = ['ticket'] |
238 | \ No newline at end of file |
239 | |
240 | === added file 'ticket_system/ticket/migrations/__init__.py' |
241 | === added file 'ticket_system/ticket/models.py' |
242 | --- ticket_system/ticket/models.py 1970-01-01 00:00:00 +0000 |
243 | +++ ticket_system/ticket/models.py 2013-12-11 18:03:47 +0000 |
244 | @@ -0,0 +1,162 @@ |
245 | +# Ubuntu Continuous Integration Engine |
246 | +# Copyright 2013 Canonical Ltd. |
247 | + |
248 | +# This program is free software: you can redistribute it and/or modify it |
249 | +# under the terms of the GNU Affero General Public License version 3, as |
250 | +# published by the Free Software Foundation. |
251 | + |
252 | +# This program is distributed in the hope that it will be useful, but |
253 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
254 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
255 | +# PURPOSE. See the GNU Affero General Public License for more details. |
256 | + |
257 | +# You should have received a copy of the GNU Affero General Public License |
258 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
259 | + |
260 | +from django.db import models |
261 | +from people.models import Person |
262 | +from project.models import SourcePackage |
263 | + |
264 | + |
265 | +class Ticket(models.Model): |
266 | + class Meta: |
267 | + db_table = 'ticket' |
268 | + |
269 | + NEW = 000 |
270 | + QUEUED = 100 |
271 | + PENDING = 110 |
272 | + COMPLETED = 1000 |
273 | + |
274 | + PKG_BUILDING = 200 |
275 | + PKG_BUILDING_WAITING = 210 |
276 | + PKG_BUILDING_INPROGRESS = 220 |
277 | + PKG_BUILDING_COMPLETED = 230 |
278 | + PKG_BUILDING_FAILED = 240 |
279 | + |
280 | + IMAGE_BUILDING = 300 |
281 | + IMAGE_BUILDING_WAITING = 310 |
282 | + IMAGE_BUILDING_INPROGRESS = 320 |
283 | + IMAGE_BUILDING_COMPLETED = 330 |
284 | + IMAGE_BUILDING_FAILED = 340 |
285 | + |
286 | + IMAGE_TESTING = 400 |
287 | + IMAGE_TESTING_WAITING = 410 |
288 | + IMAGE_TESTING_INPROGRESS = 420 |
289 | + IMAGE_TESTING_PASSED = 430 |
290 | + IMAGE_TESTING_FAILED = 440 |
291 | + |
292 | + PKG_PUBLISHING = 500 |
293 | + PKG_PUBLISHING_COMPLETED = 510 |
294 | + PKG_PUBLISHING_FAILED = 520 |
295 | + |
296 | + WORKFLOW_STEPS = ( |
297 | + (NEW, "New"), |
298 | + (QUEUED, "Queued"), |
299 | + (PKG_BUILDING, "Package building"), |
300 | + (IMAGE_BUILDING, "Image building"), |
301 | + (IMAGE_TESTING, "Image testing"), |
302 | + (PKG_PUBLISHING, "Package publishing"), |
303 | + (COMPLETED, "Completed"), |
304 | + ) |
305 | + WORKFLOW_STEP_STATUSES = ( |
306 | + (NEW, "New"), |
307 | + (PENDING, "Not started"), |
308 | + (PKG_BUILDING_WAITING, "Not started"), |
309 | + (PKG_BUILDING_INPROGRESS, "In progress"), |
310 | + (PKG_BUILDING_COMPLETED, "Completed"), |
311 | + (PKG_BUILDING_FAILED, "Failed"), |
312 | + (IMAGE_BUILDING_WAITING, "Not started"), |
313 | + (IMAGE_BUILDING_INPROGRESS, "In progress"), |
314 | + (IMAGE_BUILDING_COMPLETED, "Completed"), |
315 | + (IMAGE_BUILDING_FAILED, "Failed"), |
316 | + (IMAGE_TESTING_WAITING, "Not started"), |
317 | + (IMAGE_TESTING_INPROGRESS, "In progress"), |
318 | + (IMAGE_TESTING_PASSED, "Passed"), |
319 | + (IMAGE_TESTING_FAILED, "Failed"), |
320 | + (PKG_PUBLISHING_COMPLETED, "Completed"), |
321 | + (PKG_PUBLISHING_FAILED, "Failed"), |
322 | + (COMPLETED, "Completed"), |
323 | + ) |
324 | + owner = models.ForeignKey(Person) |
325 | + title = models.CharField(max_length=4096) |
326 | + description = models.TextField() |
327 | + bug_id = models.IntegerField(null=True, blank=True) |
328 | + current_workflow_step = models.IntegerField(choices=WORKFLOW_STEPS, |
329 | + max_length=4096, default=000) |
330 | + status = models.IntegerField(choices=WORKFLOW_STEP_STATUSES, |
331 | + max_length=4096, default=000) |
332 | + base_image = models.CharField(max_length=4096) |
333 | + |
334 | + def __unicode__(self): |
335 | + return self.title |
336 | + |
337 | + |
338 | +class SourcePackageUpload(models.Model): |
339 | + class Meta: |
340 | + db_table = 'sourcepackageupload' |
341 | + |
342 | + sourcepackage = models.ForeignKey(SourcePackage) |
343 | + version = models.CharField(max_length=4096) |
344 | + |
345 | + def __unicode__(self): |
346 | + return "{} {}".format(self.sourcepackage, self.version) |
347 | + |
348 | + |
349 | +class SubTicket(models.Model): |
350 | + class Meta: |
351 | + db_table = 'subticket' |
352 | + |
353 | + NEW = 000 |
354 | + QUEUED = 100 |
355 | + PENDING = 110 |
356 | + COMPLETED = 1000 |
357 | + |
358 | + PKG_BUILDING = 200 |
359 | + PKG_BUILDING_WAITING = 210 |
360 | + PKG_BUILDING_INPROGRESS = 220 |
361 | + PKG_BUILDING_COMPLETED = 230 |
362 | + PKG_BUILDING_FAILED = 240 |
363 | + |
364 | + WORKFLOW_STEPS = ( |
365 | + (NEW, "New"), |
366 | + (QUEUED, "Queued"), |
367 | + (PKG_BUILDING, "Package building"), |
368 | + (COMPLETED, "Completed"), |
369 | + ) |
370 | + WORKFLOW_STEP_STATUSES = ( |
371 | + (NEW, "New"), |
372 | + (QUEUED, "Queued"), |
373 | + (PKG_BUILDING_WAITING, "Not started"), |
374 | + (PKG_BUILDING_INPROGRESS, "In progress"), |
375 | + (PKG_BUILDING_COMPLETED, "Completed"), |
376 | + (PKG_BUILDING_FAILED, "Failed"), |
377 | + ) |
378 | + current_workflow_step = models.IntegerField(choices=WORKFLOW_STEPS, |
379 | + max_length=4096, default=000) |
380 | + status = models.IntegerField(choices=WORKFLOW_STEP_STATUSES, |
381 | + max_length=4096, default=000) |
382 | + ticket = models.ForeignKey(Ticket) |
383 | + assignee = models.ForeignKey(Person) |
384 | + source_package_upload = models.ForeignKey(SourcePackageUpload) |
385 | + |
386 | + def __unicode__(self): |
387 | + return "{} {}".format(self.ticket, self.sourcepackageupload) |
388 | + |
389 | + |
390 | +class Artifact(models.Model): |
391 | + class Meta: |
392 | + db_table = 'artifact' |
393 | + |
394 | + ARTIFACT_TYPE = ( |
395 | + ('RESULTS', 'Test Results'), |
396 | + ('SPU', 'Source Package Upload'), |
397 | + ('LOGS', 'Logs'), |
398 | + ) |
399 | + type = models.CharField(choices=ARTIFACT_TYPE, max_length=4096) |
400 | + ticket_component = models.ForeignKey(SubTicket, blank=True, null=True) |
401 | + # 'reference' provided by the artifact manager |
402 | + reference = models.CharField(max_length=4096) |
403 | + name = models.CharField(max_length=4096) |
404 | + |
405 | + def __unicode__(self): |
406 | + return "{} {}".format(self.name, self.type) |
407 | |
408 | === added file 'ticket_system/ticket/tests.py' |
409 | --- ticket_system/ticket/tests.py 1970-01-01 00:00:00 +0000 |
410 | +++ ticket_system/ticket/tests.py 2013-12-11 18:03:47 +0000 |
411 | @@ -0,0 +1,131 @@ |
412 | +# Ubuntu Continuous Integration Engine |
413 | +# Copyright 2013 Canonical Ltd. |
414 | + |
415 | +# This program is free software: you can redistribute it and/or modify it |
416 | +# under the terms of the GNU Affero General Public License version 3, as |
417 | +# published by the Free Software Foundation. |
418 | + |
419 | +# This program is distributed in the hope that it will be useful, but |
420 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
421 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
422 | +# PURPOSE. See the GNU Affero General Public License for more details. |
423 | + |
424 | +# You should have received a copy of the GNU Affero General Public License |
425 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
426 | + |
427 | +from django.test import TestCase |
428 | +from people.models import Person |
429 | +from project.models import SourcePackage |
430 | +from ticket.models import Ticket, SubTicket, SourcePackageUpload, Artifact |
431 | + |
432 | + |
433 | +def create_person(name='Chris Johnston', email='chrisjohnston@ubuntu.com'): |
434 | + person = Person() |
435 | + person.name = name |
436 | + person.email = email |
437 | + person.save() |
438 | + return person |
439 | + |
440 | + |
441 | +def create_sourcepackage(name='my-package'): |
442 | + sourcepackage = SourcePackage() |
443 | + sourcepackage.name = name |
444 | + sourcepackage.save() |
445 | + return sourcepackage |
446 | + |
447 | + |
448 | +def create_sourcepackageupload(sourcepackage='my-package', version='1.1'): |
449 | + spu = SourcePackageUpload() |
450 | + spu.sourcepackage = sourcepackage |
451 | + spu.version = version |
452 | + spu.save() |
453 | + return spu |
454 | + |
455 | + |
456 | +def create_ticket(title="This is my ticket", |
457 | + description="this is my ticket description", |
458 | + bug_id='12345', owner='Chris Johnston', |
459 | + current_workflow_step=Ticket.PKG_BUILDING, |
460 | + status=Ticket.PKG_BUILDING_INPROGRESS, base_image='17'): |
461 | + ticket = Ticket() |
462 | + ticket.owner = owner |
463 | + ticket.title = title |
464 | + ticket.description = description |
465 | + ticket.bug_id = bug_id |
466 | + ticket.current_workflow_step = current_workflow_step |
467 | + ticket.status = status |
468 | + ticket.base_image = base_image |
469 | + ticket.save() |
470 | + return ticket |
471 | + |
472 | + |
473 | +def create_subticket(current_workflow_step=SubTicket.PKG_BUILDING, ticket=None, |
474 | + status=SubTicket.PKG_BUILDING_INPROGRESS, |
475 | + assignee='Chris Johnston', |
476 | + source_package_upload=None): |
477 | + subticket = SubTicket() |
478 | + subticket.current_workflow_step = current_workflow_step |
479 | + subticket.status = status |
480 | + subticket.ticket = ticket |
481 | + subticket.assignee = assignee |
482 | + subticket.source_package_upload = source_package_upload |
483 | + subticket.save() |
484 | + return subticket |
485 | + |
486 | + |
487 | +def create_artifact(name="test.log", ticket_component=None, |
488 | + reference='123abc'): |
489 | + artifact = Artifact() |
490 | + artifact.name = name |
491 | + artifact.ticket_component = ticket_component |
492 | + artifact.reference = reference |
493 | + artifact.save() |
494 | + return artifact |
495 | + |
496 | + |
497 | +class TicketModelsTestCase(TestCase): |
498 | + def setUp(self): |
499 | + self.person = create_person() |
500 | + self.sourcepackage = create_sourcepackage() |
501 | + self.spu = create_sourcepackageupload( |
502 | + sourcepackage=self.sourcepackage) |
503 | + self.ticket = create_ticket(owner=self.person) |
504 | + self.subticket = create_subticket(ticket=self.ticket, |
505 | + assignee=self.person, |
506 | + source_package_upload=self.spu) |
507 | + self.artifact_1 = create_artifact(ticket_component=self.subticket) |
508 | + |
509 | + def test_creating_an_artifact(self): |
510 | + artifact_in_database = Artifact.objects.exclude( |
511 | + ticket_component=None) |
512 | + self.assertEquals(len(artifact_in_database), 1) |
513 | + only_artifact_in_database = artifact_in_database[0] |
514 | + self.assertEquals(only_artifact_in_database, self.artifact_1) |
515 | + |
516 | + self.assertEquals(only_artifact_in_database.name, "test.log") |
517 | + |
518 | + def test_creating_a_ticket(self): |
519 | + ticket_in_database = Ticket.objects.all() |
520 | + self.assertEquals(len(ticket_in_database), 1) |
521 | + only_ticket_in_database = ticket_in_database[0] |
522 | + self.assertEquals(only_ticket_in_database, self.ticket) |
523 | + |
524 | + self.assertEquals(only_ticket_in_database.title, "This is my ticket") |
525 | + |
526 | + def test_creating_a_subticket(self): |
527 | + subticket_in_database = SubTicket.objects.all() |
528 | + self.assertEquals(len(subticket_in_database), 1) |
529 | + only_subticket_in_database = subticket_in_database[0] |
530 | + self.assertEquals(only_subticket_in_database, self.subticket) |
531 | + |
532 | + self.assertEquals(only_subticket_in_database.assignee.name, |
533 | + "Chris Johnston") |
534 | + |
535 | + def test_creating_a_sourcepackageupload(self): |
536 | + spu_in_database = SourcePackageUpload.objects.all() |
537 | + self.assertEquals(len(spu_in_database), 1) |
538 | + only_spu_in_database = spu_in_database[0] |
539 | + self.assertEquals(only_spu_in_database, self.spu) |
540 | + |
541 | + self.assertEquals(only_spu_in_database.sourcepackage.name, |
542 | + "my-package") |
543 | |
544 | === modified file 'ticket_system/ticket_system/local_tests.py' |
545 | --- ticket_system/ticket_system/local_tests.py 2013-12-09 17:29:57 +0000 |
546 | +++ ticket_system/ticket_system/local_tests.py 2013-12-11 18:03:47 +0000 |
547 | @@ -1,4 +1,3 @@ |
548 | -# Houston |
549 | # Ubuntu Continuous Integration Engine |
550 | # Copyright 2013 Canonical Ltd. |
551 | |
552 | |
553 | === modified file 'ticket_system/ticket_system/settings.py' |
554 | --- ticket_system/ticket_system/settings.py 2013-12-09 19:35:22 +0000 |
555 | +++ ticket_system/ticket_system/settings.py 2013-12-11 18:03:47 +0000 |
556 | @@ -141,6 +141,7 @@ |
557 | LOCAL_APPS = ( |
558 | 'people', |
559 | 'project', |
560 | + 'ticket', |
561 | ) |
562 | |
563 | INSTALLED_APPS = LOCAL_APPS + INSTALLED_APPS |
On 12/10/2013 01:23 PM, Chris Johnston wrote:
> Chris Johnston has proposed merging lp:~cjohnston/ubuntu-ci-services-itself/ts-ticket-models into lp:ubuntu-ci-services-itself.
> === added file 'ticket_ system/ ticket/ models. py'
> +class Ticket( models. Model): BUILDING" , "Package building"), PUBLISHING" , "Package publishing"), STEP_STATUSES = ( WAITING" , "Not started"), INPROGRESS" , "In progress"), FAILED" , "Failed"), BUILD_WAITING" , "Not started"), BUILD_INPROGRES S", "In progress"), BUILD_DONE" , "Completed"), BUILD_FAILED" , "Failed"), TESTS_WAITING" , "Not started"), TESTS_INPROGRES S", "In progress"), TESTS_PASSED" , "Passed"), TESTS_FAILED" , "Failed"), G_DONE" , "Completed"), G_FAILED" , "Failed"),
> + class Meta:
> + db_table = 'ticket'
> +
> + WORKFLOW_STEPS = (
> + ("PACKAGE_
> + ("IMAGE_BUILDING", "Image building"),
> + ("IMAGE_TESTING", "Image testing"),
> + ("PACKAGE_
> + )
> + WORKFLOW_
> + ("PKG_BUILD_
> + ("PKG_BUILD_
> + ("PKG_BUILD_DONE", "Completed"),
> + ("PKG_BUILD_
> + ("IMAGE_
> + ("IMAGE_
> + ("IMAGE_
> + ("IMAGE_
> + ("IMAGE_
> + ("IMAGE_
> + ("IMAGE_
> + ("IMAGE_
> + ("PKG_PUBLISHIN
> + ("PKG_PUBLISHIN
> + )
> + current_ workflow_ step = models. CharField( choices= WORKFLOW_ STEPS, CharField( choices= WORKFLOW_ STEP_STATUSES,
> + max_length=4096)
> + status = models.
> + max_length=4096)
Would it make sense to use models.IntegerField and change these values
from strings to integers. I think that would then allow you do
eventually have logic like:
if ticket.status < Ticket. IMAGE_BUILD_ DONE:
print("No image available for ticket")
I'd define these status as multiples of 10 like: BUILD_WAITING = 0 BUILD_INPROGRES S = 10
PKG_
PKG_
because I could forsee a day when we might need new steps in the process.
> +class SourcePackageUp load(models. Model): pload' ForeignKey( SourcePackage) CharField( max_length= 4096) self.sourcepack age, self.version) models. Model): BUILDING" , "Package building"), STEP_STATUSES = ( WAITING" , "Not started"), INPROGRESS" , "In progress"), FAILED" , "Failed"),
> + class Meta:
> + db_table = 'sourcepackageu
> +
> + sourcepackage = models.
> + version = models.
> +
> + def __unicode__(self):
> + return "{} {}".format(
> +
> +
> +class SubTicket(
> + class Meta:
> + db_table = 'subticket'
> +
> + WORKFLOW_STEPS = (
> + ("WAITING", "Not started"),
> + ("PACKAGE_
> + ("COMPLETE", "Completed"),
> + )
> + WORKFLOW_
> + ("PKG_BUILD_
> + ("PKG_BUILD_
> + ("PKG_BUILD_DONE", "Completed"),
> + ("PKG_BUILD_
> + )
same point about integer field here.
> + assignee = models. ForeignKey( Person)
I assume we want subtickets to be able to have different owners than the
main ticket?
> + sourcepackageupload = models. ForeignKey( SourcePackageUp load)
> +class Artifact( models. Model):
> + ticket_component = models. ForeignKey( SubTicket, blank=True, null=True)
> + sourcepackageupload = models.ForeignKey(
> + SourcePacka...