Skip to content

Commit

Permalink
feature: Add campaign revenue data
Browse files Browse the repository at this point in the history
  • Loading branch information
kjohnson committed Oct 25, 2024
1 parent d2a5dd0 commit df44763
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 11 deletions.
17 changes: 17 additions & 0 deletions src/Campaigns/CampaignDonationQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ public function countDonors(): int
return $query->count('DISTINCT donorId.meta_value');
}

/**
* @unreleased
*/
public function getDonationsByDay(): array
{
$query = clone $this;
$query->joinDonationMeta(DonationMetaKeys::AMOUNT, 'amount');
$query->joinDonationMeta('_give_fee_donation_amount', 'intendedAmount');
$query->joinDonationMeta('_give_completed_date', 'completed');
$query->select(
'DATE(completed.meta_value) as date',
'SUM(COALESCE(NULLIF(intendedAmount.meta_value,0), NULLIF(amount.meta_value,0), 0)) as amount'
);
$query->groupBy('date');
return $query->getAll();
}

/**
* An opinionated join method for the donation meta table.
* @unreleased
Expand Down
96 changes: 96 additions & 0 deletions src/Campaigns/Routes/GetCampaignRevenue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace Give\Campaigns\Routes;

use DateInterval;
use DatePeriod;
use DateTime;
use DateTimeImmutable;
use DateTimeInterface;
use Exception;
use Give\API\RestRoute;
use Give\Campaigns\CampaignDonationQuery;
use Give\Campaigns\Models\Campaign;
use Give\Campaigns\ValueObjects\CampaignRoute;
use Give\Framework\Support\Facades\DateTime\Temporal;
use WP_REST_Response;
use WP_REST_Server;

/**
* @unreleased
*/
class GetCampaignRevenue implements RestRoute
{
/**
* @unreleased
*/
public function registerRoute()
{
register_rest_route(
CampaignRoute::NAMESPACE,
CampaignRoute::CAMPAIGN . '/revenue',
[
[
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'handleRequest'],
'permission_callback' => function () {
return current_user_can('manage_options');
},
],
'args' => [
'id' => [
'type' => 'integer',
'required' => true,
'sanitize_callback' => 'absint',
],
],
]
);
}

/**
* @unreleased
*
* @throws Exception
*/
public function handleRequest($request): WP_REST_Response
{

$campaign = Campaign::find($request->get_param('id'));

$dates = $this->getDatesFromRange(new DateTime('-7 days'), new DateTime());

$query = new CampaignDonationQuery($campaign);
$query->between(new DateTime('-7 days'), new DateTime());
$results = $query->getDonationsByDay();

foreach($results as $result) {
$dates[$result->date] = $result->amount;
}

$data = [];
foreach($dates as $date => $amount) {
$data[] = [
'date' => $date,
'amount' => $amount,
];
}

return new WP_REST_Response($data, 200);
}

public function getDatesFromRange(DateTimeInterface $startDate, DateTimeInterface $endDate): array
{
$period = new DatePeriod(
$startDate,
new DateInterval('P1D'),
$endDate
);

$dates = array_map(function($date) {
return $date->format('Y-m-d');
}, iterator_to_array($period));

return array_fill_keys($dates, 0);
}
}
3 changes: 2 additions & 1 deletion src/Campaigns/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ private function registerRoutes()
Hooks::addAction('rest_api_init', Routes\GetCampaignsListTable::class, 'registerRoute');
Hooks::addAction('rest_api_init', Routes\DeleteCampaignListTable::class, 'registerRoute');
Hooks::addAction('rest_api_init', Routes\GetCampaignStatistics::class, 'registerRoute');
Hooks::addAction('rest_api_init', Routes\GetCampaignRevenue::class, 'registerRoute');
}

