Add GPS summary sorting

Run cargo fmt/clippy
This commit is contained in:
Cameron
2026-01-28 10:52:17 -05:00
parent 7d2a3148bb
commit 1efdd02eda
4 changed files with 138 additions and 72 deletions

View File

@@ -187,10 +187,10 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
_: Claims,
request: HttpRequest,
req: Query<FilesRequest>,
app_state: web::Data<AppState>,
file_system: web::Data<FS>,
tag_dao: web::Data<Mutex<TagD>>,
exif_dao: web::Data<Mutex<Box<dyn ExifDao>>>,
app_state: Data<AppState>,
file_system: Data<FS>,
tag_dao: Data<Mutex<TagD>>,
exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> HttpResponse {
let search_path = &req.path;
@@ -756,7 +756,7 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
fn sort(mut files: Vec<FileWithTagCount>, sort_type: SortType) -> Vec<String> {
match sort_type {
SortType::Shuffle => files.shuffle(&mut thread_rng()),
SortType::NameAsc => {
NameAsc => {
files.sort_by(|l, r| l.file_name.cmp(&r.file_name));
}
SortType::NameDesc => {
@@ -786,7 +786,7 @@ fn sort(mut files: Vec<FileWithTagCount>, sort_type: SortType) -> Vec<String> {
fn sort_with_metadata(mut files: Vec<FileWithMetadata>, sort_type: SortType) -> Vec<String> {
match sort_type {
SortType::Shuffle => files.shuffle(&mut thread_rng()),
SortType::NameAsc => {
NameAsc => {
files.sort_by(|l, r| l.file_name.cmp(&r.file_name));
}
SortType::NameDesc => {
@@ -956,8 +956,8 @@ fn is_path_above_base_dir<P: AsRef<Path> + Debug>(
pub async fn get_gps_summary(
_: Claims,
request: HttpRequest,
req: web::Query<FilesRequest>,
exif_dao: web::Data<Mutex<Box<dyn ExifDao>>>,
req: Query<FilesRequest>,
exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> Result<HttpResponse, actix_web::Error> {
use crate::data::{GpsPhotoSummary, GpsPhotosResponse};
@@ -968,7 +968,10 @@ pub async fn get_gps_summary(
.start_with_context(&tracer, &parent_cx);
span.set_attribute(KeyValue::new("path", req.path.clone()));
span.set_attribute(KeyValue::new("recursive", req.recursive.unwrap_or(false).to_string()));
span.set_attribute(KeyValue::new(
"recursive",
req.recursive.unwrap_or(false).to_string(),
));
let cx = opentelemetry::Context::current_with_span(span);
@@ -989,41 +992,76 @@ pub async fn get_gps_summary(
};
let recursive = req.recursive.unwrap_or(false);
info!("Fetching GPS photos for path='{}' recursive={}", requested_path, recursive);
info!(
"Fetching GPS photos for path='{}' recursive={}",
requested_path, recursive
);
// Query database for all photos with GPS
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
match exif_dao_guard.get_all_with_gps(&cx, requested_path, recursive) {
Ok(gps_data) => {
let photos: Vec<GpsPhotoSummary> = gps_data
.into_iter()
.map(|(path, lat, lon, date_taken)| GpsPhotoSummary {
path,
lat,
lon,
date_taken,
})
.collect();
Ok(gps_data) => {
let mut photos: Vec<GpsPhotoSummary> = gps_data
.into_iter()
.map(|(path, lat, lon, date_taken)| GpsPhotoSummary {
path,
lat,
lon,
date_taken,
})
.collect();
let total = photos.len();
cx.span().set_attribute(KeyValue::new("result_count", total as i64));
cx.span().set_status(Status::Ok);
// Sort by date_taken based on request, defaulting to ascending (oldest to newest)
use crate::data::SortType;
let sort_type = req.sort.unwrap_or(SortType::DateTakenAsc);
Ok(HttpResponse::Ok().json(GpsPhotosResponse { photos, total }))
}
Err(e) => {
error!("Error querying GPS data: {:?}", e);
cx.span().set_status(Status::error(format!("Database error: {:?}", e)));
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Failed to query GPS data"
})))
match sort_type {
SortType::DateTakenDesc => {
photos.sort_by(|a, b| match (a.date_taken, b.date_taken) {
(Some(date_a), Some(date_b)) => date_b.cmp(&date_a),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => a.path.cmp(&b.path),
});
}
NameAsc => {
photos.sort_by(|a, b| a.path.cmp(&b.path));
}
SortType::NameDesc => {
photos.sort_by(|a, b| b.path.cmp(&a.path));
}
_ => {
// Default: DateTakenAsc
photos.sort_by(|a, b| match (a.date_taken, b.date_taken) {
(Some(date_a), Some(date_b)) => date_a.cmp(&date_b),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => a.path.cmp(&b.path),
});
}
}
let total = photos.len();
cx.span()
.set_attribute(KeyValue::new("result_count", total as i64));
cx.span().set_status(Status::Ok);
Ok(HttpResponse::Ok().json(GpsPhotosResponse { photos, total }))
}
Err(e) => {
error!("Error querying GPS data: {:?}", e);
cx.span()
.set_status(Status::error(format!("Database error: {:?}", e)));
Ok(HttpResponse::InternalServerError().json(serde_json::json!({
"error": "Failed to query GPS data"
})))
}
}
}
pub async fn move_file<FS: FileSystemAccess>(
_: Claims,
file_system: web::Data<FS>,
file_system: Data<FS>,
app_state: Data<AppState>,
request: web::Json<MoveFileRequest>,
) -> HttpResponse {
@@ -1223,7 +1261,7 @@ mod tests {
&mut self,
_context: &opentelemetry::Context,
_: &str,
) -> Result<Option<crate::database::models::ImageExif>, crate::database::DbError> {
) -> Result<Option<crate::database::models::ImageExif>, DbError> {
Ok(None)
}
@@ -1231,7 +1269,7 @@ mod tests {
&mut self,
_context: &opentelemetry::Context,
data: crate::database::models::InsertImageExif,
) -> Result<crate::database::models::ImageExif, crate::database::DbError> {
) -> Result<crate::database::models::ImageExif, DbError> {
// Return a dummy ImageExif for tests
Ok(crate::database::models::ImageExif {
id: 1,
@@ -1259,14 +1297,14 @@ mod tests {
&mut self,
_context: &opentelemetry::Context,
_: &str,
) -> Result<(), crate::database::DbError> {
) -> Result<(), DbError> {
Ok(())
}
fn get_all_with_date_taken(
&mut self,
_context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, crate::database::DbError> {
) -> Result<Vec<(String, i64)>, DbError> {
Ok(Vec::new())
}
@@ -1274,7 +1312,7 @@ mod tests {
&mut self,
_context: &opentelemetry::Context,
_: &[String],
) -> Result<Vec<crate::database::models::ImageExif>, crate::database::DbError> {
) -> Result<Vec<crate::database::models::ImageExif>, DbError> {
Ok(Vec::new())
}
@@ -1287,14 +1325,14 @@ mod tests {
_: Option<(f64, f64, f64, f64)>,
_: Option<i64>,
_: Option<i64>,
) -> Result<Vec<crate::database::models::ImageExif>, crate::database::DbError> {
) -> Result<Vec<crate::database::models::ImageExif>, DbError> {
Ok(Vec::new())
}
fn get_camera_makes(
&mut self,
_context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, crate::database::DbError> {
) -> Result<Vec<(String, i64)>, DbError> {
Ok(Vec::new())
}
@@ -1327,7 +1365,12 @@ mod tests {
Ok((file_paths.to_vec(), count))
}
fn get_all_with_gps(&mut self, context: &opentelemetry::Context, base_path: &str, recursive: bool) -> Result<Vec<(String, f64, f64, Option<i64>)>, DbError> {
fn get_all_with_gps(
&mut self,
_context: &opentelemetry::Context,
_base_path: &str,
_recursive: bool,
) -> Result<Vec<(String, f64, f64, Option<i64>)>, DbError> {
todo!()
}
}
@@ -1429,7 +1472,7 @@ mod tests {
Data::new(RealFileSystem::new(temp_dir.to_str().unwrap().to_string())),
Data::new(Mutex::new(SqliteTagDao::default())),
Data::new(Mutex::new(
Box::new(MockExifDao) as Box<dyn crate::database::ExifDao>
Box::new(MockExifDao) as Box<dyn ExifDao>
)),
)
.await;
@@ -1476,7 +1519,7 @@ mod tests {
Data::new(FakeFileSystem::new(HashMap::new())),
Data::new(Mutex::new(tag_dao)),
Data::new(Mutex::new(
Box::new(MockExifDao) as Box<dyn crate::database::ExifDao>
Box::new(MockExifDao) as Box<dyn ExifDao>
)),
)
.await;
@@ -1539,7 +1582,7 @@ mod tests {
Data::new(FakeFileSystem::new(HashMap::new())),
Data::new(Mutex::new(tag_dao)),
Data::new(Mutex::new(
Box::new(MockExifDao) as Box<dyn crate::database::ExifDao>
Box::new(MockExifDao) as Box<dyn ExifDao>
)),
)
.await;