Split long list for person in chunks.

This commit is contained in:
Rasmus Kaj 2017-11-19 22:01:17 +01:00
parent 36653d760f
commit 63fac99f27
5 changed files with 156 additions and 29 deletions

View File

@ -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<String>,
}
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::<Person>(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::<Photo>(c)
.unwrap();
if photos.len() < 42 {
return res.ok(|o| {
templates::person(
o,
req,
&photos.iter().map(PhotoLink::from).collect::<Vec<_>>(),
&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::<Vec<_>>(),
&person,
)
});
}
}
res.not_found("Not a person")
}

43
src/server/splitlist.rs Normal file
View File

@ -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)
}

View File

@ -433,8 +433,11 @@ pub fn prev_image<'mw>(
}
fn from_date(req: &mut Request) -> Option<NaiveDateTime> {
query_date(req, "from")
}
pub fn query_date(req: &mut Request, name: &str) -> Option<NaiveDateTime> {
req.query()
.get("from")
.get(name)
.and_then(|s| s.parse().ok())
.and_then(|i: i32| {
use schema::photos::dsl::{date, photos};

View File

@ -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), &[], {
<div class="group">
@for p in photos {@:img_link(p)}
@for p in photos {@:photo_link(p)}
</div>
})

View File

@ -0,0 +1,7 @@
@use server::PhotoLink;
@(photo: &PhotoLink)
<p class="item">
<a href="@photo.href"><img src="/img/@photo.id-s.jpg"></a>
@if let &Some(ref d) = &photo.lable {<br/>@d}
</p>