Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Count contributions prior to user joining event #202

Merged
merged 26 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7052df7
Move stats listener and calculator to a Stats namespace
psrpinto Apr 10, 2024
20038ba
Add Stats_Importer
psrpinto Apr 11, 2024
649b9ba
Make get_by_event_id() return all the stats
psrpinto Apr 11, 2024
18c6022
Add tests for stats import
psrpinto Apr 11, 2024
227f95a
Implement import_for_user_and_event()
psrpinto Apr 11, 2024
dd1106f
Add is_active() method to Event
psrpinto Apr 11, 2024
ba09601
Add is_past() to Event
psrpinto Apr 11, 2024
46c1c21
Assign user's id to a variable
psrpinto Apr 11, 2024
d073b8b
Import stats for the user when they join an event
psrpinto Apr 11, 2024
6965c76
Make sure it's not possible to attend/un-attend past events
psrpinto Apr 11, 2024
fb0e617
Update tests/stats/stats-importer.php
akirk Apr 11, 2024
fb3ddfc
Add a translation before the event
akirk Apr 11, 2024
b4e91b8
Use gmdate
akirk Apr 11, 2024
ddeb3e5
Add earlier translation earlier
akirk Apr 11, 2024
236ee07
Use is_in_the_past()
psrpinto Apr 11, 2024
2757ce2
Use locale instead of slug
psrpinto Apr 12, 2024
4512aec
Import stats for the user when they join an event
psrpinto Apr 11, 2024
d22c116
Fix tests
psrpinto Apr 12, 2024
6517074
Merge branch 'trunk' into early-contributions
psrpinto Apr 12, 2024
81afcfc
Merge branch 'trunk' into early-contributions
psrpinto Apr 15, 2024
0d5defe
Don't import rejected or old translations
psrpinto Apr 15, 2024
bd434ee
Merge branch 'trunk' into early-contributions
psrpinto Apr 15, 2024
b7b2d8b
Declare property
psrpinto Apr 15, 2024
b2fe389
Check for status in array
psrpinto Apr 15, 2024
731b789
Use between in query
psrpinto Apr 15, 2024
52b1562
Fix whitespace
psrpinto Apr 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
require_once __DIR__ . '/includes/event/event-repository.php';
require_once __DIR__ . '/includes/event/event-repository-cached.php';
require_once __DIR__ . '/includes/event/event-form-handler.php';
require_once __DIR__ . '/includes/stats-calculator.php';
require_once __DIR__ . '/includes/stats-listener.php';
require_once __DIR__ . '/includes/stats/stats-calculator.php';
require_once __DIR__ . '/includes/stats/stats-importer.php';
require_once __DIR__ . '/includes/stats/stats-listener.php';
2 changes: 1 addition & 1 deletion includes/event/event-form-handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Exception;
use GP;
use WP_Error;
use Wporg\TranslationEvents\Stats_Calculator;
use Wporg\TranslationEvents\Stats\Stats_Calculator;

