diff --git a/src/server/mod.rs b/src/server/mod.rs index 8ebab8d..a1670a2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,7 +1,8 @@ #[macro_use] mod nickelext; -mod views_by_date; mod admin; +mod splitlist; +mod views_by_date; use adm::result::Error; use chrono::Datelike; @@ -27,7 +28,6 @@ use models::{Person, Photo, Place, Tag}; use env::{dburl, env_or, jwt_key, photos_dir}; use requestloggermiddleware::RequestLoggerMiddleware; - use photosdirmiddleware::{PhotosDirMiddleware, PhotosDirRequestExtensions}; use memcachemiddleware::*; @@ -37,9 +37,56 @@ use rustc_serialize::json::ToJson; use templates; use self::nickelext::{FromSlug, MyResponse, far_expires}; - +use self::splitlist::*; use self::views_by_date::*; +pub struct PhotoLink { + pub href: String, + pub id: i32, + pub lable: Option, +} + +impl PhotoLink { + fn for_group(g: &[Photo], base_url: &str) -> PhotoLink { + PhotoLink { + href: format!( + "{}?from={}&to={}", + base_url, + g.last().map(|p| p.id).unwrap_or(0), + g.first().map(|p| p.id).unwrap_or(0), + ), + id: g.iter() + .max_by_key( + |p| p.grade.unwrap_or(2) + if p.is_public { 3 } else { 0 }, + ) + .map(|p| p.id) + .unwrap_or(0), + lable: Some(format!( + "{} - {} ({})", + g.last() + .and_then(|p| p.date) + .map(|d| format!("{}", d.format("%F %T"))) + .unwrap_or("-".into()), + g.first() + .and_then(|p| p.date) + .map(|d| format!("{}", d.format("%F %T"))) + .unwrap_or("-".into()), + g.len(), + )), + } + } +} + +impl<'a> From<&'a Photo> for PhotoLink { + fn from(p: &'a Photo) -> PhotoLink { + PhotoLink { + href: format!("/img/{}", p.id), + id: p.id, + lable: p.date.map(|d| format!("{}", d.format("%F %T"))), + } + } +} + #[derive(Debug, Clone)] pub struct Group { pub title: String, @@ -440,28 +487,54 @@ fn person_one<'mw>( use schema::people::dsl::{people, slug}; let c: &PgConnection = &req.db_conn(); if let Ok(person) = people.filter(slug.eq(tslug)).first::(c) { + let from_date = query_date(req, "from"); + let to_date = query_date(req, "to"); use schema::photo_people::dsl::{person_id, photo_id, photo_people}; - use schema::photos::dsl::{date, grade, id}; - return res.ok(|o| { - templates::person( - o, - req, - &Photo::query(req.authorized_user().is_some()) - .filter( - id.eq_any( - photo_people - .select(photo_id) - .filter(person_id.eq(person.id)), - ), - ) - .order( - (grade.desc().nulls_last(), date.desc().nulls_last()), - ) - .load(c) - .unwrap(), - &person, - ) - }); + use schema::photos::dsl::{date, id}; + let photos = Photo::query(req.authorized_user().is_some()).filter( + id.eq_any( + photo_people + .select(photo_id) + .filter(person_id.eq(person.id)), + ), + ); + let photos = if let Some(from_date) = from_date { + photos.filter(date.ge(from_date)) + } else { + photos + }; + let photos = if let Some(to_date) = to_date { + photos.filter(date.le(to_date)) + } else { + photos + }; + let photos = photos + .order(date.desc().nulls_last()) + .load::(c) + .unwrap(); + if photos.len() < 42 { + return res.ok(|o| { + templates::person( + o, + req, + &photos.iter().map(PhotoLink::from).collect::>(), + &person, + ) + }); + } else { + return res.ok(|o| { + let path = req.path_without_query().unwrap_or("/"); + templates::person( + o, + req, + &split_to_groups(&photos) + .iter() + .map(|g| PhotoLink::for_group(g, path)) + .collect::>(), + &person, + ) + }); + } } res.not_found("Not a person") } diff --git a/src/server/splitlist.rs b/src/server/splitlist.rs new file mode 100644 index 0000000..1ced7df --- /dev/null +++ b/src/server/splitlist.rs @@ -0,0 +1,43 @@ +use models::Photo; + +pub fn split_to_groups(photos: &[Photo]) -> Vec<&[Photo]> { + let wanted_groups = (photos.len() as f64).sqrt() as usize; + let mut groups = vec![&photos[..]]; + while groups.len() < wanted_groups { + let i = find_largest(&groups); + let (a, b) = split(groups[i]); + groups[i] = a; + groups.insert(i + 1, b); + } + groups +} + +fn find_largest(groups: &[&[Photo]]) -> usize { + let mut found = 0; + let mut largest = 0; + for (i, g) in groups.iter().enumerate() { + if g.len() > largest { + largest = g.len(); + found = i; + } + } + found +} + +fn split(group: &[Photo]) -> (&[Photo], &[Photo]) { + let l = group.len(); + let mut pos = 0; + let mut dist = 0; + for i in l / 8..l - l / 8 - 1 { + let tttt = timestamp(&group[i]) - timestamp(&group[i + 1]); + if tttt > dist { + dist = tttt; + pos = i + 1; + } + } + group.split_at(pos) +} + +fn timestamp(p: &Photo) -> i64 { + p.date.map(|d| d.timestamp()).unwrap_or(0) +} diff --git a/src/server/views_by_date.rs b/src/server/views_by_date.rs index 274d1d6..c27b64f 100644 --- a/src/server/views_by_date.rs +++ b/src/server/views_by_date.rs @@ -433,8 +433,11 @@ pub fn prev_image<'mw>( } fn from_date(req: &mut Request) -> Option { + query_date(req, "from") +} +pub fn query_date(req: &mut Request, name: &str) -> Option { req.query() - .get("from") + .get(name) .and_then(|s| s.parse().ok()) .and_then(|i: i32| { use schema::photos::dsl::{date, photos}; diff --git a/templates/person.rs.html b/templates/person.rs.html index c53e4b8..44fde78 100644 --- a/templates/person.rs.html +++ b/templates/person.rs.html @@ -1,10 +1,11 @@ @use nickel::Request; -@use models::{Photo, Person}; -@use templates::{page_base, img_link}; +@use models::Person; +@use server::PhotoLink; +@use templates::{page_base, photo_link}; -@(req: &Request, photos: &[Photo], person: &Person) +@(req: &Request, photos: &[PhotoLink], person: &Person) @:page_base(req, &format!("Photos with {}", person.person_name), &[], {
- @for p in photos {@:img_link(p)} + @for p in photos {@:photo_link(p)}
}) diff --git a/templates/photo_link.rs.html b/templates/photo_link.rs.html new file mode 100644 index 0000000..9674528 --- /dev/null +++ b/templates/photo_link.rs.html @@ -0,0 +1,7 @@ +@use server::PhotoLink; + +@(photo: &PhotoLink) +

+ + @if let &Some(ref d) = &photo.lable {
@d} +