Merge lp:~joetalbott/uci-engine/owner_split into lp:uci-engine

Proposed by Joe Talbott
Status: Rejected
Rejected by: Joe Talbott
Proposed branch: lp:~joetalbott/uci-engine/owner_split
Merge into: lp:uci-engine
Diff against target: 780 lines (+364/-37)
14 files modified
cli/ci_cli/tests/test_cli.py (+79/-17)
cli/ci_cli/tests/test_ticket.py (+15/-9)
cli/ci_cli/ticket.py (+10/-4)
cli/ubuntu-ci (+26/-2)
docs/api/initial-request.rst (+1/-1)
docs/usage.rst (+4/-2)
ticket_system/ticket/api.py (+1/-0)
ticket_system/ticket/migrations/0008_auto__add_field_ticket_creator__chg_field_ticket_owner.py (+111/-0)
ticket_system/ticket/migrations/0009_add_creator.py (+103/-0)
ticket_system/ticket/models.py (+2/-1)
ticket_system/ticket/tests/test_full_read_api.py (+1/-0)
ticket_system/ticket/tests/test_models.py (+4/-1)
ticket_system/ticket/tests/test_read_api.py (+5/-0)
ticket_system/ticket/tests/test_write_api.py (+2/-0)
To merge this branch: bzr merge lp:~joetalbott/uci-engine/owner_split
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Ursula Junque (community) Needs Information
Paul Larson Needs Information
Review via email: mp+230378@code.launchpad.net

Commit message

ticket-system - Split owner role into owner and creator.

* -c/--creator is now a required parameter to ubuntu-ci and -o/--owner is optional.
  if no owner is specified the creator value is used for owner as well.

Description of the change

ticket-system - Split owner role into owner and creator.

* -c/--creator is now a required parameter to ubuntu-ci and -o/--owner is optional.
  if no owner is specified the creator value is used for owner as well.

To post a comment you must log in.
742. By Joe Talbott

Fix typo

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:741
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1268/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1268/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:742
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1269/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1269/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Evan (ev) wrote :

Could you please update the docs to reflect this as well?

743. By Joe Talbott

ticket-system - Update docs to reflect new owner/creator split.

744. By Joe Talbott

cli - Update tests to match new owner/creator split.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:744
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1281/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1281/rebuild

review: Needs Fixing (continuous-integration)
745. By Joe Talbott

merge with trunk

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

FAILED: Continuous integration, rev:745
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1308/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1308/rebuild

review: Needs Fixing (continuous-integration)
746. By Joe Talbott

cli - Fix owner/creator logic when updating tickets.

* for new tickets we'll have args.creator
* for existing tickets we'll have args.owner

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:746
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1309/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1309/rebuild

review: Approve (continuous-integration)
Revision history for this message
Paul Larson (pwlars) wrote :

Can you also add a line in the docs to say why we even have this? I'm not sure I understand the use case for a different creator and owner. Also, why would we not preserve backwards compatibility with just specifying -o? We could probably change this to say if either one is specified (but not the other) then they are assumed to be the same, right?

review: Needs Information
Revision history for this message
Ursula Junque (ursinha) wrote :

I think owner is more important than creator, because the owner is the person/team you want to notify whenever the ticket has changed or requires input (like citrain spreadsheet "Lander"). This way I'd invert the logic and allow the creator to be optional, while the owner (or "lander") is required.

I agree with Paul that the way it's now it's not clear why to split these, is there a use case where you see creator being more important than owner?

review: Needs Information
747. By Joe Talbott

cli - when creating a ticket accept an owner or creator or both

At least one is required when creating a ticket.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:747
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1312/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1312/rebuild

review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:747
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1313/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1313/rebuild

review: Approve (continuous-integration)
Revision history for this message
Joe Talbott (joetalbott) wrote :

Paul and Ursula,

I've updated the MP to allow either of owner or creator to be provided individually or together. If only one is provided it's used for the other. If neither is provided a parser error is raised. I think keeping track of the ticket submitter (creator) is important and am on the fence as to whether only supplying the owner should set the creator.

Revision history for this message
Celso Providelo (cprov) wrote :

Joe,

On the other hand, once we have authentication, the creator can be obtained implicitly from self.request.user{.email or a more specific LP user reference}.

