Create Insight Generation Feature
Added integration with Messages API and Ollama
This commit is contained in:
133
src/database/insights_dao.rs
Normal file
133
src/database/insights_dao.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use diesel::prelude::*;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
use std::ops::DerefMut;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::database::models::{InsertPhotoInsight, PhotoInsight};
|
||||
use crate::database::schema;
|
||||
use crate::database::{DbError, DbErrorKind, connect};
|
||||
use crate::otel::trace_db_call;
|
||||
|
||||
pub trait InsightDao: Sync + Send {
|
||||
fn store_insight(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
insight: InsertPhotoInsight,
|
||||
) -> Result<PhotoInsight, DbError>;
|
||||
|
||||
fn get_insight(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
file_path: &str,
|
||||
) -> Result<Option<PhotoInsight>, DbError>;
|
||||
|
||||
fn delete_insight(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
file_path: &str,
|
||||
) -> Result<(), DbError>;
|
||||
|
||||
fn get_all_insights(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
) -> Result<Vec<PhotoInsight>, DbError>;
|
||||
}
|
||||
|
||||
pub struct SqliteInsightDao {
|
||||
connection: Arc<Mutex<SqliteConnection>>,
|
||||
}
|
||||
|
||||
impl Default for SqliteInsightDao {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SqliteInsightDao {
|
||||
pub fn new() -> Self {
|
||||
SqliteInsightDao {
|
||||
connection: Arc::new(Mutex::new(connect())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InsightDao for SqliteInsightDao {
|
||||
fn store_insight(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
insight: InsertPhotoInsight,
|
||||
) -> Result<PhotoInsight, DbError> {
|
||||
trace_db_call(context, "insert", "store_insight", |_span| {
|
||||
use schema::photo_insights::dsl::*;
|
||||
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
// Insert or replace on conflict (UNIQUE constraint on file_path)
|
||||
diesel::replace_into(photo_insights)
|
||||
.values(&insight)
|
||||
.execute(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Insert error"))?;
|
||||
|
||||
// Retrieve the inserted record
|
||||
photo_insights
|
||||
.filter(file_path.eq(&insight.file_path))
|
||||
.first::<PhotoInsight>(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Query error"))
|
||||
})
|
||||
.map_err(|_| DbError::new(DbErrorKind::InsertError))
|
||||
}
|
||||
|
||||
fn get_insight(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
path: &str,
|
||||
) -> Result<Option<PhotoInsight>, DbError> {
|
||||
trace_db_call(context, "query", "get_insight", |_span| {
|
||||
use schema::photo_insights::dsl::*;
|
||||
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
photo_insights
|
||||
.filter(file_path.eq(path))
|
||||
.first::<PhotoInsight>(connection.deref_mut())
|
||||
.optional()
|
||||
.map_err(|_| anyhow::anyhow!("Query error"))
|
||||
})
|
||||
.map_err(|_| DbError::new(DbErrorKind::QueryError))
|
||||
}
|
||||
|
||||
fn delete_insight(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
path: &str,
|
||||
) -> Result<(), DbError> {
|
||||
trace_db_call(context, "delete", "delete_insight", |_span| {
|
||||
use schema::photo_insights::dsl::*;
|
||||
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
diesel::delete(photo_insights.filter(file_path.eq(path)))
|
||||
.execute(connection.deref_mut())
|
||||
.map(|_| ())
|
||||
.map_err(|_| anyhow::anyhow!("Delete error"))
|
||||
})
|
||||
.map_err(|_| DbError::new(DbErrorKind::QueryError))
|
||||
}
|
||||
|
||||
fn get_all_insights(
|
||||
&mut self,
|
||||
context: &opentelemetry::Context,
|
||||
) -> Result<Vec<PhotoInsight>, DbError> {
|
||||
trace_db_call(context, "query", "get_all_insights", |_span| {
|
||||
use schema::photo_insights::dsl::*;
|
||||
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
photo_insights
|
||||
.order(generated_at.desc())
|
||||
.load::<PhotoInsight>(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Query error"))
|
||||
})
|
||||
.map_err(|_| DbError::new(DbErrorKind::QueryError))
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,12 @@ use crate::database::models::{
|
||||
};
|
||||
use crate::otel::trace_db_call;
|
||||
|
||||
pub mod insights_dao;
|
||||
pub mod models;
|
||||
pub mod schema;
|
||||
|
||||
pub use insights_dao::{InsightDao, SqliteInsightDao};
|
||||
|
||||
pub trait UserDao {
|
||||
fn create_user(&mut self, user: &str, password: &str) -> Option<User>;
|
||||
fn get_user(&mut self, user: &str, password: &str) -> Option<User>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::database::schema::{favorites, image_exif, users};
|
||||
use crate::database::schema::{favorites, image_exif, photo_insights, users};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Insertable)]
|
||||
@@ -73,3 +73,23 @@ pub struct ImageExif {
|
||||
pub created_time: i64,
|
||||
pub last_modified: i64,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = photo_insights)]
|
||||
pub struct InsertPhotoInsight {
|
||||
pub file_path: String,
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
pub generated_at: i64,
|
||||
pub model_version: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Queryable, Clone, Debug)]
|
||||
pub struct PhotoInsight {
|
||||
pub id: i32,
|
||||
pub file_path: String,
|
||||
pub title: String,
|
||||
pub summary: String,
|
||||
pub generated_at: i64,
|
||||
pub model_version: String,
|
||||
}
|
||||
|
||||
@@ -46,6 +46,17 @@ table! {
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
photo_insights (id) {
|
||||
id -> Integer,
|
||||
file_path -> Text,
|
||||
title -> Text,
|
||||
summary -> Text,
|
||||
generated_at -> BigInt,
|
||||
model_version -> Text,
|
||||
}
|
||||
}
|
||||
|
||||
table! {
|
||||
users (id) {
|
||||
id -> Integer,
|
||||
@@ -56,4 +67,11 @@ table! {
|
||||
|
||||
joinable!(tagged_photo -> tags (tag_id));
|
||||
|
||||
allow_tables_to_appear_in_same_query!(favorites, image_exif, tagged_photo, tags, users,);
|
||||
allow_tables_to_appear_in_same_query!(
|
||||
favorites,
|
||||
image_exif,
|
||||
photo_insights,
|
||||
tagged_photo,
|
||||
tags,
|
||||
users,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user