air18n 0.1.40 → 0.1.41
Sign up to get free protection for your applications and to get access to all the features.
@@ -337,10 +337,10 @@ module Air18n
|
|
337
337
|
end
|
338
338
|
|
339
339
|
def self.translator_activity_data_v3 user_id=0, opts={}
|
340
|
-
|
340
|
+
aggregate_v3_translation_activity(compute_v3_translation_activity(user_id, opts[:since], opts[:to]), opts)
|
341
341
|
end
|
342
342
|
|
343
|
-
def self.
|
343
|
+
def self.aggregate_v3_translation_activity(translation_pairs, opts={})
|
344
344
|
per_user_locale_month_year =
|
345
345
|
Hash.new {|h, year| h[year] =
|
346
346
|
Hash.new {|h, month| h[month] =
|
@@ -356,16 +356,15 @@ module Air18n
|
|
356
356
|
} } } } } }
|
357
357
|
|
358
358
|
translation_pairs.each do |pair|
|
359
|
-
words_translated, words_verified = self.word_counts_from_translation_pair(pair)
|
360
359
|
sums = per_user_locale_month_year[
|
361
360
|
pair[:datetime].year][
|
362
361
|
pair[:datetime].month][
|
363
362
|
pair[:user_id]][
|
364
363
|
pair[:locale]][
|
365
364
|
pair[:datetime].day]
|
366
|
-
sums[:words_translated] += words_translated
|
367
|
-
sums[:words_verified] += words_verified
|
368
|
-
if words_verified > 0
|
365
|
+
sums[:words_translated] += pair[:words_translated]
|
366
|
+
sums[:words_verified] += pair[:words_verified]
|
367
|
+
if pair[:words_verified] > 0
|
369
368
|
sums[:keys_verified] << pair[:phrase_key]
|
370
369
|
sums[:phrases_verified] += 1
|
371
370
|
else
|
@@ -397,9 +396,6 @@ module Air18n
|
|
397
396
|
:verified_keys => sums[:keys_verified],
|
398
397
|
}
|
399
398
|
}
|
400
|
-
LoggingHelper.info("keys translated: #{sums[:keys_translated]}")
|
401
|
-
LoggingHelper.info("keys verified: #{sums[:keys_verified]}")
|
402
|
-
LoggingHelper.info("ret now: #{ret.inspect}")
|
403
399
|
end
|
404
400
|
else
|
405
401
|
monthly_sums = {
|
@@ -429,9 +425,6 @@ module Air18n
|
|
429
425
|
:verified_keys => monthly_sums[:keys_verified],
|
430
426
|
}
|
431
427
|
}
|
432
|
-
LoggingHelper.info("keys translated: #{monthly_sums[:keys_translated]}")
|
433
|
-
LoggingHelper.info("keys verified: #{monthly_sums[:keys_verified]}")
|
434
|
-
LoggingHelper.info("ret now: #{ret.inspect}")
|
435
428
|
end
|
436
429
|
end
|
437
430
|
end
|
@@ -485,7 +478,7 @@ module Air18n
|
|
485
478
|
(translation_pair[:source_word_count] * proportion_verified).floor]
|
486
479
|
end
|
487
480
|
|
488
|
-
def self.
|
481
|
+
def self.compute_v3_translation_activity(user_id, from_date, to_date)
|
489
482
|
ret = []
|
490
483
|
Phrase.select(:id).find_in_batches do |batch|
|
491
484
|
pt_scope = PhraseTranslation.where(:phrase_id => batch)
|
@@ -502,32 +495,68 @@ module Air18n
|
|
502
495
|
phrase_to_phrase_translations.each do |(locale, phrase_id), phrase_translations|
|
503
496
|
phrase_translations.sort_by! { |pt| pt.created_at }
|
504
497
|
|
505
|
-
previous_translation =
|
506
|
-
|
507
|
-
where(:phrase_id => phrase_id).
|
508
|
-
order("created_at DESC").
|
509
|
-
first
|
498
|
+
previous_translation = :uncomputed
|
499
|
+
|
510
500
|
phrase_translations.each do |pt|
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
501
|
+
translation_pair = {
|
502
|
+
:translation => pt.value,
|
503
|
+
:locale => pt.locale,
|
504
|
+
:user_id => pt.user_id,
|
505
|
+
:datetime => pt.created_at,
|
506
|
+
:source_word_count => pt.source_word_count,
|
507
|
+
:phrase_key => pt.key,
|
508
|
+
}
|
509
|
+
|
510
|
+
if pt.payment_details.present?
|
511
|
+
payment_details = JSON.parse(pt.payment_details)
|
515
512
|
else
|
516
|
-
|
517
|
-
previous_translation_user_id = 0
|
518
|
-
was_stale = false
|
513
|
+
payment_details = {}
|
519
514
|
end
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
515
|
+
|
516
|
+
if !payment_details.include?('v3')
|
517
|
+
# If we haven't computed v3 payment details already, compute them
|
518
|
+
# and then save details to the PhraseTranslation itself.
|
519
|
+
|
520
|
+
if previous_translation == :uncomputed
|
521
|
+
previous_translation = PhraseTranslation.
|
522
|
+
where("created_at < ?", phrase_translations.first.created_at - 1.second).
|
523
|
+
where(:locale => locale).
|
524
|
+
where(:phrase_id => phrase_id).
|
525
|
+
order("created_at DESC").
|
526
|
+
first
|
527
|
+
end
|
528
|
+
|
529
|
+
if previous_translation
|
530
|
+
previous_translation_text = previous_translation.value
|
531
|
+
previous_translation_user_id = previous_translation.user_id
|
532
|
+
was_stale = previous_translation.source_hash != pt.source_hash
|
533
|
+
else
|
534
|
+
previous_translation_text = ''
|
535
|
+
previous_translation_user_id = 0
|
536
|
+
was_stale = false
|
537
|
+
end
|
538
|
+
|
539
|
+
words_translated, words_verified = self.word_counts_from_translation_pair(
|
540
|
+
translation_pair.merge(
|
541
|
+
:previous_translation => previous_translation_text,
|
542
|
+
:was_stale => was_stale,
|
543
|
+
:previous_user_id => previous_translation_user_id,
|
544
|
+
))
|
545
|
+
|
546
|
+
payment_details['v3'] = {
|
547
|
+
't' => words_translated,
|
548
|
+
'v' => words_verified
|
549
|
+
}
|
550
|
+
|
551
|
+
pt.payment_details = payment_details.to_json
|
552
|
+
LoggingHelper.info "saving payment details: "
|
553
|
+
pt.save
|
554
|
+
end
|
555
|
+
|
556
|
+
translation_pair[:words_translated] = payment_details['v3']['t']
|
557
|
+
translation_pair[:words_verified] = payment_details['v3']['v']
|
558
|
+
|
559
|
+
ret << translation_pair
|
531
560
|
|
532
561
|
previous_translation = pt
|
533
562
|
end
|
data/lib/air18n/version.rb
CHANGED
@@ -40,6 +40,7 @@ class Air18nMigration < ActiveRecord::Migration
|
|
40
40
|
t.boolean "is_latest", :default => false
|
41
41
|
t.integer "source_word_count", :default => 0
|
42
42
|
t.string "source_hash"
|
43
|
+
t.text "payment_details"
|
43
44
|
end
|
44
45
|
|
45
46
|
add_index "phrase_translations", ["locale", "is_latest", "is_stale", "is_verification"], :name => "index_phrase_translations_on_locale_and_flags"
|
@@ -374,54 +374,133 @@ describe Air18n::PhraseTranslation do
|
|
374
374
|
##### Phrase 1
|
375
375
|
|
376
376
|
# User 1 translates Phrase 1 for the first time [edit of untranslated phrase]
|
377
|
-
|
377
|
+
@translation_wih_precomputed_edits = FactoryGirl.create(
|
378
|
+
:phrase_translation,
|
379
|
+
:locale => :de,
|
380
|
+
:value => 'urgle burgle',
|
381
|
+
:phrase => @phrase1,
|
382
|
+
:user_id => @user1,
|
383
|
+
:created_at => Date.new(2012, 11, 1),
|
384
|
+
:payment_details => '{"v3":{"t":8,"v":0}}')
|
378
385
|
|
379
386
|
# User 1 tweaks his own translation of Phrase 1. [edit of own unverified translation]
|
380
|
-
|
387
|
+
FactoryGirl.create(
|
388
|
+
:phrase_translation,
|
389
|
+
:locale => :de,
|
390
|
+
:value => 'urgle burgle boo',
|
391
|
+
:phrase => @phrase1,
|
392
|
+
:user_id => @user1,
|
393
|
+
:created_at => Date.new(2012, 11, 2))
|
381
394
|
|
382
395
|
# User 2 verifies translation of Phrase 1. [pure verification]
|
383
|
-
|
396
|
+
FactoryGirl.create(
|
397
|
+
:phrase_translation,
|
398
|
+
:locale => :de,
|
399
|
+
:value => 'urgle burgle boo',
|
400
|
+
:phrase => @phrase1,
|
401
|
+
:user_id => @user2,
|
402
|
+
:is_verification => true,
|
403
|
+
:created_at => Date.new(2012, 11, 3))
|
384
404
|
|
385
405
|
# Phrase 1 becomes stale.
|
386
406
|
@phrase1.value = "one two three four five six seven eight nine ten eleven twelve thirteen"
|
387
407
|
@phrase1.save!
|
388
408
|
|
389
409
|
# User 1 reedits translation of Phrase 1. [edit of other's stale translation]
|
390
|
-
|
410
|
+
@phrase_without_payment_details = FactoryGirl.create(
|
411
|
+
:phrase_translation,
|
412
|
+
:locale => :de,
|
413
|
+
:value => 'oogie boogie boo',
|
414
|
+
:phrase => @phrase1,
|
415
|
+
:user_id => @user1,
|
416
|
+
:created_at => Date.new(2012, 11, 4))
|
391
417
|
|
392
418
|
# User 2 pure-verifies of Phrase 1.
|
393
|
-
|
419
|
+
FactoryGirl.create(
|
420
|
+
:phrase_translation,
|
421
|
+
:locale => :de,
|
422
|
+
:value => 'oogie boogie boo',
|
423
|
+
:is_verification => true,
|
424
|
+
:phrase => @phrase1,
|
425
|
+
:user_id => @user2,
|
426
|
+
:created_at => Date.new(2012, 11, 5))
|
394
427
|
|
395
428
|
# User 1 edits verified translation of Phrase 1. [edit of other's verified translation]
|
396
|
-
|
429
|
+
FactoryGirl.create(:phrase_translation,
|
430
|
+
:locale => :de,
|
431
|
+
:value => 'oogie booooogie boo hat ogl',
|
432
|
+
:phrase => @phrase1,
|
433
|
+
:user_id => @user1,
|
434
|
+
:is_verification => true,
|
435
|
+
:created_at => Date.new(2012, 11, 6))
|
397
436
|
|
398
437
|
##### Phrase 2
|
399
438
|
|
400
439
|
# User 1 translates Phrase 2 for the first time, but earlier in the year.
|
401
|
-
|
440
|
+
FactoryGirl.create(
|
441
|
+
:phrase_translation,
|
442
|
+
:locale => :de,
|
443
|
+
:value => 'The only verdict is vengence',
|
444
|
+
:phrase => @phrase2,
|
445
|
+
:user_id => @user1,
|
446
|
+
:created_at => Date.new(2012, 9, 1))
|
402
447
|
|
403
448
|
# User 1 tweaks their own translation later.
|
404
|
-
|
449
|
+
FactoryGirl.create(
|
450
|
+
:phrase_translation,
|
451
|
+
:locale => :de,
|
452
|
+
:value => 'The only verdict is VENGENCE',
|
453
|
+
:phrase => @phrase2,
|
454
|
+
:user_id => @user1,
|
455
|
+
:created_at => Date.new(2012, 11, 1))
|
405
456
|
|
406
457
|
# User 2 tweaks translation of Phrase 2.
|
407
|
-
|
458
|
+
FactoryGirl.create(
|
459
|
+
:phrase_translation,
|
460
|
+
:locale => :de,
|
461
|
+
:value => 'a vendetta, held as a votive',
|
462
|
+
:is_verification => true,
|
463
|
+
:phrase => @phrase2,
|
464
|
+
:user_id => @user2,
|
465
|
+
:created_at => Date.new(2012, 11, 2))
|
408
466
|
|
409
467
|
# User 2 tweaks their own translation of Phrase 2. [edit of own verified translation]
|
410
|
-
|
468
|
+
FactoryGirl.create(
|
469
|
+
:phrase_translation,
|
470
|
+
:locale => :de,
|
471
|
+
:value => 'a vendetta, held as a votive, not in vain',
|
472
|
+
:is_verification => true,
|
473
|
+
:phrase => @phrase2,
|
474
|
+
:user_id => @user2,
|
475
|
+
:created_at => Date.new(2012, 11, 3))
|
411
476
|
|
412
477
|
# Phrase 2 becomes stale.
|
413
478
|
@phrase2.value = "one two three four five six seven eight nine"
|
414
479
|
@phrase2.save!
|
415
480
|
|
416
481
|
# User 2 verifies existing translation of Phrase 2. [pure verification of own stale translation]
|
417
|
-
|
482
|
+
FactoryGirl.create(
|
483
|
+
:phrase_translation,
|
484
|
+
:locale => :de,
|
485
|
+
:value => 'a vendetta, held as a votive, not in vain',
|
486
|
+
:is_verification => true,
|
487
|
+
:phrase => @phrase2,
|
488
|
+
:user_id => @user2,
|
489
|
+
:created_at => Date.new(2012, 11, 4))
|
418
490
|
|
419
491
|
# Phrase 2 becomes stale again.
|
420
492
|
@phrase2.value = "one two three"
|
421
493
|
@phrase2.save!
|
422
494
|
|
423
495
|
# User 2 tweaks translation of Phrase 2. [pure verification of own stale translation]
|
424
|
-
|
496
|
+
FactoryGirl.create(
|
497
|
+
:phrase_translation,
|
498
|
+
:locale => :de,
|
499
|
+
:value => 'a vendetta, held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous',
|
500
|
+
:is_verification => true,
|
501
|
+
:phrase => @phrase2,
|
502
|
+
:user_id => @user2,
|
503
|
+
:created_at => Date.new(2012, 11, 5))
|
425
504
|
end
|
426
505
|
|
427
506
|
@golden_pairs =
|
@@ -433,6 +512,8 @@ describe Air18n::PhraseTranslation do
|
|
433
512
|
:previous_user_id=>0,
|
434
513
|
:datetime=>Date.new(2012, 11, 01),
|
435
514
|
:source_word_count=>8,
|
515
|
+
:words_translated=>8, # "" -> "urgle burgle", user 5
|
516
|
+
:words_verified=>0,
|
436
517
|
:phrase_key=>'v3 regime payments key 1'},
|
437
518
|
{:translation=>"urgle burgle boo",
|
438
519
|
:previous_translation=>"urgle burgle",
|
@@ -442,6 +523,8 @@ describe Air18n::PhraseTranslation do
|
|
442
523
|
:previous_user_id=>5,
|
443
524
|
:datetime=>Date.new(2012, 11, 02),
|
444
525
|
:source_word_count=>8,
|
526
|
+
:words_translated=>0, # same translator making an edit, user 5
|
527
|
+
:words_verified=>0,
|
445
528
|
:phrase_key=>'v3 regime payments key 1'},
|
446
529
|
{:translation=>"urgle burgle boo",
|
447
530
|
:previous_translation=>"urgle burgle boo",
|
@@ -451,6 +534,8 @@ describe Air18n::PhraseTranslation do
|
|
451
534
|
:previous_user_id=>5,
|
452
535
|
:datetime=>Date.new(2012, 11, 03),
|
453
536
|
:source_word_count=>8,
|
537
|
+
:words_translated=>0, # different translator verifying, user 6
|
538
|
+
:words_verified=>8,
|
454
539
|
:phrase_key=>'v3 regime payments key 1'},
|
455
540
|
{:translation=>"oogie boogie boo",
|
456
541
|
:previous_translation=>"urgle burgle boo",
|
@@ -460,6 +545,8 @@ describe Air18n::PhraseTranslation do
|
|
460
545
|
:previous_user_id=>6,
|
461
546
|
:datetime=>Date.new(2012, 11, 04),
|
462
547
|
:source_word_count=>13,
|
548
|
+
:words_translated=>9, # edit of stale phrase; 2/3 words changed, 2/3*13=8.66; ceils to 9, user 5
|
549
|
+
:words_verified=>4,
|
463
550
|
:phrase_key=>'v3 regime payments key 1'},
|
464
551
|
{:translation=>"oogie boogie boo",
|
465
552
|
:previous_translation=>"oogie boogie boo",
|
@@ -469,6 +556,8 @@ describe Air18n::PhraseTranslation do
|
|
469
556
|
:previous_user_id=>5,
|
470
557
|
:datetime=>Date.new(2012, 11, 05),
|
471
558
|
:source_word_count=>13,
|
559
|
+
:words_translated=>0, # verification, user 6
|
560
|
+
:words_verified=>13,
|
472
561
|
:phrase_key=>'v3 regime payments key 1'},
|
473
562
|
{:translation=>"oogie booooogie boo hat ogl",
|
474
563
|
:previous_translation=>"oogie boogie boo",
|
@@ -478,6 +567,8 @@ describe Air18n::PhraseTranslation do
|
|
478
567
|
:previous_user_id=>6,
|
479
568
|
:datetime=>Date.new(2012, 11, 06),
|
480
569
|
:source_word_count=>13,
|
570
|
+
:words_translated=>13, # 3 new words; 3/3 words changed, 3/3*13=13, user 5
|
571
|
+
:words_verified=>0,
|
481
572
|
:phrase_key=>'v3 regime payments key 1'},
|
482
573
|
{:translation=>"The only verdict is VENGENCE",
|
483
574
|
:previous_translation=>"The only verdict is vengence",
|
@@ -487,6 +578,8 @@ describe Air18n::PhraseTranslation do
|
|
487
578
|
:previous_user_id=>5,
|
488
579
|
:datetime=>Date.new(2012, 11, 01),
|
489
580
|
:source_word_count=>6,
|
581
|
+
:words_translated=>0, # Tweak of a translation that was done in past by same person, user 5
|
582
|
+
:words_verified=>0,
|
490
583
|
:phrase_key=>'v3 regime payments key 2'},
|
491
584
|
{:translation=>"a vendetta, held as a votive",
|
492
585
|
:previous_translation=>"The only verdict is VENGENCE",
|
@@ -496,6 +589,8 @@ describe Air18n::PhraseTranslation do
|
|
496
589
|
:previous_user_id=>5,
|
497
590
|
:datetime=>Date.new(2012, 11, 02),
|
498
591
|
:source_word_count=>6,
|
592
|
+
:words_translated=>6, # Rewrite of translation by other person, user 6
|
593
|
+
:words_verified=>0,
|
499
594
|
:phrase_key=>'v3 regime payments key 2'},
|
500
595
|
{:translation=>"a vendetta, held as a votive, not in vain",
|
501
596
|
:previous_translation=>"a vendetta, held as a votive",
|
@@ -505,6 +600,8 @@ describe Air18n::PhraseTranslation do
|
|
505
600
|
:previous_user_id=>6,
|
506
601
|
:datetime=>Date.new(2012, 11, 03),
|
507
602
|
:source_word_count=>6,
|
603
|
+
:words_translated=>0, # Tweak by same person, user 6
|
604
|
+
:words_verified=>0,
|
508
605
|
:phrase_key=>'v3 regime payments key 2'},
|
509
606
|
{:translation=>"a vendetta, held as a votive, not in vain",
|
510
607
|
:previous_translation=>"a vendetta, held as a votive, not in vain",
|
@@ -514,6 +611,8 @@ describe Air18n::PhraseTranslation do
|
|
514
611
|
:previous_user_id=>6,
|
515
612
|
:datetime=>Date.new(2012, 11, 04),
|
516
613
|
:source_word_count=>9,
|
614
|
+
:words_translated=>0, # Pure verification of stale, user 6
|
615
|
+
:words_verified=>9,
|
517
616
|
:phrase_key=>'v3 regime payments key 2'},
|
518
617
|
{:translation=>
|
519
618
|
"a vendetta, held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous",
|
@@ -524,6 +623,8 @@ describe Air18n::PhraseTranslation do
|
|
524
623
|
:previous_user_id=>6,
|
525
624
|
:datetime=>Date.new(2012, 11, 05),
|
526
625
|
:source_word_count=>3,
|
626
|
+
:words_translated=>3, # Edit of stale, user 6
|
627
|
+
:words_verified=>0,
|
527
628
|
:phrase_key=>'v3 regime payments key 2'}]
|
528
629
|
|
529
630
|
@golden_word_counts =
|
@@ -572,10 +673,16 @@ describe Air18n::PhraseTranslation do
|
|
572
673
|
end
|
573
674
|
|
574
675
|
it 'should be able to enumerate pairs of translations' do
|
575
|
-
pairs = Air18n::PhraseTranslation.
|
676
|
+
pairs = Air18n::PhraseTranslation.compute_v3_translation_activity(0, Date.new(2012, 11, 1), Date.new(2012, 12, 1))
|
576
677
|
pairs = pairs.select { |pair| pair[:locale] == 'de' }
|
577
678
|
|
578
|
-
|
679
|
+
(0...@golden_pairs.size).each do |i|
|
680
|
+
# Check that @golden_pairs[i] includes all keys/values in pairs[i].
|
681
|
+
pairs[i].should == @golden_pairs[i].slice(*pairs[i].keys)
|
682
|
+
end
|
683
|
+
|
684
|
+
# Check that it stores computed payment details in the database.
|
685
|
+
@phrase_without_payment_details.reload.payment_details.should == '{"v3":{"t":9,"v":4}}'
|
579
686
|
end
|
580
687
|
|
581
688
|
it 'should be able to count words proportional to amount of translation edited' do
|
@@ -682,13 +789,13 @@ describe Air18n::PhraseTranslation do
|
|
682
789
|
end
|
683
790
|
|
684
791
|
it 'should compute correct monthly activities' do
|
685
|
-
data = Air18n::PhraseTranslation.
|
792
|
+
data = Air18n::PhraseTranslation.aggregate_v3_translation_activity(@golden_pairs, :daily => false)
|
686
793
|
|
687
794
|
data.should == @golden_monthly_activity
|
688
795
|
end
|
689
796
|
|
690
797
|
it 'should compute correct daily activities' do
|
691
|
-
data = Air18n::PhraseTranslation.
|
798
|
+
data = Air18n::PhraseTranslation.aggregate_v3_translation_activity(@golden_pairs, :daily => true)
|
692
799
|
|
693
800
|
data.first.should ==
|
694
801
|
{:year=>2012,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: air18n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.41
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2012-11-
|
16
|
+
date: 2012-11-28 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: i18n
|