Skip to content

Commit

Permalink
feat: added support for updating a set
Browse files Browse the repository at this point in the history
This allows users to have a Forne set be continually updated from
something like their on notes, seamlessly!
  • Loading branch information
arctic-hen7 committed May 26, 2023
1 parent 994df2d commit 1902302
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 20 deletions.
59 changes: 42 additions & 17 deletions src/adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,50 @@ impl Set {
method: RawMethod,
engine: &Engine,
) -> Result<Self> {
// Create an empty set and then populate it
let mut set = Self {
method: match &method {
RawMethod::Inbuilt(name) => name,
RawMethod::Custom { name, .. } => name,
}
.to_string(),
cards: HashMap::new(),
run_state: None,
test_in_progress: false,
};
set.update_with_adapter(script, src, method, engine)?;

Ok(set)
}
/// Updates this set from the given source. This will add any new question/answer pairs the adapter script finds,
/// and will update any answers that change. If a question changes, it will be registered as a new card. Any cards
/// whose answers change will have their metadata reset in order to allow the user to learn the new card.
///
/// The arguments provided to this function must satisfy the same requirements as those provided to
/// [`Self::new_with_adapter`].
pub(crate) fn update_with_adapter(
&mut self,
script: &str,
src: String,
method: RawMethod,
engine: &Engine,
) -> Result<()> {
let method = method.into_method(engine)?;

let mut scope = Scope::new();
scope.push_constant("SOURCE", src);
// This will get *all* the cards in the source, which we will then compare
// with what we already have
let raw_array: Vec<Dynamic> = engine
.eval_with_scope(&mut scope, script)
.with_context(|| "failed to run adapter script")?;
let mut cards = HashMap::new();

for dyn_elem in raw_array {
let elems: Vec<String> = dyn_elem
.into_typed_array()
.map_err(|_| anyhow!("couldn't parse adapter results"))?;

let card = Card {
let new_card = Card {
question: elems
.get(0)
.ok_or_else(|| anyhow!("adapter did not return question for card"))?
Expand All @@ -46,22 +75,18 @@ impl Set {
starred: false,
method_data: (method.get_default_metadata)()?,
};
cards.insert(Uuid::new_v4(), card);
// If we've already got this question, update the answer if necessary, otherwise add it afresh
let found = self
.cards
.iter_mut()
.find(|(_id, card)| card.question == new_card.question);
if let Some((_id, card)) = found {
*card = new_card;
} else {
self.cards.insert(Uuid::new_v4(), new_card);
}
}

Ok(Self {
method: method.name,
cards,
run_state: None,
test_in_progress: false,
})
Ok(())
}
// /// Updates this set from the given source. This will add any new question/answer pairs the adapter script finds,
// /// and will update any answers that change. If a question changes, it will be registered as a new card. None of
// /// the metadata on existing cards will be altered.
// pub(crate) fn update_with_adapter(&mut self, script: &str, src: String, engine: &Engine) -> Result<()> {
// let mut scope = Scope::new();
// scope.push_constant("SOURCE", src);
// let pairs: Vec<(String, String)> = engine.eval_with_scope(&mut scope, script).with_context(|| "failed to run adapter script")?;
// }
}
37 changes: 37 additions & 0 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ fn main() -> anyhow::Result<()> {

println!("New set created!");
}
Command::Update {
set: set_file,
source,
adapter,
method,
} => {
let json =
fs::read_to_string(&set_file).with_context(|| "failed to read from set file")?;
let set = Set::from_json(&json)?;
let source =
fs::read_to_string(source).with_context(|| "failed to read from source file")?;
let adapter_script =
fs::read_to_string(adapter).with_context(|| "failed to read adapter script")?;
let method = method_from_string(method)?;

let mut forne = Forne::from_set(set);
forne.update(source, &adapter_script, method)?;
let new_json = forne.save_set()?;
fs::write(set_file, new_json)
.with_context(|| "failed to write updated set to output file")?;

println!("New set created!");
}
Command::Learn {
set: set_file,
method,
Expand Down Expand Up @@ -304,6 +327,20 @@ mod opts {
#[arg(short, long)]
method: String, // Secondary parsing
},
/// Updates an existing set with some new terms
Update {
/// The existing set file
set: String,
/// The file to update the set with
#[arg(short, long)]
source: String,
/// The path to the adapter script to be used to parse the set
#[arg(short, long)]
adapter: PathBuf,
/// The learning method to use for the new set
#[arg(short, long)]
method: String, // Secondary parsing
},
/// Starts or resumes a learning session on the given set
Learn {
/// The file the set is in
Expand Down
12 changes: 11 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@ impl Forne {
rhai_engine: engine,
})
}
/// Updates the given set from a source. See [`Set::update_with_adapter`] for the exact behaviour of this method.
pub fn update(
&mut self,
src: String,
adapter_script: &str,
raw_method: RawMethod,
) -> Result<()> {
self.set
.update_with_adapter(adapter_script, src, raw_method, &self.rhai_engine)
}
/// Creates a new Forne engine. While not inherently expensive, this should generally only be called once, or when
/// the system needs to restart.
pub fn from_set(set: Set) -> Self {
Expand Down Expand Up @@ -163,7 +173,7 @@ impl Forne {
match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
Ok(duration) => duration.as_secs() as i64,
// If we're before 01/01/1970...well ok then!
Err(err) => err.duration().as_secs() as i64 * -1,
Err(err) => -(err.duration().as_secs() as i64),
}
},
);
Expand Down
2 changes: 1 addition & 1 deletion tests/output.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"method":"speed-v1","cards":{"d2b9e892-aa49-4fae-8fbf-3faf508b3a5c":{"question":"Hello ...","answer":"world!","seen_in_test":false,"difficult":false,"starred":false,"method_data":{"weight":1.0}},"37b42e0e-ddfa-4071-b85e-fbe9de41dce4":{"question":"Foo","answer":"bar","seen_in_test":false,"difficult":false,"starred":false,"method_data":{"weight":1.0}}},"run_state":null,"test_in_progress":false}
{"method":"speed-v1","cards":{"d50d21e9-1fba-4f60-b7e3-e186a0b8186d":{"question":"Test","answer":"123","seen_in_test":false,"difficult":false,"starred":false,"method_data":{"weight":1.0}},"a9e1f4d3-4ff9-4681-946a-cf8e76fdc67b":{"question":"Hello ...","answer":"world!","seen_in_test":false,"difficult":false,"starred":false,"method_data":{"weight":1.0}},"0fda3cd7-ee0c-4dc0-8e40-6fd696c0f508":{"question":"Foo","answer":"baz","seen_in_test":false,"difficult":false,"starred":false,"method_data":{"weight":1.0}},"dda72753-24db-402b-9c6f-feffa00aeeb9":{"question":"Hello","answer":"worlds!","seen_in_test":false,"difficult":false,"starred":false,"method_data":{"weight":1.0}}},"run_state":null,"test_in_progress":false}
4 changes: 3 additions & 1 deletion tests/test.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Hello ... -> world!
Foo -> bar
Foo -> baz
Test -> 123
Hello -> worlds!

0 comments on commit 1902302

Please sign in to comment.