~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
Rosetta gives Karma to the users that do some kind of actions.

This test documents when and why Rosetta does it.

Note, that once we commit the transaction, we need to fetch again any
SQLObject we need to use to be sure we have the right information. Seems
like SQLObjects are not persistent between transactions.

    >>> import transaction
    >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
    >>> from lp.registry.interfaces.karma import IKarmaActionSet
    >>> from lp.registry.interfaces.person import IPersonSet
    >>> from lp.translations.enums import RosettaImportStatus
    >>> from lp.translations.interfaces.translationimportqueue import (
    ...     ITranslationImportQueue)
    >>> from lp.services.database.sqlbase import flush_database_caches
    >>> from lp.translations.model.potemplate import POTemplate

    >>> translation_import_queue = getUtility(ITranslationImportQueue)
    >>> karma_action_set = getUtility(IKarmaActionSet)
    >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts

Setup an event listener to help ensure karma is assigned when it should.

    >>> from lp.testing.karma import KarmaAssignedEventListener
    >>> karma_helper = KarmaAssignedEventListener()
    >>> karma_helper.register_listener()


Uploading a .pot file
=====================

The action of upload a .pot file is rewarded with some karma.
The .pot files are supposed to come always from upstream so the action
of upload it increases the value of our data because we are more up to date
with upstream.

Let's say that we have this .pot file to import:

    >>> potemplate_contents = r'''
    ... msgid ""
    ... msgstr ""
    ... "Content-Type: text/plain; charset=CHARSET\n"
    ...
    ... msgid "foo"
    ... msgstr ""
    ... '''
    >>> potemplate = POTemplate.get(1)

We are going to import it as the Rosetta expert team, like we do with
automatic imports from Ubuntu. In this case, we shouldn't give any kind
of karma to that user.

    >>> uploaded_by_maintainer = True
    >>> entry = translation_import_queue.addOrUpdateEntry(
    ...     potemplate.path, potemplate_contents, uploaded_by_maintainer,
    ...     rosetta_experts, productseries=potemplate.productseries,
    ...     potemplate=potemplate)

    # Login as a rosetta expert to be able to change the import's status.
    >>> login('carlos@canonical.com')
    >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
    >>> entry_id = entry.id

The file data is stored in the Librarian, so we have to commit the transaction
to make sure it's stored properly.

    >>> transaction.commit()

We tell the PO template to import from the file data it has.  If any karma is
assigned to the team, our karma_helper will print it out here.

    >>> entry = translation_import_queue[entry_id]
    >>> potemplate = POTemplate.get(1)
    >>> (subject, message) = potemplate.importFromQueue(entry)

(Nothing printed means no karma was assigned)

    >>> transaction.commit()

Let's do the same import as the Foo Bar user.

    >>> personset = getUtility(IPersonSet)
    >>> foo_bar = personset.getByEmail('foo.bar@canonical.com')
    >>> login('foo.bar@canonical.com')

Do the import.

    >>> potemplate = POTemplate.get(1)
    >>> entry = translation_import_queue.addOrUpdateEntry(
    ...     potemplate.path, potemplate_contents, uploaded_by_maintainer,
    ...     foo_bar, productseries=potemplate.productseries,
    ...     potemplate=potemplate)
    >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
    >>> entry_id = entry.id

The file data is stored in the Librarian, so we have to commit the transaction
to make sure it's stored properly.

    >>> transaction.commit()

Tell the PO template to import from the file data it has, and see the karma
being assigned.

    >>> entry = translation_import_queue[entry_id]
    >>> potemplate = POTemplate.get(1)
    >>> (subject, message) = potemplate.importFromQueue(entry)
    Karma added: action=translationtemplateimport, product=evolution
    >>> transaction.commit()


Uploading a .po file
====================

The action of upload a .po file is rewarded with some karma if it comes
from upstream. If it's just a translation update, we don't give karma, for
the upload action, you will get it from the translations you are adding.

Let's say that we have this .po file to import:

    >>> import datetime
    >>> import pytz
    >>> UTC = pytz.timezone('UTC')
    >>> pofile_contents = r'''
    ... msgid ""
    ... msgstr ""
    ... "Content-Type: text/plain; charset=UTF-8\n"
    ... "X-Rosetta-Export-Date: %s\n"
    ...
    ... msgid "foo"
    ... msgstr "bar"
    ... ''' % datetime.datetime.now(UTC).isoformat()
    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')

