Skip to content

Commit

Permalink
Merge pull request #61 from ontodev/actions-and-git
Browse files Browse the repository at this point in the history
First pass at actions and git support
  • Loading branch information
jamesaoverton authored Jun 28, 2023
2 parents f2534a1 + c4f3a57 commit 1cfce0e
Show file tree
Hide file tree
Showing 8 changed files with 542 additions and 38 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ sqlx = { version = "0.6", features = [ "runtime-async-std-rustls", "any", "postg
tabwriter = { version = "1.2.1" }
tokio = { version = "1.22.0", features = ["full"] }
tokio-test = "0.4.2"
toml = { version = "0.5.9" }
toml = "0.7.5"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
urlencoding = "2.1.2"
Expand All @@ -32,6 +32,10 @@ async-recursion = "1.0.2"
reqwest = { version = "0.11.14", features = ["blocking"] }
itertools = "0.10.5"
thiserror = "1.0"
indexmap = { version = "2.0.0", features = ["serde"] }
git2 = "0.17.2"
chrono = "0.4.26"
ansi-to-html = "0.1.3"

[dependencies.ontodev_hiccup]
git = "https://github.com/ontodev/hiccup.rs"
Expand Down
50 changes: 50 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ format:
build:
cargo build --release

build/:
mkdir -p $@

target/release/nanobot: src/
cargo build --release

Expand Down Expand Up @@ -63,3 +66,50 @@ penguins: target/release/nanobot examples/penguins/
&& python3 generate.py \
&& ../../$< init \
&& ../../$< serve

build/synthea.zip: | build
curl -L -o build/synthea.zip "https://synthetichealth.github.io/synthea-sample-data/downloads/synthea_sample_data_csv_apr2020.zip"

build/synthea/: build/synthea.zip examples/synthea/
mkdir -p build/synthea/src/data
cp -r examples/synthea/* build/synthea/
unzip $< -d build/synthea/
sed 's/,/ /g' build/synthea/csv/patients.csv > build/synthea/src/data/patients.tsv
sed 's/,/ /g' build/synthea/csv/observations.csv > build/synthea/src/data/observations.tsv

# && ~/valve.rs/target/release/ontodev_valve src/schema/table.tsv .nanobot.db
.PHONY: synthea
synthea: target/release/nanobot
rm -rf build/synthea/
make build/synthea/
cd build/synthea/ \
&& time ../../$< init \
&& ../../$< serve

TODAY := $(shell date +%Y-%m-%d)
ARCH := x86_64-unknown-linux-musl
TARGET := build/nanobot-$(ARCH)

target/$(ARCH)/release/nanobot: src
docker pull clux/muslrust:stable
docker run \
-v cargo-cache:/root/.cargo/registry \
-v $$PWD:/volume \
--rm -t clux/muslrust:stable \
cargo build --release

.PHONY: musl
musl: target/$(ARCH)/release/nanobot src/ | build/

.PHONY: upload
upload: target/$(ARCH)/release/nanobot | build/
cp $< $(TARGET)
gh release upload --clobber v$(TODAY) $(TARGET)

.PHONY: release
release: target/$(ARCH)/release/nanobot | build/
cp $< $(TARGET)
gh release create --draft --prerelease \
--title "$(TODAY) Alpha Release" \
--generate-notes \
v$(TODAY) $(TARGET)
22 changes: 22 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use indexmap::map::IndexMap;
use ontodev_valve::{
get_compiled_datatype_conditions, get_compiled_rule_conditions,
get_parsed_structure_conditions, valve, valve_grammar::StartParser, ColumnRule,
Expand All @@ -22,6 +23,7 @@ pub struct Config {
pub valve_path: String,
pub valve: Option<ValveConfig>,
pub template_path: Option<String>,
pub actions: IndexMap<String, ActionConfig>,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd, Ord, PartialEq, Eq)]
Expand Down Expand Up @@ -53,6 +55,7 @@ pub struct TomlConfig {
pub database: Option<DatabaseConfig>,
pub valve: Option<ValveTomlConfig>,
pub templates: Option<TemplatesConfig>,
pub actions: Option<IndexMap<String, ActionConfig>>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand Down Expand Up @@ -106,6 +109,23 @@ pub struct TemplatesConfig {
pub path: Option<String>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ActionConfig {
pub label: String,
pub inputs: Option<Vec<InputConfig>>,
pub commands: Vec<Vec<String>>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct InputConfig {
pub name: String,
pub label: String,
pub value: Option<String>,
pub default: Option<String>,
pub placeholder: Option<String>,
pub test: Option<String>,
}

pub type SerdeMap = serde_json::Map<String, SerdeValue>;

pub const DEFAULT_TOML: &str = "[nanobot]
Expand Down Expand Up @@ -155,6 +175,7 @@ impl Config {
None => None,
}
},
actions: user.actions.unwrap_or_default(),
};

Ok(config)
Expand Down Expand Up @@ -267,5 +288,6 @@ pub fn to_toml(config: &Config) -> TomlConfig {
templates: Some(TemplatesConfig {
path: config.template_path.clone(),
}),
actions: Some(config.actions.clone()),
}
}
115 changes: 112 additions & 3 deletions src/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use crate::sql::{
get_count_from_pool, get_message_counts_from_pool, get_table_from_pool, get_total_from_pool,
LIMIT_DEFAULT, LIMIT_MAX,
};
use chrono::prelude::{DateTime, Utc};
use enquote::unquote;
use git2::Repository;
use minijinja::{Environment, Source};
use ontodev_sqlrest::{Direction, OrderByColumn, Select};
use ontodev_valve::get_sql_type_from_global_config;
Expand All @@ -16,11 +18,14 @@ use serde_json::{json, to_string_pretty, Map, Value};
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io::Write;
use std::path::Path;
use tabwriter::TabWriter;
use urlencoding::decode;

pub type SerdeMap = serde_json::Map<String, serde_json::Value>;

#[derive(Debug)]
pub struct GetError {
details: String,
Expand All @@ -44,15 +49,33 @@ impl Error for GetError {
}
}

impl From<String> for GetError {
fn from(error: String) -> GetError {
GetError::new(error)
}
}

impl From<std::io::Error> for GetError {
fn from(error: std::io::Error) -> GetError {
GetError::new(format!("{:?}", error))
}
}

impl From<sqlx::Error> for GetError {
fn from(error: sqlx::Error) -> GetError {
GetError::new(format!("{:?}", error))
}
}

impl From<String> for GetError {
fn from(error: String) -> GetError {
GetError::new(error)
impl From<git2::Error> for GetError {
fn from(error: git2::Error) -> GetError {
GetError::new(format!("{:?}", error))
}
}

impl From<std::time::SystemTimeError> for GetError {
fn from(error: std::time::SystemTimeError) -> GetError {
GetError::new(format!("{:?}", error))
}
}

Expand Down Expand Up @@ -857,6 +880,8 @@ async fn get_page(
"select": select,
"select_params": select2.to_params().unwrap_or_default(),
"elapsed": elapsed,
"actions": get_action_map(&config).unwrap_or_default(),
"repo": get_repo_details().unwrap_or_default(),
},
"table": this_table,
"column": column_map,
Expand All @@ -866,6 +891,84 @@ async fn get_page(
Ok(result)
}

pub fn get_action_map(config: &Config) -> Result<SerdeMap, GetError> {
let action_map: SerdeMap = config
.actions
.iter()
.map(|(k, v)| (k.into(), v.clone().label.into()))
.collect();
Ok(action_map)
}

pub fn get_repo_details() -> Result<SerdeMap, GetError> {
let mut result = SerdeMap::new();

let repo = Repository::open_from_env().expect("Couldn't open repository");
let head = match repo.head() {
Ok(head) => Some(head),
Err(e) => return Err(GetError::new(e.to_string())),
};
let head = head
.as_ref()
.and_then(|h| h.shorthand())
.unwrap_or_default();
let local = repo.find_branch(&head, git2::BranchType::Local)?;
tracing::debug!("GIT got local: {head}, {:?}", local.name()?);
result.insert("head".into(), head.into());
result.insert("local".into(), local.name()?.into());

let upstream = local.upstream();
if let Ok(upstream) = upstream {
let (ahead, behind) = repo.graph_ahead_behind(
local.get().target().unwrap(),
upstream.get().target().unwrap(),
)?;
let remote = repo.find_remote("origin")?;
let remote_url = format!(
"{}/tree/{}",
remote
.url()
.ok_or("No URL?")
.unwrap_or_default()
.trim_end_matches(".git"),
upstream
.name()?
.unwrap_or_default()
.trim_start_matches("origin/")
);
tracing::debug!(
"GIT got remote: {ahead} ahead {behind} behind {:?}, {remote_url}",
upstream.name()?
);
result.insert("upstream".into(), upstream.name()?.into());
result.insert("remote_url".into(), remote_url.into());
result.insert("ahead".into(), ahead.into());
result.insert("behind".into(), behind.into());
} else {
tracing::debug!("GIT no upstream branch");
}

// https://github.com/ontodev/nanobot.rs/tree/refine-ui
let mut opts = git2::StatusOptions::new();
opts.include_ignored(false);
opts.include_untracked(false);
opts.exclude_submodules(true);
if let Ok(statuses) = repo.statuses(Some(&mut opts)) {
let uncommitted = statuses.len() > 0;
tracing::debug!("GIT got status: {uncommitted}");
result.insert("uncommitted".into(), uncommitted.into());
}
let path = repo.path().join("FETCH_HEAD");
tracing::debug!("GIT repo path: {path:?} {}", path.is_file());
if path.is_file() {
let dt: DateTime<Utc> = fs::metadata(path)?.modified()?.clone().into();
let fetched = format!("{}", dt.to_rfc3339());
result.insert("fetched".into(), fetched.into());
}

Ok(result)
}

fn value_rows_to_text(rows: &Vec<Map<String, Value>>) -> Result<String, GetError> {
// This would be nicer with map, but I got weird borrowing errors.
let mut lines: Vec<String> = vec![];
Expand Down Expand Up @@ -951,6 +1054,7 @@ pub fn page_to_html(config: &Config, template: &str, page: &Value) -> Result<Str
let page_html = include_str!("resources/page.html");
let table_html = include_str!("resources/table.html");
let form_html = include_str!("resources/form.html");
let action_html = include_str!("resources/action.html");

let mut env = Environment::new();
env.add_filter("level_to_bootstrap", level_to_bootstrap);
Expand All @@ -971,11 +1075,16 @@ pub fn page_to_html(config: &Config, template: &str, page: &Value) -> Result<Str
if !path.is_file() {
env.add_template("form.html", form_html).unwrap();
}
let path = Path::new(t).join("action.html");
if !path.is_file() {
env.add_template("action.html", action_html).unwrap();
}
} else {
tracing::info!("Adding default templates");
env.add_template("page.html", page_html).unwrap();
env.add_template("table.html", table_html).unwrap();
env.add_template("form.html", form_html).unwrap();
env.add_template("action.html", action_html).unwrap();
}

let template = match env.get_template(format!("{}.html", template).as_str()) {
Expand Down
Loading

0 comments on commit 1cfce0e

Please sign in to comment.