Use diesel 2.0
Most of the changes are for database access now requiring an &mut connection.
This commit is contained in:
parent
f7c3d60e77
commit
03dd6a5465
@ -11,6 +11,9 @@ The format is based on
|
||||
* Improved logging by using tracing and tracing-subscriber rather than
|
||||
log and env_logger.
|
||||
* Four more kinds of OSM areas to recognize.
|
||||
* Update diesel to 2.0.0: Mainly most operations now needs a `&mut
|
||||
PgConnection`. Also, getting parts of dates are now done by sql
|
||||
functions.
|
||||
* Update ructe to 0.15.0.
|
||||
* Add this changelog.
|
||||
|
||||
|
@ -11,7 +11,7 @@ ructe = { version = "0.15.0", features = ["sass", "warp03"] }
|
||||
|
||||
[dependencies]
|
||||
brotli = "3.3.0"
|
||||
chrono = "0.4.0" # Must match version used by diesel
|
||||
chrono = "0.4.19" # Must match version used by diesel
|
||||
clap = { version = "3.2.8", features = ["derive", "terminal_size", "wrap_help", "env"] }
|
||||
dotenv = "0.15"
|
||||
flate2 = "1.0.14"
|
||||
@ -39,7 +39,7 @@ version = "1.1.1"
|
||||
[dependencies.diesel]
|
||||
default-features = false
|
||||
features = ["r2d2", "chrono", "postgres"]
|
||||
version = "1.4.0"
|
||||
version = "2.0.0"
|
||||
|
||||
[dependencies.warp]
|
||||
default-features = false
|
||||
|
5
migrations/2022-04-29-203357_datefuncs/down.sql
Normal file
5
migrations/2022-04-29-203357_datefuncs/down.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- This file should undo anything in `up.sql`
|
||||
|
||||
drop function year_of_timestamp;
|
||||
drop function month_of_timestamp;
|
||||
drop function day_of_timestamp;
|
16
migrations/2022-04-29-203357_datefuncs/up.sql
Normal file
16
migrations/2022-04-29-203357_datefuncs/up.sql
Normal file
@ -0,0 +1,16 @@
|
||||
-- SQL functions for handling dates
|
||||
|
||||
create function year_of_timestamp(arg timestamp with time zone)
|
||||
returns smallint
|
||||
language sql immutable strict parallel safe
|
||||
as $func$ select cast(date_part('year', arg at time zone 'UTC') as smallint); $func$;
|
||||
|
||||
create function month_of_timestamp(arg timestamp with time zone)
|
||||
returns smallint
|
||||
language sql immutable strict parallel safe
|
||||
as $func$ select cast(date_part('month', arg at time zone 'UTC') as smallint); $func$;
|
||||
|
||||
create function day_of_timestamp(arg timestamp with time zone)
|
||||
returns smallint
|
||||
language sql immutable strict parallel safe
|
||||
as $func$ select cast(date_part('day', arg at time zone 'UTC') as smallint); $func$;
|
@ -23,15 +23,15 @@ pub struct Findphotos {
|
||||
impl Findphotos {
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let pd = PhotosDir::new(&self.photos.photos_dir);
|
||||
let db = self.db.connect()?;
|
||||
let mut db = self.db.connect()?;
|
||||
if !self.base.is_empty() {
|
||||
for base in &self.base {
|
||||
crawl(&db, &pd, Path::new(base)).map_err(|e| {
|
||||
crawl(&mut db, &pd, Path::new(base)).map_err(|e| {
|
||||
Error::Other(format!("Failed to crawl {}: {}", base, e))
|
||||
})?;
|
||||
}
|
||||
} else {
|
||||
crawl(&db, &pd, Path::new("")).map_err(|e| {
|
||||
crawl(&mut db, &pd, Path::new("")).map_err(|e| {
|
||||
Error::Other(format!("Failed to crawl: {}", e))
|
||||
})?;
|
||||
}
|
||||
@ -40,35 +40,36 @@ impl Findphotos {
|
||||
}
|
||||
|
||||
fn crawl(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
photos: &PhotosDir,
|
||||
only_in: &Path,
|
||||
) -> Result<(), Error> {
|
||||
photos.find_files(
|
||||
only_in,
|
||||
&|path, exif| match save_photo(db, path, exif) {
|
||||
photos.find_files(only_in, &mut |path, exif| match save_photo(
|
||||
db, path, exif,
|
||||
) {
|
||||
Ok(()) => debug!("Saved photo {}", path),
|
||||
Err(e) => warn!("Failed to save photo {}: {:?}", path, e),
|
||||
},
|
||||
)?;
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_photo(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
file_path: &str,
|
||||
exif: &ExifData,
|
||||
) -> Result<(), Error> {
|
||||
let width = exif.width.ok_or(Error::MissingWidth)?;
|
||||
let height = exif.height.ok_or(Error::MissingHeight)?;
|
||||
let rot = exif.rotation()?;
|
||||
let cam = find_camera(db, exif)?;
|
||||
let photo = match Photo::create_or_set_basics(
|
||||
db,
|
||||
file_path,
|
||||
width as i32,
|
||||
height as i32,
|
||||
exif.date(),
|
||||
exif.rotation()?,
|
||||
find_camera(db, exif)?,
|
||||
rot,
|
||||
cam,
|
||||
)? {
|
||||
Modification::Created(photo) => {
|
||||
info!("Created #{}, {}", photo.id, photo.path);
|
||||
@ -116,7 +117,7 @@ fn save_photo(
|
||||
}
|
||||
|
||||
fn find_camera(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
exif: &ExifData,
|
||||
) -> Result<Option<Camera>, Error> {
|
||||
if let Some((make, model)) = exif.camera() {
|
||||
|
@ -30,7 +30,7 @@ pub struct Makepublic {
|
||||
|
||||
impl Makepublic {
|
||||
pub fn run(&self) -> Result<(), Error> {
|
||||
let db = self.db.connect()?;
|
||||
let mut db = self.db.connect()?;
|
||||
match (
|
||||
self.list.as_ref().map(AsRef::as_ref),
|
||||
&self.tag,
|
||||
@ -38,12 +38,12 @@ impl Makepublic {
|
||||
) {
|
||||
(Some("-"), None, None) => {
|
||||
let list = io::stdin();
|
||||
by_file_list(&db, list.lock())?;
|
||||
by_file_list(&mut db, list.lock())?;
|
||||
Ok(())
|
||||
}
|
||||
(Some(list), None, None) => {
|
||||
let list = BufReader::new(File::open(list)?);
|
||||
by_file_list(&db, list)
|
||||
by_file_list(&mut db, list)
|
||||
}
|
||||
(None, Some(tag), None) => {
|
||||
use crate::schema::photo_tags::dsl as pt;
|
||||
@ -60,17 +60,17 @@ impl Makepublic {
|
||||
),
|
||||
)
|
||||
.set(p::is_public.eq(true))
|
||||
.execute(&db)?;
|
||||
.execute(&mut db)?;
|
||||
println!("Made {} images public.", n);
|
||||
Ok(())
|
||||
}
|
||||
(None, None, Some(image)) => one(&db, image),
|
||||
(None, None, Some(image)) => one(&mut db, image),
|
||||
_ => Err(Error::Other("bad command".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn one(db: &PgConnection, tpath: &str) -> Result<(), Error> {
|
||||
pub fn one(db: &mut PgConnection, tpath: &str) -> Result<(), Error> {
|
||||
use crate::schema::photos::dsl::*;
|
||||
match update(photos.filter(path.eq(&tpath)))
|
||||
.set(is_public.eq(true))
|
||||
@ -88,7 +88,7 @@ pub fn one(db: &PgConnection, tpath: &str) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
pub fn by_file_list<In: BufRead + Sized>(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
list: In,
|
||||
) -> Result<(), Error> {
|
||||
for line in list.lines() {
|
||||
|
@ -37,7 +37,7 @@ impl Args {
|
||||
let (mut n, mut n_stored) = (0, 0);
|
||||
let photos = Photo::query(true)
|
||||
.order((is_public.desc(), date.desc().nulls_last()))
|
||||
.load::<Photo>(&self.db.connect()?)?;
|
||||
.load::<Photo>(&mut self.db.connect()?)?;
|
||||
let no_expire = 0;
|
||||
let pd = PhotosDir::new(&self.photos.photos_dir);
|
||||
for photo in photos {
|
||||
|
@ -3,12 +3,12 @@ use crate::schema::people::dsl::people;
|
||||
use crate::schema::photos::dsl::photos;
|
||||
use crate::schema::places::dsl::places;
|
||||
use crate::schema::tags::dsl::tags;
|
||||
use diesel::expression::dsl::{count_star, sql};
|
||||
use diesel::dsl::{count_star, sql};
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sql_types::{BigInt, Double, Nullable};
|
||||
|
||||
pub fn show_stats(db: &PgConnection) -> Result<(), Error> {
|
||||
pub fn show_stats(db: &mut PgConnection) -> Result<(), Error> {
|
||||
println!(
|
||||
"There are {} photos in total.",
|
||||
photos.select(count_star()).first::<i64>(db)?,
|
||||
|
@ -6,7 +6,7 @@ use djangohashers::make_password;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::iter::Iterator;
|
||||
|
||||
pub fn list(db: &PgConnection) -> Result<(), Error> {
|
||||
pub fn list(db: &mut PgConnection) -> Result<(), Error> {
|
||||
use crate::schema::users::dsl::*;
|
||||
println!(
|
||||
"Existing users: {:?}.",
|
||||
@ -15,7 +15,7 @@ pub fn list(db: &PgConnection) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn passwd(db: &PgConnection, uname: &str) -> Result<(), Error> {
|
||||
pub fn passwd(db: &mut PgConnection, uname: &str) -> Result<(), Error> {
|
||||
let pword = random_password(14);
|
||||
let hashword = make_password(&pword);
|
||||
use crate::schema::users::dsl::*;
|
||||
|
@ -38,7 +38,7 @@ impl Fetchplaces {
|
||||
))
|
||||
.order(pos::photo_id.desc())
|
||||
.limit(self.limit)
|
||||
.load::<(i32, Coord)>(&db.get()?)?;
|
||||
.load::<(i32, Coord)>(&mut db.get()?)?;
|
||||
for (photo_id, coord) in result {
|
||||
println!("Find places for #{}, {:?}", photo_id, coord);
|
||||
self.overpass.update_image_places(&db, photo_id).await?;
|
||||
@ -74,7 +74,9 @@ impl OverpassOpt {
|
||||
.filter(photo_id.eq(image))
|
||||
.select((latitude, longitude))
|
||||
.first::<Coord>(
|
||||
&db.get().map_err(|e| Error::Pool(image, e.to_string()))?,
|
||||
&mut db
|
||||
.get()
|
||||
.map_err(|e| Error::Pool(image, e.to_string()))?,
|
||||
)
|
||||
.optional()
|
||||
.map_err(|e| Error::Db(image, e))?
|
||||
@ -96,13 +98,15 @@ impl OverpassOpt {
|
||||
.and_then(|o| o.get("elements"))
|
||||
.and_then(Value::as_array)
|
||||
{
|
||||
let c = db.get().map_err(|e| Error::Pool(image, e.to_string()))?;
|
||||
let mut c =
|
||||
db.get().map_err(|e| Error::Pool(image, e.to_string()))?;
|
||||
for obj in elements {
|
||||
if let (Some(t_osm_id), Some((name, level))) =
|
||||
(osm_id(obj), name_and_level(obj))
|
||||
{
|
||||
debug!("{}: {} (level {})", t_osm_id, name, level);
|
||||
let place = get_or_create_place(&c, t_osm_id, name, level)
|
||||
let place =
|
||||
get_or_create_place(&mut c, t_osm_id, name, level)
|
||||
.map_err(|e| Error::Db(image, e))?;
|
||||
if place.osm_id.is_none() {
|
||||
debug!("Matched {:?} by name, update osm info", place);
|
||||
@ -113,7 +117,7 @@ impl OverpassOpt {
|
||||
osm_id.eq(Some(t_osm_id)),
|
||||
osm_level.eq(level),
|
||||
))
|
||||
.execute(&c)
|
||||
.execute(&mut c)
|
||||
.map_err(|e| Error::Db(image, e))?;
|
||||
}
|
||||
use crate::models::PhotoPlace;
|
||||
@ -121,7 +125,7 @@ impl OverpassOpt {
|
||||
let q = photo_places
|
||||
.filter(photo_id.eq(image))
|
||||
.filter(place_id.eq(place.id));
|
||||
if q.first::<PhotoPlace>(&c).is_ok() {
|
||||
if q.first::<PhotoPlace>(&mut c).is_ok() {
|
||||
debug!(
|
||||
"Photo #{} already has {} ({})",
|
||||
image, place.id, place.place_name
|
||||
@ -132,7 +136,7 @@ impl OverpassOpt {
|
||||
photo_id.eq(image),
|
||||
place_id.eq(place.id),
|
||||
))
|
||||
.execute(&c)
|
||||
.execute(&mut c)
|
||||
.map_err(|e| Error::Db(image, e))?;
|
||||
}
|
||||
} else {
|
||||
@ -291,7 +295,7 @@ fn tag_str<'a>(tags: &'a Value, name: &str) -> Option<&'a str> {
|
||||
}
|
||||
|
||||
fn get_or_create_place(
|
||||
c: &PgConnection,
|
||||
c: &mut PgConnection,
|
||||
t_osm_id: i64,
|
||||
name: &str,
|
||||
level: i16,
|
||||
|
@ -102,9 +102,11 @@ async fn run(args: &RPhotos) -> Result<(), Error> {
|
||||
match args {
|
||||
RPhotos::Findphotos(cmd) => cmd.run(),
|
||||
RPhotos::Makepublic(cmd) => cmd.run(),
|
||||
RPhotos::Stats(db) => show_stats(&db.connect()?),
|
||||
RPhotos::Userlist { db } => users::list(&db.connect()?),
|
||||
RPhotos::Userpass { db, user } => users::passwd(&db.connect()?, user),
|
||||
RPhotos::Stats(db) => show_stats(&mut db.connect()?),
|
||||
RPhotos::Userlist { db } => users::list(&mut db.connect()?),
|
||||
RPhotos::Userpass { db, user } => {
|
||||
users::passwd(&mut db.connect()?, user)
|
||||
}
|
||||
RPhotos::Fetchplaces(cmd) => cmd.run().await,
|
||||
RPhotos::Precache(cmd) => cmd.run().await,
|
||||
RPhotos::Storestatics { dir } => storestatics::to_dir(dir),
|
||||
|
@ -28,7 +28,7 @@ pub struct PhotoDetails {
|
||||
pub camera: Option<Camera>,
|
||||
}
|
||||
impl PhotoDetails {
|
||||
pub fn load(id: i32, db: &PgConnection) -> Result<Self, Error> {
|
||||
pub fn load(id: i32, db: &mut PgConnection) -> Result<Self, Error> {
|
||||
use crate::schema::photos::dsl::photos;
|
||||
let photo = photos.find(id).first::<Photo>(db)?;
|
||||
let attribution = photo
|
||||
@ -133,7 +133,7 @@ impl Photo {
|
||||
}
|
||||
|
||||
pub fn update_by_path(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
file_path: &str,
|
||||
newwidth: i32,
|
||||
newheight: i32,
|
||||
@ -178,7 +178,7 @@ impl Photo {
|
||||
}
|
||||
|
||||
pub fn create_or_set_basics(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
file_path: &str,
|
||||
newwidth: i32,
|
||||
newheight: i32,
|
||||
@ -239,7 +239,7 @@ impl Photo {
|
||||
}
|
||||
|
||||
pub trait Facet {
|
||||
fn by_slug(slug: &str, db: &PgConnection) -> Result<Self, Error>
|
||||
fn by_slug(slug: &str, db: &mut PgConnection) -> Result<Self, Error>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
@ -252,7 +252,7 @@ pub struct Tag {
|
||||
}
|
||||
|
||||
impl Facet for Tag {
|
||||
fn by_slug(slug: &str, db: &PgConnection) -> Result<Tag, Error> {
|
||||
fn by_slug(slug: &str, db: &mut PgConnection) -> Result<Tag, Error> {
|
||||
t::tags.filter(t::slug.eq(slug)).first(db)
|
||||
}
|
||||
}
|
||||
@ -273,7 +273,7 @@ pub struct Person {
|
||||
|
||||
impl Person {
|
||||
pub fn get_or_create_name(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
name: &str,
|
||||
) -> Result<Person, Error> {
|
||||
h::people
|
||||
@ -291,7 +291,7 @@ impl Person {
|
||||
}
|
||||
|
||||
impl Facet for Person {
|
||||
fn by_slug(slug: &str, db: &PgConnection) -> Result<Person, Error> {
|
||||
fn by_slug(slug: &str, db: &mut PgConnection) -> Result<Person, Error> {
|
||||
h::people.filter(h::slug.eq(slug)).first(db)
|
||||
}
|
||||
}
|
||||
@ -313,7 +313,7 @@ pub struct Place {
|
||||
}
|
||||
|
||||
impl Facet for Place {
|
||||
fn by_slug(slug: &str, db: &PgConnection) -> Result<Place, Error> {
|
||||
fn by_slug(slug: &str, db: &mut PgConnection) -> Result<Place, Error> {
|
||||
l::places.filter(l::slug.eq(slug)).first(db)
|
||||
}
|
||||
}
|
||||
@ -334,7 +334,7 @@ pub struct Camera {
|
||||
|
||||
impl Camera {
|
||||
pub fn get_or_create(
|
||||
db: &PgConnection,
|
||||
db: &mut PgConnection,
|
||||
make: &str,
|
||||
modl: &str,
|
||||
) -> Result<Camera, Error> {
|
||||
@ -362,8 +362,8 @@ pub struct Coord {
|
||||
impl Queryable<(Integer, Integer), Pg> for Coord {
|
||||
type Row = (i32, i32);
|
||||
|
||||
fn build(row: Self::Row) -> Self {
|
||||
Coord::from((row.0, row.1))
|
||||
fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
|
||||
Ok(Coord::from((row.0, row.1)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ impl PhotosDir {
|
||||
pub fn find_files(
|
||||
&self,
|
||||
dir: &Path,
|
||||
cb: &dyn Fn(&str, &ExifData),
|
||||
cb: &mut dyn FnMut(&str, &ExifData),
|
||||
) -> io::Result<()> {
|
||||
let absdir = self.basedir.join(dir);
|
||||
if fs::metadata(&absdir)?.is_dir() {
|
||||
|
@ -35,8 +35,8 @@ fn rotate(context: Context, form: RotateForm) -> Result<Response> {
|
||||
}
|
||||
info!("Should rotate #{} by {}", form.image, form.angle);
|
||||
use crate::schema::photos::dsl::photos;
|
||||
let c = context.db()?;
|
||||
let c: &PgConnection = &c;
|
||||
let mut c = context.db()?;
|
||||
let c: &mut PgConnection = &mut c;
|
||||
let mut image =
|
||||
or_404q!(photos.find(form.image).first::<Photo>(c), context);
|
||||
let newvalue = (360 + image.rotation + form.angle) % 360;
|
||||
@ -58,19 +58,19 @@ async fn set_tag(context: Context, form: TagForm) -> Result<Response> {
|
||||
if !context.is_authorized() {
|
||||
return Err(ViewError::PermissionDenied);
|
||||
}
|
||||
let c = context.db()?;
|
||||
let mut c = context.db()?;
|
||||
use crate::models::Tag;
|
||||
let tag = {
|
||||
use crate::schema::tags::dsl::*;
|
||||
tags.filter(tag_name.ilike(&form.tag))
|
||||
.first::<Tag>(&c)
|
||||
.first::<Tag>(&mut c)
|
||||
.or_else(|_| {
|
||||
diesel::insert_into(tags)
|
||||
.values((
|
||||
tag_name.eq(&form.tag),
|
||||
slug.eq(&slugify(&form.tag)),
|
||||
))
|
||||
.get_result::<Tag>(&c)
|
||||
.get_result::<Tag>(&mut c)
|
||||
})?
|
||||
};
|
||||
use crate::schema::photo_tags::dsl::*;
|
||||
@ -78,13 +78,13 @@ async fn set_tag(context: Context, form: TagForm) -> Result<Response> {
|
||||
.filter(photo_id.eq(form.image))
|
||||
.filter(tag_id.eq(tag.id))
|
||||
.count();
|
||||
if q.get_result::<i64>(&c)? > 0 {
|
||||
if q.get_result::<i64>(&mut c)? > 0 {
|
||||
info!("Photo #{} already has {:?}", form.image, form.tag);
|
||||
} else {
|
||||
info!("Add {:?} on photo #{}!", form.tag, form.image);
|
||||
diesel::insert_into(photo_tags)
|
||||
.values((photo_id.eq(form.image), tag_id.eq(tag.id)))
|
||||
.execute(&c)?;
|
||||
.execute(&mut c)?;
|
||||
}
|
||||
Ok(redirect_to_img(form.image))
|
||||
}
|
||||
@ -99,20 +99,20 @@ async fn set_person(context: Context, form: PersonForm) -> Result<Response> {
|
||||
if !context.is_authorized() {
|
||||
return Err(ViewError::PermissionDenied);
|
||||
}
|
||||
let c = context.db()?;
|
||||
let mut c = context.db()?;
|
||||
use crate::models::{Person, PhotoPerson};
|
||||
let person = Person::get_or_create_name(&c, &form.person)?;
|
||||
let person = Person::get_or_create_name(&mut c, &form.person)?;
|
||||
use crate::schema::photo_people::dsl::*;
|
||||
let q = photo_people
|
||||
.filter(photo_id.eq(form.image))
|
||||
.filter(person_id.eq(person.id));
|
||||
if q.first::<PhotoPerson>(&c).optional()?.is_some() {
|
||||
if q.first::<PhotoPerson>(&mut c).optional()?.is_some() {
|
||||
info!("Photo #{} already has {:?}", form.image, person);
|
||||
} else {
|
||||
info!("Add {:?} on photo #{}!", person, form.image);
|
||||
diesel::insert_into(photo_people)
|
||||
.values((photo_id.eq(form.image), person_id.eq(person.id)))
|
||||
.execute(&c)?;
|
||||
.execute(&mut c)?;
|
||||
}
|
||||
Ok(redirect_to_img(form.image))
|
||||
}
|
||||
@ -132,7 +132,7 @@ async fn set_grade(context: Context, form: GradeForm) -> Result<Response> {
|
||||
use crate::schema::photos::dsl::{grade, photos};
|
||||
let q =
|
||||
diesel::update(photos.find(form.image)).set(grade.eq(form.grade));
|
||||
match q.execute(&context.db()?)? {
|
||||
match q.execute(&mut context.db()?)? {
|
||||
1 => {
|
||||
return Ok(redirect_to_img(form.image));
|
||||
}
|
||||
@ -173,7 +173,7 @@ async fn set_location(context: Context, form: CoordForm) -> Result<Response> {
|
||||
.on_conflict(photo_id)
|
||||
.do_update()
|
||||
.set((latitude.eq(lat), longitude.eq(lng)))
|
||||
.execute(&context.db()?)?;
|
||||
.execute(&mut context.db()?)?;
|
||||
match context
|
||||
.overpass()
|
||||
.update_image_places(&context.db_pool(), form.image)
|
||||
|
@ -60,9 +60,9 @@ fn w<T: Serialize>(result: ApiResult<T>) -> Response {
|
||||
}
|
||||
|
||||
fn login(context: Context, form: LoginForm) -> ApiResult<LoginOk> {
|
||||
let db = context.db()?;
|
||||
let mut db = context.db()?;
|
||||
let user = form
|
||||
.validate(&db)
|
||||
.validate(&mut db)
|
||||
.ok_or_else(|| ApiError::bad_request("login failed"))?;
|
||||
tracing::info!("Api login {user:?} ok");
|
||||
Ok(LoginOk {
|
||||
@ -98,7 +98,7 @@ enum ImgIdentifier {
|
||||
}
|
||||
|
||||
impl ImgIdentifier {
|
||||
fn load(&self, db: &PgConnection) -> Result<Option<Photo>, DbError> {
|
||||
fn load(&self, db: &mut PgConnection) -> Result<Option<Photo>, DbError> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
match &self {
|
||||
ImgIdentifier::Id(ref id) => {
|
||||
@ -114,8 +114,8 @@ impl ImgIdentifier {
|
||||
|
||||
fn get_img(context: Context, q: ImgQuery) -> ApiResult<GetImgResult> {
|
||||
let id = q.validate().map_err(ApiError::bad_request)?;
|
||||
let db = context.db()?;
|
||||
let img = id.load(&db)?.ok_or(NOT_FOUND)?;
|
||||
let mut db = context.db()?;
|
||||
let img = id.load(&mut db)?.ok_or(NOT_FOUND)?;
|
||||
if !context.is_authorized() && !img.is_public() {
|
||||
return Err(NOT_FOUND);
|
||||
}
|
||||
@ -130,12 +130,12 @@ fn make_public(context: Context, q: ImgQuery) -> ApiResult<GetImgResult> {
|
||||
});
|
||||
}
|
||||
let id = q.validate().map_err(ApiError::bad_request)?;
|
||||
let db = context.db()?;
|
||||
let img = id.load(&db)?.ok_or(NOT_FOUND)?;
|
||||
let mut db = context.db()?;
|
||||
let img = id.load(&mut db)?.ok_or(NOT_FOUND)?;
|
||||
use crate::schema::photos::dsl as p;
|
||||
let img = update(p::photos.find(img.id))
|
||||
.set(p::is_public.eq(true))
|
||||
.get_result(&db)?;
|
||||
.get_result(&mut db)?;
|
||||
Ok(GetImgResult::for_img(&img))
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ use crate::schema::photos::dsl as p;
|
||||
use crate::schema::places::dsl as l;
|
||||
use crate::schema::tags::dsl as t;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sql_types::{Integer, Text};
|
||||
use diesel::sql_types::Text;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::Display;
|
||||
@ -160,8 +160,8 @@ fn select_tags(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
tp::photo_id.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
)))
|
||||
};
|
||||
let db = context.db()?;
|
||||
Ok(query.order((tpos, t::tag_name)).limit(10).load(&db)?)
|
||||
let mut db = context.db()?;
|
||||
Ok(query.order((tpos, t::tag_name)).limit(10).load(&mut db)?)
|
||||
}
|
||||
|
||||
fn select_people(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
@ -182,8 +182,11 @@ fn select_people(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
),
|
||||
)
|
||||
};
|
||||
let db = context.db()?;
|
||||
Ok(query.order((ppos, h::person_name)).limit(10).load(&db)?)
|
||||
let mut db = context.db()?;
|
||||
Ok(query
|
||||
.order((ppos, h::person_name))
|
||||
.limit(10)
|
||||
.load(&mut db)?)
|
||||
}
|
||||
|
||||
fn select_places(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
@ -204,6 +207,6 @@ fn select_places(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
),
|
||||
)
|
||||
};
|
||||
let db = context.db()?;
|
||||
Ok(query.order((lpos, l::place_name)).limit(10).load(&db)?)
|
||||
let mut db = context.db()?;
|
||||
Ok(query.order((lpos, l::place_name)).limit(10).load(&mut db)?)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use warp::reply::Response;
|
||||
|
||||
pub async fn show_image(img: ImgName, context: Context) -> Result<Response> {
|
||||
use crate::schema::photos::dsl::photos;
|
||||
let tphoto = photos.find(img.id).first::<Photo>(&context.db()?);
|
||||
let tphoto = photos.find(img.id).first::<Photo>(&mut context.db()?);
|
||||
if let Ok(tphoto) = tphoto {
|
||||
if context.is_authorized() || tphoto.is_public() {
|
||||
if img.size == SizeTag::Large {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::{wrap, BuilderExt, Context, ContextFilter, RenderRucte, Result};
|
||||
use crate::templates;
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
use lazy_regex::regex_is_match;
|
||||
use serde::Deserialize;
|
||||
@ -36,7 +37,8 @@ struct NextQ {
|
||||
|
||||
fn post_login(context: Context, form: LoginForm) -> Result<Response> {
|
||||
let next = sanitize_next(form.next.as_ref().map(AsRef::as_ref));
|
||||
if let Some(user) = form.validate(&*context.db()?) {
|
||||
let mut db = context.db()?;
|
||||
if let Some(user) = form.validate(&mut db) {
|
||||
let token = context.make_token(&user)?;
|
||||
return Ok(Builder::new()
|
||||
.header(
|
||||
@ -61,7 +63,7 @@ pub struct LoginForm {
|
||||
|
||||
impl LoginForm {
|
||||
/// Retur user if and only if password is correct for user.
|
||||
pub fn validate(&self, db: &PgConnection) -> Option<String> {
|
||||
pub fn validate(&self, db: &mut PgConnection) -> Option<String> {
|
||||
use crate::schema::users::dsl::*;
|
||||
if let Ok(hash) = users
|
||||
.filter(username.eq(&self.user))
|
||||
|
@ -141,21 +141,21 @@ async fn static_file(name: Tail) -> Result<Response> {
|
||||
|
||||
fn random_image(context: Context) -> Result<Response> {
|
||||
use crate::schema::photos::dsl::id;
|
||||
use diesel::expression::dsl::sql;
|
||||
use diesel::dsl::sql;
|
||||
use diesel::sql_types::Integer;
|
||||
let photo = Photo::query(context.is_authorized())
|
||||
.select(id)
|
||||
.limit(1)
|
||||
.order(sql::<Integer>("random()"))
|
||||
.first(&context.db()?)?;
|
||||
.first(&mut context.db()?)?;
|
||||
|
||||
info!("Random: {:?}", photo);
|
||||
Ok(redirect_to_img(photo))
|
||||
}
|
||||
|
||||
fn photo_details(id: i32, context: Context) -> Result<Response> {
|
||||
let c = context.db()?;
|
||||
let photo = or_404q!(PhotoDetails::load(id, &c), context);
|
||||
let mut c = context.db()?;
|
||||
let photo = or_404q!(PhotoDetails::load(id, &mut c), context);
|
||||
|
||||
if context.is_authorized() || photo.is_public() {
|
||||
Ok(Builder::new().html(|o| {
|
||||
|
@ -19,8 +19,8 @@ pub fn search(
|
||||
context: Context,
|
||||
query: Vec<(String, String)>,
|
||||
) -> Result<Response> {
|
||||
let db = context.db()?;
|
||||
let query = SearchQuery::load(query, &db)?;
|
||||
let mut db = context.db()?;
|
||||
let query = SearchQuery::load(query, &mut db)?;
|
||||
|
||||
let mut photos = Photo::query(context.is_authorized());
|
||||
if let Some(since) = query.since.as_ref() {
|
||||
@ -71,10 +71,10 @@ pub fn search(
|
||||
|
||||
let photos = photos
|
||||
.order((p::date.desc().nulls_last(), p::id.desc()))
|
||||
.load(&db)?;
|
||||
.load(&mut db)?;
|
||||
|
||||
let n = photos.len();
|
||||
let coords = get_positions(&photos, &db)?;
|
||||
let coords = get_positions(&photos, &mut db)?;
|
||||
let links = split_to_group_links(&photos, &query.to_base_url(), true);
|
||||
|
||||
Ok(Builder::new().html(|o| {
|
||||
@ -104,7 +104,7 @@ pub struct Filter<T> {
|
||||
}
|
||||
|
||||
impl<T: Facet> Filter<T> {
|
||||
fn load(val: &str, db: &PgConnection) -> Option<Filter<T>> {
|
||||
fn load(val: &str, db: &mut PgConnection) -> Option<Filter<T>> {
|
||||
let (inc, slug) = match val.strip_prefix('!') {
|
||||
Some(val) => (false, val),
|
||||
None => (true, val),
|
||||
@ -120,7 +120,10 @@ impl<T: Facet> Filter<T> {
|
||||
}
|
||||
|
||||
impl SearchQuery {
|
||||
fn load(query: Vec<(String, String)>, db: &PgConnection) -> Result<Self> {
|
||||
fn load(
|
||||
query: Vec<(String, String)>,
|
||||
db: &mut PgConnection,
|
||||
) -> Result<Self> {
|
||||
let mut result = SearchQuery::default();
|
||||
let (mut s_d, mut s_t, mut u_d, mut u_t) = (None, None, None, None);
|
||||
for (key, val) in &query {
|
||||
@ -218,7 +221,7 @@ impl QueryDateTime {
|
||||
let until_midnight = NaiveTime::from_hms_milli(23, 59, 59, 999);
|
||||
QueryDateTime::new(datetime_from_parts(date, time, until_midnight))
|
||||
}
|
||||
fn from_img(photo_id: i32, db: &PgConnection) -> Result<Self> {
|
||||
fn from_img(photo_id: i32, db: &mut PgConnection) -> Result<Self> {
|
||||
Ok(QueryDateTime::new(
|
||||
p::photos
|
||||
.select(p::date)
|
||||
|
@ -13,26 +13,27 @@ pub fn links_by_time(
|
||||
range: ImgRange,
|
||||
with_date: bool,
|
||||
) -> Result<(Vec<PhotoLink>, Vec<(Coord, i32)>)> {
|
||||
let c = context.db()?;
|
||||
let mut c = context.db()?;
|
||||
use crate::schema::photos::dsl::{date, id};
|
||||
let photos =
|
||||
if let Some(from_date) = range.from.map(|i| date_of_img(&c, i)) {
|
||||
if let Some(from_date) = range.from.map(|i| date_of_img(&mut c, i)) {
|
||||
photos.filter(date.ge(from_date))
|
||||
} else {
|
||||
photos
|
||||
};
|
||||
let photos = if let Some(to_date) = range.to.map(|i| date_of_img(&c, i)) {
|
||||
let photos =
|
||||
if let Some(to_date) = range.to.map(|i| date_of_img(&mut c, i)) {
|
||||
photos.filter(date.le(to_date))
|
||||
} else {
|
||||
photos
|
||||
};
|
||||
let photos = photos
|
||||
.order((date.desc().nulls_last(), id.desc()))
|
||||
.load(&c)?;
|
||||
.load(&mut c)?;
|
||||
let baseurl = UrlString::new(context.path_without_query());
|
||||
Ok((
|
||||
split_to_group_links(&photos, &baseurl, with_date),
|
||||
get_positions(&photos, &c)?,
|
||||
get_positions(&photos, &mut c)?,
|
||||
))
|
||||
}
|
||||
|
||||
@ -58,7 +59,7 @@ pub fn split_to_group_links(
|
||||
|
||||
pub fn get_positions(
|
||||
photos: &[Photo],
|
||||
c: &PgConnection,
|
||||
c: &mut PgConnection,
|
||||
) -> Result<Vec<(Coord, i32)>> {
|
||||
use crate::schema::positions::dsl::*;
|
||||
Ok(positions
|
||||
|
@ -57,7 +57,7 @@ fn person_all(context: Context) -> Result<Response> {
|
||||
pp::photo_id.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
)))
|
||||
};
|
||||
let images = query.order(person_name).load(&context.db()?)?;
|
||||
let images = query.order(person_name).load(&mut context.db()?)?;
|
||||
Ok(Builder::new().html(|o| templates::people(o, &context, &images))?)
|
||||
}
|
||||
|
||||
@ -67,9 +67,11 @@ fn person_one(
|
||||
context: Context,
|
||||
) -> Result<Response> {
|
||||
use crate::schema::people::dsl::{people, slug};
|
||||
let c = context.db()?;
|
||||
let person =
|
||||
or_404q!(people.filter(slug.eq(tslug)).first::<Person>(&c), context);
|
||||
let mut c = context.db()?;
|
||||
let person = or_404q!(
|
||||
people.filter(slug.eq(tslug)).first::<Person>(&mut c),
|
||||
context
|
||||
);
|
||||
use crate::schema::photo_people::dsl::{
|
||||
person_id, photo_id, photo_people,
|
||||
};
|
||||
@ -98,7 +100,7 @@ fn tag_all(context: Context) -> Result<Response> {
|
||||
tp::photo_id.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
)))
|
||||
};
|
||||
let taggs = query.load(&context.db()?)?;
|
||||
let taggs = query.load(&mut context.db()?)?;
|
||||
Ok(Builder::new().html(|o| templates::tags(o, &context, &taggs))?)
|
||||
}
|
||||
|
||||
@ -109,7 +111,7 @@ fn tag_one(
|
||||
) -> Result<Response> {
|
||||
use crate::schema::tags::dsl::{slug, tags};
|
||||
let tag = or_404q!(
|
||||
tags.filter(slug.eq(tslug)).first::<Tag>(&context.db()?),
|
||||
tags.filter(slug.eq(tslug)).first::<Tag>(&mut context.db()?),
|
||||
context
|
||||
);
|
||||
|
||||
@ -135,7 +137,7 @@ fn place_all(context: Context) -> Result<Response> {
|
||||
pp::photo_id.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
)))
|
||||
};
|
||||
let found = query.order(place_name).load(&context.db()?)?;
|
||||
let found = query.order(place_name).load(&mut context.db()?)?;
|
||||
Ok(Builder::new().html(|o| templates::places(o, &context, &found))?)
|
||||
}
|
||||
|
||||
@ -146,7 +148,9 @@ fn place_one(
|
||||
) -> Result<Response> {
|
||||
use crate::schema::places::dsl::{places, slug};
|
||||
let place = or_404q!(
|
||||
places.filter(slug.eq(tslug)).first::<Place>(&context.db()?),
|
||||
places
|
||||
.filter(slug.eq(tslug))
|
||||
.first::<Place>(&mut context.db()?),
|
||||
context
|
||||
);
|
||||
|
||||
|
@ -7,9 +7,9 @@ use crate::models::{Photo, SizeTag};
|
||||
use crate::templates::{self, RenderRucte};
|
||||
use chrono::naive::{NaiveDate, NaiveDateTime};
|
||||
use chrono::{Datelike, Duration, Local};
|
||||
use diesel::dsl::sql;
|
||||
use diesel::dsl::count_star;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sql_types::{BigInt, Integer, Nullable};
|
||||
use diesel::sql_types::{Bool, Nullable, Timestamp};
|
||||
use serde::Deserialize;
|
||||
use warp::filters::BoxedFilter;
|
||||
use warp::http::response::Builder;
|
||||
@ -74,18 +74,47 @@ pub fn routes(s: ContextFilter) -> BoxedFilter<(Response,)> {
|
||||
.boxed()
|
||||
}
|
||||
|
||||
sql_function! {
|
||||
#[aggregate]
|
||||
fn year_of_timestamp(date: Nullable<Timestamp>) -> Nullable<SmallInt>
|
||||
}
|
||||
sql_function! {
|
||||
#[aggregate]
|
||||
fn month_of_timestamp(date: Nullable<Timestamp>) -> Nullable<SmallInt>
|
||||
}
|
||||
sql_function! {
|
||||
#[aggregate]
|
||||
fn day_of_timestamp(date: Nullable<Timestamp>) -> Nullable<SmallInt>
|
||||
}
|
||||
|
||||
mod filter {
|
||||
use diesel::sql_types::{Nullable, Timestamp};
|
||||
|
||||
sql_function! {
|
||||
fn year_of_timestamp(date: Nullable<Timestamp>) -> Nullable<SmallInt>
|
||||
}
|
||||
sql_function! {
|
||||
fn month_of_timestamp(date: Nullable<Timestamp>) -> Nullable<SmallInt>
|
||||
}
|
||||
sql_function! {
|
||||
fn day_of_timestamp(date: Nullable<Timestamp>) -> Nullable<SmallInt>
|
||||
}
|
||||
}
|
||||
fn all_years(context: Context) -> Result<Response> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
let db = context.db()?;
|
||||
let groups = Photo::query(context.is_authorized())
|
||||
.select(sql::<(Nullable<Integer>, BigInt)>(
|
||||
"cast(extract(year from date) as int) y, count(*)",
|
||||
))
|
||||
.group_by(sql::<Nullable<Integer>>("y"))
|
||||
.order(sql::<Nullable<Integer>>("y").desc().nulls_last())
|
||||
.load::<(Option<i32>, i64)>(&db)?
|
||||
let mut db = context.db()?;
|
||||
let y = year_of_timestamp(p::date);
|
||||
let groups = p::photos
|
||||
.filter(p::path.not_like("%.CR2"))
|
||||
.filter(p::path.not_like("%.dng"))
|
||||
.filter(p::is_public.or::<_, Bool>(context.is_authorized()))
|
||||
.select((y, count_star()))
|
||||
.group_by(y)
|
||||
.order(y.desc().nulls_last())
|
||||
.load::<(Option<i16>, i64)>(&mut db)?
|
||||
.iter()
|
||||
.map(|&(year, count)| {
|
||||
let year: Option<i32> = year.map(|y| y as i32);
|
||||
let q = Photo::query(context.is_authorized())
|
||||
.order((p::grade.desc().nulls_last(), p::date.asc()))
|
||||
.limit(1);
|
||||
@ -95,7 +124,7 @@ fn all_years(context: Context) -> Result<Response> {
|
||||
} else {
|
||||
q.filter(p::date.is_null())
|
||||
};
|
||||
let photo = photo.first::<Photo>(&db)?;
|
||||
let photo = photo.first::<Photo>(&mut db)?;
|
||||
Ok(PhotoLink {
|
||||
title: Some(
|
||||
year.map(|y| format!("{}", y))
|
||||
@ -122,25 +151,27 @@ fn months_in_year(year: i32, context: Context) -> Result<Response> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
|
||||
let title: String = format!("Photos from {}", year);
|
||||
let db = context.db()?;
|
||||
let groups = Photo::query(context.is_authorized())
|
||||
let mut db = context.db()?;
|
||||
let m = month_of_timestamp(p::date);
|
||||
let groups = p::photos
|
||||
.filter(p::path.not_like("%.CR2"))
|
||||
.filter(p::path.not_like("%.dng"))
|
||||
.filter(p::is_public.or::<_, Bool>(context.is_authorized()))
|
||||
.filter(p::date.ge(start_of_year(year)))
|
||||
.filter(p::date.lt(start_of_year(year + 1)))
|
||||
.select(sql::<(Integer, BigInt)>(
|
||||
"cast(extract(month from date) as int) m, count(*)",
|
||||
))
|
||||
.group_by(sql::<Integer>("m"))
|
||||
.order(sql::<Integer>("m").desc().nulls_last())
|
||||
.load::<(i32, i64)>(&db)?
|
||||
.select((m, count_star()))
|
||||
.group_by(m)
|
||||
.order(m.desc().nulls_last())
|
||||
.load::<(Option<i16>, i64)>(&mut db)?
|
||||
.iter()
|
||||
.map(|&(month, count)| {
|
||||
let month = month as u32;
|
||||
let month = month.unwrap() as u32; // cant be null when in range!
|
||||
let photo = Photo::query(context.is_authorized())
|
||||
.filter(p::date.ge(start_of_month(year, month)))
|
||||
.filter(p::date.lt(start_of_month(year, month + 1)))
|
||||
.order((p::grade.desc().nulls_last(), p::date.asc()))
|
||||
.limit(1)
|
||||
.first::<Photo>(&db)?;
|
||||
.first::<Photo>(&mut db)?;
|
||||
|
||||
Ok(PhotoLink {
|
||||
title: Some(monthname(month).to_string()),
|
||||
@ -163,7 +194,7 @@ fn months_in_year(year: i32, context: Context) -> Result<Response> {
|
||||
.filter(p::date.ge(start_of_year(year)))
|
||||
.filter(p::date.lt(start_of_year(year + 1)))
|
||||
.select((photo_id, latitude, longitude))
|
||||
.load(&db)?
|
||||
.load(&mut db)?
|
||||
.into_iter()
|
||||
.map(|(p_id, lat, long): (i32, i32, i32)| {
|
||||
((lat, long).into(), p_id)
|
||||
@ -186,22 +217,24 @@ fn start_of_month(year: i32, month: u32) -> NaiveDateTime {
|
||||
|
||||
fn days_in_month(year: i32, month: u32, context: Context) -> Result<Response> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
let d = day_of_timestamp(p::date);
|
||||
|
||||
let lpath: Vec<Link> = vec![Link::year(year)];
|
||||
let title: String = format!("Photos from {} {}", monthname(month), year);
|
||||
let db = context.db()?;
|
||||
let groups = Photo::query(context.is_authorized())
|
||||
let mut db = context.db()?;
|
||||
let groups = p::photos
|
||||
.filter(p::path.not_like("%.CR2"))
|
||||
.filter(p::path.not_like("%.dng"))
|
||||
.filter(p::is_public.or::<_, Bool>(context.is_authorized()))
|
||||
.filter(p::date.ge(start_of_month(year, month)))
|
||||
.filter(p::date.lt(start_of_month(year, month + 1)))
|
||||
.select(sql::<(Integer, BigInt)>(
|
||||
"cast(extract(day from date) as int) d, count(*)",
|
||||
))
|
||||
.group_by(sql::<Integer>("d"))
|
||||
.order(sql::<Integer>("d").desc().nulls_last())
|
||||
.load::<(i32, i64)>(&db)?
|
||||
.select((d, count_star()))
|
||||
.group_by(d)
|
||||
.order(d.desc().nulls_last())
|
||||
.load::<(Option<i16>, i64)>(&mut db)?
|
||||
.iter()
|
||||
.map(|&(day, count)| {
|
||||
let day = day as u32;
|
||||
let day = day.unwrap() as u32;
|
||||
let fromdate =
|
||||
NaiveDate::from_ymd(year, month, day).and_hms(0, 0, 0);
|
||||
let photo = Photo::query(context.is_authorized())
|
||||
@ -209,7 +242,7 @@ fn days_in_month(year: i32, month: u32, context: Context) -> Result<Response> {
|
||||
.filter(p::date.lt(fromdate + Duration::days(1)))
|
||||
.order((p::grade.desc().nulls_last(), p::date.asc()))
|
||||
.limit(1)
|
||||
.first::<Photo>(&db)?;
|
||||
.first::<Photo>(&mut db)?;
|
||||
|
||||
Ok(PhotoLink {
|
||||
title: Some(format!("{}", day)),
|
||||
@ -230,7 +263,7 @@ fn days_in_month(year: i32, month: u32, context: Context) -> Result<Response> {
|
||||
.filter(p::date.ge(start_of_month(year, month)))
|
||||
.filter(p::date.lt(start_of_month(year, month + 1)))
|
||||
.select((ps::photo_id, ps::latitude, ps::longitude))
|
||||
.load(&db)?
|
||||
.load(&mut db)?
|
||||
.into_iter()
|
||||
.map(|(p_id, lat, long): (i32, i32, i32)| {
|
||||
((lat, long).into(), p_id)
|
||||
@ -248,7 +281,7 @@ fn all_null_date(context: Context) -> Result<Response> {
|
||||
.filter(p::date.is_null())
|
||||
.order(p::path.asc())
|
||||
.limit(500)
|
||||
.load(&context.db()?)?
|
||||
.load(&mut context.db()?)?
|
||||
.iter()
|
||||
.map(PhotoLink::no_title)
|
||||
.collect::<Vec<_>>();
|
||||
@ -303,40 +336,39 @@ fn on_this_day(context: Context) -> Result<Response> {
|
||||
let today = Local::now();
|
||||
(today.month(), today.day())
|
||||
};
|
||||
let db = context.db()?;
|
||||
let mut db = context.db()?;
|
||||
let pos = Photo::query(context.is_authorized())
|
||||
.inner_join(ps::positions)
|
||||
.filter(
|
||||
sql("extract(month from date)=").bind::<Integer, _>(month as i32),
|
||||
)
|
||||
.filter(sql("extract(day from date)=").bind::<Integer, _>(day as i32))
|
||||
.filter(filter::month_of_timestamp(p::date).eq(month as i16))
|
||||
.filter(filter::day_of_timestamp(p::date).eq(day as i16))
|
||||
.select((ps::photo_id, ps::latitude, ps::longitude))
|
||||
.load(&db)?
|
||||
.load(&mut db)?
|
||||
.into_iter()
|
||||
.map(|(p_id, lat, long): (i32, i32, i32)| ((lat, long).into(), p_id))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let photos = Photo::query(context.is_authorized())
|
||||
.select(sql::<(Integer, BigInt)>(
|
||||
"cast(extract(year from date) as int) y, count(*)",
|
||||
))
|
||||
.group_by(sql::<Integer>("y"))
|
||||
.filter(
|
||||
sql("extract(month from date)=").bind::<Integer, _>(month as i32),
|
||||
)
|
||||
.filter(sql("extract(day from date)=").bind::<Integer, _>(day as i32))
|
||||
.order(sql::<Integer>("y").desc())
|
||||
.load::<(i32, i64)>(&db)?
|
||||
let y = year_of_timestamp(p::date);
|
||||
let photos = p::photos
|
||||
.filter(p::path.not_like("%.CR2"))
|
||||
.filter(p::path.not_like("%.dng"))
|
||||
.filter(p::is_public.or::<_, Bool>(context.is_authorized()))
|
||||
.filter(filter::month_of_timestamp(p::date).eq(month as i16))
|
||||
.filter(filter::day_of_timestamp(p::date).eq(day as i16))
|
||||
.select((y, count_star()))
|
||||
.group_by(y)
|
||||
.order(y.desc())
|
||||
.load::<(Option<i16>, i64)>(&mut db)?
|
||||
.iter()
|
||||
.map(|&(year, count)| {
|
||||
let year = year.unwrap(); // matching date can't be null
|
||||
let fromdate =
|
||||
NaiveDate::from_ymd(year, month as u32, day).and_hms(0, 0, 0);
|
||||
NaiveDate::from_ymd(year.into(), month, day).and_hms(0, 0, 0);
|
||||
let photo = Photo::query(context.is_authorized())
|
||||
.filter(p::date.ge(fromdate))
|
||||
.filter(p::date.lt(fromdate + Duration::days(1)))
|
||||
.order((p::grade.desc().nulls_last(), p::date.asc()))
|
||||
.limit(1)
|
||||
.first::<Photo>(&db)?;
|
||||
.first::<Photo>(&mut db)?;
|
||||
Ok(PhotoLink {
|
||||
title: Some(format!("{}", year)),
|
||||
href: format!("/{}/{}/{}", year, month, day),
|
||||
@ -360,8 +392,8 @@ fn on_this_day(context: Context) -> Result<Response> {
|
||||
|
||||
fn next_image(context: Context, param: FromParam) -> Result<Response> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
let db = context.db()?;
|
||||
let from_date = or_404!(date_of_img(&db, param.from), context);
|
||||
let mut db = context.db()?;
|
||||
let from_date = or_404!(date_of_img(&mut db, param.from), context);
|
||||
let photo = or_404q!(
|
||||
Photo::query(context.is_authorized())
|
||||
.select(p::id)
|
||||
@ -371,7 +403,7 @@ fn next_image(context: Context, param: FromParam) -> Result<Response> {
|
||||
.or(p::date.eq(from_date).and(p::id.gt(param.from))),
|
||||
)
|
||||
.order((p::date, p::id))
|
||||
.first::<i32>(&db),
|
||||
.first::<i32>(&mut db),
|
||||
context
|
||||
);
|
||||
Ok(redirect_to_img(photo))
|
||||
@ -379,8 +411,8 @@ fn next_image(context: Context, param: FromParam) -> Result<Response> {
|
||||
|
||||
fn prev_image(context: Context, param: FromParam) -> Result<Response> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
let db = context.db()?;
|
||||
let from_date = or_404!(date_of_img(&db, param.from), context);
|
||||
let mut db = context.db()?;
|
||||
let from_date = or_404!(date_of_img(&mut db, param.from), context);
|
||||
let photo = or_404q!(
|
||||
Photo::query(context.is_authorized())
|
||||
.select(p::id)
|
||||
@ -390,7 +422,7 @@ fn prev_image(context: Context, param: FromParam) -> Result<Response> {
|
||||
.or(p::date.eq(from_date).and(p::id.lt(param.from))),
|
||||
)
|
||||
.order((p::date.desc().nulls_last(), p::id.desc()))
|
||||
.first::<i32>(&db),
|
||||
.first::<i32>(&mut db),
|
||||
context
|
||||
);
|
||||
Ok(redirect_to_img(photo))
|
||||
@ -401,7 +433,10 @@ struct FromParam {
|
||||
from: i32,
|
||||
}
|
||||
|
||||
pub fn date_of_img(db: &PgConnection, photo_id: i32) -> Option<NaiveDateTime> {
|
||||
pub fn date_of_img(
|
||||
db: &mut PgConnection,
|
||||
photo_id: i32,
|
||||
) -> Option<NaiveDateTime> {
|
||||
use crate::schema::photos::dsl as p;
|
||||
p::photos
|
||||
.find(photo_id)
|
||||
|
Loading…
Reference in New Issue
Block a user