Skip to content

Commit

Permalink
feat(analytics): add sessionized_metrics and currency_conversion
Browse files Browse the repository at this point in the history
…for refunds analytics (#6419)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
2 people authored and Sayak Bhattacharya committed Nov 26, 2024
1 parent 8b68fdd commit 54286c6
Show file tree
Hide file tree
Showing 17 changed files with 634 additions and 34 deletions.
14 changes: 12 additions & 2 deletions crates/analytics/src/payment_intents/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,21 @@ pub async fn get_sankey(
i.refunds_status.unwrap_or_default().as_ref(),
i.attempt_count,
) {
(IntentStatus::Succeeded, SessionizerRefundStatus::FullRefunded, 1) => {
sankey_response.refunded += i.count;
sankey_response.normal_success += i.count
}
(IntentStatus::Succeeded, SessionizerRefundStatus::PartialRefunded, 1) => {
sankey_response.partial_refunded += i.count;
sankey_response.normal_success += i.count
}
(IntentStatus::Succeeded, SessionizerRefundStatus::FullRefunded, _) => {
sankey_response.refunded += i.count
sankey_response.refunded += i.count;
sankey_response.smart_retried_success += i.count
}
(IntentStatus::Succeeded, SessionizerRefundStatus::PartialRefunded, _) => {
sankey_response.partial_refunded += i.count
sankey_response.partial_refunded += i.count;
sankey_response.smart_retried_success += i.count
}
(
IntentStatus::Succeeded
Expand Down
4 changes: 2 additions & 2 deletions crates/analytics/src/payments/accumulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl PaymentMetricsAccumulator {
payment_processed_count,
payment_processed_amount_without_smart_retries,
payment_processed_count_without_smart_retries,
payment_processed_amount_usd,
payment_processed_amount_in_usd,
payment_processed_amount_without_smart_retries_usd,
) = self.processed_amount.collect();
let (
Expand Down Expand Up @@ -417,7 +417,7 @@ impl PaymentMetricsAccumulator {
payments_failure_rate_distribution_without_smart_retries,
failure_reason_count,
failure_reason_count_without_smart_retries,
payment_processed_amount_usd,
payment_processed_amount_in_usd,
payment_processed_amount_without_smart_retries_usd,
}
}
Expand Down
8 changes: 4 additions & 4 deletions crates/analytics/src/payments/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ pub async fn get_metrics(
let mut total_payment_processed_count_without_smart_retries = 0;
let mut total_failure_reasons_count = 0;
let mut total_failure_reasons_count_without_smart_retries = 0;
let mut total_payment_processed_amount_usd = 0;
let mut total_payment_processed_amount_in_usd = 0;
let mut total_payment_processed_amount_without_smart_retries_usd = 0;
let query_data: Vec<MetricsBucketResponse> = metrics_accumulator
.into_iter()
Expand All @@ -251,9 +251,9 @@ pub async fn get_metrics(
})
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
.unwrap_or_default();
collected_values.payment_processed_amount_usd = amount_in_usd;
collected_values.payment_processed_amount_in_usd = amount_in_usd;
total_payment_processed_amount += amount;
total_payment_processed_amount_usd += amount_in_usd.unwrap_or(0);
total_payment_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
}
if let Some(count) = collected_values.payment_processed_count {
total_payment_processed_count += count;
Expand Down Expand Up @@ -299,7 +299,7 @@ pub async fn get_metrics(
query_data,
meta_data: [PaymentsAnalyticsMetadata {
total_payment_processed_amount: Some(total_payment_processed_amount),
total_payment_processed_amount_usd: Some(total_payment_processed_amount_usd),
total_payment_processed_amount_in_usd: Some(total_payment_processed_amount_in_usd),
total_payment_processed_amount_without_smart_retries: Some(
total_payment_processed_amount_without_smart_retries,
),
Expand Down
15 changes: 9 additions & 6 deletions crates/analytics/src/refunds/accumulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub struct RefundMetricsAccumulator {
pub refund_success_rate: SuccessRateAccumulator,
pub refund_count: CountAccumulator,
pub refund_success: CountAccumulator,
pub processed_amount: SumAccumulator,
pub processed_amount: PaymentProcessedAmountAccumulator,
}

#[derive(Debug, Default)]
Expand All @@ -22,7 +22,7 @@ pub struct CountAccumulator {
}
#[derive(Debug, Default)]
#[repr(transparent)]
pub struct SumAccumulator {
pub struct PaymentProcessedAmountAccumulator {
pub total: Option<i64>,
}

Expand Down Expand Up @@ -50,8 +50,8 @@ impl RefundMetricAccumulator for CountAccumulator {
}
}

impl RefundMetricAccumulator for SumAccumulator {
type MetricOutput = Option<u64>;
impl RefundMetricAccumulator for PaymentProcessedAmountAccumulator {
type MetricOutput = (Option<u64>, Option<u64>);
#[inline]
fn add_metrics_bucket(&mut self, metrics: &RefundMetricRow) {
self.total = match (
Expand All @@ -68,7 +68,7 @@ impl RefundMetricAccumulator for SumAccumulator {
}
#[inline]
fn collect(self) -> Self::MetricOutput {
self.total.and_then(|i| u64::try_from(i).ok())
(self.total.and_then(|i| u64::try_from(i).ok()), Some(0))
}
}

Expand Down Expand Up @@ -98,11 +98,14 @@ impl RefundMetricAccumulator for SuccessRateAccumulator {

impl RefundMetricsAccumulator {
pub fn collect(self) -> RefundMetricsBucketValue {
let (refund_processed_amount, refund_processed_amount_in_usd) =
self.processed_amount.collect();
RefundMetricsBucketValue {
refund_success_rate: self.refund_success_rate.collect(),
refund_count: self.refund_count.collect(),
refund_success_count: self.refund_success.collect(),
refund_processed_amount: self.processed_amount.collect(),
refund_processed_amount,
refund_processed_amount_in_usd,
}
}
}
65 changes: 50 additions & 15 deletions crates/analytics/src/refunds/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ use api_models::analytics::{
refunds::{
RefundDimensions, RefundMetrics, RefundMetricsBucketIdentifier, RefundMetricsBucketResponse,
},
AnalyticsMetadata, GetRefundFilterRequest, GetRefundMetricRequest, MetricsResponse,
RefundFilterValue, RefundFiltersResponse,
GetRefundFilterRequest, GetRefundMetricRequest, RefundFilterValue, RefundFiltersResponse,
RefundsAnalyticsMetadata, RefundsMetricsResponse,
};
use bigdecimal::ToPrimitive;
use common_enums::Currency;
use currency_conversion::{conversion::convert, types::ExchangeRates};
use error_stack::ResultExt;
use router_env::{
logger,
Expand All @@ -29,9 +32,10 @@ use crate::{

pub async fn get_metrics(
pool: &AnalyticsProvider,
ex_rates: &ExchangeRates,
auth: &AuthInfo,
req: GetRefundMetricRequest,
) -> AnalyticsResult<MetricsResponse<RefundMetricsBucketResponse>> {
) -> AnalyticsResult<RefundsMetricsResponse<RefundMetricsBucketResponse>> {
let mut metrics_accumulator: HashMap<RefundMetricsBucketIdentifier, RefundMetricsAccumulator> =
HashMap::new();
let mut set = tokio::task::JoinSet::new();
Expand Down Expand Up @@ -86,16 +90,20 @@ pub async fn get_metrics(
logger::debug!(bucket_id=?id, bucket_value=?value, "Bucket row for metric {metric}");
let metrics_builder = metrics_accumulator.entry(id).or_default();
match metric {
RefundMetrics::RefundSuccessRate => metrics_builder
.refund_success_rate
.add_metrics_bucket(&value),
RefundMetrics::RefundCount => {
RefundMetrics::RefundSuccessRate | RefundMetrics::SessionizedRefundSuccessRate => {
metrics_builder
.refund_success_rate
.add_metrics_bucket(&value)
}
RefundMetrics::RefundCount | RefundMetrics::SessionizedRefundCount => {
metrics_builder.refund_count.add_metrics_bucket(&value)
}
RefundMetrics::RefundSuccessCount => {
RefundMetrics::RefundSuccessCount
| RefundMetrics::SessionizedRefundSuccessCount => {
metrics_builder.refund_success.add_metrics_bucket(&value)
}
RefundMetrics::RefundProcessedAmount => {
RefundMetrics::RefundProcessedAmount
| RefundMetrics::SessionizedRefundProcessedAmount => {
metrics_builder.processed_amount.add_metrics_bucket(&value)
}
}
Expand All @@ -107,18 +115,45 @@ pub async fn get_metrics(
metrics_accumulator
);
}
let mut total_refund_processed_amount = 0;
let mut total_refund_processed_amount_in_usd = 0;
let query_data: Vec<RefundMetricsBucketResponse> = metrics_accumulator
.into_iter()
.map(|(id, val)| RefundMetricsBucketResponse {
values: val.collect(),
dimensions: id,
.map(|(id, val)| {
let mut collected_values = val.collect();
if let Some(amount) = collected_values.refund_processed_amount {
let amount_in_usd = id
.currency
.and_then(|currency| {
i64::try_from(amount)
.inspect_err(|e| logger::error!("Amount conversion error: {:?}", e))
.ok()
.and_then(|amount_i64| {
convert(ex_rates, currency, Currency::USD, amount_i64)
.inspect_err(|e| {
logger::error!("Currency conversion error: {:?}", e)
})
.ok()
})
})
.map(|amount| (amount * rust_decimal::Decimal::new(100, 0)).to_u64())
.unwrap_or_default();
collected_values.refund_processed_amount_in_usd = amount_in_usd;
total_refund_processed_amount += amount;
total_refund_processed_amount_in_usd += amount_in_usd.unwrap_or(0);
}
RefundMetricsBucketResponse {
values: collected_values,
dimensions: id,
}
})
.collect();

Ok(MetricsResponse {
Ok(RefundsMetricsResponse {
query_data,
meta_data: [AnalyticsMetadata {
current_time_range: req.time_range,
meta_data: [RefundsAnalyticsMetadata {
total_refund_processed_amount: Some(total_refund_processed_amount),
total_refund_processed_amount_in_usd: Some(total_refund_processed_amount_in_usd),
}],
})
}
Expand Down
21 changes: 21 additions & 0 deletions crates/analytics/src/refunds/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod refund_count;
mod refund_processed_amount;
mod refund_success_count;
mod refund_success_rate;
mod sessionized_metrics;
use std::collections::HashSet;

use refund_count::RefundCount;
Expand Down Expand Up @@ -101,6 +102,26 @@ where
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedRefundSuccessRate => {
sessionized_metrics::RefundSuccessRate::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedRefundCount => {
sessionized_metrics::RefundCount::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedRefundSuccessCount => {
sessionized_metrics::RefundSuccessCount::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
Self::SessionizedRefundProcessedAmount => {
sessionized_metrics::RefundProcessedAmount::default()
.load_metrics(dimensions, auth, filters, granularity, time_range, pool)
.await
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ where
alias: Some("total"),
})
.switch()?;
query_builder.add_select_column("currency").switch()?;
query_builder
.add_select_column(Aggregate::Min {
field: "created_at",
Expand All @@ -78,6 +79,7 @@ where
query_builder.add_group_by_clause(dim).switch()?;
}

query_builder.add_group_by_clause("currency").switch()?;
if let Some(granularity) = granularity.as_ref() {
granularity
.set_group_by_clause(&mut query_builder)
Expand Down
11 changes: 11 additions & 0 deletions crates/analytics/src/refunds/metrics/sessionized_metrics/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod refund_count;
mod refund_processed_amount;
mod refund_success_count;
mod refund_success_rate;

pub(super) use refund_count::RefundCount;
pub(super) use refund_processed_amount::RefundProcessedAmount;
pub(super) use refund_success_count::RefundSuccessCount;
pub(super) use refund_success_rate::RefundSuccessRate;

pub use super::{RefundMetric, RefundMetricAnalytics, RefundMetricRow};
Loading

0 comments on commit 54286c6

Please sign in to comment.