Skip to content

Commit

Permalink
feat: chart endpoint init endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
matthew-hagemann committed Nov 27, 2023
1 parent 6440891 commit b311c5e
Show file tree
Hide file tree
Showing 13 changed files with 272 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*.rock
*.nix

proto/*.rs
/proto/*.rs
venv/
build/
*.charm
Expand Down
40 changes: 17 additions & 23 deletions src/features/app/infrastructure.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,41 @@
use crate::{app::AppContext, features::common::entities::Vote};
use sqlx::Row;
use crate::{app::AppContext, features::common::entities::VoteSummary};
use tracing::error;

use super::errors::AppError;

pub(crate) async fn get_votes_by_snap_id(
app_ctx: &AppContext,
snap_id: &str,
) -> Result<Vec<Vote>, AppError> {
let mut pool = app_ctx
) -> Result<VoteSummary, AppError> {
let mut pool = app_ctx
.infrastructure()
.repository()
.await
.map_err(|error| {
error!("{error:?}");
AppError::FailedToGetRating
})?;
let result = sqlx::query(

let result = sqlx::query_as::<_, VoteSummary>( // Changed to query_as with VoteSummary
r#"
SELECT
votes.id,
votes.snap_id,
votes.vote_up
FROM
votes
WHERE
votes.snap_id = $1
"#,
SELECT
$1 as snap_id, // Explicitly select snap_id since it's not part of the GROUP BY
COUNT(*) AS total_votes,
COUNT(*) FILTER (WHERE votes.vote_up) AS positive_votes
FROM
votes
WHERE
votes.snap_id = $1
GROUP BY votes.snap_id
"#,
)
.bind(snap_id)
.fetch_all(&mut *pool)
.fetch_one(&mut *pool) // Changed to fetch_one since we're expecting a single summarized row
.await
.map_err(|error| {
error!("{error:?}");
AppError::Unknown
})?;

let votes: Vec<Vote> = result
.into_iter()
.map(|row| Vote {
vote_up: row.get("vote_up"),
})
.collect();

Ok(votes)
Ok(result)
}
2 changes: 1 addition & 1 deletion src/features/app/use_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub async fn get_rating(app_ctx: &AppContext, snap_id: String) -> Result<Rating,
AppError::Unknown
})?;

let rating = Rating::new(snap_id, votes);
let rating = Rating::new(votes);

Ok(rating)
}
61 changes: 61 additions & 0 deletions src/features/chart/entities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use sqlx::FromRow;

use crate::features::common::entities::{calculate_band, Rating, VoteSummary};
use crate::features::pb::chart as pb;

pub type ClientHash = String;

pub struct Chart {
pub timeframe: pb::Timeframe,
pub chart_data: Vec<ChartData>,
}

impl Chart {
pub fn new(timeframe: pb::Timeframe, data: Vec<VoteSummary>) -> Self {
let mut chart_data: Vec<ChartData> = data
.into_iter()
.map(|vote_summary| ChartData::from_vote_summary(vote_summary))
.collect();

chart_data.sort_by(|a, b| {
b.raw_rating
.partial_cmp(&a.raw_rating)
.unwrap_or(std::cmp::Ordering::Equal)
});

Chart {
timeframe,
chart_data,
}
}
}

#[derive(Debug, Clone, FromRow)]
pub struct ChartData {
pub raw_rating: f32,
pub rating: Rating,
}

impl ChartData {
pub fn new(raw_rating: f32, rating: Rating) -> Self {
ChartData { raw_rating, rating }
}

pub fn from_vote_summary(vote_summary: VoteSummary) -> Self {
let (raw_rating, ratings_band) = calculate_band(&vote_summary);
let rating = Rating {
snap_id: vote_summary.snap_id,
total_votes: vote_summary.total_votes as u64,
ratings_band,
};
let raw_rating = raw_rating.unwrap_or(0.0) as f32;
Self { raw_rating, rating }
}

pub fn into_dto(self) -> pb::ChartData {
pb::ChartData {
raw_rating: self.raw_rating,
rating: Some(self.rating.into_dto()),
}
}
}
9 changes: 9 additions & 0 deletions src/features/chart/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ChartError {
#[error("failed to get chart for timeframe")]
FailedToGetChart,
#[error("unknown chart error")]
Unknown,
}
52 changes: 52 additions & 0 deletions src/features/chart/infrastructure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::features::pb::chart::Timeframe;
use crate::app::AppContext;
use crate::features::common::entities::VoteSummary;
use tracing::error;

use super::errors::ChartError;

pub(crate) async fn get_votes_summary_by_timeframe(
app_ctx: &AppContext,
timeframe: Timeframe,
) -> Result<Vec<VoteSummary>, ChartError> {
let mut pool = app_ctx
.infrastructure()
.repository()
.await
.map_err(|error| {
error!("{error:?}");
ChartError::FailedToGetChart
})?;

// Generate WHERE clause based on timeframe
let where_clause = match timeframe {
Timeframe::Week => "WHERE votes.created >= NOW() - INTERVAL '1 week'",
Timeframe::Month => "WHERE votes.created >= NOW() - INTERVAL '1 month'",
Timeframe::Unspecified => "", // Adjust as needed for Unspecified case
};

let query = format!(
r#"
SELECT
votes.snap_id,
COUNT(*) AS total_votes,
COUNT(*) FILTER (WHERE votes.vote_up) AS positive_votes
FROM
votes
{}
GROUP BY votes.snap_id
"#,
where_clause
);

let result = sqlx::query_as::<_, VoteSummary>(&query)
.fetch_all(&mut *pool)
.await
.map_err(|error| {
error!("{error:?}");
ChartError::Unknown
})?;

Ok(result)
}

38 changes: 38 additions & 0 deletions src/features/chart/interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::app::AppContext;

use crate::features::pb::chart::{GetChartRequest, GetChartResponse, Timeframe};
use tonic::{Request, Response, Status};

use crate::features::pb::chart::chart_server::Chart;

use super::{service::ChartService, use_cases};

#[tonic::async_trait]
impl Chart for ChartService {
#[tracing::instrument]
async fn get_chart(
&self,
request: Request<GetChartRequest>,
) -> Result<Response<GetChartResponse>, Status> {
let app_ctx = request.extensions().get::<AppContext>().unwrap().clone();

let GetChartRequest { timeframe } = request.into_inner();

let timeframe = match timeframe {
0 => Timeframe::Unspecified,
1 => Timeframe::Week,
2=> Timeframe::Month,
_ => Timeframe::Unspecified
};

let result = use_cases::get_chart(&app_ctx, timeframe).await;

match result {
Ok(rating) => {
let payload = GetChartResponse { timeframe: timeframe.into(), ordered_chart_data: todo!() };
Ok(Response::new(payload))
}
Err(_error) => Err(Status::unknown("Internal server error")),
}
}
}
6 changes: 6 additions & 0 deletions src/features/chart/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod entities;
mod errors;
mod infrastructure;
pub mod interface;
pub mod service;
mod use_cases;
9 changes: 9 additions & 0 deletions src/features/chart/service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::features::pb::chart::chart_server::ChartServer;

#[derive(Debug, Default)]
pub struct ChartService;

pub fn build_service() -> ChartServer<ChartService> {
let service = ChartService;
ChartServer::new(service)
}
19 changes: 19 additions & 0 deletions src/features/chart/use_cases.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use crate::app::AppContext;
use crate::features::chart::entities::Chart;
use tracing::error;
use super::{errors::ChartError, infrastructure::get_votes_summary_by_timeframe};
use crate::features::pb::chart::Timeframe;
// use super::{entities::Rating, errors::AppError, infrastructure::get_votes_by_snap_id};

pub async fn get_chart(app_ctx: &AppContext, timeframe: Timeframe) -> Result<Chart, ChartError> {
let votes = get_votes_summary_by_timeframe(app_ctx, timeframe)
.await
.map_err(|error| {
error!("{error:?}");
ChartError::Unknown
})?;

let chart = Chart::new(timeframe, votes);

Ok(chart)
}
Loading

0 comments on commit b311c5e

Please sign in to comment.