Skip to content

Commit

Permalink
Run cargo fmt
Browse files Browse the repository at this point in the history
  • Loading branch information
cubetastic33 committed Feb 11, 2024
1 parent 1f53d79 commit 36afae4
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 83 deletions.
26 changes: 18 additions & 8 deletions src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ impl Sentence for (String, String, String, String, HashSet<char>, Option<usize>)
}
}

pub fn fill_sentences<T: Sentence>(client: &mut Client, sentences: &mut Vec<T>, add_overrides: bool) {
pub fn fill_sentences<T: Sentence>(
client: &mut Client,
sentences: &mut Vec<T>,
add_overrides: bool,
) {
let mut queue: HashMap<i32, Vec<usize>> = HashMap::new();
for (i, sentence) in sentences.iter().enumerate() {
if queue.contains_key(&sentence.get_id()) {
Expand Down Expand Up @@ -143,8 +147,12 @@ pub fn get_sentences(
let large_enough = kanji_in_sentence.len() >= quiz_settings.min;
let small_enough = kanji_in_sentence.len() <= quiz_settings.max;

if kanji_in_sentence.is_subset(&known_kanji)&& large_enough && small_enough &&
(known_priority_kanji.is_empty() || !kanji_in_sentence.is_disjoint(&known_priority_kanji)) {
if kanji_in_sentence.is_subset(&known_kanji)
&& large_enough
&& small_enough
&& (known_priority_kanji.is_empty()
|| !kanji_in_sentence.is_disjoint(&known_priority_kanji))
{
sentences.push([
id.to_owned(),
jap_sentence.to_owned(),
Expand All @@ -162,10 +170,7 @@ pub fn get_sentences(
Ok(sentences)
}

pub fn generate_essay(
client: &mut Client,
quiz_settings: Form<QuizSettings>,
) -> Vec<[String; 4]> {
pub fn generate_essay(client: &mut Client, quiz_settings: Form<QuizSettings>) -> Vec<[String; 4]> {
let mut essay = Vec::new();
let mut sentences = Vec::new();
let mut rng = thread_rng();
Expand Down Expand Up @@ -237,7 +242,12 @@ pub fn generate_essay(

// Add a random sentence with a lot of known kanji to the essay
let choice = tuples.choose(&mut rng).unwrap();
essay.push([choice.0.to_owned(), choice.1.to_owned(), choice.2.to_owned(), choice.3.to_owned()]);
essay.push([
choice.0.to_owned(),
choice.1.to_owned(),
choice.2.to_owned(),
choice.3.to_owned(),
]);
known_kanji = known_kanji.difference(&choice.4).map(|x| *x).collect();
}

Expand Down
115 changes: 89 additions & 26 deletions src/admin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Has functions used by routes from the admin page

use crate::{actions::{Sentence, fill_sentences}, Report, AdminReport, AdminOverride, AddOverride, EditOverride};
use crate::{
actions::{fill_sentences, Sentence},
AddOverride, AdminOverride, AdminReport, EditOverride, Report,
};
use postgres::Client;
use rocket::request::Form;
use std::fs;
Expand Down Expand Up @@ -114,10 +117,13 @@ pub fn get_admin_stuff(client: &mut Client) -> (i64, Vec<AdminReport>, Vec<Admin
// Variable to store a queue of sentence IDs that'll be used after we've collected all of them
let mut reports_sentence_ids = Vec::new();
// Get the reports from the database
for row in client.query(
"SELECT * FROM reports WHERE reviewed = FALSE ORDER BY id DESC",
&[]
).unwrap() {
for row in client
.query(
"SELECT * FROM reports WHERE reviewed = FALSE ORDER BY id DESC",
&[],
)
.unwrap()
{
reports_sentence_ids.push(row.get::<_, i32>("sentence_id").to_string());
let reported_at: chrono::DateTime<chrono::Utc> = row.get("reported_at");
reports.push(AdminReport {
Expand All @@ -129,7 +135,9 @@ pub fn get_admin_stuff(client: &mut Client) -> (i64, Vec<AdminReport>, Vec<Admin
report_type: row.get("report_type"),
suggested: row.get("suggested"),
comment: row.get("comment"),
reported_at: reported_at.with_timezone(&chrono_tz::US::Central).to_rfc3339(),
reported_at: reported_at
.with_timezone(&chrono_tz::US::Central)
.to_rfc3339(),
});
}
// Get the overrides from the database
Expand Down Expand Up @@ -172,21 +180,30 @@ pub fn get_admin_stuff(client: &mut Client) -> (i64, Vec<AdminReport>, Vec<Admin
fill_sentences(client, &mut reports, true);
fill_sentences(client, &mut overrides, false);

// Get the number of reviews that have been reviewed but don't have overrides, i.e., have been
// rejected
let rows = client.query("SELECT COUNT(*) FROM reports r LEFT JOIN overrides o ON report_id = r.id WHERE reviewed = TRUE AND o.id IS NULL", &[]).unwrap();
(rows[0].get(0), reports, overrides)
}

pub fn mark_reviewed(client: &mut Client, id: i32) -> Result<String, String> {
client.execute("UPDATE reports SET reviewed = TRUE WHERE id = $1", &[&id]).unwrap();
client
.execute("UPDATE reports SET reviewed = TRUE WHERE id = $1", &[&id])
.unwrap();
Ok("success".to_string())
}

pub fn add_override(client: &mut Client, override_details: Form<AddOverride>) -> Result<String, String> {
pub fn add_override(
client: &mut Client,
override_details: Form<AddOverride>,
) -> Result<String, String> {
// Get the sentence ID from the report
let row = client.query_one(
"SELECT sentence_id FROM reports WHERE id = $1",
&[&override_details.report_id]
).unwrap();
let row = client
.query_one(
"SELECT sentence_id FROM reports WHERE id = $1",
&[&override_details.report_id],
)
.unwrap();
let sentence_id: i32 = row.get("sentence_id");
let mut original_question = String::new();
let mut original_translation = String::new();
Expand Down Expand Up @@ -218,11 +235,14 @@ pub fn add_override(client: &mut Client, override_details: Form<AddOverride>) ->
let mut skip_translation = override_details.translation == original_translation;
let mut skip_reading = override_details.reading == original_reading;
// Compare with the existing overrides
for row in client.query(
"SELECT override_type, value FROM overrides
for row in client
.query(
"SELECT override_type, value FROM overrides
WHERE sentence_id = $1 AND (primary_value = TRUE OR override_type != 'reading')",
&[&sentence_id]
).unwrap() {
&[&sentence_id],
)
.unwrap()
{
let override_type: String = row.get("override_type");
if override_type == "question" && !skip_question {
skip_question = override_details.question == row.get::<_, String>("value");
Expand All @@ -239,19 +259,51 @@ pub fn add_override(client: &mut Client, override_details: Form<AddOverride>) ->
let statement = client.prepare("INSERT INTO overrides (sentence_id, override_type, value, primary_value, report_id) VALUES ($1, 'question', $2, FALSE, $3);").or_else(|e| Err(e.to_string()))?;

if !skip_question {
client.execute(&statement, &[&sentence_id, &override_details.question, &override_details.report_id]).or_else(|e| Err(e.to_string()))?;
client
.execute(
&statement,
&[
&sentence_id,
&override_details.question,
&override_details.report_id,
],
)
.or_else(|e| Err(e.to_string()))?;
something_changed = true;
}
if !skip_translation {
client.execute(&statement, &[&sentence_id, &override_details.translation, &override_details.report_id]).or_else(|e| Err(e.to_string()))?;
client
.execute(
&statement,
&[
&sentence_id,
&override_details.translation,
&override_details.report_id,
],
)
.or_else(|e| Err(e.to_string()))?;
something_changed = true;
}
if !skip_reading {
client.execute(&statement, &[&sentence_id, &override_details.reading, &override_details.report_id]).or_else(|e| Err(e.to_string()))?;
client
.execute(
&statement,
&[
&sentence_id,
&override_details.reading,
&override_details.report_id,
],
)
.or_else(|e| Err(e.to_string()))?;
something_changed = true;
}
if let Some(reading) = override_details.additional_reading.clone() {
client.execute(&statement, &[&sentence_id, &reading, &override_details.report_id]).or_else(|e| Err(e.to_string()))?;
client
.execute(
&statement,
&[&sentence_id, &reading, &override_details.report_id],
)
.or_else(|e| Err(e.to_string()))?;
something_changed = true;
}
if something_changed {
Expand All @@ -261,16 +313,27 @@ pub fn add_override(client: &mut Client, override_details: Form<AddOverride>) ->
}
}

pub fn edit_override(client: &mut Client, override_details: Form<EditOverride>) -> Result<String, String> {
client.execute(
"UPDATE overrides SET value = $1, primary_value = $2 WHERE id = $3",
&[&override_details.value, &override_details.primary_value, &override_details.override_id]
).or_else(|e| Err(e.to_string()))?;
pub fn edit_override(
client: &mut Client,
override_details: Form<EditOverride>,
) -> Result<String, String> {
client
.execute(
"UPDATE overrides SET value = $1, primary_value = $2 WHERE id = $3",
&[
&override_details.value,
&override_details.primary_value,
&override_details.override_id,
],
)
.or_else(|e| Err(e.to_string()))?;
Ok(String::from("success"))
}

pub fn delete_override(client: &mut Client, id: i32) -> Result<String, String> {
client.execute("UPDATE reports SET reviewed = FALSE WHERE id = (SELECT report_id FROM overrides WHERE id = $1)", &[&id]).or_else(|e| Err(e.to_string()))?;
client.execute("DELETE FROM overrides WHERE id = $1", &[&id]).or_else(|e| Err(e.to_string()))?;
client
.execute("DELETE FROM overrides WHERE id = $1", &[&id])
.or_else(|e| Err(e.to_string()))?;
Ok(String::from("success"))
}
81 changes: 49 additions & 32 deletions src/kanji_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ use super::OrderedImport;
use regex::Regex;
use rocket::{http::Status, request::Form, response::status::Custom};
use rusqlite::{Connection, NO_PARAMS};
use std::{
collections::HashSet,
error::Error,
fs,
io::{Cursor, Read, Write},
};
use uuid::Uuid;
use std::{error::Error, collections::HashSet, io::{Cursor, Read, Write}, fs};

pub enum KanjiOrder {
WaniKani,
Expand All @@ -29,62 +34,68 @@ pub fn extract_kanji_from_anki_deck(
println!("An error occurred: {:?}", e);
// Delete the database file since we're returning early from an error
fs::remove_file(file_name).expect("Couldn't delete the anki database file");
let _ = fs::remove_file(&format!("{}-shm", file_name)); // Delete if exists
let _ = fs::remove_file(&format!("{}-wal", file_name)); // Delete if exists
Err(Custom(
Status::InternalServerError,
e.to_string(),
))
// Delete the temp db files if they exist
let _ = fs::remove_file(&format!("{}-shm", file_name));
let _ = fs::remove_file(&format!("{}-wal", file_name));
Err(Custom(Status::InternalServerError, e.to_string()))
}

let mut contents = Vec::new();
// Get the database file
if let Ok(file) = zip.by_name("collection.anki21b") {
// This deck uses the Anki scheduler v3
// This requires us to use zstd to decompress the file
zstd::stream::copy_decode(file, &mut contents).or_else(|e| return_err(&file_name, e))?;
zstd::stream::copy_decode(file, &mut contents)
.or_else(|e| return_err(&file_name, e))?;
}
if contents.len() == 0 {
if let Ok(mut file) = zip.by_name("collection.anki21") {
// This deck uses the Anki 2.1 scheduler
file.read_to_end(&mut contents).or_else(|e| return_err(&file_name, e))?;
file.read_to_end(&mut contents)
.or_else(|e| return_err(&file_name, e))?;
}
}
if contents.len() == 0 {
if let Ok(mut file) = zip.by_name("collection.anki2") {
// This deck doesn't use the Anki 2.1 scheduler
file.read_to_end(&mut contents).or_else(|e| return_err(&file_name, e))?;
file.read_to_end(&mut contents)
.or_else(|e| return_err(&file_name, e))?;
}
}
if contents.len() > 0 {
// We now have the sqlite3 database with the notes
// Write the database to a file
let mut f = fs::File::create(&file_name).or_else(|e| return_err(&file_name, e))?;
f.write_all(&contents).or_else(|e| return_err(&file_name, e))?;
f.write_all(&contents)
.or_else(|e| return_err(&file_name, e))?;
let conn = Connection::open(&file_name).or_else(|e| return_err(&file_name, e))?;
// Create a variable to store the kanji
let mut kanji: HashSet<String> = HashSet::new();
// Regex to find kanji
let kanji_regex = Regex::new(r"[\p{Han}]").unwrap();
/*
* In most decks I checked the kanji was in the sort field (sfld) column, but some
* decks have numbers there, and the kanji is in the fields (flds) column. In this
* case it's more complicated because there can be multiple fields and the kanji
* could be in any one of those fields. So we take the sfld column if it has kanji,
* otherwise as a secondary option we take the flds column.
*
* The queue column in the cards table tells us if the card is already learnt, is
* being learnt, or has never been seen before. if the only_learnt parameter is
* true, we should only consider cards that are in queue 2 (learnt).
*
* Despite the DISTINCT clause, it is still necessary to filter duplicates because
* different notes of the same kanji could be in different queues.
*/
let mut statement = conn.prepare(
"SELECT DISTINCT cards.queue, notes.sfld, notes.flds
FROM cards INNER JOIN notes on notes.id = cards.nid"
).or_else(|e| return_err(&file_name, e))?;
let mut rows = statement.query(NO_PARAMS).or_else(|e| return_err(&file_name, e))?;
* In most decks I checked the kanji was in the sort field (sfld) column, but some
* decks have numbers there, and the kanji is in the fields (flds) column. In this
* case it's more complicated because there can be multiple fields and the kanji
* could be in any one of those fields. So we take the sfld column if it has kanji,
* otherwise as a secondary option we take the flds column.
*
* The queue column in the cards table tells us if the card is already learnt, is
* being learnt, or has never been seen before. if the only_learnt parameter is
* true, we should only consider cards that are in queue 2 (learnt).
*
* Despite the DISTINCT clause, it is still necessary to filter duplicates because
* different notes of the same kanji could be in different queues.
*/
let mut statement = conn
.prepare(
"SELECT DISTINCT cards.queue, notes.sfld, notes.flds
FROM cards INNER JOIN notes on notes.id = cards.nid",
)
.or_else(|e| return_err(&file_name, e))?;
let mut rows = statement
.query(NO_PARAMS)
.or_else(|e| return_err(&file_name, e))?;
while let Some(row) = rows.next().unwrap() {
if !only_learnt || row.get::<_, i32>(0).unwrap() == 2 {
let mut no_kanji_found = true;
Expand All @@ -108,8 +119,9 @@ pub fn extract_kanji_from_anki_deck(
}
// Delete the database file
fs::remove_file(&file_name).expect("Couldn't delete the anki database file");
let _ = fs::remove_file(&format!("{}-shm", file_name)); // Delete if exists
let _ = fs::remove_file(&format!("{}-wal", file_name)); // Delete if exists
// Delete the temp db files if they exist
let _ = fs::remove_file(&format!("{}-shm", file_name));
let _ = fs::remove_file(&format!("{}-wal", file_name));
// Return all the extracted kanji
return Ok(kanji
.iter()
Expand Down Expand Up @@ -179,7 +191,12 @@ pub fn kanji_from_wanikani(api_key: &str) -> Result<String, Custom<String>> {
// Since we restrict to 1000 ids at a time, we shouldn't need to paginate
url = String::from("https://api.wanikani.com/v2/subjects");
let start = i * 1000;
let end = start + if i == ids.len() / 1000 {ids.len() % 1000} else {1000};
let end = start
+ if i == ids.len() / 1000 {
ids.len() % 1000
} else {
1000
};

let json = client
.get(&url)
Expand Down
Loading

0 comments on commit 36afae4

Please sign in to comment.