late 0.0.1364

API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal. Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema). Request ids: responses carry an X-Request-Id header with the id we log the request under. Quote it when reporting a problem. A valid x-request-id you send is reused as that id.
Documentation
/*
 * Zernio API
 *
 * API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api  Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal.  Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema).  Request ids: responses carry an X-Request-Id header with the id we log the request under. Quote it when reporting a problem. A valid x-request-id you send is reused as that id.
 *
 * The version of the OpenAPI document: 1.563.0
 * Contact: support@zernio.com
 * Generated by: https://openapi-generator.tech
 */

use crate::models;
use serde::{Deserialize, Serialize};

#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct PostAnalytics {
    #[serde(rename = "impressions", skip_serializing_if = "Option::is_none")]
    pub impressions: Option<i32>,
    #[serde(rename = "reach", skip_serializing_if = "Option::is_none")]
    pub reach: Option<i32>,
    #[serde(rename = "likes", skip_serializing_if = "Option::is_none")]
    pub likes: Option<i32>,
    #[serde(rename = "comments", skip_serializing_if = "Option::is_none")]
    pub comments: Option<i32>,
    #[serde(rename = "shares", skip_serializing_if = "Option::is_none")]
    pub shares: Option<i32>,
    /// Number of saves/bookmarks (Instagram, Pinterest, X)
    #[serde(rename = "saves", skip_serializing_if = "Option::is_none")]
    pub saves: Option<i32>,
    /// Link clicks where the platform reports them. Always 0 on Facebook Page stories: Meta exposes no link-click metric for stories.
    #[serde(rename = "clicks", skip_serializing_if = "Option::is_none")]
    pub clicks: Option<i32>,
    #[serde(rename = "views", skip_serializing_if = "Option::is_none")]
    pub views: Option<i32>,
    /// Accounts that started following from this post.  - Reported for Instagram feed posts and stories (organic follows), Facebook Reels (Meta post_video_followers) and the TikTok business lane. - Null on Instagram Reels and non-Reels video, and on Facebook posts that are not Reels, where Meta does not expose this metric for the media. - 0 for other platforms.
    #[serde(
        rename = "follows",
        default,
        with = "::serde_with::rust::double_option",
        skip_serializing_if = "Option::is_none"
    )]
    pub follows: Option<Option<i32>>,
    /// Average watch time per play, in milliseconds, for Instagram Reels, Facebook Reels and TikTok videos (business accounts). On Facebook it includes replays within a play, so it can exceed the Reel length. 0 for other media and platforms, including regular Facebook videos.
    #[serde(
        rename = "igReelsAvgWatchTime",
        skip_serializing_if = "Option::is_none"
    )]
    pub ig_reels_avg_watch_time: Option<i32>,
    /// Total watch time including replays, in milliseconds, for Instagram Reels, Facebook Reels and TikTok videos (business accounts). 0 for other media and platforms, including regular Facebook videos.
    #[serde(
        rename = "igReelsVideoViewTotalTime",
        skip_serializing_if = "Option::is_none"
    )]
    pub ig_reels_video_view_total_time: Option<i32>,
    /// Instagram Reels only: percentage (0-100) of initial views that skipped the reel within its first 3 seconds, as reported by Meta.  - Meta labels the metric estimated and in development, so it can move between syncs. - 0 for non-Reels media and other platforms. - For a post published to several accounts, the aggregate is weighted by views.
    #[serde(rename = "reelsSkipRate", skip_serializing_if = "Option::is_none")]
    pub reels_skip_rate: Option<f64>,
    /// Share of viewers who watched the video to the end, 0 to 1, as TikTok reports it. Only for TikTok accounts connected through the TikTok for Business app.  - Delayed T+24-48h, and only for posts active in the last 7 days. - 0 for other platforms. - For a post published to several accounts, the aggregate is weighted by views.
    #[serde(rename = "completionRate", skip_serializing_if = "Option::is_none")]
    pub completion_rate: Option<f64>,
    /// Instagram feed posts (images and carousels; 0 on Reels and Stories) and TikTok accounts connected through the TikTok for Business app: profile views from users who reached the profile through this post (TikTok T+24-48h). 0 for other platforms.
    #[serde(rename = "profileViews", skip_serializing_if = "Option::is_none")]
    pub profile_views: Option<i32>,
    /// TikTok accounts connected through the TikTok for Business app only: clicks on the profile website link attributed to this post (T+24-48h). Also counted inside `clicks`, which sums every profile-link type (website, phone, email, address, app download). 0 for other platforms.
    #[serde(rename = "websiteClicks", skip_serializing_if = "Option::is_none")]
    pub website_clicks: Option<i32>,
    /// Share of views by surface, as fractions 0 to 1. Only for TikTok accounts connected through the TikTok for Business app (T+24-48h, only for posts active in the last 7 days).  - Keys: `forYou`, `follow`, `search`, `personalProfile`, `sound`, `directMessage`, `other`. A surface TikTok adds later appears under a camelCase key derived from its name. - Empty object when TikTok reports nothing, and for other platforms. - When a post is published to several accounts, each share is weighted by views.
    #[serde(rename = "impressionSources", skip_serializing_if = "Option::is_none")]
    pub impression_sources: Option<std::collections::HashMap<String, f64>>,
    /// TikTok accounts connected through the TikTok for Business app only: two viewer splits as fractions 0 to 1 (T+24-48h).  - Each pair sums to 1 when present: `follower` + `nonFollower`, and `newViewer` + `returnViewer`. TikTok can report one pair without the other. - Empty object when TikTok reports nothing, and for other platforms. - Views-weighted across accounts, like impressionSources.
    #[serde(rename = "audienceTypes", skip_serializing_if = "Option::is_none")]
    pub audience_types: Option<std::collections::HashMap<String, f64>>,
    /// TikTok accounts connected through the TikTok for Business app only: share of views by viewer country, as fractions 0 to 1, keyed by upper-case ISO-3166 alpha-2 code. Available T+24-48h, only for posts active in the last 7 days.  At most 20 country keys plus `other`. The catch-all bucket TikTok sends, any country below 0.001 and anything past the twentieth all sum into `other`, so the values still add up to 1.  Empty object when TikTok reports nothing, and for other platforms. Views-weighted across accounts like impressionSources.
    #[serde(rename = "audienceCountries", skip_serializing_if = "Option::is_none")]
    pub audience_countries: Option<std::collections::HashMap<String, f64>>,
    /// Facebook Reels only: plays that were replays (Meta fb_reels_replay_count). Summed across accounts. 0 for other media and platforms.
    #[serde(rename = "replays", skip_serializing_if = "Option::is_none")]
    pub replays: Option<i32>,
    /// Facebook Reels and TikTok business lane: share of viewers still watching at each second of playback, as fractions 0 to 1. Keys are whole seconds from the start of a play (\"3\" is the share still watching at 3 s). Empty object for other media and platforms.  - Facebook Reels (Meta post_video_retention_graph): the base is a subset of plays that Meta selects and does not expose. On small Reels it can be far below views (6 of 51 measured), so a value is a share of that subset, not of all plays. - Facebook Reels length: loops count as continued playback, so a short Reel's curve runs past its length (an 8 s Reel has keys \"0\" to \"12\"). Meta returns at most 41 points, so a long Reel covers only its first 40 s. - TikTok accounts connected through the TikTok for Business app (TikTok video_view_retention): one point per whole second, at most 61 points, thinned evenly on videos longer than 60 s.  On a cross-posted post this is the curve of the account with the most views among those reporting one, never a blend: curves from different platforms and accounts measure different audiences and different seconds. Each platformAnalytics entry keeps its own curve.
    #[serde(rename = "retentionCurve", skip_serializing_if = "Option::is_none")]
    pub retention_curve: Option<std::collections::HashMap<String, f64>>,
    /// Reposts of the media by other users, minus deleted reposts, on feed posts, reels and stories. Instagram accounts connected with Facebook Login only.  - Instagram Login accounts: always 0 (Meta does not expose this metric for them). - Other platforms: 0, including Threads, where reposts are counted in shares instead.
    #[serde(rename = "reposts", skip_serializing_if = "Option::is_none")]
    pub reposts: Option<i32>,
    /// Views across every surface Meta counts for the post (Meta total_views_count): Instagram itself, the same post shared to Facebook, and its boosts and ads, replays included. Instagram videos and Reels on accounts connected with Facebook Login only. `views` stays Instagram organic only.  - Null for Instagram Login accounts, other platforms, and snapshots taken before the field existed (it fills on the post's next analytics sync). - On a post published to several accounts, the sum across accounts, counting views for the accounts that do not report it. Null when no account reports it. - Also null for images and carousels.
    #[serde(
        rename = "totalViews",
        default,
        with = "::serde_with::rust::double_option",
        skip_serializing_if = "Option::is_none"
    )]
    pub total_views: Option<Option<i32>>,
    /// Likes across every surface Meta counts for the post (Meta total_like_count): Instagram itself, the same post shared to Facebook, and its boosts and ads. Instagram accounts connected with Facebook Login only. `likes` stays Instagram organic only.  - Null for Instagram Login accounts, other platforms, and snapshots taken before the field existed (it fills on the post's next analytics sync). - On a post published to several accounts, the sum across accounts, counting likes for the accounts that do not report it. Null when no account reports it.
    #[serde(
        rename = "totalLikes",
        default,
        with = "::serde_with::rust::double_option",
        skip_serializing_if = "Option::is_none"
    )]
    pub total_likes: Option<Option<i32>>,
    /// Comments across every surface Meta counts for the post (Meta total_comments_count): Instagram itself, the same post shared to Facebook, and its boosts and ads. Instagram accounts connected with Facebook Login only. `comments` stays Instagram organic only.  - Null for Instagram Login accounts, other platforms, and snapshots taken before the field existed (it fills on the post's next analytics sync). - On a post published to several accounts, the sum across accounts, counting comments for the accounts that do not report it. Null when no account reports it.
    #[serde(
        rename = "totalComments",
        default,
        with = "::serde_with::rust::double_option",
        skip_serializing_if = "Option::is_none"
    )]
    pub total_comments: Option<Option<i32>>,
    /// Video length in seconds. Null when unknown: platforms that do not report a length, non-video media, or when Instagram does not expose the media URL (e.g. reels with copyrighted audio).  - YouTube: filled for public videos once their analytics next refresh (from the Data API; live streams may report null). The Data API has no Shorts flag: a video over 180 seconds cannot be a Short, but a shorter one may be a regular upload. - Instagram Reels: also filled. Combine with igReelsAvgWatchTime (ms) to estimate retention.
    #[serde(
        rename = "videoDurationSeconds",
        default,
        with = "::serde_with::rust::double_option",
        skip_serializing_if = "Option::is_none"
    )]
    pub video_duration_seconds: Option<Option<i32>>,
    /// Percentage, rounded to 2 decimals: (likes + comments + shares + saves) / (impressions or reach or views) * 100. Clicks and follows are never counted.  The denominator is the FIRST of impressions, reach, views that is non-zero, so the basis varies by post: impressions if present, else reach, else views. For a single consistent basis (e.g. interactions / reach), compute it from the raw fields above.  On the post-level roll-up of a cross-posted post, the denominator is the SUM of the denominators resolved per platform entry. An entry reporting none of impressions, reach or views is excluded from both sides of the ratio. Each platformAnalytics entry below keeps its own single denominator.  The engagementRate on the LinkedIn account endpoints uses a different formula.
    #[serde(rename = "engagementRate", skip_serializing_if = "Option::is_none")]
    pub engagement_rate: Option<f64>,
    #[serde(rename = "lastUpdated", skip_serializing_if = "Option::is_none")]
    pub last_updated: Option<String>,
}

impl PostAnalytics {
    pub fn new() -> PostAnalytics {
        PostAnalytics {
            impressions: None,
            reach: None,
            likes: None,
            comments: None,
            shares: None,
            saves: None,
            clicks: None,
            views: None,
            follows: None,
            ig_reels_avg_watch_time: None,
            ig_reels_video_view_total_time: None,
            reels_skip_rate: None,
            completion_rate: None,
            profile_views: None,
            website_clicks: None,
            impression_sources: None,
            audience_types: None,
            audience_countries: None,
            replays: None,
            retention_curve: None,
            reposts: None,
            total_views: None,
            total_likes: None,
            total_comments: None,
            video_duration_seconds: None,
            engagement_rate: None,
            last_updated: None,
        }
    }
}