As we can see, we don't have any information in that file about who
did the translations, so we will get that credit to the person that
did the upload.

First, we are going to import it as the Rosetta expert team, like we do with
automatic imports from Ubuntu. In this case, we shouldn't give any kind
of karma to that user.

Do the import.

    >>> entry = translation_import_queue.addOrUpdateEntry(
    ...     pofile.path, pofile_contents, uploaded_by_maintainer,
    ...     rosetta_experts, productseries=potemplate.productseries,
    ...     potemplate=potemplate, pofile=pofile)
    >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
    >>> entry_id = entry.id

The file data is stored in the Librarian, so we have to commit the transaction
to make sure it's stored properly.

    >>> transaction.commit()

Tell the PO template to import from the file data it has.  If any karma is
assigned to the team, our karma_helper will print it out here.

    >>> potemplate = POTemplate.get(1)
    >>> entry = translation_import_queue[entry_id]
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> (subject, message) = pofile.importFromQueue(entry)
    >>> transaction.commit()


We attach the new file as comming from upstream, that means that we
will give karma only for the upload action.

    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> entry = translation_import_queue.addOrUpdateEntry(
    ...     pofile.path, pofile_contents, uploaded_by_maintainer, foo_bar,
    ...     productseries=potemplate.productseries, potemplate=potemplate,
    ...     pofile=pofile)
    >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
    >>> entry_id = entry.id

The file data is stored in the Librarian, so we have to commit the transaction
to make sure it's stored properly.

    >>> transaction.commit()

Tell the PO file to import from the file data it has.

    >>> potemplate = POTemplate.get(1)
    >>> entry = translation_import_queue[entry_id]
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> (subject, message) = pofile.importFromQueue(entry)
    Karma added: action=translationimportupstream, product=evolution

Now, the user is going to upload a local edition of the .po file. In this
case, we will give karma *only* because the translation change.

    >>> pofile_contents = r'''
    ... msgid ""
    ... msgstr ""
    ... "Content-Type: text/plain; charset=UTF-8\n"
    ... "X-Rosetta-Export-Date: %s\n"
    ...
    ... msgid "foo"
    ... msgstr "bars"
    ... ''' % datetime.datetime.now(UTC).isoformat()

We attach the new file as not comming from upstream.

    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> entry = translation_import_queue.addOrUpdateEntry(
    ...     pofile.path, pofile_contents, not uploaded_by_maintainer, foo_bar,
    ...     productseries=potemplate.productseries, potemplate=potemplate,
    ...     pofile=pofile)
    >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
    >>> entry_id = entry.id

The file data is stored in the Librarian, so we have to commit the transaction
to make sure it's stored properly.

    >>> transaction.commit()

Tell the PO file to import from the file data it has.  The user has rights
to edit translations directly, so his suggestion is approved directly.
No karma is awarded for this action.

    >>> potemplate = POTemplate.get(1)
    >>> entry = translation_import_queue[entry_id]
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> (subject, message) = pofile.importFromQueue(entry)
    >>> transaction.commit()

Let's try the case when a file is uploaded, but no translation is changed.
To do this test, we are going to repeat previous import.

We import it again without changes and see that we don't get karma changes.

    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> entry = translation_import_queue.addOrUpdateEntry(
    ...     pofile.path, pofile_contents, not uploaded_by_maintainer, foo_bar,
    ...     productseries=potemplate.productseries, potemplate=potemplate,
    ...     pofile=pofile)
    >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
    >>> entry_id = entry.id

The file data is stored in the Librarian, so we have to commit the transaction
to make sure it's stored properly.

    >>> transaction.commit()

Tell the PO file to import from the file data it has and see that no karma is
assigned.  If it was, it'd be printed after the call to importFromQueue().

    >>> potemplate = POTemplate.get(1)
    >>> entry = translation_import_queue[entry_id]
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> (subject, message) = pofile.importFromQueue(entry)
    >>> transaction.commit()


Translating from the web UI
===========================

Translating something using the website UI can give you three kind of karma
actions:

 - translationsuggestionadded: When you add a translation but you are not
   allowed to do modifications directly to those translations.
 - translationsuggestionapproved: When you added a translation that is
   actually used because you have edition rights or because a reviewer
   approved your suggestion.
 - translationreview: When you approve a translation from someone else as a
   valid translation to use.


