summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Perry <avaglir@gmail.com>2018-04-05 22:52:02 -0400
committerNathan Perry <avaglir@gmail.com>2018-04-05 22:52:02 -0400
commitee1d099c6acdb38a173a7455804724f3a1b78836 (patch)
tree0f4a8a949de370874d3e61788f5882dd7dddcf43
parent1fda857d25c3d33e593951eef3ce713fa69a7025 (diff)
consolidate memes to one table
-rw-r--r--migrations/2018-04-06-005800_consolidate_memes/down.sql41
-rw-r--r--migrations/2018-04-06-005800_consolidate_memes/up.sql19
-rw-r--r--src/commands/meme.rs118
-rw-r--r--src/db/mod.rs52
-rw-r--r--src/db/models.rs94
-rw-r--r--src/db/schema.rs46
6 files changed, 202 insertions, 168 deletions
diff --git a/migrations/2018-04-06-005800_consolidate_memes/down.sql b/migrations/2018-04-06-005800_consolidate_memes/down.sql
new file mode 100644
index 0000000..339bf8f
--- /dev/null
+++ b/migrations/2018-04-06-005800_consolidate_memes/down.sql
@@ -0,0 +1,41 @@
+DROP INDEX memes_audio;
+DROP INDEX memes_content;
+DROP INDEX memes_image;
+
+ALTER TABLE memes RENAME TO text_memes;
+
+CREATE TABLE image_memes (
+ id SERIAL PRIMARY KEY,
+ title varchar UNIQUE NOT NULL,
+ image_id INTEGER REFERENCES images NOT NULL,
+
+ metadata_id INTEGER REFERENCES metadata UNIQUE NOT NULL,
+ UNIQUE(title, image_id)
+);
+
+
+CREATE TABLE audio_memes (
+ id SERIAL PRIMARY KEY,
+ title varchar UNIQUE NOT NULL,
+ audio_id INTEGER REFERENCES audio NOT NULL,
+
+ metadata_id INTEGER REFERENCES metadata UNIQUE NOT NULL,
+ UNIQUE(title, audio_id)
+);
+
+
+INSERT INTO audio_memes(title, audio_id, metadata_id) SELECT title, audio_id, metadata_id FROM text_memes
+ WHERE audio_id IS NOT NULL AND content IS NULL;
+
+DELETE FROM text_memes WHERE audio_id IS NOT NULL AND content IS NULL;
+
+
+INSERT INTO image_memes(title, image_id, metadata_id) SELECT title, image_id, metadata_id FROM text_memes
+ WHERE image_id IS NOT NULL AND content IS NULL;
+
+DELETE FROM text_memes WHERE image_id IS NOT NULL AND content IS NULL;
+
+
+ALTER TABLE text_memes ALTER COLUMN content SET NOT NULL;
+ALTER TABLE text_memes DROP CONSTRAINT text_memes_image_or_audio_null;
+ALTER TABLE text_memes DROP CONSTRAINT text_memes_content_not_all_null;
diff --git a/migrations/2018-04-06-005800_consolidate_memes/up.sql b/migrations/2018-04-06-005800_consolidate_memes/up.sql
new file mode 100644
index 0000000..084525b
--- /dev/null
+++ b/migrations/2018-04-06-005800_consolidate_memes/up.sql
@@ -0,0 +1,19 @@
+ALTER TABLE text_memes ADD CONSTRAINT text_memes_content_not_all_null
+ CHECK (content IS NOT NULL OR image_id IS NOT NULL OR audio_id IS NOT NULL);
+
+ALTER TABLE text_memes ADD CONSTRAINT text_memes_image_or_audio_null
+ CHECK (image_id IS NULL OR audio_id IS NULL);
+
+ALTER TABLE text_memes ALTER COLUMN content DROP NOT NULL;
+
+INSERT INTO text_memes(audio_id, metadata_id, title) SELECT audio_id, metadata_id, title FROM audio_memes;
+INSERT INTO text_memes(image_id, metadata_id, title) SELECT image_id, metadata_id, title FROM image_memes;
+
+DROP TABLE audio_memes;
+DROP TABLE image_memes;
+
+ALTER TABLE text_memes RENAME TO memes;
+
+CREATE INDEX memes_audio ON memes (audio_id);
+CREATE INDEX memes_image ON memes (image_id);
+CREATE INDEX memes_content ON memes (content);
diff --git a/src/commands/meme.rs b/src/commands/meme.rs
index bb7315c..43362e1 100644
--- a/src/commands/meme.rs
+++ b/src/commands/meme.rs
@@ -30,89 +30,85 @@ command!(meme(ctx, msg, args) {
let ch = msg.channel_id;
if args.len() == 0 {
- let conn = connection()?;
+ rand_meme(ctx, msg)?;
+ return Ok(());
+ }
+});
- let should_audio = ctx.currently_playing() && ctx.users_listening()?;
- let dist: WeightedChoice<'static, MemeType> = if should_audio {
- WeightedChoice::new(unsafe { &mut MEME_WEIGHTS })
- } else {
- WeightedChoice::new(unsafe { &mut MEME_WEIGHTS[..2] })
- };
+fn rand_meme(ctx: &Context, message: &Message) -> Result<()> {
+ let conn = connection()?;
- match dist.sample(&mut thread_rng()) {
- MemeType::Text => {
- let mut text_meme = rand_text(&conn)?;
+ let should_audio = ctx.currently_playing() && ctx.users_listening()?;
+ let dist: WeightedChoice<'static, MemeType> = if should_audio {
+ WeightedChoice::new(unsafe { &mut MEME_WEIGHTS })
+ } else {
+ WeightedChoice::new(unsafe { &mut MEME_WEIGHTS[..2] })
+ };
- let mut ctr = 0;
- while !should_audio && text_meme.audio_id.is_some() {
- text_meme = rand_text(&conn)?;
+ match dist.sample(&mut thread_rng()) {
+ MemeType::Text => {
+ let mut text_meme = rand_text(&conn)?;
- ctr += 1;
- if ctr > 10 {
- warn!("looped 10 times trying to find a non-audio text meme");
- return Ok(());
- }
- }
+ let mut ctr = 0;
+ while !should_audio && text_meme.audio_id.is_some() {
+ text_meme = rand_text(&conn)?;
- send_text(ctx, &text_meme, &conn, msg)?;
- },
- MemeType::Image => {
- let image_meme = rand_image(&conn)?;
- let image = image_meme.associated_data(&conn)?;
-
- send_image(&image_meme, &image, ch)?;
- },
- MemeType::Audio => {
- let audio = rand_audio(&conn)?.associated_data(&conn)?;
- send_audio(ctx, msg, &audio)?;
+ ctr += 1;
+ if ctr > 10 {
+ warn!("looped 10 times trying to find a non-audio text meme");
+ return Ok(());
+ }
}
- }
+
+ send_meme(ctx, &text_meme, &conn, message)?;
+ },
+ MemeType::Image => send_meme(ctx, &rand_image(&conn)?, &conn, message)?,
+ MemeType::Audio => send_meme(ctx, &rand_audio(&conn)?, &conn, message)?,
}
-});
-fn send_text(ctx: &Context, t: &TextMeme, conn: &PgConnection, msg: &Message) -> Result<()> {
- let (image, audio) = t.associated_data(conn)?;
+ Ok(())
+}
+
+
+fn send_meme(ctx: &Context, t: &Meme, conn: &PgConnection, msg: &Message) -> Result<()> {
+ let image = t.image(conn);
+ let audio = t.audio(conn);
let dist = WeightedChoice::new(unsafe { &mut TTS_WEIGHTS });
- let create_msg = |m: CreateMessage| m
- .tts(dist.sample(&mut thread_rng()))
- .content(&t.content);
+ let create_msg = |m: CreateMessage| {
+ let ret = m
+ .tts(dist.sample(&mut thread_rng()));
+
+ match t.content {
+ Some(ref text) => ret.content(text),
+ None => ret
+ }
+ };
match image {
- Some(image) => msg.channel_id.send_files(vec!(AttachmentType::Bytes((&image.data, &t.title))), create_msg)?,
+ Some(image) => msg.channel_id.send_files(vec!(AttachmentType::Bytes((&image?.data, &t.title))), create_msg)?,
None => msg.channel_id.send_message(create_msg)?,
};
+ // note: slight edge-case race condition here: there could have been something queued since we
+ // checked whether anything was playing. not a significant negative impact and unlikely, so i'm
+ // not worrying about it
if let Some(audio) = audio {
- send_audio(ctx, msg, &audio)?;
- }
-
- Ok(())
-}
+ let audio = audio?;
+ let queue_lock = ctx.data.lock().get::<PlayQueue>().cloned().unwrap();
+ let mut play_queue = queue_lock.write().unwrap();
-fn send_image(image_meme: &ImageMeme, image: &Image, ch: ChannelId) -> Result<()> {
- ch.send_files(vec!(AttachmentType::Bytes((&image.data, &image_meme.title))), |m| m.content(""))?;
- Ok(())
-}
-
-// note: slight edge-case race condition here: there could have been something queued since we
-// checked whether anything was playing. not a significant negative impact and unlikely, so i'm
-// not worrying about it
-fn send_audio(ctx: &Context, msg: &Message, audio: &Audio) -> Result<()> {
- let queue_lock = ctx.data.lock().get::<PlayQueue>().cloned().unwrap();
- let mut play_queue = queue_lock.write().unwrap();
-
- play_queue.queue.push_front(PlayArgs{
- initiator: msg.author.name.clone(),
- data: ::either::Right(audio.data.clone()),
- sender_channel: msg.channel_id,
- });
+ play_queue.queue.push_front(PlayArgs{
+ initiator: msg.author.name.clone(),
+ data: ::either::Right(audio.data.clone()),
+ sender_channel: msg.channel_id,
+ });
+ }
Ok(())
}
-
pub fn db_fallback(ctx: &mut Context, msg: &Message, s: &str) -> Result<()> {
diff --git a/src/db/mod.rs b/src/db/mod.rs
index e1c3a55..258d5a5 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -19,60 +19,58 @@ pub fn connection() -> Result<PgConnection> {
CONN_MGR.connect().map_err(Error::from)
}
-pub trait AssociatedData {
- type Associated;
-
- fn associated_data(&self, conn: &PgConnection) -> Result<Self::Associated>;
-}
-
-pub fn find_text(conn: &PgConnection, search: String) -> Result<TextMeme> {
+pub fn find_text(conn: &PgConnection, search: String) -> Result<Meme> {
+ use diesel::dsl::sql;
let format_search = format!("%{}%", search);
- text_memes::table
- .filter(text_memes::title.ilike(&format_search).or(text_memes::content.ilike(&format_search)))
+ memes::table
+ .filter(memes::title.ilike(&format_search).or(sql(&format!("content ILIKE %{}%", search))))
.limit(1)
- .first::<TextMeme>(conn)
+ .first::<Meme>(conn)
.map_err(Error::from)
}
-pub fn find_audio(conn: &PgConnection, search: String) -> Result<AudioMeme> {
+pub fn find_audio(conn: &PgConnection, search: String) -> Result<Meme> {
let format_search = format!("%{}%", search);
- audio_memes::table
- .filter(audio_memes::title.ilike(format_search))
+ memes::table
+ .filter(memes::title.ilike(format_search).and(memes::audio_id.is_not_null()))
.limit(1)
- .first::<AudioMeme>(conn)
+ .first::<Meme>(conn)
.map_err(Error::from)
}
-pub fn find_image(conn: &PgConnection, search: String) -> Result<ImageMeme> {
+pub fn find_image(conn: &PgConnection, search: String) -> Result<Meme> {
let format_search = format!("%{}%", search);
- image_memes::table
- .filter(image_memes::title.ilike(format_search))
+ memes::table
+ .filter(memes::title.ilike(format_search).and(memes::image_id.is_not_null()))
.limit(1)
- .first::<ImageMeme>(conn)
+ .first::<Meme>(conn)
.map_err(Error::from)
}
-pub fn rand_text(conn: &PgConnection) -> Result<TextMeme> {
- text_memes::table
+pub fn rand_text(conn: &PgConnection) -> Result<Meme> {
+ memes::table
+ .filter(memes::content.is_not_null())
.order(random.desc())
- .first::<TextMeme>(conn)
+ .first::<Meme>(conn)
.map_err(Error::from)
}
-pub fn rand_image(conn: &PgConnection) -> Result<ImageMeme> {
- image_memes::table
+pub fn rand_image(conn: &PgConnection) -> Result<Meme> {
+ memes::table
+ .filter(memes::image_id.is_not_null())
.order(random.desc())
- .first::<ImageMeme>(conn)
+ .first::<Meme>(conn)
.map_err(Error::from)
}
-pub fn rand_audio(conn: &PgConnection) -> Result<AudioMeme> {
- audio_memes::table
+pub fn rand_audio(conn: &PgConnection) -> Result<Meme> {
+ memes::table
+ .filter(memes::audio_id.is_not_null())
.order(random.desc())
- .first::<AudioMeme>(conn)
+ .first::<Meme>(conn)
.map_err(Error::from)
}
diff --git a/src/db/models.rs b/src/db/models.rs
index c07a12a..7479fff 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -2,85 +2,73 @@ use chrono::naive::NaiveDateTime;
use diesel::prelude::*;
use super::schema::*;
-use super::AssociatedData;
use ::{Result, Error};
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
-#[table_name="text_memes"]
-pub struct TextMeme {
+#[derive(Queryable, Identifiable, PartialEq, Debug)]
+#[table_name="memes"]
+pub struct Meme {
pub id: i32,
pub title: String,
- pub content: String,
+ pub content: Option<String>,
pub image_id: Option<i32>,
pub audio_id: Option<i32>,
pub metadata_id: i32,
}
-impl AssociatedData for TextMeme {
- type Associated = (Option<Image>, Option<Audio>);
-
- fn associated_data(&self, conn: &PgConnection) -> Result<Self::Associated> {
- let image = self.image_id.map(|x: i32| images::table.find(x).first(conn)).transpose()?;
- let audio = self.audio_id.map(|x: i32| audio::table.find(x).first(conn)).transpose()?;
+impl Meme {
+ pub fn image(&self, conn: &PgConnection) -> Option<Result<Image>> {
+ self.image_id.map(|x: i32| images::table.find(x).first(conn).map_err(Error::from))
+ }
- Ok((image, audio))
+ pub fn audio(&self, conn: &PgConnection) -> Option<Result<Audio>> {
+ self.audio_id.map(|x: i32| audio::table.find(x).first(conn).map_err(Error::from))
}
}
-
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
-#[table_name="image_memes"]
-pub struct ImageMeme {
- pub id: i32,
+#[derive(Insertable, PartialEq, Debug)]
+#[table_name="memes"]
+pub struct NewMeme {
pub title: String,
- pub image_id: i32,
+ pub content: Option<String>,
+ pub image_id: Option<i32>,
+ pub audio_id: Option<i32>,
pub metadata_id: i32,
}
-impl AssociatedData for ImageMeme {
- type Associated = Image;
-
- fn associated_data(&self, conn: &PgConnection) -> Result<Self::Associated> {
- images::table.find(self.image_id).first(conn).map_err(Error::from)
- }
-}
-
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
-#[table_name="audio_memes"]
-pub struct AudioMeme {
+#[derive(Queryable, Identifiable, PartialEq, Debug)]
+#[table_name="audio"]
+pub struct Audio {
pub id: i32,
- pub title: String,
- pub audio_id: i32,
+ pub data: Vec<u8>,
pub metadata_id: i32,
}
-impl AssociatedData for AudioMeme {
- type Associated = Audio;
-
- fn associated_data(&self, conn: &PgConnection) -> Result<Self::Associated> {
- audio::table.find(self.audio_id).first(conn).map_err(Error::from)
- }
+#[derive(Insertable, PartialEq, Debug)]
+#[table_name="audio"]
+pub struct NewAudio {
+ pub data: Vec<u8>,
+ pub metadata_id: i32,
}
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
-#[table_name="audio"]
-pub struct Audio {
+#[derive(Queryable, Identifiable, PartialEq, Debug)]
+#[table_name="images"]
+pub struct Image {
pub id: i32,
pub data: Vec<u8>,
pub metadata_id: i32,
}
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
+#[derive(Insertable, PartialEq, Debug)]
#[table_name="images"]
-pub struct Image {
- pub id: i32,
+pub struct NewImage {
pub data: Vec<u8>,
pub metadata_id: i32,
}
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
+
+#[derive(Queryable, Identifiable, PartialEq, Debug)]
#[table_name="metadata"]
pub struct Metadata {
pub id: i32,
@@ -88,7 +76,15 @@ pub struct Metadata {
pub created_by: i64,
}
-#[derive(Insertable, Queryable, Identifiable, PartialEq, AsChangeset, Debug)]
+#[derive(Insertable, PartialEq, Debug)]
+#[table_name="metadata"]
+pub struct NewMetadata {
+ pub created: NaiveDateTime,
+ pub created_by: i64,
+}
+
+
+#[derive(Queryable, Identifiable, PartialEq, Debug)]
#[table_name="audit_records"]
pub struct AuditRecord {
pub id: i32,
@@ -96,3 +92,11 @@ pub struct AuditRecord {
pub updated_by: i64,
pub metadata_id: i32,
}
+
+#[derive(Insertable, PartialEq, Debug)]
+#[table_name="audit_records"]
+pub struct NewAuditRecord {
+ pub updated: NaiveDateTime,
+ pub updated_by: i64,
+ pub metadata_id: i32,
+}
diff --git a/src/db/schema.rs b/src/db/schema.rs
index 40891a5..cf15dcd 100644
--- a/src/db/schema.rs
+++ b/src/db/schema.rs
@@ -7,15 +7,6 @@ table! {
}
table! {
- audio_memes (id) {
- id -> Int4,
- title -> Varchar,
- audio_id -> Int4,
- metadata_id -> Int4,
- }
-}
-
-table! {
audit_records (id) {
id -> Int4,
updated -> Timestamp,
@@ -25,18 +16,20 @@ table! {
}
table! {
- image_memes (id) {
+ images (id) {
id -> Int4,
- title -> Varchar,
- image_id -> Int4,
+ data -> Bytea,
metadata_id -> Int4,
}
}
table! {
- images (id) {
+ memes (id) {
id -> Int4,
- data -> Bytea,
+ title -> Varchar,
+ content -> Nullable<Text>,
+ image_id -> Nullable<Int4>,
+ audio_id -> Nullable<Int4>,
metadata_id -> Int4,
}
}
@@ -49,34 +42,17 @@ table! {
}
}
-table! {
- text_memes (id) {
- id -> Int4,
- title -> Varchar,
- content -> Text,
- image_id -> Nullable<Int4>,
- audio_id -> Nullable<Int4>,
- metadata_id -> Int4,
- }
-}
-
joinable!(audio -> metadata (metadata_id));
-joinable!(audio_memes -> audio (audio_id));
-joinable!(audio_memes -> metadata (metadata_id));
joinable!(audit_records -> metadata (metadata_id));
-joinable!(image_memes -> images (image_id));
-joinable!(image_memes -> metadata (metadata_id));
joinable!(images -> metadata (metadata_id));
-joinable!(text_memes -> audio (audio_id));
-joinable!(text_memes -> images (image_id));
-joinable!(text_memes -> metadata (metadata_id));
+joinable!(memes -> audio (audio_id));
+joinable!(memes -> images (image_id));
+joinable!(memes -> metadata (metadata_id));
allow_tables_to_appear_in_same_query!(
audio,
- audio_memes,
audit_records,
- image_memes,
images,
+ memes,
metadata,
- text_memes,
);