More refactoring.
Get rid of a bunch of remaining `unwrap`s, and consolidate some parameters to a template that had too many.
This commit is contained in:
parent
b69f57a0e8
commit
c6124b9084
133
src/models.rs
133
src/models.rs
@ -15,10 +15,79 @@ use diesel::pg::{Pg, PgConnection};
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
use diesel::sql_types::Integer;
|
||||
use log::error;
|
||||
use slug::slugify;
|
||||
use std::cmp::max;
|
||||
|
||||
pub struct PhotoDetails {
|
||||
photo: Photo,
|
||||
pub people: Vec<Person>,
|
||||
pub places: Vec<Place>,
|
||||
pub tags: Vec<Tag>,
|
||||
pub pos: Option<Coord>,
|
||||
pub attribution: Option<String>,
|
||||
pub camera: Option<Camera>,
|
||||
}
|
||||
impl PhotoDetails {
|
||||
pub fn load(id: i32, db: &PgConnection) -> Result<Self, Error> {
|
||||
use crate::schema::photos::dsl::photos;
|
||||
let photo = photos.find(id).first::<Photo>(db)?;
|
||||
let attribution = photo
|
||||
.attribution_id
|
||||
.map(|i| a::attributions.find(i).select(a::name).first(db))
|
||||
.transpose()?;
|
||||
let camera = photo
|
||||
.camera_id
|
||||
.map(|i| c::cameras.find(i).first(db))
|
||||
.transpose()?;
|
||||
|
||||
Ok(PhotoDetails {
|
||||
photo,
|
||||
people: h::people
|
||||
.filter(
|
||||
h::id.eq_any(
|
||||
ph::photo_people
|
||||
.select(ph::person_id)
|
||||
.filter(ph::photo_id.eq(id)),
|
||||
),
|
||||
)
|
||||
.load(db)?,
|
||||
places: l::places
|
||||
.filter(
|
||||
l::id.eq_any(
|
||||
pl::photo_places
|
||||
.select(pl::place_id)
|
||||
.filter(pl::photo_id.eq(id)),
|
||||
),
|
||||
)
|
||||
.order(l::osm_level.desc().nulls_first())
|
||||
.load(db)?,
|
||||
tags: t::tags
|
||||
.filter(
|
||||
t::id.eq_any(
|
||||
pt::photo_tags
|
||||
.select(pt::tag_id)
|
||||
.filter(pt::photo_id.eq(id)),
|
||||
),
|
||||
)
|
||||
.load(db)?,
|
||||
pos: pos::positions
|
||||
.filter(pos::photo_id.eq(id))
|
||||
.select((pos::latitude, pos::longitude))
|
||||
.first(db)
|
||||
.optional()?,
|
||||
attribution,
|
||||
camera,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for PhotoDetails {
|
||||
type Target = Photo;
|
||||
fn deref(&self) -> &Photo {
|
||||
&self.photo
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AsChangeset, Clone, Debug, Identifiable, Queryable)]
|
||||
pub struct Photo {
|
||||
pub id: i32,
|
||||
@ -136,68 +205,6 @@ impl Photo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_people(
|
||||
&self,
|
||||
db: &PgConnection,
|
||||
) -> Result<Vec<Person>, Error> {
|
||||
h::people
|
||||
.filter(
|
||||
h::id.eq_any(
|
||||
ph::photo_people
|
||||
.select(ph::person_id)
|
||||
.filter(ph::photo_id.eq(self.id)),
|
||||
),
|
||||
)
|
||||
.load(db)
|
||||
}
|
||||
|
||||
pub fn load_places(&self, db: &PgConnection) -> Result<Vec<Place>, Error> {
|
||||
l::places
|
||||
.filter(
|
||||
l::id.eq_any(
|
||||
pl::photo_places
|
||||
.select(pl::place_id)
|
||||
.filter(pl::photo_id.eq(self.id)),
|
||||
),
|
||||
)
|
||||
.order(l::osm_level.desc().nulls_first())
|
||||
.load(db)
|
||||
}
|
||||
pub fn load_tags(&self, db: &PgConnection) -> Result<Vec<Tag>, Error> {
|
||||
t::tags
|
||||
.filter(
|
||||
t::id.eq_any(
|
||||
pt::photo_tags
|
||||
.select(pt::tag_id)
|
||||
.filter(pt::photo_id.eq(self.id)),
|
||||
),
|
||||
)
|
||||
.load(db)
|
||||
}
|
||||
|
||||
pub fn load_position(&self, db: &PgConnection) -> Option<Coord> {
|
||||
match pos::positions
|
||||
.filter(pos::photo_id.eq(self.id))
|
||||
.select((pos::latitude, pos::longitude))
|
||||
.first::<(i32, i32)>(db)
|
||||
{
|
||||
Ok(c) => Some(c.into()),
|
||||
Err(diesel::NotFound) => None,
|
||||
Err(err) => {
|
||||
error!("Failed to read position: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn load_attribution(&self, db: &PgConnection) -> Option<String> {
|
||||
self.attribution_id.and_then(|i| {
|
||||
a::attributions.find(i).select(a::name).first(db).ok()
|
||||
})
|
||||
}
|
||||
pub fn load_camera(&self, db: &PgConnection) -> Option<Camera> {
|
||||
self.camera_id
|
||||
.and_then(|i| c::cameras.find(i).first(db).ok())
|
||||
}
|
||||
pub fn get_size(&self, size: SizeTag) -> (u32, u32) {
|
||||
let (width, height) = (self.width, self.height);
|
||||
let scale = f64::from(size.px()) / f64::from(max(width, height));
|
||||
|
@ -25,7 +25,7 @@ use self::views_by_date::monthname;
|
||||
use super::{CacheOpt, DbOpt, DirOpt};
|
||||
use crate::adm::result::Error;
|
||||
use crate::fetch_places::OverpassOpt;
|
||||
use crate::models::Photo;
|
||||
use crate::models::{Photo, PhotoDetails};
|
||||
use crate::pidfiles::handle_pid_file;
|
||||
use crate::templates::{self, Html, RenderRucte};
|
||||
use chrono::Datelike;
|
||||
@ -155,34 +155,27 @@ fn random_image(context: Context) -> Result<Response> {
|
||||
}
|
||||
|
||||
fn photo_details(id: i32, context: Context) -> Result<Response> {
|
||||
use crate::schema::photos::dsl::photos;
|
||||
let c = context.db()?;
|
||||
let tphoto = or_404q!(photos.find(id).first::<Photo>(&c), context);
|
||||
let photo = or_404q!(PhotoDetails::load(id, &c), context);
|
||||
|
||||
if context.is_authorized() || tphoto.is_public() {
|
||||
if context.is_authorized() || photo.is_public() {
|
||||
Ok(Builder::new().html(|o| {
|
||||
templates::details(
|
||||
o,
|
||||
&context,
|
||||
&tphoto
|
||||
&photo
|
||||
.date
|
||||
.map(|d| {
|
||||
vec![
|
||||
Link::year(d.year()),
|
||||
Link::month(d.year(), d.month()),
|
||||
Link::day(d.year(), d.month(), d.day()),
|
||||
Link::prev(tphoto.id),
|
||||
Link::next(tphoto.id),
|
||||
Link::prev(photo.id),
|
||||
Link::next(photo.id),
|
||||
]
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
&tphoto.load_people(&c).unwrap(),
|
||||
&tphoto.load_places(&c).unwrap(),
|
||||
&tphoto.load_tags(&c).unwrap(),
|
||||
&tphoto.load_position(&c),
|
||||
&tphoto.load_attribution(&c),
|
||||
&tphoto.load_camera(&c),
|
||||
&tphoto,
|
||||
&photo,
|
||||
)
|
||||
})?)
|
||||
} else {
|
||||
|
@ -1,15 +1,15 @@
|
||||
@use super::base;
|
||||
@use crate::models::{Photo, Person, Place, Tag, Camera, Coord, SizeTag};
|
||||
@use crate::models::{PhotoDetails, SizeTag};
|
||||
@use crate::server::{Context, Link};
|
||||
|
||||
@(context: &Context, lpath: &[Link], people: &[Person], places: &[Place], tags: &[Tag], position: &Option<Coord>, attribution: &Option<String>, camera: &Option<Camera>, photo: &Photo)
|
||||
@(context: &Context, lpath: &[Link], photo: &PhotoDetails)
|
||||
@:base(context, "Photo details", lpath, {
|
||||
<meta property='og:title' content='Photo @if let Some(d) = photo.date {(@d.format("%F"))}'>
|
||||
<meta property='og:type' content='image' />
|
||||
<meta property='og:image' content='/img/@photo.id-m.jpg' />
|
||||
<meta property='og:description' content='@for p in people {@p.person_name, }@for t in tags {#@t.tag_name, }@if let Some(p) = places.first() {@p.place_name}'>
|
||||
<meta property='og:description' content='@for p in &photo.people {@p.person_name, }@for t in &photo.tags {#@t.tag_name, }@if let Some(p) = &photo.places.first() {@p.place_name}'>
|
||||
}, {
|
||||
<main class="details" data-imgid="@photo.id"@if let Some(g) = photo.grade { data-grade="@g"}@if let Some(ref p) = *position { data-position="[@p.x, @p.y]"}>
|
||||
<main class="details" data-imgid="@photo.id"@if let Some(g) = photo.grade { data-grade="@g"}@if let Some(ref p) = photo.pos { data-position="[@p.x, @p.y]"}>
|
||||
<h1>Photo details</h1>
|
||||
<img class="item" src="/img/@photo.id-m.jpg" width="@photo.get_size(SizeTag::Medium).0" height="@photo.get_size(SizeTag::Medium).1">
|
||||
<div class="meta">
|
||||
@ -20,15 +20,15 @@
|
||||
}
|
||||
@if let Some(g) = photo.grade {<p>Grade: @g</p>}
|
||||
@if let Some(d) = photo.date {<p>Time: @d.format("%F %T")</p>}
|
||||
@if !people.is_empty() {
|
||||
<p>People: @for p in people {<a href="/person/@p.slug">@p.person_name</a>, }</p>}
|
||||
@if !tags.is_empty() {
|
||||
<p>Tags: @for t in tags {<a href="/tag/@t.slug">@t.tag_name</a>, }</p>}
|
||||
@if !places.is_empty() {
|
||||
<p class="places">Places: @for p in places {<a href="/place/@p.slug">@p.place_name</a>, }</p>}
|
||||
@if let Some(ref pos) = *position {<p>Position: @pos.x @pos.y</p>}
|
||||
@if let Some(ref a) = *attribution {<p>Av: @a</p>}
|
||||
@if let Some(ref c) = *camera {<p>Camera: @c.model (@c.manufacturer)</p>}
|
||||
@if !photo.people.is_empty() {
|
||||
<p>People: @for p in &photo.people {<a href="/person/@p.slug">@p.person_name</a>, }</p>}
|
||||
@if !photo.tags.is_empty() {
|
||||
<p>Tags: @for t in &photo.tags {<a href="/tag/@t.slug">@t.tag_name</a>, }</p>}
|
||||
@if !photo.places.is_empty() {
|
||||
<p class="places">Places: @for p in &photo.places {<a href="/place/@p.slug">@p.place_name</a>, }</p>}
|
||||
@if let Some(ref pos) = photo.pos {<p>Position: @pos.x @pos.y</p>}
|
||||
@if let Some(ref a) = photo.attribution {<p>Av: @a</p>}
|
||||
@if let Some(ref c) = photo.camera {<p>Camera: @c.model (@c.manufacturer)</p>}
|
||||
</div>
|
||||
</main>
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user