AI generated program that reports project and user story status
Some checks failed
Monorepo build / build-flake (push) Has been cancelled

This commit is contained in:
Savanni D'Gerinel 2025-12-02 11:31:08 -05:00
parent c1d65862a6
commit c014c5eeba
6 changed files with 122 additions and 1 deletions

28
Cargo.lock generated
View File

@ -3118,6 +3118,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scripts"
version = "0.1.0"
dependencies = [
"serde",
"serde_yaml",
"walkdir",
]
[[package]]
name = "security-framework"
version = "2.11.1"
@ -3259,6 +3268,19 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -3927,6 +3949,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "url"
version = "2.5.7"

View File

@ -23,6 +23,7 @@ members = [
# "hex-grid",
# "icon-test",
"l10n-db",
"planning/scripts",
# "memorycache",
# "nom-training",
# "otg/core",

View File

@ -20,6 +20,10 @@ tasks:
cmds:
- cargo clippy
report:
cmds:
- cargo run --bin planning-report planning/
test-all:
cmds:
- which cargo
@ -31,4 +35,5 @@ tasks:
- cargo test -p l10n-db
- cargo test -p memorycache
- cd visions/server && task test
- cd visions/visions-core && task test
- cd visions/ui && task test

View File

@ -1,5 +1,5 @@
---
id: US0000
id: US0006
type: userstory
status: Done
priority: P0

View File

@ -0,0 +1,13 @@
[package]
name = "scripts"
version = "0.1.0"
edition = "2024"
[dependencies]
serde.workspace = true
serde_yaml = "0.9.34"
walkdir = "2.5.0"
[[bin]]
name = "planning-report"
path = "src/main.rs"

View File

@ -0,0 +1,74 @@
use serde::Deserialize;
use std::{
collections::HashMap,
ffi::OsStr,
fs,
path::{Component, Path},
};
#[derive(Debug, Deserialize)]
struct FrontMatter {
id: String,
#[serde(rename = "type")]
item_type: String,
status: String,
story: Option<String>, // Only for tasks
}
fn main() -> std::io::Result<()> {
let planning_dir = std::env::args().skip(1).next().unwrap();
let mut user_stories: HashMap<String, Vec<String>> = HashMap::new();
// Traverse the planning directory
for entry in walkdir::WalkDir::new(planning_dir) {
let entry = entry?;
if entry.file_type().is_file() && !is_template(entry.path()) && is_markdown(entry.path()) {
let content = fs::read_to_string(entry.path())?;
if let Some(frontmatter) = extract_frontmatter(&content) {
if frontmatter.item_type == "userstory" && frontmatter.status != "Done" {
user_stories.entry(frontmatter.id.clone()).or_insert(vec![]);
} else if frontmatter.item_type == "task" && frontmatter.status != "Done" {
if let Some(story_id) = frontmatter.story {
user_stories
.entry(story_id)
.or_insert(vec![])
.push(frontmatter.id);
}
}
}
}
}
// Generate the report
println!("Open User Stories Report:");
for (story, tasks) in &user_stories {
println!("\nUser Story: {}", story);
if tasks.is_empty() {
println!(" No open tasks.");
} else {
println!(" Open Tasks:");
for task in tasks {
println!(" - {}", task);
}
}
}
Ok(())
}
fn is_template(path: &Path) -> bool {
path.components()
.find(|component| *component == Component::Normal(OsStr::new("Templates")))
.is_some()
}
fn is_markdown(path: &Path) -> bool {
path.extension().and_then(|s| s.to_str()) == Some("md")
}
fn extract_frontmatter(content: &str) -> Option<FrontMatter> {
let start = content.find("---")?;
let end = content[start + 3..].find("---")? + start + 3;
let yaml = &content[start + 3..end];
serde_yaml::from_str(yaml).ok()
}