Skip to content

Commit 56a61cb

Browse files
feat: backend for checkboxes
1 parent 3c0e3e3 commit 56a61cb

File tree

4 files changed

+31
-55
lines changed

4 files changed

+31
-55
lines changed

backend/src/db/mod.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
use color_eyre::eyre::eyre;
44
use models::DBAdminDashboardQP;
5-
pub use queries::ExamFilter;
65
use sqlx::{postgres::PgPoolOptions, prelude::FromRow, PgPool, Postgres, Transaction};
76
use std::time::Duration;
87

@@ -68,18 +67,11 @@ impl Database {
6867
pub async fn search_papers(
6968
&self,
7069
query: &str,
71-
exam_filter: ExamFilter,
72-
exam_filter_str: String,
70+
exam_filter: Vec<Exam>,
7371
) -> Result<Vec<qp::BaseQP>, sqlx::Error> {
74-
let (query_sql, use_exam_arg) = queries::get_qp_search_query(exam_filter);
72+
let query_sql = queries::get_qp_search_query(exam_filter);
7573
let query = sqlx::query_as(&query_sql).bind(query);
7674

77-
let query = if use_exam_arg {
78-
query.bind(exam_filter_str)
79-
} else {
80-
query
81-
};
82-
8375
let papers: Vec<models::DBBaseQP> = query.fetch_all(&self.connection).await?;
8476

8577
Ok(papers

backend/src/db/queries.rs

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -101,27 +101,6 @@ pub fn get_all_unapproved_query() -> String {
101101
format!("SELECT {} FROM iqps WHERE approve_status = false and is_deleted=false ORDER BY upload_timestamp ASC", ADMIN_DASHBOARD_QP_FIELDS)
102102
}
103103

104-
/// An enum representing the exam filter for the search query
105-
pub enum ExamFilter {
106-
Exam(Exam), // Match an exact exam or use `ct` substring match
107-
Any, // Match anything
108-
MidEnd, // Midsem or endsem
109-
}
110-
111-
impl TryFrom<&String> for ExamFilter {
112-
type Error = color_eyre::eyre::Error;
113-
114-
fn try_from(value: &String) -> Result<Self, Self::Error> {
115-
if value.is_empty() {
116-
Ok(ExamFilter::Any)
117-
} else if value == "midend" {
118-
Ok(ExamFilter::MidEnd)
119-
} else {
120-
Ok(ExamFilter::Exam(Exam::try_from(value)?))
121-
}
122-
}
123-
}
124-
125104
/// Returns the query for searching question papers. It is mostly voodoo, see [blog post](https://rajivharlalka.in/posts/iqps-search-development/).
126105
///
127106
/// Optionally, the `exam` argument can be used to also add a clause to match the exam field.
@@ -131,21 +110,26 @@ impl TryFrom<&String> for ExamFilter {
131110
/// $2 - Exam filter string (can be midsem, endsem, midend, or ct)
132111
///
133112
/// Returns the query and a boolean representing whether the second argument is required
134-
pub fn get_qp_search_query(exam_filter: ExamFilter) -> (String, bool) {
135-
let (exam_filter, use_exam_arg) = match exam_filter {
136-
ExamFilter::Any => ("", false),
137-
ExamFilter::MidEnd => (
138-
"WHERE (exam = 'midsem' OR exam = 'endsem' OR exam = '')",
139-
false,
140-
),
141-
ExamFilter::Exam(exam) => match exam {
142-
Exam::CT(_) => ("WHERE (exam LIKE 'ct%' OR exam = '')", false),
143-
_ => ("WHERE (exam = $2 OR exam = '')", true),
144-
},
113+
pub fn get_qp_search_query(exam_filter: Vec<Exam>) -> String {
114+
let exam_filter_clause = exam_filter
115+
.iter()
116+
.map(|&exam| {
117+
if let Exam::CT(_) = exam {
118+
"exam LIKE 'ct%'".into()
119+
} else {
120+
format!("exam = '{}'", String::from(exam))
121+
}
122+
})
123+
.collect::<Vec<String>>()
124+
.join(" OR ");
125+
126+
let exam_clause_str = if exam_filter_clause.is_empty() {
127+
"".into()
128+
} else {
129+
format!("WHERE ({} OR exam = '')", exam_filter_clause)
145130
};
146131

147-
(
148-
format!("
132+
format!("
149133
WITH filtered AS (
150134
SELECT * from iqps {exam_filter} ORDER BY year DESC
151135
),
@@ -189,11 +173,9 @@ pub fn get_qp_search_query(exam_filter: ExamFilter) -> (String, bool) {
189173
) SELECT {search_qp_fields} FROM result",
190174
search_qp_fields = SEARCH_QP_FIELDS,
191175
to_tsquery = "to_tsquery('simple', websearch_to_tsquery('simple', $1)::text || ':*')",
192-
exam_filter = exam_filter,
176+
exam_filter = exam_clause_str,
193177
intermediate_fields = ADMIN_DASHBOARD_QP_FIELDS.split(", ").map(|field| format!("filtered.{}", field)).collect::<Vec<String>>().join(", ")
194-
),
195-
use_exam_arg
196-
)
178+
)
197179
}
198180

199181
/// List of fields in the [`crate::db::models::DBAdminDashboardQP`] to be used with SELECT clauses

backend/src/routing/handlers.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use serde::Deserialize;
2323
use crate::{
2424
auth::{self, Auth},
2525
pathutils::PaperCategory,
26-
qp::{self, AdminDashboardQP, WithUrl},
26+
qp::{self, AdminDashboardQP, Exam, WithUrl},
2727
};
2828

2929
use super::{AppError, BackendResponse, RouterState, Status};
@@ -68,11 +68,13 @@ pub async fn search(
6868
.map(|value| value.to_owned())
6969
.unwrap_or("".into());
7070

71-
if let Ok(exam_filter) = (&exam_query_str).try_into() {
72-
let papers = state
73-
.db
74-
.search_papers(query, exam_filter, exam_query_str.to_owned())
75-
.await?;
71+
if let Ok(exam_filter) = exam_query_str
72+
.split(',')
73+
.filter(|val| !val.trim().is_empty())
74+
.map(|val| Exam::try_from(&val.to_owned()))
75+
.collect::<Result<Vec<Exam>, _>>()
76+
{
77+
let papers = state.db.search_papers(query, exam_filter).await?;
7678

7779
let papers = papers
7880
.iter()

backend/src/routing/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ impl IntoResponse for AppError {
152152
tracing::error!("An error occured: {}", self.0);
153153

154154
BackendResponse::<()>::error(
155-
format!("An internal server error occured. Please try again later."),
155+
"An internal server error occured. Please try again later.".into(),
156156
StatusCode::INTERNAL_SERVER_ERROR,
157157
)
158158
.into_response()

0 commit comments

Comments
 (0)