view.rs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. use std::{fmt::Write, str::FromStr};
  2. use prettytable::{
  3. cell,
  4. format::{consts::FORMAT_NO_COLSEP, FormatBuilder, LinePosition, LineSeparator},
  5. row, table, Cell, Row, Table,
  6. };
  7. use darkfi::{
  8. util::time::{timestamp_to_date, DateFormat},
  9. Result,
  10. };
  11. use textwrap::fill;
  12. use crate::{
  13. filter::apply_filter,
  14. primitives::{Comment, State, TaskInfo},
  15. TaskEvent,
  16. };
  17. pub fn print_task_list(tasks: Vec<TaskInfo>, ws: String, filters: Vec<String>) -> Result<()> {
  18. let mut tasks = tasks;
  19. let mut table = Table::new();
  20. table.set_format(
  21. FormatBuilder::new()
  22. .padding(1, 1)
  23. .separators(&[LinePosition::Title], LineSeparator::new('-', ' ', ' ', ' '))
  24. .build(),
  25. );
  26. table.set_titles(row!["ID", "Title", "Project", "Assigned", "Due", "Rank"]);
  27. for filter in filters {
  28. apply_filter(&mut tasks, &filter);
  29. }
  30. tasks.sort_by(|a, b| b.rank.partial_cmp(&a.rank).unwrap());
  31. let mut min_rank = 0.0;
  32. let mut max_rank = 0.0;
  33. if let Some(first) = tasks.first() {
  34. max_rank = first.rank;
  35. }
  36. if let Some(last) = tasks.last() {
  37. min_rank = last.rank;
  38. }
  39. for task in tasks {
  40. let state = State::from_str(&task.state.clone())?;
  41. let (max_style, min_style, mid_style, gen_style) = if state.is_start() {
  42. ("bFg", "Fc", "Fg", "Fg")
  43. } else if state.is_pause() {
  44. ("iFYBd", "iFYBd", "iFYBd", "iFYBd")
  45. } else {
  46. ("", "", "", "")
  47. };
  48. let rank = task.rank.to_string();
  49. table.add_row(Row::new(vec![
  50. Cell::new(&task.id.to_string()).style_spec(gen_style),
  51. Cell::new(&task.title).style_spec(gen_style),
  52. Cell::new(&task.project.join(", ")).style_spec(gen_style),
  53. Cell::new(&task.assign.join(", ")).style_spec(gen_style),
  54. Cell::new(&timestamp_to_date(task.due.unwrap_or(0), DateFormat::Date))
  55. .style_spec(gen_style),
  56. if task.rank == max_rank {
  57. Cell::new(&rank).style_spec(max_style)
  58. } else if task.rank == min_rank {
  59. Cell::new(&rank).style_spec(min_style)
  60. } else {
  61. Cell::new(&rank).style_spec(mid_style)
  62. },
  63. ]));
  64. }
  65. let workspace = format!("Workspace: {}", ws);
  66. let mut ws_table = table!([workspace]);
  67. ws_table.set_format(
  68. FormatBuilder::new()
  69. .padding(1, 1)
  70. .separators(&[LinePosition::Bottom], LineSeparator::new('-', ' ', ' ', ' '))
  71. .build(),
  72. );
  73. ws_table.printstd();
  74. table.printstd();
  75. Ok(())
  76. }
  77. pub fn print_task_info(taskinfo: TaskInfo) -> Result<()> {
  78. let due = timestamp_to_date(taskinfo.due.unwrap_or(0), DateFormat::Date);
  79. let created_at = timestamp_to_date(taskinfo.created_at, DateFormat::DateTime);
  80. let mut table = table!(
  81. [Bd => "ref_id", &taskinfo.ref_id],
  82. ["workspace", &taskinfo.workspace],
  83. [Bd =>"id", &taskinfo.id.to_string()],
  84. ["owner", &taskinfo.owner],
  85. [Bd =>"title", &taskinfo.title],
  86. ["desc", &taskinfo.desc.to_string()],
  87. [Bd =>"assign", taskinfo.assign.join(", ")],
  88. ["project", taskinfo.project.join(", ")],
  89. [Bd =>"due", due],
  90. ["rank", &taskinfo.rank.to_string()],
  91. [Bd =>"created_at", created_at],
  92. ["current_state", &taskinfo.state]);
  93. table.set_format(
  94. FormatBuilder::new()
  95. .padding(1, 1)
  96. .separators(&[LinePosition::Title], LineSeparator::new('-', ' ', ' ', ' '))
  97. .build(),
  98. );
  99. table.set_titles(row!["Name", "Value"]);
  100. table.printstd();
  101. let (events, timestamps) = &events_as_string(taskinfo.events);
  102. let mut event_table = table!([events, timestamps]);
  103. event_table.set_format(*FORMAT_NO_COLSEP);
  104. event_table.printstd();
  105. Ok(())
  106. }
  107. pub fn comments_as_string(comments: Vec<Comment>) -> String {
  108. let mut comments_str = String::new();
  109. for comment in comments {
  110. writeln!(comments_str, "{}", comment).unwrap();
  111. }
  112. comments_str.pop();
  113. comments_str
  114. }
  115. pub fn events_as_string(events: Vec<TaskEvent>) -> (String, String) {
  116. let mut events_str = String::new();
  117. let mut timestamps_str = String::new();
  118. let long_comment_width = 50;
  119. for event in events {
  120. writeln!(timestamps_str, "{}", event.timestamp).unwrap();
  121. match event.action.as_str() {
  122. "state" => {
  123. writeln!(events_str, "- {} changed state to {}", event.author, event.content)
  124. .unwrap();
  125. }
  126. "assign" => {
  127. writeln!(events_str, "- {} assigned {}", event.author, event.content).unwrap();
  128. }
  129. "comment" => {
  130. // wrap long comments
  131. let ev_content = fill(
  132. &event.content,
  133. textwrap::Options::new(long_comment_width).subsequent_indent(" "),
  134. );
  135. // skip wrapped lines to align timestamp with the first line
  136. for _ in 1..ev_content.lines().collect::<Vec<&str>>().len() {
  137. writeln!(timestamps_str, " ").unwrap();
  138. }
  139. writeln!(events_str, "- {} made a comment: {}", event.author, ev_content).unwrap();
  140. }
  141. _ => {}
  142. }
  143. }
  144. (events_str, timestamps_str)
  145. }