Skip to content

Analytics routes #461

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

Merged
merged 27 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d91948e
initial progress
simzzz Nov 23, 2021
0394416
Merge branch 'aip-61-adex-v5' into analytics-routes
simzzz Nov 23, 2021
cbc4cd6
initial implementation for analytics v5 routes finished
simzzz Nov 25, 2021
c1e90a0
code clean up
simzzz Nov 29, 2021
e10ceaa
(still WIP) requested changes + test coverage
simzzz Dec 3, 2021
00397f6
modified output + output is now split by timeframe
simzzz Dec 6, 2021
5be038a
primitives - analytics - rename_all=camelCase
elpiel Dec 7, 2021
150b1dd
improved query, small changes to logic
simzzz Dec 7, 2021
4346906
Merge branch 'analytics-routes' of https://github.com/AmbireTech/adex…
simzzz Dec 7, 2021
989f66d
improvements on analytics
elpiel Dec 7, 2021
fb6c8cf
fix impl Display for DateHour
elpiel Dec 8, 2021
dc97852
analytics query can now accept both timestamps and date strings
simzzz Dec 8, 2021
526b708
tests almost ready + changes to logic
simzzz Dec 9, 2021
381533d
Additional tests
simzzz Dec 10, 2021
743c1c3
test progress
simzzz Dec 13, 2021
e11962f
fixed failing tests and logic + added myself as author
simzzz Dec 14, 2021
35942fb
output is represented correctly + other minor changes
simzzz Dec 14, 2021
38ccc03
Cargo - patch postgres-types
elpiel Dec 16, 2021
69a2448
primitives - senty:
elpiel Dec 31, 2021
96fec64
Cargo patch postgres-types:
elpiel Dec 31, 2021
8e842ee
primitives - analytics - enum AllowedKeys & struct Time
elpiel Dec 31, 2021
1fea534
adapter - fix dummy warning
elpiel Jan 4, 2022
7d6a276
Analytics changes:
elpiel Jan 10, 2022
04fe52b
sentry - Event improvements & new enum EventType
elpiel Jan 14, 2022
69f4acf
sentry - db - analytics testing:
elpiel Jan 17, 2022
7e8d449
sentry - routes - analytics - finish route & tests
elpiel Jan 17, 2022
b647999
Merge branch 'aip-61-adex-v5' into analytics-routes
elpiel Jan 17, 2022
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
1 change: 1 addition & 0 deletions docs/config/dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ ethereum_adapter_relayer = 'https://goerli-relayer.adex.network'

creators_whitelist = []
validators_whitelist = []
admins = ['0xce07CbB7e054514D590a0262C93070D838bFBA2e']

[[token_address_whitelist]]
# DAI
Expand Down
2 changes: 1 addition & 1 deletion docs/config/ganache.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ ethereum_adapter_relayer = 'http://localhost:8888'

creators_whitelist = []
validators_whitelist = []

admins = ['0xce07CbB7e054514D590a0262C93070D838bFBA2e']

[[token_address_whitelist]]
# Mocked TOKEN
Expand Down
2 changes: 1 addition & 1 deletion docs/config/prod.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ ethereum_adapter_relayer = 'https://relayer.adex.network'

creators_whitelist = []
validators_whitelist = []

admins = ['0x5d6A3F1AD7b124ecDFDf4841D9bB246eD5fBF04c']

[[token_address_whitelist]]
# DAI
Expand Down
143 changes: 103 additions & 40 deletions primitives/src/analytics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{ChannelId, DomainError};
use crate::ChannelId;
use chrono::{DateTime, Utc};
use parse_display::Display;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -76,10 +77,72 @@ pub struct AnalyticsQuery {
#[serde(default = "default_event_type")]
pub event_type: String,
#[serde(default = "default_metric")]
pub metric: String,
pub metric: Metric,
#[serde(default = "default_timeframe")]
pub timeframe: String,
pub segment_by_channel: Option<String>,
pub timeframe: Timeframe,
pub segment_by: Option<String>,
pub start: Option<DateTime<Utc>>,
pub end: Option<DateTime<Utc>>,
// #[serde(default = "default_timezone")]
// pub timezone: String,
pub campaign_id: Option<String>,
pub ad_unit: Option<String>,
pub ad_slot: Option<String>,
pub ad_slot_type: Option<String>,
pub advertiser: Option<String>,
pub publisher: Option<String>,
pub hostname: Option<String>,
pub country: Option<String>,
pub os_name: Option<String>,
}

