mirror of
https://github.com/modrinth/code.git
synced 2026-09-04 22:10:15 +00:00
day distribution logic
This commit is contained in:
@@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::{Datelike, Months};
|
use chrono::{Datelike, Months, NaiveDate};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use eyre::{Result, eyre};
|
use eyre::{Result, eyre};
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use xredis::RedisPool;
|
use xredis::RedisPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -15,19 +16,20 @@ use crate::{
|
|||||||
|
|
||||||
const REDIS_KEY: &str = "aditude_month_estimate_v1";
|
const REDIS_KEY: &str = "aditude_month_estimate_v1";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct PeriodEstimate {
|
pub struct PeriodEstimate {
|
||||||
pub period: YearMonth,
|
pub period: YearMonth,
|
||||||
pub days: Vec<DayEstimate>,
|
pub days: Vec<DayEstimate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct DayEstimate {
|
pub struct DayEstimate {
|
||||||
pub day: u32,
|
pub date: NaiveDate,
|
||||||
pub raw_estimated_revenue_usd: Decimal,
|
pub raw_estimated_revenue_usd: Decimal,
|
||||||
pub impressions: u128,
|
pub impressions: u128,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get per-month and per-day estimated ad provider info.
|
||||||
pub async fn estimate(
|
pub async fn estimate(
|
||||||
aditude: &aditude::Client,
|
aditude: &aditude::Client,
|
||||||
redis: &RedisPool,
|
redis: &RedisPool,
|
||||||
@@ -93,7 +95,7 @@ async fn fetch_estimates(
|
|||||||
|
|
||||||
let day = date.day();
|
let day = date.day();
|
||||||
days.push(DayEstimate {
|
days.push(DayEstimate {
|
||||||
day,
|
date,
|
||||||
raw_estimated_revenue_usd: row
|
raw_estimated_revenue_usd: row
|
||||||
.revenue
|
.revenue
|
||||||
.wrap_err_with(|| eyre!("no revenue data for day {day}"))?,
|
.wrap_err_with(|| eyre!("no revenue data for day {day}"))?,
|
||||||
@@ -107,7 +109,7 @@ async fn fetch_estimates(
|
|||||||
// we have no clue if the Aditude row return order is stable,
|
// we have no clue if the Aditude row return order is stable,
|
||||||
// so for safety sort the days here
|
// so for safety sort the days here
|
||||||
for period in map.values_mut() {
|
for period in map.values_mut() {
|
||||||
period.days.sort_unstable_by_key(|day| day.day);
|
period.days.sort_unstable_by_key(|day| day.date);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(map.into_iter().collect::<DashMap<_, _>>())
|
Ok(map.into_iter().collect::<DashMap<_, _>>())
|
||||||
|
|||||||
@@ -62,4 +62,84 @@
|
|||||||
//! epoch)
|
//! epoch)
|
||||||
//! - the decimal fraction of variance to apply
|
//! - the decimal fraction of variance to apply
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use rust_decimal::{Decimal, dec};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod estimate;
|
mod estimate;
|
||||||
|
|
||||||
|
/// Fraction defining much of the net revenue goes to the platform.
|
||||||
|
const PLATFORM_REVENUE_SPLIT: Decimal = dec!(0.25);
|
||||||
|
|
||||||
|
/// How input revenue is distributed for a specific day.
|
||||||
|
///
|
||||||
|
/// This may refer to either estimated or actual revenue.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct DayDistribution {
|
||||||
|
pub raw_revenue_usd: Decimal,
|
||||||
|
pub fees_usd: Decimal,
|
||||||
|
pub variance_usd: Decimal,
|
||||||
|
pub sum_adjustments_usd: Decimal,
|
||||||
|
pub net_revenue_usd: Decimal,
|
||||||
|
pub platform_net_revenue_usd: Decimal,
|
||||||
|
pub creator_net_revenue_usd: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PayoutVariance {
|
||||||
|
pub starts_at: NaiveDate,
|
||||||
|
pub frac: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PayoutVariances {
|
||||||
|
pub fracs: Vec<PayoutVariance>,
|
||||||
|
pub default_frac: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PayoutVariances {
|
||||||
|
pub const ZERO: Self = Self {
|
||||||
|
fracs: Vec::new(),
|
||||||
|
default_frac: Decimal::ZERO,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute the [`DayDistribution`] for an input revenue and impressions amount.
|
||||||
|
///
|
||||||
|
/// This logic may be used on both estimated and actual revenue. For actual
|
||||||
|
/// revenue, [`PayoutVariances::ZERO`] should be used, since variance only
|
||||||
|
/// applies to estimated days.
|
||||||
|
pub fn distribution_for_day(
|
||||||
|
date: NaiveDate,
|
||||||
|
raw_revenue_usd: Decimal,
|
||||||
|
impressions: u128,
|
||||||
|
sum_adjustments_usd: Decimal,
|
||||||
|
variances: &PayoutVariances,
|
||||||
|
) -> DayDistribution {
|
||||||
|
let fees_usd = {
|
||||||
|
let clean_io_cpm = Decimal::from(8) / Decimal::from(1000);
|
||||||
|
clean_io_cpm * Decimal::from(impressions) / Decimal::from(1000)
|
||||||
|
};
|
||||||
|
let variance_frac = variances
|
||||||
|
.fracs
|
||||||
|
.iter()
|
||||||
|
.find(|v| v.starts_at >= date)
|
||||||
|
.map(|v| v.frac)
|
||||||
|
.unwrap_or(variances.default_frac);
|
||||||
|
let variance_usd = raw_revenue_usd * variance_frac;
|
||||||
|
|
||||||
|
let net_estimated_revenue_usd =
|
||||||
|
raw_revenue_usd - fees_usd - variance_usd + sum_adjustments_usd;
|
||||||
|
|
||||||
|
DayDistribution {
|
||||||
|
raw_revenue_usd,
|
||||||
|
fees_usd,
|
||||||
|
variance_usd,
|
||||||
|
sum_adjustments_usd,
|
||||||
|
net_revenue_usd: net_estimated_revenue_usd,
|
||||||
|
platform_net_revenue_usd: net_estimated_revenue_usd
|
||||||
|
* PLATFORM_REVENUE_SPLIT,
|
||||||
|
creator_net_revenue_usd: net_estimated_revenue_usd
|
||||||
|
* (dec!(1) - PLATFORM_REVENUE_SPLIT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user