Implement status messages

This commit is contained in:
Andreas Tsouchlos 2025-12-30 02:35:35 +02:00
parent 890cb0be5e
commit c1882e50df
4 changed files with 72 additions and 13 deletions

View File

@ -28,6 +28,19 @@ pub enum SnowballingStep {
Forward,
}
#[derive(Serialize, Deserialize, Clone)]
pub enum StatusMessage {
Info(String),
Warning(String),
Error(String),
}
impl Default for StatusMessage {
fn default() -> Self {
StatusMessage::Info("".to_string())
}
}
impl ToString for SnowballingStep {
fn to_string(&self) -> String {
match self {
@ -104,7 +117,8 @@ pub struct App {
pub snowballing_step: SnowballingStep,
pub status_message: String,
#[serde(skip)]
pub status_message: StatusMessage,
#[serde(skip)]
pub should_quit: bool,
@ -117,7 +131,6 @@ pub struct App {
// TODO: Implement export of included papers into zotero (Use RIS format somehow)
// TODO: Log everything relevant
impl App {
// TODO: Show error somehow
pub async fn add_seed_paper(&mut self, api_link: &String) {
let publ =
get_publication_by_id(api_link, "an.tsouchlos@gmail.com").await;
@ -129,10 +142,18 @@ impl App {
"Failed to get publication metadata using OpenAlex API: {}",
err
);
self.set_status_message(StatusMessage::Error(format!(
"Failed to get publication metadata using OpenAlex API: {}",
err
)));
}
}
}
pub fn set_status_message(&mut self, s: StatusMessage) {
self.status_message = s;
}
pub async fn handle_key(&mut self, key: KeyCode) {
if KeyCode::Esc == key {
self.should_quit = true;
@ -164,7 +185,11 @@ impl App {
"The next snowballing step can only be initiated \
after screening all pending publications"
);
// TODO: Show warning/error somehow
self.set_status_message(StatusMessage::Warning(
"The next snowballing step can only be initiated \
after screening all pending publications"
.to_string(),
));
return;
}
@ -173,13 +198,21 @@ impl App {
// TODO: Implement
}
SnowballingStep::Backward => {
for publication in &self.included_publications {
self.set_status_message(StatusMessage::Info(
"Fetching references...".to_string(),
));
// TODO: Find a way to not clone the publications
for publication in
self.included_publications.clone()
{
// TODO: In addition to the referenced_works do
// an API call for citations
for reference in &publication.referenced_works {
let api_link = format!(
"https://api.openalex.org/{}",
&reference[21..]
);
// https://openalex.org/W2085881930
let publ = get_publication_by_id(
&api_link,
"an.tsouchlos@gmail.com",
@ -190,7 +223,6 @@ impl App {
Ok(publ) => {
self.pending_publications.push(publ)
}
// TODO: Show error somehow
Err(err) => {
warn!(
"Failed to get publication\
@ -198,9 +230,20 @@ impl App {
{}",
err
);
self.set_status_message(
StatusMessage::Error(format!(
"Failed to get publication\
metadata using OpenAlex API: \
{}",
err
)),
);
}
}
}
self.set_status_message(StatusMessage::Info("Done".to_string()));
}
}
}

View File

@ -88,12 +88,12 @@ async fn main() {
.init();
match run(&args).await {
Ok(()) => info!("Application completed successfully"),
Err(e) => {
error!("Application error: {}", e);
print!("{e:?}");
std::process::exit(1);
}
_ => {}
}
}

View File

@ -5,14 +5,14 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use unicode_general_category::{GeneralCategory, get_general_category};
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Authorship {
pub author_position: String,
pub raw_author_name: String,
}
// TODO: Handle duplicates by having vectors of ids
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Publication {
pub id: String,
pub display_name: Option<String>,

View File

@ -1,5 +1,5 @@
use crate::{
app::{ActivePane, ActiveTab, App},
app::{ActivePane, ActiveTab, App, StatusMessage},
snowballing::Publication,
};
use ratatui::{
@ -20,7 +20,11 @@ pub fn draw(f: &mut Frame, app: &mut App) {
fn draw_seeding_tab(f: &mut Frame, app: &mut App) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(3), Constraint::Length(3)])
.constraints([
Constraint::Min(3),
Constraint::Length(3),
Constraint::Length(1),
])
.split(f.area());
// Included publication list
@ -62,6 +66,10 @@ fn draw_seeding_tab(f: &mut Frame, app: &mut App) {
.block(Block::bordered().title("Input"));
f.render_widget(input, chunks[1]);
// Status line
draw_status_line(f, app, chunks[2]);
}
fn draw_snowballing_tab(f: &mut Frame, app: &mut App) {
@ -322,8 +330,16 @@ fn draw_right_pane(frame: &mut Frame, app: &mut App, area: Rect) {
}
fn draw_status_line(frame: &mut Frame, app: &App, area: Rect) {
let line = Paragraph::new(app.status_message.clone())
.style(Style::default().bg(Color::Rgb(60, 56, 54)));
let line = Paragraph::new(Line::from(match app.status_message.clone() {
StatusMessage::Info(s) => Span::raw(s),
StatusMessage::Warning(s) => {
Span::styled(s, Style::default().fg(Color::Yellow))
}
StatusMessage::Error(s) => {
Span::styled(s, Style::default().fg(Color::Red))
}
}))
.style(Style::default().bg(Color::Rgb(60, 56, 54)));
frame.render_widget(line, area);
}