1use crate::error::Result;
7use crate::research::experiments::{Experiment, ExperimentResult};
8use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11use std::path::PathBuf;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Publication {
16 pub id: String,
18 pub title: String,
20 pub abstracttext: String,
22 pub authors: Vec<Author>,
24 pub publication_type: PublicationType,
26 pub venue: Option<Venue>,
28 pub status: PublicationStatus,
30 pub keywords: Vec<String>,
32 pub sections: Vec<ManuscriptSection>,
34 pub bibliography: Bibliography,
36 pub experiment_ids: Vec<String>,
38 pub submission_history: Vec<SubmissionRecord>,
40 pub reviews: Vec<Review>,
42 pub metadata: PublicationMetadata,
44 pub created_at: DateTime<Utc>,
46 pub modified_at: DateTime<Utc>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct Author {
53 pub name: String,
55 pub email: String,
57 pub affiliations: Vec<Affiliation>,
59 pub orcid: Option<String>,
61 pub position: AuthorPosition,
63 pub contributions: Vec<String>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct Affiliation {
70 pub institution: String,
72 pub department: Option<String>,
74 pub address: String,
76 pub country: String,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
82pub enum AuthorPosition {
83 First,
85 Corresponding,
87 Senior,
89 EqualContribution,
91 Regular,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
97pub enum PublicationType {
98 ConferencePaper,
100 JournalArticle,
102 WorkshopPaper,
104 TechnicalReport,
106 Preprint,
108 Thesis,
110 BookChapter,
112 Patent,
114 SoftwarePaper,
116 DatasetPaper,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct Venue {
123 pub name: String,
125 pub venue_type: VenueType,
127 pub abbreviation: Option<String>,
129 pub publisher: Option<String>,
131 pub impact_factor: Option<f64>,
133 pub h_index: Option<u32>,
135 pub acceptance_rate: Option<f64>,
137 pub ranking: Option<String>,
139 pub url: Option<String>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
145pub enum VenueType {
146 Conference,
148 Journal,
150 Workshop,
152 Symposium,
154 PreprintServer,
156 Repository,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
162pub enum PublicationStatus {
163 Draft,
165 ReadyForSubmission,
167 Submitted,
169 UnderReview,
171 RevisionRequested,
173 Accepted,
175 Published,
177 Rejected,
179 Withdrawn,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct ManuscriptSection {
186 pub title: String,
188 pub content: String,
190 pub order: usize,
192 pub section_type: SectionType,
194 pub word_count: usize,
196 pub figures: Vec<Figure>,
198 pub tables: Vec<Table>,
199 pub references: Vec<String>,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
205pub enum SectionType {
206 Abstract,
208 Introduction,
210 RelatedWork,
212 Methodology,
214 Experiments,
216 Results,
218 Discussion,
220 Conclusion,
222 Acknowledgments,
224 References,
226 Appendix,
228 Custom(String),
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct Figure {
235 pub caption: String,
237 pub file_path: PathBuf,
239 pub figure_type: FigureType,
241 pub number: usize,
243 pub width: Option<f64>,
245 pub height: Option<f64>,
247 pub experiment_id: Option<String>,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
253pub enum FigureType {
254 Plot,
256 Diagram,
258 Flowchart,
260 Architecture,
262 Screenshot,
264 Photo,
266 Other,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct Table {
273 pub caption: String,
275 pub data: Vec<Vec<String>>,
277 pub headers: Vec<String>,
279 pub number: usize,
281 pub experiment_id: Option<String>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct Bibliography {
288 pub entries: HashMap<String, BibTeXEntry>,
290 pub citation_style: CitationStyle,
292 pub file_path: Option<PathBuf>,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct BibTeXEntry {
299 pub key: String,
301 pub entry_type: String,
303 pub fields: HashMap<String, String>,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
309pub enum CitationStyle {
310 APA,
312 IEEE,
314 ACM,
316 Nature,
318 Science,
320 Chicago,
322 MLA,
324 Harvard,
326 Custom(String),
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct SubmissionRecord {
333 pub submitted_at: DateTime<Utc>,
335 pub venue: Venue,
337 pub submission_id: Option<String>,
339 pub status: SubmissionStatus,
341 pub decision_date: Option<DateTime<Utc>>,
343 pub decision: Option<Decision>,
345 pub editor_comments: Option<String>,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
351pub enum SubmissionStatus {
352 Submitted,
354 UnderReview,
356 Decided,
358 Withdrawn,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
364pub enum Decision {
365 Accept,
367 AcceptMinorRevisions,
369 AcceptMajorRevisions,
371 RejectAndResubmit,
373 Reject,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize)]
379pub struct Review {
380 pub id: String,
382 pub reviewer: ReviewerInfo,
384 pub overall_score: Option<f64>,
386 pub confidence_score: Option<f64>,
388 pub detailed_scores: HashMap<String, f64>,
390 pub reviewtext: String,
392 pub strengths: Vec<String>,
394 pub weaknesses: Vec<String>,
396 pub questions: Vec<String>,
398 pub recommendation: ReviewRecommendation,
400 pub submitted_at: DateTime<Utc>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct ReviewerInfo {
407 pub anonymous_id: String,
409 pub expertise_level: ExpertiseLevel,
411 pub research_areas: Vec<String>,
413}
414
415#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
417pub enum ExpertiseLevel {
418 Expert,
420 Knowledgeable,
422 SomeKnowledge,
424 LimitedKnowledge,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
430pub enum ReviewRecommendation {
431 StrongAccept,
433 Accept,
435 WeakAccept,
437 Borderline,
439 WeakReject,
441 Reject,
443 StrongReject,
445}
446
447#[derive(Debug, Clone, Serialize, Deserialize, Default)]
449pub struct PublicationMetadata {
450 pub doi: Option<String>,
452 pub arxiv_id: Option<String>,
454 pub pages: Option<String>,
456 pub volume: Option<String>,
458 pub issue: Option<String>,
460 pub year: Option<u32>,
462 pub month: Option<u32>,
464 pub isbn_issn: Option<String>,
466 pub license: Option<String>,
468 pub open_access: bool,
470}
471
472#[derive(Debug)]
474pub struct PublicationGenerator {
475 templates: HashMap<PublicationType, PublicationTemplate>,
477 default_citation_style: CitationStyle,
479 output_dir: PathBuf,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct PublicationTemplate {
486 pub name: String,
488 pub sections: Vec<SectionTemplate>,
490 pub formatting: FormattingOptions,
492 pub venue_constraints: VenueConstraints,
494}
495
496#[derive(Debug, Clone, Serialize, Deserialize)]
498pub struct SectionTemplate {
499 pub section_type: SectionType,
501 pub template: String,
503 pub required_fields: Vec<String>,
505 pub target_word_count: Option<usize>,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
511pub struct FormattingOptions {
512 pub format: DocumentFormat,
514 pub font_size: u32,
516 pub line_spacing: f64,
518 pub margins: Margins,
520 pub citation_format: CitationFormat,
522 pub figure_numbering: NumberingStyle,
524 pub table_numbering: NumberingStyle,
526}
527
528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
530pub enum DocumentFormat {
531 LaTeX,
533 Markdown,
535 HTML,
537 Word,
539 PDF,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct Margins {
546 pub top: f64,
548 pub bottom: f64,
550 pub left: f64,
552 pub right: f64,
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
558pub enum CitationFormat {
559 Numbered,
561 AuthorYear,
563 Superscript,
565 Footnote,
567}
568
569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
571pub enum NumberingStyle {
572 Arabic,
574 Roman,
576 Letters,
578 None,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
584pub struct VenueConstraints {
585 pub max_word_count: Option<usize>,
587 pub max_page_count: Option<usize>,
589 pub required_sections: Vec<SectionType>,
591 pub forbidden_sections: Vec<SectionType>,
593 pub max_figures: Option<usize>,
595 pub max_tables: Option<usize>,
597 pub max_references: Option<usize>,
599}
600
601impl Publication {
602 pub fn new(title: &str) -> Self {
604 let now = Utc::now();
605 Self {
606 id: uuid::Uuid::new_v4().to_string(),
607 title: title.to_string(),
608 abstracttext: String::new(),
609 authors: Vec::new(),
610 publication_type: PublicationType::ConferencePaper,
611 venue: None,
612 status: PublicationStatus::Draft,
613 keywords: Vec::new(),
614 sections: Vec::new(),
615 bibliography: Bibliography::new(),
616 experiment_ids: Vec::new(),
617 submission_history: Vec::new(),
618 reviews: Vec::new(),
619 metadata: PublicationMetadata::default(),
620 created_at: now,
621 modified_at: now,
622 }
623 }
624
625 pub fn abstracttext(mut self, abstracttext: &str) -> Self {
627 self.abstracttext = abstracttext.to_string();
628 self.modified_at = Utc::now();
629 self
630 }
631
632 pub fn add_author(mut self, author: Author) -> Self {
634 self.authors.push(author);
635 self.modified_at = Utc::now();
636 self
637 }
638
639 pub fn publication_type(mut self, pubtype: PublicationType) -> Self {
641 self.publication_type = pubtype;
642 self.modified_at = Utc::now();
643 self
644 }
645
646 pub fn venue(mut self, venue: Venue) -> Self {
648 self.venue = Some(venue);
649 self.modified_at = Utc::now();
650 self
651 }
652
653 pub fn keywords(mut self, keywords: Vec<String>) -> Self {
655 self.keywords = keywords;
656 self.modified_at = Utc::now();
657 self
658 }
659
660 pub fn add_experiment(&mut self, experiment_id: &str) {
662 self.experiment_ids.push(experiment_id.to_string());
663 self.modified_at = Utc::now();
664 }
665
666 pub fn add_section(&mut self, section: ManuscriptSection) {
668 self.sections.push(section);
669 self.modified_at = Utc::now();
670 }
671
672 pub fn generate_latex(&self) -> Result<String> {
674 let mut latex = String::new();
675
676 latex.push_str("\\documentclass[conference]{IEEEtran}\n");
678 latex.push_str("\\usepackage{graphicx}\n");
679 latex.push_str("\\usepackage{booktabs}\n");
680 latex.push_str("\\usepackage{amsmath}\n");
681 latex.push_str("\\usepackage{url}\n\n");
682
683 latex.push_str("\\begin{document}\n\n");
684
685 latex.push_str(&format!("\\title{{{}}}\n\n", self.title));
687
688 latex.push_str("\\author{\n");
689 for (i, author) in self.authors.iter().enumerate() {
690 if i > 0 {
691 latex.push_str("\\and\n");
692 }
693 latex.push_str(&format!("\\IEEEauthorblockN{{{}}}\n", author.name));
694 if !author.affiliations.is_empty() {
695 latex.push_str(&format!(
696 "\\IEEEauthorblockA{{{}}}\n",
697 author.affiliations[0].institution
698 ));
699 }
700 }
701 latex.push_str("}\n\n");
702
703 latex.push_str("\\maketitle\n\n");
704
705 if !self.abstracttext.is_empty() {
707 latex.push_str("\\begin{abstract}\n");
708 latex.push_str(&self.abstracttext);
709 latex.push_str("\n\\end{abstract}\n\n");
710 }
711
712 if !self.keywords.is_empty() {
714 latex.push_str("\\begin{IEEEkeywords}\n");
715 latex.push_str(&self.keywords.join(", "));
716 latex.push_str("\n\\end{IEEEkeywords}\n\n");
717 }
718
719 let mut sorted_sections = self.sections.clone();
721 sorted_sections.sort_by_key(|s| s.order);
722
723 for section in sorted_sections {
724 match section.section_type {
725 SectionType::Abstract => continue, SectionType::References => continue, _ => {
728 latex.push_str(&format!("\\section{{{}}}\n", section.title));
729 latex.push_str(§ion.content);
730 latex.push_str("\n\n");
731 }
732 }
733 }
734
735 if !self.bibliography.entries.is_empty() {
737 latex.push_str("\\begin{thebibliography}{99}\n");
738 for entry in self.bibliography.entries.values() {
739 latex.push_str(&self.format_bibtex_entry_latex(entry));
740 }
741 latex.push_str("\\end{thebibliography}\n\n");
742 }
743
744 latex.push_str("\\end{document}\n");
745
746 Ok(latex)
747 }
748
749 fn format_bibtex_entry_latex(&self, entry: &BibTeXEntry) -> String {
750 format!(
751 "\\bibitem{{{}}}\n{}\n\n",
752 entry.key,
753 self.format_bibtex_fields(&entry.fields)
754 )
755 }
756
757 fn format_bibtex_fields(&self, fields: &HashMap<String, String>) -> String {
758 let mut result = String::new();
759
760 if let Some(author) = fields.get("author") {
761 result.push_str(author);
762 }
763
764 if let Some(title) = fields.get("title") {
765 result.push_str(&format!(", ``{}'', ", title));
766 }
767
768 if let Some(journal) = fields.get("journal") {
769 result.push_str(&format!("\\emph{{{}}}, ", journal));
770 } else if let Some(booktitle) = fields.get("booktitle") {
771 result.push_str(&format!("in \\emph{{{}}}, ", booktitle));
772 }
773
774 if let Some(year) = fields.get("year") {
775 result.push_str(year);
776 }
777
778 result
779 }
780
781 pub fn generate_markdown(&self) -> Result<String> {
783 let mut markdown = String::new();
784
785 markdown.push_str(&format!("# {}\n\n", self.title));
787
788 if !self.authors.is_empty() {
790 markdown.push_str("**Authors**: ");
791 let author_names: Vec<String> = self.authors.iter().map(|a| a.name.clone()).collect();
792 markdown.push_str(&author_names.join(", "));
793 markdown.push_str("\n\n");
794 }
795
796 if !self.abstracttext.is_empty() {
798 markdown.push_str("## Abstract\n\n");
799 markdown.push_str(&self.abstracttext);
800 markdown.push_str("\n\n");
801 }
802
803 if !self.keywords.is_empty() {
805 markdown.push_str("**Keywords**: ");
806 markdown.push_str(&self.keywords.join(", "));
807 markdown.push_str("\n\n");
808 }
809
810 let mut sorted_sections = self.sections.clone();
812 sorted_sections.sort_by_key(|s| s.order);
813
814 for section in sorted_sections {
815 match section.section_type {
816 SectionType::Abstract => continue, _ => {
818 markdown.push_str(&format!("## {}\n\n", section.title));
819 markdown.push_str(§ion.content);
820 markdown.push_str("\n\n");
821 }
822 }
823 }
824
825 if !self.bibliography.entries.is_empty() {
827 markdown.push_str("## References\n\n");
828 for (i, entry) in self.bibliography.entries.values().enumerate() {
829 markdown.push_str(&format!(
830 "{}. {}\n",
831 i + 1,
832 self.format_bibtex_entry_markdown(entry)
833 ));
834 }
835 }
836
837 Ok(markdown)
838 }
839
840 fn format_bibtex_entry_markdown(&self, entry: &BibTeXEntry) -> String {
841 let mut result = String::new();
842
843 if let Some(author) = entry.fields.get("author") {
844 result.push_str(author);
845 }
846
847 if let Some(title) = entry.fields.get("title") {
848 result.push_str(&format!(". \"{}\". ", title));
849 }
850
851 if let Some(journal) = entry.fields.get("journal") {
852 result.push_str(&format!("*{}*. ", journal));
853 } else if let Some(booktitle) = entry.fields.get("booktitle") {
854 result.push_str(&format!("In *{}*. ", booktitle));
855 }
856
857 if let Some(year) = entry.fields.get("year") {
858 result.push_str(year);
859 }
860
861 result
862 }
863
864 pub fn submission_statistics(&self) -> SubmissionStatistics {
866 let total_submissions = self.submission_history.len();
867 let accepted = self
868 .submission_history
869 .iter()
870 .filter(|s| {
871 matches!(
872 s.decision,
873 Some(Decision::Accept)
874 | Some(Decision::AcceptMinorRevisions)
875 | Some(Decision::AcceptMajorRevisions)
876 )
877 })
878 .count();
879 let rejected = self
880 .submission_history
881 .iter()
882 .filter(|s| matches!(s.decision, Some(Decision::Reject)))
883 .count();
884
885 let avg_review_time = if !self.submission_history.is_empty() {
886 let total_days: i64 = self
887 .submission_history
888 .iter()
889 .filter_map(|s| {
890 s.decision_date
891 .map(|decision_date| (decision_date - s.submitted_at).num_days())
892 })
893 .sum();
894 total_days as f64 / self.submission_history.len() as f64
895 } else {
896 0.0
897 };
898
899 SubmissionStatistics {
900 total_submissions,
901 accepted,
902 rejected,
903 pending: total_submissions - accepted - rejected,
904 acceptance_rate: if total_submissions > 0 {
905 accepted as f64 / total_submissions as f64
906 } else {
907 0.0
908 },
909 avg_review_time_days: avg_review_time,
910 }
911 }
912}
913
914#[derive(Debug, Clone, Serialize, Deserialize)]
916pub struct SubmissionStatistics {
917 pub total_submissions: usize,
919 pub accepted: usize,
921 pub rejected: usize,
923 pub pending: usize,
925 pub acceptance_rate: f64,
927 pub avg_review_time_days: f64,
929}
930
931impl Default for Bibliography {
932 fn default() -> Self {
933 Self::new()
934 }
935}
936
937impl Bibliography {
938 pub fn new() -> Self {
940 Self {
941 entries: HashMap::new(),
942 citation_style: CitationStyle::IEEE,
943 file_path: None,
944 }
945 }
946
947 pub fn add_entry(&mut self, entry: BibTeXEntry) {
949 self.entries.insert(entry.key.clone(), entry);
950 }
951
952 pub fn load_bibtex_file(&mut self, filepath: &PathBuf) -> Result<()> {
954 let content = std::fs::read_to_string(filepath)?;
955 self.parse_bibtex(&content)?;
956 self.file_path = Some(filepath.clone());
957 Ok(())
958 }
959
960 pub fn parse_bibtex(&mut self, content: &str) -> Result<()> {
962 let lines: Vec<&str> = content.lines().collect();
965 let mut current_entry: Option<BibTeXEntry> = None;
966
967 for line in lines {
968 let line = line.trim();
969 if line.starts_with('@') {
970 if let Some(entry) = current_entry.take() {
972 self.entries.insert(entry.key.clone(), entry);
973 }
974
975 if let Some(pos) = line.find('{') {
977 let entry_type = line[1..pos].to_lowercase();
978 let key_part = &line[pos + 1..];
979 if let Some(comma_pos) = key_part.find(',') {
980 let key = key_part[..comma_pos].trim().to_string();
981 current_entry = Some(BibTeXEntry {
982 key,
983 entry_type,
984 fields: HashMap::new(),
985 });
986 }
987 }
988 } else if line.contains('=') && current_entry.is_some() {
989 if let Some(eq_pos) = line.find('=') {
991 let field_name = line[..eq_pos].trim().to_lowercase();
992 let field_value = line[eq_pos + 1..]
993 .trim()
994 .trim_start_matches('{')
995 .trim_end_matches("},")
996 .trim_start_matches('"')
997 .trim_end_matches("\",")
998 .to_string();
999
1000 if let Some(ref mut entry) = current_entry {
1001 entry.fields.insert(field_name, field_value);
1002 }
1003 }
1004 }
1005 }
1006
1007 if let Some(entry) = current_entry {
1009 self.entries.insert(entry.key.clone(), entry);
1010 }
1011
1012 Ok(())
1013 }
1014}
1015
1016impl PublicationGenerator {
1017 pub fn new(_outputdir: PathBuf) -> Self {
1019 Self {
1020 templates: HashMap::new(),
1021 default_citation_style: CitationStyle::IEEE,
1022 output_dir: _outputdir,
1023 }
1024 }
1025
1026 pub fn generate_from_experiments(
1028 &self,
1029 experiments: &[Experiment],
1030 template: &PublicationTemplate,
1031 ) -> Result<Publication> {
1032 let mut publication = Publication::new("Generated Publication");
1033
1034 let abstracttext = self.generate_abstract(experiments)?;
1036 publication.abstracttext = abstracttext;
1037
1038 for section_template in &template.sections {
1040 let section = self.generate_section(section_template, experiments)?;
1041 publication.add_section(section);
1042 }
1043
1044 Ok(publication)
1045 }
1046
1047 fn generate_abstract(&self, experiments: &[Experiment]) -> Result<String> {
1048 let mut abstracttext = String::new();
1050
1051 abstracttext.push_str(
1052 "This paper presents experimental results comparing various optimization algorithms. ",
1053 );
1054
1055 if !experiments.is_empty() {
1056 abstracttext.push_str(&format!(
1057 "We conducted {} experiments evaluating the performance of different optimizers. ",
1058 experiments.len()
1059 ));
1060 }
1061
1062 abstracttext.push_str("Our results demonstrate significant differences in convergence behavior and final performance across different optimization methods.");
1063
1064 Ok(abstracttext)
1065 }
1066
1067 fn generate_section(
1068 &self,
1069 template: &SectionTemplate,
1070 experiments: &[Experiment],
1071 ) -> Result<ManuscriptSection> {
1072 let content = match template.section_type {
1073 SectionType::Introduction => self.generate_introduction(experiments)?,
1074 SectionType::Methodology => self.generate_methodology(experiments)?,
1075 SectionType::Experiments => self.generate_experiments_section(experiments)?,
1076 SectionType::Results => self.generate_results(experiments)?,
1077 SectionType::Conclusion => self.generate_conclusion(experiments)?,
1078 _ => template.template.clone(),
1079 };
1080
1081 Ok(ManuscriptSection {
1082 title: match template.section_type {
1083 SectionType::Abstract => "Abstract".to_string(),
1084 SectionType::Introduction => "Introduction".to_string(),
1085 SectionType::RelatedWork => "Related Work".to_string(),
1086 SectionType::Methodology => "Methodology".to_string(),
1087 SectionType::Experiments => "Experiments".to_string(),
1088 SectionType::Results => "Results".to_string(),
1089 SectionType::Discussion => "Discussion".to_string(),
1090 SectionType::Conclusion => "Conclusion".to_string(),
1091 SectionType::Acknowledgments => "Acknowledgments".to_string(),
1092 SectionType::References => "References".to_string(),
1093 SectionType::Appendix => "Appendix".to_string(),
1094 SectionType::Custom(ref name) => name.clone(),
1095 },
1096 content,
1097 order: 0, section_type: template.section_type.clone(),
1099 word_count: 0, figures: Vec::new(),
1101 tables: Vec::new(),
1102 references: Vec::new(),
1103 })
1104 }
1105
1106 fn generate_introduction(&self, experiments: &[Experiment]) -> Result<String> {
1107 Ok("This section introduces the research problem and motivation for comparing optimization algorithms.".to_string())
1108 }
1109
1110 fn generate_methodology(&self, experiments: &[Experiment]) -> Result<String> {
1111 let mut content = String::new();
1112 content.push_str("We evaluate the following optimization algorithms:\n\n");
1113
1114 for experiment in experiments {
1115 for optimizer_name in experiment.optimizer_configs.keys() {
1116 content.push_str(&format!("- {}\n", optimizer_name));
1117 }
1118 }
1119
1120 Ok(content)
1121 }
1122
1123 fn generate_experiments_section(&self, experiments: &[Experiment]) -> Result<String> {
1124 let mut content = String::new();
1125 content.push_str("We conducted the following experiments:\n\n");
1126
1127 for experiment in experiments {
1128 content.push_str(&format!(
1129 "**{}**: {}\n\n",
1130 experiment.name, experiment.description
1131 ));
1132 }
1133
1134 Ok(content)
1135 }
1136
1137 fn generate_results(&self, experiments: &[Experiment]) -> Result<String> {
1138 let mut content = String::new();
1139 content.push_str("The experimental results are summarized below:\n\n");
1140
1141 for experiment in experiments {
1142 if !experiment.results.is_empty() {
1143 content.push_str(&format!("### {}\n\n", experiment.name));
1144 content.push_str(&format!("Number of runs: {}\n\n", experiment.results.len()));
1145 }
1146 }
1147
1148 Ok(content)
1149 }
1150
1151 fn generate_conclusion(&self, experiments: &[Experiment]) -> Result<String> {
1152 Ok("This section summarizes the key findings and implications of the experimental results.".to_string())
1153 }
1154}
1155
1156#[cfg(test)]
1157mod tests {
1158 use super::*;
1159
1160 #[test]
1161 fn test_publication_creation() {
1162 let publication = Publication::new("Test Publication")
1163 .abstracttext("Test abstract")
1164 .publication_type(PublicationType::ConferencePaper)
1165 .keywords(vec![
1166 "optimization".to_string(),
1167 "machine learning".to_string(),
1168 ]);
1169
1170 assert_eq!(publication.title, "Test Publication");
1171 assert_eq!(publication.abstracttext, "Test abstract");
1172 assert_eq!(
1173 publication.publication_type,
1174 PublicationType::ConferencePaper
1175 );
1176 assert_eq!(publication.keywords.len(), 2);
1177 }
1178
1179 #[test]
1180 fn test_bibliography() {
1181 let mut bibliography = Bibliography::new();
1182
1183 let entry = BibTeXEntry {
1184 key: "test2023".to_string(),
1185 entry_type: "article".to_string(),
1186 fields: {
1187 let mut fields = HashMap::new();
1188 fields.insert("author".to_string(), "Test Author".to_string());
1189 fields.insert("title".to_string(), "Test Title".to_string());
1190 fields.insert("year".to_string(), "2023".to_string());
1191 fields
1192 },
1193 };
1194
1195 bibliography.add_entry(entry);
1196 assert_eq!(bibliography.entries.len(), 1);
1197 assert!(bibliography.entries.contains_key("test2023"));
1198 }
1199
1200 #[test]
1201 fn test_markdown_generation() {
1202 let mut publication = Publication::new("Test Paper");
1203 publication.abstracttext = "This is a test abstract.".to_string();
1204 publication.keywords = vec!["test".to_string(), "paper".to_string()];
1205
1206 let markdown = publication.generate_markdown().unwrap();
1207 assert!(markdown.contains("# Test Paper"));
1208 assert!(markdown.contains("## Abstract"));
1209 assert!(markdown.contains("This is a test abstract."));
1210 assert!(markdown.contains("**Keywords**: test, paper"));
1211 }
1212}