batuta/recipes/
research_artifact.rs1use crate::experiment::{
4 CitationMetadata, CitationType, CreditRole, ResearchArtifact, ResearchContributor,
5};
6use crate::recipes::RecipeResult;
7
8#[derive(Debug)]
10pub struct ResearchArtifactRecipe {
11 artifact: ResearchArtifact,
12}
13
14impl ResearchArtifactRecipe {
15 pub fn new(title: impl Into<String>, abstract_text: impl Into<String>) -> Self {
17 Self {
18 artifact: ResearchArtifact {
19 title: title.into(),
20 abstract_text: abstract_text.into(),
21 contributors: Vec::new(),
22 keywords: Vec::new(),
23 doi: None,
24 arxiv_id: None,
25 license: "MIT".to_string(),
26 created_at: chrono::Utc::now().to_rfc3339(),
27 datasets: Vec::new(),
28 code_repositories: Vec::new(),
29 pre_registration: None,
30 },
31 }
32 }
33
34 pub fn add_contributor(
36 &mut self,
37 name: impl Into<String>,
38 affiliation: impl Into<String>,
39 roles: Vec<CreditRole>,
40 ) {
41 self.artifact.contributors.push(ResearchContributor {
42 name: name.into(),
43 orcid: None,
44 affiliation: affiliation.into(),
45 roles,
46 email: None,
47 });
48 }
49
50 pub fn add_keywords(&mut self, keywords: Vec<String>) {
52 self.artifact.keywords.extend(keywords);
53 }
54
55 pub fn set_doi(&mut self, doi: impl Into<String>) {
57 self.artifact.doi = Some(doi.into());
58 }
59
60 pub fn add_dataset(&mut self, dataset: impl Into<String>) {
62 self.artifact.datasets.push(dataset.into());
63 }
64
65 pub fn add_repository(&mut self, repo: impl Into<String>) {
67 self.artifact.code_repositories.push(repo.into());
68 }
69
70 pub fn generate_citation(&self) -> CitationMetadata {
72 let authors: Vec<String> =
73 self.artifact.contributors.iter().map(|c| c.name.clone()).collect();
74
75 let now = chrono::Utc::now();
76
77 CitationMetadata {
78 citation_type: CitationType::Software,
79 title: self.artifact.title.clone(),
80 authors,
81 year: now.format("%Y").to_string().parse().unwrap_or(2024),
82 month: Some(now.format("%m").to_string().parse().unwrap_or(1)),
83 doi: self.artifact.doi.clone(),
84 url: self.artifact.code_repositories.first().cloned(),
85 venue: None,
86 volume: None,
87 pages: None,
88 publisher: None,
89 version: Some("1.0.0".to_string()),
90 }
91 }
92
93 pub fn build(&self) -> RecipeResult {
95 let mut result = RecipeResult::success("research-artifact");
96 result = result.with_metric("contributor_count", self.artifact.contributors.len() as f64);
97 result = result.with_metric("keyword_count", self.artifact.keywords.len() as f64);
98 result = result.with_metric("dataset_count", self.artifact.datasets.len() as f64);
99
100 if self.artifact.doi.is_some() {
101 result = result.with_artifact("DOI registered");
102 }
103
104 let citation = self.generate_citation();
105 result = result.with_artifact(format!("BibTeX: {}", citation.to_bibtex("artifact")));
106
107 result
108 }
109
110 pub fn artifact(&self) -> &ResearchArtifact {
112 &self.artifact
113 }
114}