I would consider a minimum-impact path, by populating "creator" with the already provided "owner" in Ticket.save(), so zero changes in the CLI (after all we don't want user foo creating tickets as bar). That would carry us safely to the point where we have authenticated requests implemented to extend tastypie views accordingly.

Revision history for this message
Ursula Junque (ursinha) wrote :

> Joe,
>
> On the other hand, once we have authentication, the creator can be obtained
> implicitly from self.request.user{.email or a more specific LP user
> reference}.
>
> I would consider a minimum-impact path, by populating "creator" with the
> already provided "owner" in Ticket.save(), so zero changes in the CLI (after
> all we don't want user foo creating tickets as bar). That would carry us
> safely to the point where we have authenticated requests implemented to extend
> tastypie views accordingly.

Just to keep in mind: the reason to have an owner is to annotate the relevant people to be notified about ticket changes, and it's possible that more than one person needs to be notified. We still haven't defined how to notify, if using an IRC bot (then we would need this to be populated with one or more IRC nicks), or else, so assuming the owner is the same as the creator (LP username or email) might need to be changed in a near future.

Revision history for this message
Joe Talbott (joetalbott) wrote :

I'm going to reject this MP and submit a new one based on our conversation today.

Unmerged revisions

747. By Joe Talbott

cli - when creating a ticket accept an owner or creator or both

At least one is required when creating a ticket.

746. By Joe Talbott

cli - Fix owner/creator logic when updating tickets.

* for new tickets we'll have args.creator
* for existing tickets we'll have args.owner

745. By Joe Talbott

merge with trunk

744. By Joe Talbott

cli - Update tests to match new owner/creator split.

743. By Joe Talbott

ticket-system - Update docs to reflect new owner/creator split.

742. By Joe Talbott

Fix typo

741. By Joe Talbott

ticket-system - Split owner role into owner/creator roles.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cli/ci_cli/tests/test_cli.py'
2--- cli/ci_cli/tests/test_cli.py 2014-08-12 23:55:22 +0000
3+++ cli/ci_cli/tests/test_cli.py 2014-08-21 13:48:34 +0000
4@@ -54,6 +54,7 @@
5 "title": "My cool feature",
6 "id": '5',
7 "description": "my new feature",
8+ "creator": "user@example.com",
9 "owner": "user@example.com",
10 "created": "Wed, 12 Mar 2014 02:18:28 +0000",
11 "updated": "Wed, 12 Mar 2014 02:30:22 +0000",
12@@ -128,7 +129,7 @@
13 'create_ticket',
14 '-t', 'New feature',
15 '-b', '4321',
16- '-o', 'someone@example.com',
17+ '-c', 'someone@example.com',
18 '-d', 'This is a cool new feature',
19 '-s', changes_1,
20 '-s', changes_2,
21@@ -150,7 +151,7 @@
22 changes_2 = get_test_file_path(
23 'foobar-unsupported_0.1-1ubuntu1_source.changes')
24
25- args = ['create_ticket', '-t', '"New feature"', '-b', '4321', '-o',
26+ args = ['create_ticket', '-t', '"New feature"', '-b', '4321', '-c',
27 'someone@example.com', '-d', '"This is a cool new feature"',
28 '-s', changes_1, '-s', changes_2]
29
30@@ -274,6 +275,65 @@
31 'create_ticket',
32 '-t', 'New feature',
33 '-d', 'New feature description',
34+ '-c', 'someone@example.com',
35+ '-s', foobar_changes,
36+ ]
37+ with LogCapture() as lc:
38+ logger = logging.getLogger()
39+ self.cli.main(args, log=logger)
40+ ticket_url = urlparse.urljoin(
41+ utils.CI_URL, '/ticket.html?ticket_id=1')
42+ # The first output line contains a local absolute host path, so
43+ # it is ignored.
44+ self.assertEquals(
45+ ['Parsing foobar_0.1-1_source.changes...',
46+ 'Validating .changes...',
47+ 'foobar_0.1-1 parsed and validated.',
48+ 'Created source package upload: http://mocked/api/v1/ticket/1/',
49+ 'Created subticket: http://mocked/api/v1/ticket/1/',
50+ 'Uploading foobar_0.1-1.dsc ...',
51+ 'Uploading foobar_0.1.orig.tar.gz ...',
52+ 'Uploading foobar_0.1-1.debian.tar.gz ...',
53+ 'Uploading foobar_0.1-1_source.changes ...',
54+ 'Done! Access {} for more information.'.format(ticket_url),
55+ 'Created artifact: http://mocked/api/v1/ticket/1/',
56+ 'Created artifact: http://mocked/api/v1/ticket/1/',
57+ 'Created artifact: http://mocked/api/v1/ticket/1/',
58+ 'Created artifact: http://mocked/api/v1/ticket/1/'],
59+ list([r.msg for r in lc.records]))
60+
61+ @mock.patch('ci_cli.utils.post',
62+ return_value='http://mocked/api/v1/ticket/1/')
63+ @mock.patch('ci_cli.utils.get')
64+ @mock.patch('ci_cli.utils.patch')
65+ @mock.patch('ci_cli.ticket._open_file')
66+ @mock.patch('requests.put')
67+ @mock.patch('requests.post')
68+ def test_create_ticket_with_owner(self, mocked_post, mocked_put, mocked_of,
69+ mocked_upa, mocked_ug, mocked_upo):
70+ # CLI 'create_ticket' action allows users to upload new sources
71+ # via gatekeeper (without swift credentials).
72+ mocked_post.side_effect = [
73+ FakeResponse(201, '/ticket/1/sandbox/UUID'),
74+ FakeResponse(201, '/ticket.html?ticket_id=1'),
75+ ]
76+ mocked_put.side_effect = [
77+ FakeResponse(200, 'A_TEMP_URL'),
78+ FakeResponse(201, '/a_sandbox_object_url/'),
79+ FakeResponse(200, 'A_TEMP_URL'),
80+ FakeResponse(201, '/a_sandbox_object_url/'),
81+ FakeResponse(200, 'A_TEMP_URL'),
82+ FakeResponse(201, '/a_sandbox_object_url/'),
83+ FakeResponse(200, 'A_TEMP_URL'),
84+ FakeResponse(201, '/a_sandbox_object_url/'),
85+ ]
86+ mocked_of.return_value = 'a_content'
87+ foobar_changes = get_test_file_path('foobar_0.1-1_source.changes')
88+ args = [
89+ '-v2',
90+ 'create_ticket',
91+ '-t', 'New feature',
92+ '-d', 'New feature description',
93 '-o', 'someone@example.com',
94 '-s', foobar_changes,
95 ]
96@@ -334,7 +394,7 @@
97 'create_ticket',
98 '-t', 'New feature',
99 '-d', 'New feature description',
100- '-o', 'someone@example.com',
101+ '-c', 'someone@example.com',
102 '-s', foobar_changes,
103 ]
104 mocked_patch.side_effect = [
105@@ -397,31 +457,33 @@
106 with capture_stderr(self.assertRaises, SystemExit, self.cli.main,
107 args) as cm:
108 self.assertNotEqual(cm, None)
109- self.assertTrue(expected_msg in cm)
110+ self.assertTrue(expected_msg in cm, "cm: {}, expected_msg: {}".format(
111+ cm, expected_msg))
112
113 def test_cli_arguments_are_required(self):
114 """Verify cli arguments are required."""
115 args = ['create_ticket']
116 self._test_parser_missing_arguments(
117 args, "create_ticket: error: argument -t/--title is required")
118-
119 args.extend(['-t', '"New feature"'])
120+
121 self._test_parser_missing_arguments(
122 args,
123 "create_ticket: error: argument -d/--description is required")
124-
125 args.extend(['-d', '"This is a cool new feature"'])
126- self._test_parser_missing_arguments(
127- args, "create_ticket: error: argument -o/--owner is required")
128-
129- args.extend(['-o', 'someone@example.com'])
130
131 self._test_parser_missing_arguments(
132 args, "create_ticket: error: argument -s/--sources is required")
133+ args.extend(['-s', 'foobar.changesss'])
134+
135+ self._test_parser_missing_arguments(
136+ args, "error: at least one of the arguments "
137+ "-o/--owner or -c/--creator is required")
138+
139+ args.extend(['-c', 'someone@example.com'])
140
141 # All arguments were provided. It should fail because file is not
142 # found.
143- args.extend(['-s', 'foobar.changesss'])
144 with LogCapture() as lc:
145 logger = logging.getLogger()
146 self.cli.main(args, log=logger)
147@@ -432,7 +494,7 @@
148 """Verify cli fails in case file isn't prefixed .changes."""
149 wrong_changes = get_test_file_path('foobar_0.1-1.dsc')
150 args = ['create_ticket', '-t', '"New feature"', '-d',
151- '"This is a cool new feature"', '-o', 'someone@example.com',
152+ '"This is a cool new feature"', '-c', 'someone@example.com',
153 '-s', wrong_changes]
154 with LogCapture() as lc:
155 logger = logging.getLogger()
156@@ -453,7 +515,7 @@
157 """Verify cli behavior when .changes file is a .dsc."""
158 wrong_changes = get_test_file_path('foobar_0.1-1_dsc.changes')
159 args = ['create_ticket', '-t', '"New feature"', '-d',
160- '"This is a cool new feature"', '-o', 'someone@example.com',
161+ '"This is a cool new feature"', '-c', 'someone@example.com',
162 '-s', wrong_changes]
163 with LogCapture() as lc:
164 logger = logging.getLogger()
165@@ -470,7 +532,7 @@
166 """Verify cli behavior when provided .changes file isn't a .changes."""
167 wrong_changes = get_test_file_path('foobar_0.1-1_random.changes')
168 args = ['create_ticket', '-t', '"New feature"', '-d',
169- '"This is a cool new feature"', '-o', 'someone@example.com',
170+ '"This is a cool new feature"', '-c', 'someone@example.com',
171 '-s', wrong_changes]
172 with LogCapture() as lc:
173 logger = logging.getLogger()
174@@ -488,7 +550,7 @@
175 unsigned_changes = get_test_file_path(
176 'foobar_0.1-1_unsigned.changes')
177 args = ['create_ticket', '-t', '"New feature"', '-d',
178- '"This is a cool new feature"', '-o', 'someone@example.com',
179+ '"This is a cool new feature"', '-c', 'someone@example.com',
180 '-s', unsigned_changes]
181 with LogCapture() as lc:
182 logger = logging.getLogger()
183@@ -507,7 +569,7 @@
184 changes_filename = 'foobar_0.1-1_unreleased.changes'
185 wrong_changes = get_test_file_path(changes_filename)
186 args = ['create_ticket', '-t', '"New feature"', '-d',
187- '"This is a cool new feature"', '-o', 'someone@example.com',
188+ '"This is a cool new feature"', '-c', 'someone@example.com',
189 '-s', wrong_changes]
190 with LogCapture() as lc:
191 logger = logging.getLogger()
192@@ -527,7 +589,7 @@
193 changes_filename = 'foobar-unsupported_0.1-1ubuntu1_source.changes'
194 wrong_changes = get_test_file_path(changes_filename)
195 args = ['create_ticket', '-t', '"New feature"', '-d',
196- '"This is a cool new feature"', '-o', 'someone@example.com',
197+ '"This is a cool new feature"', '-c', 'someone@example.com',
198 '-s', wrong_changes]
199 with LogCapture() as lc:
200 logger = logging.getLogger()
201
202=== modified file 'cli/ci_cli/tests/test_ticket.py'
203--- cli/ci_cli/tests/test_ticket.py 2014-08-13 13:13:21 +0000
204+++ cli/ci_cli/tests/test_ticket.py 2014-08-21 13:48:34 +0000
205@@ -208,7 +208,8 @@
206 @mock.patch('ci_cli.utils.post',
207 return_value='http://www.example.com/api/v1/sourcepackage/4/')
208 def test_create_sourcepackage(self, mock_post):
209- new_subticket = SubTicket(owner='foobar@example.com', ticket_id=4)
210+ new_subticket = SubTicket(creator='foobar@example.com',
211+ owner='foobar@example.com', ticket_id=4)
212 with mock.patch('sys.stdout'):
213 new_subticket._parse_changes(self.changes)
214 sourcepackage_uri = new_subticket._create_sourcepackage()
215@@ -220,7 +221,8 @@
216 @mock.patch('ci_cli.utils.post',
217 return_value='http://www.example.com/api/v1/spu/38/')
218 def test_create_spu(self, mock_post, mock_sp_uri):
219- new_subticket = SubTicket(owner='foobar@example.com', ticket_id=3)
220+ new_subticket = SubTicket(creator='foobar@example.com',
221+ owner='foobar@example.com', ticket_id=3)
222 with mock.patch('sys.stdout'):
223 new_subticket._parse_changes(self.changes)
224 new_subticket._create_spu()
225@@ -232,7 +234,8 @@
226 return_value='http://www.example.com/api/v1/spu/39/')
227 def test_create_spu_sourcepackage_not_found(self, mock_spu_post, mock_post,
228 mock_sp_uri):
229- new_subticket = SubTicket(owner='foobar@example.com', ticket_id=3)
230+ new_subticket = SubTicket(creator='foobar@example.com',
231+ owner='foobar@example.com', ticket_id=3)
232 with mock.patch('sys.stdout'):
233 new_subticket._parse_changes(self.changes)
234 new_subticket._create_spu()
235@@ -242,7 +245,8 @@
236 return_value='http://www.example.com/api/v1/subticketartifact/38/',
237 )
238 def test_create_artifact(self, mock_post):
239- new_subticket = SubTicket(owner='foobar@example.com', ticket_id=2)
240+ new_subticket = SubTicket(creator='foobar@example.com',
241+ owner='foobar@example.com', ticket_id=2)
242 file = 'foobar_source.changes'
243 location = 'http://www.example.com/path/to/foobar_source.changes'
244 new_subticket._create_artifact(file, location)
245@@ -251,7 +255,8 @@
246 @mock.patch('ci_cli.utils.post',
247 return_value='http://www.example.com/api/v1/subticket/1/')
248 def test_create_subticket(self, mock_post):
249- new_subticket = SubTicket(owner='foobar@example.com', ticket_id=1)
250+ new_subticket = SubTicket(creator='foobar@example.com',
251+ owner='foobar@example.com', ticket_id=1)
252 new_subticket._create_subticket()
253
254
255@@ -261,7 +266,7 @@
256 return_value='http://www.example.com/api/v1/ticket/38/')
257 def test_create_ticket(self, mock_post):
258 changes = get_test_file_path('foobar_0.1-1_source.changes')
259- args = ['create_ticket', '-t', 'New feature', '-b', '1234', '-o',
260+ args = ['create_ticket', '-t', 'New feature', '-b', '1234', '-c',
261 'someone@example.com', '-d', 'This is a cool new feature',
262 '-s', changes]
263
264@@ -280,7 +285,7 @@
265 'create_ticket',
266 '-t', 'New feature',
267 '-b', '1234',
268- '-o', 'someone@example.com',
269+ '-c', 'someone@example.com',
270 '-d', 'This is a cool new feature',
271 '-s', changes,
272 ]
273@@ -307,7 +312,7 @@
274 'create_ticket',
275 '-t', 'New feature',
276 '-b', '1234',
277- '-o', 'someone@example.com',
278+ '-c', 'someone@example.com',
279 '-d', 'This is a cool new feature',
280 '-s', changes,
281 '-w'
282@@ -342,6 +347,7 @@
283 '{}/api/v1/ticket/'.format(utils.CI_URL),
284 data={
285 'owner': 'someone@example.com',
286+ 'creator': 'someone@example.com',
287 'series': 'trusty',
288 'bug_id': '1234',
289 'description': 'This is a cool new feature',
290@@ -367,7 +373,7 @@
291 'create_ticket',
292 '-t', 'New feature',
293 '-b', '4321',
294- '-o', 'someone@example.com',
295+ '-c', 'someone@example.com',
296 '-d', 'This is a cool new feature',
297 '-s', changes_1,
298 '-s', changes_2,
299
300=== modified file 'cli/ci_cli/ticket.py'
301--- cli/ci_cli/ticket.py 2014-08-18 19:01:55 +0000
302+++ cli/ci_cli/ticket.py 2014-08-21 13:48:34 +0000
303@@ -138,8 +138,9 @@
304
305 class SubTicket():
306
307- def __init__(self, owner, ticket_id=None):
308- self.owner = owner
309+ def __init__(self, creator, ticket_id=None, owner=None):
310+ self.creator = creator
311+ self.owner = owner if owner is not None else creator
312 self.ticket_id = ticket_id
313 self.sourcepackage = ''
314 self.files = ''
315@@ -241,7 +242,8 @@
316 def _create_ticket(self, args):
317 data = {
318 "title": args.title,
319- "owner": args.owner,
320+ "owner": args.owner if args.owner else args.creator,
321+ "creator": args.creator if args.creator else args.owner,
322 "description": args.description,
323 "series": self.suite.split("-")[0],
324 "bug_id": args.bug,
325@@ -280,9 +282,13 @@
326 to the same suite.
327 """
328 suites = set()
329+ if hasattr(args, 'creator'):
330+ creator = args.creator
331+ else:
332+ creator = args.owner
333 # First, process all sources and validate the series.
334 for source in args.sources:
335- subticket = SubTicket(owner=args.owner)
336+ subticket = SubTicket(owner=args.owner, creator=creator)
337 subticket._parse_changes(source)
338 suites.add(subticket.suite)
339 self.subtickets[subticket.sourcepackage] = subticket
340
341=== modified file 'cli/ubuntu-ci'
342--- cli/ubuntu-ci 2014-08-14 18:57:54 +0000
343+++ cli/ubuntu-ci 2014-08-21 13:48:34 +0000
344@@ -53,8 +53,10 @@
345 help='Ticket description')
346 ticket_parser.add_argument('-b', '--bug',
347 help='Related bug number')
348- ticket_parser.add_argument('-o', '--owner', required=True,
349+ ticket_parser.add_argument('-o', '--owner',
350 help='Email address of the ticket owner')
351+ ticket_parser.add_argument('-c', '--creator',
352+ help='Email address of the ticket creator')
353 ticket_parser.add_argument('-s', '--sources', action='append',
354 help='Path to source.changes files. '
355 'Source package files (e.g. source.dsc, '
356@@ -110,7 +112,29 @@
357 help='Desired file name (and path) for the '
358 'downloaded image', required=True)
359 image_parser.set_defaults(func=image.get_image)
360- return parser.parse_args(args)
361+ args = parser.parse_args(args)
362+
363+ if not _has_valid_owner_creator(args):
364+ parser.error("at least one of the arguments -o/--owner or "
365+ "-c/--creator is required")
366+
367+ return args
368+
369+
370+def _has_valid_owner_creator(args):
371+ if hasattr(args, 'func'):
372+ # only certain subcommands need to require an owner/creator
373+ if args.func.func_name not in ['new_ticket']:
374+ return True
375+
376+ if not (hasattr(args, 'owner') or hasattr(args, 'creator')):
377+ return False
378+
379+ if (hasattr(args, 'owner') and args.owner is None) and (
380+ hasattr(args, 'creator') and args.creator is None):
381+ return False
382+
383+ return True
384
385
386 def set_log_level(logger, loglevel):
387
388=== modified file 'docs/api/initial-request.rst'
389--- docs/api/initial-request.rst 2014-08-05 18:14:05 +0000
390+++ docs/api/initial-request.rst 2014-08-21 13:48:34 +0000
391@@ -10,7 +10,7 @@
392
393 ::
394
395- python ubuntu-ci create_ticket -t "Ticket name" -d "Ticket description" -b 123 -o user@example.com -s /full/path/to/_source.changes -s /full/path/to/_source.changes
396+ python ubuntu-ci create_ticket -t "Ticket name" -d "Ticket description" -b 123 -c user@example.com -s /full/path/to/_source.changes -s /full/path/to/_source.changes
397
398 CLI -> Ticket System
399 --------------------
400
401=== modified file 'docs/usage.rst'
402--- docs/usage.rst 2014-08-05 18:14:05 +0000
403+++ docs/usage.rst 2014-08-21 13:48:34 +0000
404@@ -53,7 +53,7 @@
405
406 .. code-block:: none
407
408- ubuntu-ci [-S|--url] create_ticket -t "Ticket name" -d "Ticket description" -b 123 -o user@example.com -s /full/path/to/_source.changes -s /full/path/to/_source.changes
409+ ubuntu-ci [-S|--url] create_ticket -t "Ticket name" -d "Ticket description" -b 123 -c user@example.com -s /full/path/to/_source.changes -s /full/path/to/_source.changes
410
411 This returns a ticket ID which can later be used to monitor and check for
412 results. Once the ticket is created, the Ubuntu CI Engine does the rest. The
413@@ -86,8 +86,10 @@
414 -d DESCRIPTION, --description DESCRIPTION
415 Ticket description
416 -b BUG, --bug BUG Related bug number
417+ -c CREATOR, --creator CREATOR
418+ Email address of the ticket creator
419 -o OWNER, --owner OWNER
420- Email address of the ticket owner
421+ Email address of the ticket owner (default: CREATOR)
422 -s SOURCES, --sources SOURCES
423 Path to source.changes files. Source package files
424 (e.g. source.dsc, source.orig.tar.gz, etc.) are
425
426=== modified file 'ticket_system/ticket/api.py'
427--- ticket_system/ticket/api.py 2014-08-07 20:45:58 +0000
428+++ ticket_system/ticket/api.py 2014-08-21 13:48:34 +0000
429@@ -403,6 +403,7 @@
430 return ip
431 url = 'http://{}:8080/api/v1/ticket/'.format(ip)
432 ticket_content = {"owner": "create@example.com",
433+ "creator": "creator@example.com",
434 "title": "Created ticket",
435 "description": "This ticket is for creation test",
436 "bug_id": "111111",
437
438=== added file 'ticket_system/ticket/migrations/0008_auto__add_field_ticket_creator__chg_field_ticket_owner.py'
439--- ticket_system/ticket/migrations/0008_auto__add_field_ticket_creator__chg_field_ticket_owner.py 1970-01-01 00:00:00 +0000
440+++ ticket_system/ticket/migrations/0008_auto__add_field_ticket_creator__chg_field_ticket_owner.py 2014-08-21 13:48:34 +0000
441@@ -0,0 +1,111 @@
442+# -*- coding: utf-8 -*-
443+import datetime
444+from south.db import db
445+from south.v2 import SchemaMigration
446+from django.db import models
447+
448+
449+class Migration(SchemaMigration):
450+
451+ def forwards(self, orm):
452+ # Adding field 'Ticket.creator'
453+ db.add_column('ticket', 'creator',
454+ self.gf('django.db.models.fields.EmailField')(default='unknown', max_length=4096),
455+ keep_default=False)
456+
457+
458+ # Changing field 'Ticket.owner'
459+ db.alter_column('ticket', 'owner', self.gf('django.db.models.fields.EmailField')(max_length=4096))
460+
461+ def backwards(self, orm):
462+ # Deleting field 'Ticket.creator'
463+ db.delete_column('ticket', 'creator')
464+
465+
466+ # Changing field 'Ticket.owner'
467+ db.alter_column('ticket', 'owner', self.gf('django.db.models.fields.EmailField')(max_length=254))
468+
469+ models = {
470+ u'project.sourcepackage': {
471+ 'Meta': {'object_name': 'SourcePackage', 'db_table': "'sourcepackage'"},
472+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
473+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'})
474+ },
475+ u'ticket.mergeproposal': {
476+ 'Meta': {'object_name': 'MergeProposal', 'db_table': "'mergeproposal'"},
477+ 'approved_revno': ('django.db.models.fields.IntegerField', [], {}),
478+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
479+ 'lp_url': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
480+ 'project': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
481+ 'sourcepackage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['project.SourcePackage']"}),
482+ 'target_branch': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
483+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
484+ },
485+ u'ticket.review': {
486+ 'Meta': {'object_name': 'Review'},
487+ 'completed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
488+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
489+ 'review_type': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
490+ 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']"}),
491+ 'workflow_step': ('django.db.models.fields.IntegerField', [], {})
492+ },
493+ u'ticket.sourcepackageupload': {
494+ 'Meta': {'object_name': 'SourcePackageUpload', 'db_table': "'sourcepackageupload'"},
495+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
496+ 'sourcepackage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['project.SourcePackage']"}),
497+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
498+ },
499+ u'ticket.subticket': {
500+ 'Meta': {'object_name': 'SubTicket', 'db_table': "'subticket'"},
501+ 'assignee': ('django.db.models.fields.EmailField', [], {'max_length': '254'}),
502+ 'current_workflow_step': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
503+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
504+ 'merge_proposal': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['ticket.MergeProposal']", 'null': 'True', 'blank': 'True'}),
505+ 'source_package_upload': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['ticket.SourcePackageUpload']", 'null': 'True', 'blank': 'True'}),
506+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
507+ 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']"})
508+ },
509+ u'ticket.subticketartifact': {
510+ 'Meta': {'object_name': 'SubTicketArtifact', 'db_table': "'subticket_artifact'"},
511+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
512+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
513+ 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
514+ 'subticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']", 'null': 'True'}),
515+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
516+ },
517+ u'ticket.ticket': {
518+ 'Meta': {'object_name': 'Ticket', 'db_table': "'ticket'"},
519+ 'base_image': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}),
520+ 'bug_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
521+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
522+ 'creator': ('django.db.models.fields.EmailField', [], {'max_length': '4096'}),
523+ 'current_workflow_step': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
524+ 'description': ('django.db.models.fields.TextField', [], {}),
525+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
526+ 'lander_unit': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
527+ 'master_ppa': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}),
528+ 'owner': ('django.db.models.fields.EmailField', [], {'max_length': '4096'}),
529+ 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
530+ 'series': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}),
531+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
532+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
533+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
534+ 'uuid': ('django.db.models.fields.CharField', [], {'default': "'1b1a4510-1db3-11e4-9bc5-4437e68484d8'", 'unique': 'True', 'max_length': '36'}),
535+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'default': "'default'", 'to': u"orm['ticket.Workflow']"})
536+ },
537+ u'ticket.ticketartifact': {
538+ 'Meta': {'object_name': 'TicketArtifact', 'db_table': "'ticket_artifact'"},
539+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
540+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
541+ 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
542+ 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']", 'null': 'True'}),
543+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
544+ },
545+ u'ticket.workflow': {
546+ 'Meta': {'object_name': 'Workflow'},
547+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'primary_key': 'True'}),
548+ 'steps': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
549+ }
550+ }
551+
552+ complete_apps = ['ticket']
553\ No newline at end of file
554
555=== added file 'ticket_system/ticket/migrations/0009_add_creator.py'
556--- ticket_system/ticket/migrations/0009_add_creator.py 1970-01-01 00:00:00 +0000
557+++ ticket_system/ticket/migrations/0009_add_creator.py 2014-08-21 13:48:34 +0000
558@@ -0,0 +1,103 @@
559+# -*- coding: utf-8 -*-
560+import datetime
561+from south.db import db
562+from south.v2 import DataMigration
563+from django.db import models
564+
565+class Migration(DataMigration):
566+
567+ def forwards(self, orm):
568+ # Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..."
569+
570+ for ticket in orm.Ticket.objects.all():
571+ ticket.creator = ticket.owner
572+ ticket.save()
573+
574+ def backwards(self, orm):
575+ raise RuntimeError("This migration cannot be reversed")
576+
577+ models = {
578+ u'project.sourcepackage': {
579+ 'Meta': {'object_name': 'SourcePackage', 'db_table': "'sourcepackage'"},
580+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
581+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '4096'})
582+ },
583+ u'ticket.mergeproposal': {
584+ 'Meta': {'object_name': 'MergeProposal', 'db_table': "'mergeproposal'"},
585+ 'approved_revno': ('django.db.models.fields.IntegerField', [], {}),
586+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
587+ 'lp_url': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
588+ 'project': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
589+ 'sourcepackage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['project.SourcePackage']"}),
590+ 'target_branch': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
591+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
592+ },
593+ u'ticket.review': {
594+ 'Meta': {'object_name': 'Review'},
595+ 'completed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
596+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
597+ 'review_type': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
598+ 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']"}),
599+ 'workflow_step': ('django.db.models.fields.IntegerField', [], {})
600+ },
601+ u'ticket.sourcepackageupload': {
602+ 'Meta': {'object_name': 'SourcePackageUpload', 'db_table': "'sourcepackageupload'"},
603+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
604+ 'sourcepackage': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['project.SourcePackage']"}),
605+ 'version': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
606+ },
607+ u'ticket.subticket': {
608+ 'Meta': {'object_name': 'SubTicket', 'db_table': "'subticket'"},
609+ 'assignee': ('django.db.models.fields.EmailField', [], {'max_length': '254'}),
610+ 'current_workflow_step': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
611+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
612+ 'merge_proposal': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['ticket.MergeProposal']", 'null': 'True', 'blank': 'True'}),
613+ 'source_package_upload': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['ticket.SourcePackageUpload']", 'null': 'True', 'blank': 'True'}),
614+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
615+ 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']"})
616+ },
617+ u'ticket.subticketartifact': {
618+ 'Meta': {'object_name': 'SubTicketArtifact', 'db_table': "'subticket_artifact'"},
619+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
620+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
621+ 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
622+ 'subticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.SubTicket']", 'null': 'True'}),
623+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
624+ },
625+ u'ticket.ticket': {
626+ 'Meta': {'object_name': 'Ticket', 'db_table': "'ticket'"},
627+ 'base_image': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}),
628+ 'bug_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
629+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
630+ 'creator': ('django.db.models.fields.EmailField', [], {'max_length': '4096'}),
631+ 'current_workflow_step': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
632+ 'description': ('django.db.models.fields.TextField', [], {}),
633+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
634+ 'lander_unit': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
635+ 'master_ppa': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}),
636+ 'owner': ('django.db.models.fields.EmailField', [], {'max_length': '4096'}),
637+ 'private': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
638+ 'series': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '4096'}),
639+ 'status': ('django.db.models.fields.IntegerField', [], {'default': '0', 'max_length': '4096'}),
640+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
641+ 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
642+ 'uuid': ('django.db.models.fields.CharField', [], {'default': "'458c437a-1db3-11e4-ad48-4437e68484d8'", 'unique': 'True', 'max_length': '36'}),
643+ 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'default': "'default'", 'to': u"orm['ticket.Workflow']"})
644+ },
645+ u'ticket.ticketartifact': {
646+ 'Meta': {'object_name': 'TicketArtifact', 'db_table': "'ticket_artifact'"},
647+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
648+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
649+ 'reference': ('django.db.models.fields.CharField', [], {'max_length': '4096'}),
650+ 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['ticket.Ticket']", 'null': 'True'}),
651+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
652+ },
653+ u'ticket.workflow': {
654+ 'Meta': {'object_name': 'Workflow'},
655+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '4096', 'primary_key': 'True'}),
656+ 'steps': ('django.db.models.fields.CharField', [], {'max_length': '4096'})
657+ }
658+ }
659+
660+ complete_apps = ['ticket']
661+ symmetrical = True
662
663=== modified file 'ticket_system/ticket/models.py'
664--- ticket_system/ticket/models.py 2014-08-18 19:23:55 +0000
665+++ ticket_system/ticket/models.py 2014-08-21 13:48:34 +0000
666@@ -108,7 +108,8 @@
667 max_length=36, editable=False, unique=True, default=get_ticket_uuid,
668 help_text=('UUIDv1 ticket identification represented as '
669 'string (aka \'hyphenated\').'))
670- owner = models.EmailField(max_length=254)
671+ owner = models.EmailField(max_length=4096)
672+ creator = models.EmailField(max_length=4096)
673 title = models.CharField(max_length=4096)
674 description = models.TextField()
675 bug_id = models.IntegerField(null=True, blank=True)
676
677=== modified file 'ticket_system/ticket/tests/test_full_read_api.py'
678--- ticket_system/ticket/tests/test_full_read_api.py 2014-08-05 00:42:26 +0000
679+++ ticket_system/ticket/tests/test_full_read_api.py 2014-08-21 13:48:34 +0000
680@@ -93,6 +93,7 @@
681 u'created': unicode(self.ticket.created.strftime(
682 settings.TEST_DATETIME_FORMAT)),
683 u'owner': unicode(self.ticket.owner),
684+ u'creator': unicode(self.ticket.creator),
685 u'base_image': unicode(self.ticket.base_image),
686 u'id': self.ticket.pk,
687 u'lander_unit': 0,
688
689=== modified file 'ticket_system/ticket/tests/test_models.py'
690--- ticket_system/ticket/tests/test_models.py 2014-08-05 20:20:26 +0000
691+++ ticket_system/ticket/tests/test_models.py 2014-08-21 13:48:34 +0000
692@@ -70,12 +70,14 @@
693 def create_ticket(title="This is my ticket",
694 description="this is my ticket description",
695 bug_id='12345', owner='test@example.com',
696+ creator='test@example.com',
697 current_workflow_step=TicketWorkflowStep.PKG_BUILDING,
698 status=TicketWorkflowStepStatus.INPROGRESS,
699 base_image='17', series='saucy',
700 master_ppa='ci-engine/ppa'):
701 ticket = Ticket()
702 ticket.owner = owner
703+ ticket.creator = creator
704 ticket.title = title
705 ticket.description = description
706 ticket.bug_id = bug_id
707@@ -149,7 +151,8 @@
708 self.addCleanup(_m.stop)
709 # Creating tickets, independently of its status, creates its
710 # container.
711- self.ticket = create_ticket(owner='test@example.com')
712+ self.ticket = create_ticket(owner='test@example.com',
713+ creator='test@example.com')
714 self.create_container.assert_called_once()
715
716 def test_creating_an_artifact_subticket(self):
717
718=== modified file 'ticket_system/ticket/tests/test_read_api.py'
719--- ticket_system/ticket/tests/test_read_api.py 2014-08-05 00:42:26 +0000
720+++ ticket_system/ticket/tests/test_read_api.py 2014-08-21 13:48:34 +0000
721@@ -105,6 +105,7 @@
722 u'title': unicode(self.ticket.title),
723 u'bug_id': self.ticket.bug_id,
724 u'owner': unicode(self.ticket.owner),
725+ u'creator': unicode(self.ticket.creator),
726 u'base_image': unicode(self.ticket.base_image),
727 u'id': self.ticket.pk,
728 u'lander_unit': 0,
729@@ -142,6 +143,7 @@
730 u'title': unicode(self.ticket.title),
731 u'bug_id': self.ticket.bug_id,
732 u'owner': unicode(self.ticket.owner),
733+ u'creator': unicode(self.ticket.creator),
734 u'base_image': unicode(self.ticket.base_image),
735 u'id': self.ticket.pk,
736 u'lander_unit': 0,
737@@ -194,6 +196,7 @@
738 u'lander_unit': 0,
739 u'master_ppa': unicode(self.ticket.master_ppa),
740 u'owner': unicode(self.ticket.owner),
741+ u'creator': unicode(self.ticket.creator),
742 u'status': unicode(get_enum_title(self.ticket.status,
743 TicketWorkflowStepStatus)),
744 u'title': unicode(self.ticket.title),
745@@ -220,6 +223,7 @@
746 u'title': unicode(self.ticket.title),
747 u'bug_id': self.ticket.bug_id,
748 u'owner': unicode(self.ticket.owner),
749+ u'creator': unicode(self.ticket.creator),
750 u'base_image': unicode(self.ticket.base_image),
751 u'id': self.ticket.pk,
752 u'lander_unit': 0,
753@@ -445,6 +449,7 @@
754 u'title': unicode(self.ticket_6.title),
755 u'bug_id': self.ticket_6.bug_id,
756 u'owner': unicode(self.ticket_6.owner),
757+ u'creator': unicode(self.ticket_6.creator),
758 u'base_image': unicode(self.ticket_6.base_image),
759 u'id': self.ticket_6.pk,
760 u'lander_unit': self.ticket_6.lander_unit,
761
762=== modified file 'ticket_system/ticket/tests/test_write_api.py'
763--- ticket_system/ticket/tests/test_write_api.py 2014-08-05 00:42:26 +0000
764+++ ticket_system/ticket/tests/test_write_api.py 2014-08-21 13:48:34 +0000
765@@ -61,6 +61,7 @@
766 self.detail_url = self.resource + '{0}/'.format(self.ticket.pk)
767 self.post_ticket_data = {
768 'owner': 'owner@example.com',
769+ 'creator': 'creator@example.com',
770 'title': 'My first ticket!',
771 'description': 'This if my first ticket. See what it can do',
772 'bug_id': '12345',
773@@ -84,6 +85,7 @@
774 a_uuid = str(uuid.uuid1())
775 params = {
776 'owner': 'owner@example.com',
777+ 'creator': 'creator@example.com',
778 'title': 'UUID FTW!',
779 'description': 'This ticket has UUID',
780 'bug_id': '12345',

Subscribers

People subscribed via source and target branches