add exporting

This commit is contained in:
Michael Zhang 2024-05-26 21:56:12 -05:00
parent ec375f14f7
commit bcd2e9086b
9 changed files with 137 additions and 1 deletions

4
Cargo.lock generated
View file

@ -2790,6 +2790,10 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "panorama-macros"
version = "0.1.0"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.3" version = "0.12.3"

1
crates/panorama-daemon/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
export

View file

@ -0,0 +1,82 @@
use std::{
collections::HashMap,
fs::{self, File},
io::{BufWriter, Write},
path::PathBuf,
};
use axum::extract::State;
use cozo::ScriptMutability;
use crate::{error::AppResult, AppState};
pub async fn export(State(state): State<AppState>) -> AppResult<()> {
let result = state.db.run_script(
"::relations",
Default::default(),
ScriptMutability::Immutable,
)?;
let name_index = result.headers.iter().position(|x| x == "name").unwrap();
let relation_names = result
.rows
.into_iter()
.map(|row| row[name_index].get_str().unwrap().to_owned())
.collect::<Vec<_>>();
let mut relation_columns = HashMap::new();
for relation_name in relation_names.iter() {
let result = state.db.run_script(
&format!("::columns {relation_name}"),
Default::default(),
ScriptMutability::Immutable,
)?;
let column_index =
result.headers.iter().position(|x| x == "column").unwrap();
let columns = result
.rows
.into_iter()
.map(|row| row[column_index].get_str().unwrap().to_owned())
.collect::<Vec<_>>();
relation_columns.insert(relation_name.clone(), columns);
}
println!("columns: {relation_columns:?}");
let base_dir = PathBuf::from("export");
fs::create_dir_all(&base_dir);
let tx = state.db.multi_transaction(false);
for relation_name in relation_names.iter() {
let relation_path = base_dir.join(format!("{relation_name}.ndjson"));
let mut file = File::create(&relation_path).unwrap();
let columns = relation_columns
.get(relation_name.as_str())
.unwrap()
.join(", ");
let query = format!("?[{columns}] := *{relation_name} {{ {columns} }}");
println!("Query: {query}");
let result = tx.run_script(&query, Default::default())?;
for row in result.rows.into_iter() {
let mut object = HashMap::new();
for (idx, col) in row.into_iter().enumerate() {
let row_name = result.headers[idx].clone();
object.insert(row_name, col);
}
let serialized = serde_json::to_string(&object).unwrap();
file.write(serialized.as_bytes());
file.write(b"\n");
}
}
Ok(())
}

View file

@ -8,9 +8,11 @@ extern crate serde_json;
extern crate sugars; extern crate sugars;
mod error; mod error;
mod export;
mod journal; mod journal;
mod migrations; mod migrations;
mod node; mod node;
mod query_builder;
use std::fs; use std::fs;
@ -27,6 +29,7 @@ use tower::ServiceBuilder;
use tower_http::cors::{self, CorsLayer}; use tower_http::cors::{self, CorsLayer};
use crate::{ use crate::{
export::export,
journal::get_todays_journal_id, journal::get_todays_journal_id,
migrations::run_migrations, migrations::run_migrations,
node::{get_node, node_types, update_node}, node::{get_node, node_types, update_node},
@ -64,6 +67,7 @@ async fn main() -> Result<()> {
// build our application with a single route // build our application with a single route
let app = Router::new() let app = Router::new()
.route("/", get(|| async { "Hello, World!" })) .route("/", get(|| async { "Hello, World!" }))
.route("/export", get(export))
.route("/node/:id", get(get_node)) .route("/node/:id", get(get_node))
.route("/node/:id", post(update_node)) .route("/node/:id", post(update_node))
.route("/node/types", get(node_types)) .route("/node/types", get(node_types))

View file

@ -117,6 +117,15 @@ fn migration_01(db: &DbInstance) -> Result<()> {
{ :create node_has_key { key: String => id: String } } { :create node_has_key { key: String => id: String } }
{ :create node_managed_by_app { node_id: String => app: String } } { :create node_managed_by_app { node_id: String => app: String } }
{ :create node_refers_to { node_id: String => other_node_id: String } } { :create node_refers_to { node_id: String => other_node_id: String } }
{
:create fqkey_to_dbkey {
key: String
=>
relation: String,
field_name: String,
type: String,
}
}
# Create journal type # Create journal type
{ :create journal { node_id: String => content: Json } } { :create journal { node_id: String => content: Json } }

View file

@ -5,7 +5,7 @@ use axum::{
http::StatusCode, http::StatusCode,
Json, Json,
}; };
use cozo::ScriptMutability; use cozo::{DataValue, ScriptMutability};
use serde_json::Value; use serde_json::Value;
use crate::{error::AppResult, AppState}; use crate::{error::AppResult, AppState};
@ -70,6 +70,30 @@ pub async fn update_node(
) -> AppResult<Json<Value>> { ) -> AppResult<Json<Value>> {
println!("Update data: {:?}", update_data); println!("Update data: {:?}", update_data);
let tx = state.db.multi_transaction(true);
if let Some(extra_data) = update_data.extra_data {
let result = tx.run_script(
"
?[relation, field_name, type] :=
*fqkey_to_dbkey{key, relation, field_name, type},
key = $key
",
btmap! {
"key".to_owned() => DataValue::List(
extra_data
.keys()
.map(|s| DataValue::from(s.as_str()))
.collect::<Vec<_>>()
),
},
)?;
println!("Result: {result:?}");
}
tx.commit()?;
Ok(Json(json!({}))) Ok(Json(json!({})))
} }

View file

@ -0,0 +1,2 @@
#[derive(Default)]
pub struct QueryBuilder {}

View file

@ -0,0 +1,9 @@
[package]
name = "panorama-macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]

View file

@ -0,0 +1 @@
// TODO: derive named rows