class Event_Form_Handler {
private Event_Repository_Interface $event_repository;
Expand Down
11 changes: 11 additions & 0 deletions includes/event/event.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Wporg\TranslationEvents\Event;

use DateTimeImmutable;
use DateTimeZone;
use Exception;
use Throwable;
Expand Down Expand Up @@ -86,6 +87,16 @@ public function end(): Event_End_Date {
return $this->end;
}

public function is_active(): bool {
$now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
return $now >= $this->start->utc() && $now < $this->end->utc();
}

public function is_past(): bool {
$now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
return $this->end->utc() < $now;
psrpinto marked this conversation as resolved.
Show resolved Hide resolved
}

public function timezone(): DateTimeZone {
return $this->timezone;
}
Expand Down
2 changes: 1 addition & 1 deletion includes/routes/event/details.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use Wporg\TranslationEvents\Attendee\Attendee_Repository;
use Wporg\TranslationEvents\Event\Event_Repository_Interface;
use Wporg\TranslationEvents\Routes\Route;
use Wporg\TranslationEvents\Stats_Calculator;
use Wporg\TranslationEvents\Stats\Stats_Calculator;
use Wporg\TranslationEvents\Translation_Events;

/**
Expand Down
2 changes: 1 addition & 1 deletion includes/routes/event/edit.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Wporg\TranslationEvents\Event\Event_Repository_Interface;
use Wporg\TranslationEvents\Routes\Route;
use Wporg\TranslationEvents\Stats_Calculator;
use Wporg\TranslationEvents\Stats\Stats_Calculator;
use Wporg\TranslationEvents\Translation_Events;

/**
Expand Down
20 changes: 17 additions & 3 deletions includes/routes/user/attend-event.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Wporg\TranslationEvents\Attendee\Attendee_Repository;
use Wporg\TranslationEvents\Event\Event_Repository_Interface;
use Wporg\TranslationEvents\Routes\Route;
use Wporg\TranslationEvents\Stats\Stats_Importer;
use Wporg\TranslationEvents\Translation_Events;

/**
Expand All @@ -16,30 +17,43 @@
class Attend_Event_Route extends Route {
private Event_Repository_Interface $event_repository;
private Attendee_Repository $attendee_repository;
private Stats_Importer $stats_importer;

public function __construct() {
parent::__construct();
$this->event_repository = Translation_Events::get_event_repository();
$this->attendee_repository = Translation_Events::get_attendee_repository();
$this->stats_importer = new Stats_Importer();
}

public function handle( int $event_id ): void {
$user = wp_get_current_user();
if ( ! $user ) {
$this->die_with_error( esc_html__( 'Only logged-in users can attend events', 'gp-translation-events' ), 403 );
}
$user_id = $user->ID;

$event = $this->event_repository->get_event( $event_id );
if ( ! $event ) {
$this->die_with_404();
}

$attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID );
if ( $event->is_past() ) {
$this->die_with_error( esc_html__( 'Cannot attend or un-attend a past event', 'gp-translation-events' ), 403 );
}

$attendee = $this->attendee_repository->get_attendee( $event->id(), $user_id );
if ( $attendee instanceof Attendee ) {
$this->attendee_repository->remove_attendee( $event->id(), $user->ID );
$this->attendee_repository->remove_attendee( $event->id(), $user_id );
} else {
$attendee = new Attendee( $event->id(), $user->ID );
$attendee = new Attendee( $event->id(), $user_id );
$this->attendee_repository->insert_attendee( $attendee );

// If the event is active right now,
// import stats for translations the user created since the event started.
if ( $event->is_active() ) {
$this->stats_importer->import_for_user_and_event( $user_id, $event );
}
}

wp_safe_redirect( gp_url( "/events/{$event->slug()}" ) );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Wporg\TranslationEvents;
namespace Wporg\TranslationEvents\Stats;

use Exception;
use WP_Post;
Expand Down
41 changes: 41 additions & 0 deletions includes/stats/stats-importer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Wporg\TranslationEvents\Stats;

use Wporg\TranslationEvents\Event\Event;

class Stats_Importer {
/**
* Imports the contributions a user made while a given event was active.
*/
public function import_for_user_and_event( int $user_id, Event $event ): void {
global $wpdb, $gp_table_prefix;

// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query(
$wpdb->prepare(
"
insert ignore into {$gp_table_prefix}event_actions
(event_id, user_id, original_id, locale, action, happened_at)
select %d, t.user_id, t.original_id, ts.slug, %s, t.date_added
psrpinto marked this conversation as resolved.
Show resolved Hide resolved
from {$gp_table_prefix}translations t,
{$gp_table_prefix}translation_sets ts
where t.user_id = %d
and t.translation_set_id = ts.id
AND date_added >= %s
psrpinto marked this conversation as resolved.
Show resolved Hide resolved
AND date_added <= %s
",
array(
'event_id' => $event->id(),
'action' => Stats_Listener::ACTION_CREATE,
'user_id' => $user_id,
'date_added_after' => $event->start()->utc()->format( 'Y-m-d H:i:s' ),
'date_added_before' => $event->end()->utc()->format( 'Y-m-d H:i:s' ),
),
),
);
// phpcs:enable
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Wporg\TranslationEvents;
namespace Wporg\TranslationEvents\Stats;

use DateTimeImmutable;
use DateTimeZone;
Expand Down
1 change: 1 addition & 0 deletions includes/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Exception;
use WP_Query;
use Wporg\TranslationEvents\Attendee\Attendee;
use Wporg\TranslationEvents\Stats\Stats_Calculator;

class Upgrade {
private const VERSION = 2;
Expand Down
2 changes: 2 additions & 0 deletions templates/event.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

use Wporg\TranslationEvents\Event\Event_End_Date;
use Wporg\TranslationEvents\Event\Event_Start_Date;
use Wporg\TranslationEvents\Stats\Event_Stats;
use Wporg\TranslationEvents\Stats\Stats_Row;
akirk marked this conversation as resolved.
Show resolved Hide resolved

/** @var int $event_id */
/** @var string $event_title */
Expand Down
1 change: 1 addition & 0 deletions templates/events-my-events.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
namespace Wporg\TranslationEvents;

use Wporg\TranslationEvents\Event\Events_Query_Result;
use Wporg\TranslationEvents\Stats\Stats_Calculator;

/** @var Events_Query_Result $events_i_created_query */
/** @var Events_Query_Result $events_i_attended_query */
Expand Down
80 changes: 80 additions & 0 deletions tests/event/event.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,84 @@ public function test_validates_status() {
'',
);
}

public function test_is_active() {
$timezone = new DateTimeZone( 'Europe/Lisbon' );
$start = new Event_Start_Date( 'now' );
$end = new Event_End_Date( 'now' );

$past_event = new Event(
0,
$start->modify( '-1 hour' ),
$end,
$timezone,
'publish',
'Foo title',
'',
);

$active_event = new Event(
0,
$start,
$end->modify( '+1 hour' ),
$timezone,
'publish',
'Foo title',
'',
);

$future_event = new Event(
0,
$start->modify( '+1 hour' ),
$end->modify( '+2 hours' ),
$timezone,
'publish',
'Foo title',
'',
);

$this->assertFalse( $past_event->is_active() );
$this->assertTrue( $active_event->is_active() );
$this->assertFalse( $future_event->is_active() );
}

public function test_is_past() {
$timezone = new DateTimeZone( 'Europe/Lisbon' );
$start = new Event_Start_Date( 'now' );
$end = new Event_End_Date( 'now' );

$past_event = new Event(
0,
$start->modify( '-1 hour' ),
$end->modify( '-30 minutes' ),
$timezone,
'publish',
'Foo title',
'',
);

$active_event = new Event(
0,
$start,
$end->modify( '+1 hour' ),
$timezone,
'publish',
'Foo title',
'',
);

$future_event = new Event(
0,
$start->modify( '+1 hour' ),
$end->modify( '+2 hours' ),
$timezone,
'publish',
'Foo title',
'',
);

$this->assertTrue( $past_event->is_past() );
$this->assertFalse( $active_event->is_past() );
$this->assertFalse( $future_event->is_past() );
}
}
2 changes: 1 addition & 1 deletion tests/lib/stats-factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function get_by_event_id( $event_id ): array {
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
return $wpdb->get_row( $wpdb->prepare( "select * from {$gp_table_prefix}event_actions where event_id = %s", $event_id ), ARRAY_A );
return $wpdb->get_results( $wpdb->prepare( "select * from {$gp_table_prefix}event_actions where event_id = %s", $event_id ), ARRAY_A );
// phpcs:enable
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php

namespace Wporg\Tests;
namespace Wporg\Tests\Stats;

use GP_UnitTestCase;
use Wporg\TranslationEvents\Stats_Calculator;
use Wporg\TranslationEvents\Stats\Stats_Calculator;
use Wporg\TranslationEvents\Tests\Event_Factory;
use Wporg\TranslationEvents\Tests\Stats_Factory;

Expand Down
74 changes: 74 additions & 0 deletions tests/stats/stats-importer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Wporg\Tests\Stats;

use DateTimeImmutable;
use DateTimeZone;
use GP_UnitTestCase;
use Wporg\TranslationEvents\Attendee\Attendee_Repository;
use Wporg\TranslationEvents\Event\Event_Repository;
use Wporg\TranslationEvents\Stats\Stats_Importer;
use Wporg\TranslationEvents\Tests\Event_Factory;
use Wporg\TranslationEvents\Tests\Stats_Factory;
use Wporg\TranslationEvents\Tests\Translation_Factory;

class Stats_Importer_Test extends GP_UnitTestCase {
private Translation_Factory $translation_factory;
private Event_Factory $event_factory;
private Stats_Factory $stats_factory;
private Stats_Importer $importer;
private Event_Repository $event_repository;

public function setUp(): void {
parent::setUp();
$this->translation_factory = new Translation_Factory( $this->factory );
$this->event_factory = new Event_Factory();
$this->stats_factory = new Stats_Factory();
$this->event_repository = new Event_Repository( new Attendee_Repository() );
$this->importer = new Stats_Importer();
}

public function test_import_for_user_and_event() {
$this->set_normal_user_as_current();
$user_id = wp_get_current_user()->ID;

// Create a translation before the event starts, which should not be imported.
$this->translation_factory->create( $user_id );

// Wait for the event to start.
// It needs to be 2 seconds because the event starts at "now -1 second".
// TODO: This makes test runs take longer, we should find another way.
sleep( 2 );

$event_id = $this->event_factory->create_active();
akirk marked this conversation as resolved.
Show resolved Hide resolved
$event = $this->event_repository->get_event( $event_id );

// Create translations while the event is active.
$translation1 = $this->translation_factory->create( $user_id );
$translation2 = $this->translation_factory->create( $user_id );

// Make sure no stats were created yet.
$this->assertEquals( 0, $this->stats_factory->get_count() );

$this->importer->import_for_user_and_event( $user_id, $event );

$stats = $this->stats_factory->get_by_event_id( $event_id );
$this->assertCount( 2, $stats );

$stats1 = $stats[0];
$this->assertEquals( $event_id, $stats1['event_id'] );
$this->assertEquals( $user_id, $stats1['user_id'] );
$this->assertEquals( $translation1->original_id, $stats1['original_id'] );
$this->assertEquals( 'create', $stats1['action'] );
$this->assertEquals( 'default', $stats1['locale'] );
$this->assertEquals( $translation1->date_added, $stats1['happened_at'] );

$stats2 = $stats[1];
$this->assertEquals( $event_id, $stats2['event_id'] );
$this->assertEquals( $user_id, $stats2['user_id'] );
$this->assertEquals( $translation2->original_id, $stats2['original_id'] );
$this->assertEquals( 'create', $stats2['action'] );
$this->assertEquals( 'default', $stats2['locale'] );
$this->assertEquals( $translation2->date_added, $stats1['happened_at'] );
}
}
Loading
Loading