impl AnalyticsQuery {
pub fn keys(&self) -> Vec<String> {
let mut keys = vec![];
if self.campaign_id.is_some() {
keys.push("campaignId".into())
}
if self.ad_unit.is_some() {
keys.push("adUnit".into())
}
if self.ad_slot.is_some() {
keys.push("adslot".into())
}
if self.ad_slot_type.is_some() {
keys.push("adSlotType".into())
}
if self.advertiser.is_some() {
keys.push("advertiser".into())
}
if self.publisher.is_some() {
keys.push("publisher".into())
}
if self.hostname.is_some() {
keys.push("hostname".into())
}
if self.campaign_id.is_some() {
keys.push("country".into())
}
if self.campaign_id.is_some() {
keys.push("osName".into())
}
keys
}

pub fn try_get_key(&self, key: &str) -> &Option<String> {
match key {
"campaignId" => &self.campaign_id,
"adUnit" => &self.ad_unit,
"adSlot" => &self.ad_slot,
"adSlotType" => &self.ad_slot_type,
"advertiser" => &self.advertiser,
"publisher" => &self.publisher,
"hostname" => &self.hostname,
"country" => &self.country,
"osName" => &self.os_name,
_ => &None,
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Display, Hash, Eq)]
Expand All @@ -91,6 +154,34 @@ pub enum OperatingSystem {
Other,
}

#[derive(Debug, Clone, Serialize, Deserialize, Display)]
pub enum Timeframe {
Year,
Month,
Week,
Day,
}

#[derive(Debug, Clone, Serialize, Deserialize, Display)]
pub enum Metric {
Count,
Paid,
}

impl Timeframe {
pub fn get_period_in_hours(&self) -> i64 {
let hour = 1;
let day = 24 * hour;
let year = 365 * day;
match self {
Timeframe::Day => day,
Timeframe::Week => 7 * day,
Timeframe::Month => year / 12,
Timeframe::Year => year,
}
}
}

impl Default for OperatingSystem {
fn default() -> Self {
Self::Other
Expand Down Expand Up @@ -171,38 +262,6 @@ impl OperatingSystem {
}
}

impl AnalyticsQuery {
pub fn is_valid(&self) -> Result<(), DomainError> {
let valid_event_types = ["IMPRESSION", "CLICK"];
let valid_metric = ["eventPayouts", "eventCounts"];
let valid_timeframe = ["year", "month", "week", "day", "hour"];

if !valid_event_types.contains(&self.event_type.as_str()) {
Err(DomainError::InvalidArgument(format!(
"invalid event_type, possible values are: {}",
valid_event_types.join(" ,")
)))
} else if !valid_metric.contains(&self.metric.as_str()) {
Err(DomainError::InvalidArgument(format!(
"invalid metric, possible values are: {}",
valid_metric.join(" ,")
)))
} else if !valid_timeframe.contains(&self.timeframe.as_str()) {
Err(DomainError::InvalidArgument(format!(
"invalid timeframe, possible values are: {}",
valid_timeframe.join(" ,")
)))
} else if self.limit > ANALYTICS_QUERY_LIMIT {
Err(DomainError::InvalidArgument(format!(
"invalid limit {}, maximum value 200",
self.limit
)))
} else {
Ok(())
}
}
}

fn default_limit() -> u32 {
100
}
Expand All @@ -211,14 +270,18 @@ fn default_event_type() -> String {
"IMPRESSION".into()
}

fn default_metric() -> String {
"eventCounts".into()
fn default_metric() -> Metric {
Metric::Count
}

fn default_timeframe() -> String {
"hour".into()
fn default_timeframe() -> Timeframe {
Timeframe::Day
}

// fn default_timezone() -> String {
// "UTC".into()
// }

#[cfg(test)]
mod test {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions primitives/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub struct Config {
pub ethereum_adapter_relayer: String,
pub creators_whitelist: Vec<Address>,
pub validators_whitelist: Vec<ValidatorId>,
pub admins: Vec<String>,
#[serde(deserialize_with = "deserialize_token_whitelist")]
pub token_address_whitelist: HashMap<Address, TokenInfo>,
}
Expand Down
19 changes: 18 additions & 1 deletion primitives/src/sentry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,14 @@ pub struct Analytics {
pub payout_count: u32,
}

// TODO: Verify this is the needed output
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct FetchedAnalytics {
pub payout_amount: Option<UnifiedNum>,
pub payout_count: Option<u32>,
}

#[derive(Debug, Error, PartialEq, Eq)]
#[error("Minutes ({minutes}), seconds ({seconds}) & nanoseconds ({nanoseconds}) should all be set to 0 (zero)")]
pub struct DateHourError {
Expand Down Expand Up @@ -751,7 +759,7 @@ pub mod campaign_create {

#[cfg(feature = "postgres")]
mod postgres {
use super::{Analytics, DateHour, MessageResponse, ValidatorMessage};
use super::{Analytics, DateHour, FetchedAnalytics, MessageResponse, ValidatorMessage};
use crate::{
sentry::EventAggregate,
validator::{messages::Type as MessageType, MessageTypes},
Expand Down Expand Up @@ -892,6 +900,15 @@ mod postgres {
accepts!(TIMESTAMPTZ);
to_sql_checked!();
}

impl From<&Row> for FetchedAnalytics {
fn from(row: &Row) -> Self {
Self {
payout_amount: row.get("payout_amount"),
payout_count: Some(row.get::<_, i32>("payout_count").unsigned_abs()),
}
}
}
}

#[cfg(test)]
Expand Down
Loading