Let's say that we are a translator that is not an editor for the team that
handles translations for a given pofile.

No Privileges Person is a translator that fits this requirement.

    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> no_priv = personset.getByEmail('no-priv@canonical.com')
    >>> pofile.canEditTranslations(no_priv)
    False

We are going to add a suggestion that already exists from other user,
that should not add any kind of karma to this user.

    >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText(u'foo')
    >>> new_translations = {0: 'bar'}
    >>> fuzzy = False
    >>> by_maintainer = False

And we can see as he won't get any karma activity from that, otherwise it'd be
printed after the call to set current translation.

    >>> translationmessage = factory.makeCurrentTranslationMessage(
    ...     pofile, potmsgset, no_priv, translations=new_translations,
    ...     current_other=by_maintainer)
    >>> flush_database_caches()

But now, he will provide a new suggestion.

    >>> new_translations = {0: u'somethingelse'}

At this moment, karma is assigned and thus is printed here.

    >>> no_priv = personset.getByEmail('no-priv@canonical.com')
    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText(u'foo')
    >>> translationmessage = potmsgset.submitSuggestion(
    ...     pofile, no_priv, new_translations)
    Karma added: action=translationsuggestionadded, product=evolution
    >>> transaction.commit()

Now, a reviewer for the Spanish team is going to review that translation and
do other translations.

    >>> kurem = personset.getByEmail('kurem@debian.cz')

Now, he will approve a suggestion.  This will give him karma for reviewing the
suggestion and will also give karma to the user who made the suggestion for it
being approved.

    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText(u'foo')
    >>> new_translations = {0: u'somethingelse'}
    >>> translationmessage = potmsgset.findTranslationMessage(
    ...     pofile, new_translations)
    >>> translationmessage.approve(pofile, kurem)
    Karma added: action=translationsuggestionapproved, product=evolution
    Karma added: action=translationreview, product=evolution
    >>> transaction.commit()

Finally, this reviewer, is going to add a new translation directly. He should
get karma for his translation, but not for a review.

    >>> kurem = personset.getByEmail('kurem@debian.cz')
    >>> potemplate = POTemplate.get(1)
    >>> pofile = potemplate.getPOFileByLang('es')
    >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText(u'foo')
    >>> new_translations = {0: u'changed again'}
    >>> translationmessage = potmsgset.submitSuggestion(
    ...     pofile, kurem, new_translations)
    Karma added: action=translationsuggestionadded, product=evolution
    >>> translationmessage.approve(pofile, kurem)
    >>> transaction.commit()


IPOTemplate description change
==============================

When someone adds a description for an IPOTemplate, we give them some karma
because they are giving more information to our users about the usage of
that template.

We are going to use Sample Person for this test as he's the owner of the
product from where the IPOTemplate is and he has rights to change the
description.

    >>> from lp.translations.publisher import TranslationsLayer
    >>> sample_person = personset.getByEmail('test@canonical.com')
    >>> login('test@canonical.com')
    >>> form = {
    ...     u'field.owner': u'test@canonical.com',
    ...     u'field.name': u'test',
    ...     u'field.priority': u'0',
    ...     u'field.description': u'This is a new description',
    ...     u'field.actions.change': u'Change'}
    >>> potemplate_view = create_view(
    ...     potemplate, '+edit', form=form, layer=TranslationsLayer)
    >>> potemplate_view.request.method = 'POST'

Let's see the description we have atm:

    >>> potemplate.description
    u'Template for evolution in hoary'

We do the update and see the karma being assigned.

    >>> status = potemplate_view.initialize()
    Karma added:
    action=translationtemplatedescriptionchanged, product=evolution

And the new one is:

    >>> potemplate.description
    u'This is a new description'

Now, let's ensure that we've covered every one of Rosetta's karma
actions.

    >>> from lp.registry.model.karma import KarmaCategory
    >>> translation_category = KarmaCategory.byName('translations')
    >>> for karma_action in translation_category.karmaactions:
    ...     assert karma_action in karma_helper.added_karma_actions, (
    ...         '%s was not test!' % karma_action.name)

Unregister the event listener to make sure we won't interfere in other tests.

    >>> karma_helper.unregister_listener()