/**
Expand Down Expand Up @@ -138,7 +139,7 @@ private function setupCampaignForms()
if ( ! defined('GIVE_IS_ALL_STATS_COLUMNS_ASYNC_ON_ADMIN_FORM_LIST_VIEWS')) {
define('GIVE_IS_ALL_STATS_COLUMNS_ASYNC_ON_ADMIN_FORM_LIST_VIEWS', false);
}

Hooks::addAction('save_post_give_forms', AddCampaignFormFromRequest::class, 'optionBasedFormEditor', 10, 3);
Hooks::addAction('givewp_donation_form_created', AddCampaignFormFromRequest::class, 'visualFormBuilder');
Hooks::addAction('givewp_campaign_created', CreateDefaultCampaignForm::class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
import React from "react";
import React, {useEffect, useState} from "react";
import Chart from "react-apexcharts";
import apiFetch from "@wordpress/api-fetch";
import {addQueryArgs} from "@wordpress/url";

const campaignId = new URLSearchParams(window.location.search).get('id');

const RevenueChart = () => {

const [max, setMax] = useState(0);
const [categories, setCategories] = useState([]);
const [series, setSeries] = useState([{name: "Revenue", data: []}]);

useEffect(() => {
apiFetch({path: addQueryArgs( '/give-api/v2/campaigns/' + campaignId +'/revenue' ) } )
.then((data: {date: string, amount: number}[]) => {

setMax(Math.max(...data.map(item => item.amount)) * 1.1)

setCategories(data.map(item => item.date))

setSeries([{
name: "Revenue",
data: data.map(item => item.amount)
}])
});
}, [])

const options = {
chart: {
id: "campaign-revenue",
Expand All @@ -11,10 +34,11 @@ const RevenueChart = () => {
},
},
xaxis: {
categories: ['Aug 06', 'Aug 07', 'Aug 08', 'Aug 09']
categories,
type: 'datetime' as "datetime" | "category" | "numeric",
},
yaxis: {
max: 200,
max,
},
stroke: {
color: ['#60a1e2'],
Expand Down Expand Up @@ -51,13 +75,6 @@ const RevenueChart = () => {
}
};

const series = [
{
name: "Revenue",
data: [0, 100, 50, 150]
}
];

return (
<>
<Chart
Expand Down
48 changes: 48 additions & 0 deletions tests/Unit/Campaigns/CampaignDonationQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Give\Tests\Unit\Campaigns;

use DateTime;
use Give\Campaigns\CampaignDonationQuery;
use Give\Campaigns\Models\Campaign;
use Give\DonationForms\Models\DonationForm;
Expand Down Expand Up @@ -97,6 +98,9 @@ public function testCountCampaignDonors()
$this->assertEquals(2, $query->countDonors());
}

/**
* @unreleased
*/
public function testCoalesceIntendedAmountWithoutRecoveredFees()
{
$campaign = Campaign::factory()->create();
Expand All @@ -116,4 +120,48 @@ public function testCoalesceIntendedAmountWithoutRecoveredFees()

$this->assertEquals(10.00, $query->sumIntendedAmount());
}

/**
* @unreleased
*/
public function testGetDonationsByDate()
{
$campaign = Campaign::factory()->create();
$form = DonationForm::factory()->create();

$db = DB::table('give_campaign_forms');
$db->insert(['form_id' => $form->id, 'campaign_id' => $campaign->id]);

$donations = [
Donation::factory()->create([
'formId' => $form->id,
'status' => DonationStatus::COMPLETE(),
'amount' => new Money(1000, 'USD'),
'createdAt' => new DateTime('2021-01-01 00:00:00'),
]),
Donation::factory()->create([
'formId' => $form->id,
'status' => DonationStatus::COMPLETE(),
'amount' => new Money(1000, 'USD'),
'createdAt' => new DateTime('2021-01-02 00:00:00'),
]),
Donation::factory()->create([
'formId' => $form->id,
'status' => DonationStatus::COMPLETE(),
'amount' => new Money(1000, 'USD'),
'createdAt' => new DateTime('2021-01-02 00:00:00'),
]),
];

foreach($donations as $donation) {
give_update_meta($donation->id, '_give_completed_date', $donation->createdAt->format('Y-m-d H:i:s'));
}

$query = new CampaignDonationQuery($campaign);

$this->assertEquals([
(object) ['date' => '2021-01-01', 'amount' => 10.00],
(object) ['date' => '2021-01-02', 'amount' => 20.00],
], $query->getDonationsByDay());
}
}

0 comments on commit df44763

Please sign in to comment.