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:
Rasmus Kaj 2022-02-19 00:23:47 +01:00
parent b69f57a0e8
commit c6124b9084
3 changed files with 90 additions and 90 deletions

View File

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

View File

@ -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 {

View File

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