plansheet 0.30.1 → 0.34.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19345ed604fc0cbbde2039faf1ffb9b11906c15d7810ad32706beb8ce3ff3fce
4
- data.tar.gz: 4c155173ebb564eeed831258435ee2fae898f859027c453ca2bc66076713e5c1
3
+ metadata.gz: 0127f89bb4b286c6bda0d5ff1d1c8e02d4ceb26f0898f78f2031ad7d69913bf1
4
+ data.tar.gz: 0ce4e76d7693d51bfdd96cd6390124955858c15b5fe109f03d11d4aa707bdf98
5
5
  SHA512:
6
- metadata.gz: 6c48221ba589561d204608a4bab90a1f8c05f19ee5e264835f2a663782b1a51f9ed9ac9e43562228ac055c563c443645f4a4488a381063d184031fff1b42348b
7
- data.tar.gz: c1aa45f17d310b9a9b7dbf06ff1d2bc5e488b44ab87fcbb990c54edd14ca1f0e7e5538c4e4c86eabb7887aba02f88e55d8c1b5112b2c87069ac283c30c155206
6
+ metadata.gz: 7d694e749bff3c2a4b7f6983f75ecc6861eb8a793752658439bc0e2a3381c0037aa09edad3ab4f33fc8365992ee21ab12bdc450eb6283a4e1fa8b2f7250078a3
7
+ data.tar.gz: 81e8c85c1914e225b2bca1fe8655e367b1d36a830ea0905d0bf449fa4c3ced79b023c92ecd9d655cf09a5843ec9be78de67a63e5efdaa341e843999dec736eb6
data/.rubocop.yml CHANGED
@@ -1,5 +1,2 @@
1
1
  inherit_gem:
2
- dc-rubocop: default.yml
3
-
4
- AllCops:
5
- TargetRubyVersion: 2.6
2
+ dc-rubocop: ruby26.yml
data/Gemfile CHANGED
@@ -2,10 +2,9 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- # Specify your gem's dependencies in plansheet.gemspec
6
5
  gemspec
7
6
 
8
7
  group :development, optional: true do
9
- gem "dc-devtools"
8
+ gem "dc-devtools", "~> 0.5"
10
9
  gem "rdoc"
11
10
  end
data/Rakefile CHANGED
@@ -9,8 +9,6 @@ Rake::TestTask.new(:test) do |t|
9
9
  t.test_files = FileList["test/**/test_*.rb"]
10
10
  end
11
11
 
12
- require "rubocop/rake_task"
13
-
14
- RuboCop::RakeTask.new
12
+ require "dc_rake"
15
13
 
16
14
  task default: %i[test rubocop]
data/exe/plansheet CHANGED
@@ -4,11 +4,14 @@
4
4
  require "plansheet"
5
5
  require "optparse"
6
6
 
7
+ options = {}
7
8
  parser = OptionParser.new
8
9
  parser.on(
9
- "--sheet",
10
+ "--sheet [TYPE]",
10
11
  "Generates MD/LaTeX project PDF"
11
- )
12
+ ) do |sheet_type|
13
+ options[:sheet] = sheet_type || "projects"
14
+ end
12
15
  parser.on(
13
16
  "--sort",
14
17
  "Sort project files"
@@ -29,6 +32,10 @@ parser.on(
29
32
  "--time-roi",
30
33
  "Show projects with a time return-on-investment"
31
34
  )
35
+ parser.on(
36
+ "--needs-thinking",
37
+ "Show projects that need tasks, time estimates, etc (WIP)"
38
+ )
32
39
  parser.on(
33
40
  "--calendar",
34
41
  "List of projects ordered by due date"
@@ -37,7 +44,6 @@ parser.on(
37
44
  "--location_filter LOCATION",
38
45
  "location filter for CLI dump (WIP)"
39
46
  )
40
- options = {}
41
47
  parser.parse!(into: options)
42
48
 
43
49
  config = Plansheet.load_config
@@ -45,9 +51,21 @@ pool = Plansheet::Pool.new({ projects_dir: config["projects_dir"],
45
51
  sort_order: config["sort_order"] })
46
52
 
47
53
  if options[:sheet] || options.empty?
54
+ # TODO: Add pdflatex command (customizable path)
48
55
  require "plansheet/sheet"
49
56
  FileUtils.mkdir_p config["output_dir"]
50
- Plansheet::LaTeXSheet.new("#{config["output_dir"]}/projects.tex", pool.projects)
57
+ case options[:sheet]
58
+ when "daily"
59
+ Plansheet::DailyLaTeXSheet.new("#{config["output_dir"]}/daily.tex", config["daily"])
60
+ when "weekly"
61
+ Plansheet::WeeklyLaTeXSheet.new("#{config["output_dir"]}/weekly.tex", pool.projects, config["weekly"])
62
+ when "monthly"
63
+ Plansheet::MonthlyLaTeXSheet.new("#{config["output_dir"]}/monthly.tex", config["monthly"])
64
+ else
65
+ project_arr = pool.projects
66
+ project_arr.delete_if { |x| %w[dropped done paused].include? x.status }
67
+ Plansheet::LaTeXSheet.new("#{config["output_dir"]}/projects.tex", project_arr)
68
+ end
51
69
  elsif options[:irb]
52
70
  binding.irb # rubocop:disable Lint/Debugger
53
71
  elsif options[:stats]
@@ -68,6 +86,15 @@ elsif options[:"time-roi"]
68
86
  puts "time ROI payoff: #{proj.time_roi_payoff}"
69
87
  puts "\n"
70
88
  end
89
+ elsif options[:"needs-thinking"]
90
+ puts "Projects with no tasks"
91
+ project_arr = pool.projects.sort
92
+ project_arr.delete_if(&:dropped_or_done?)
93
+ project_arr.delete_if(&:tasks)
94
+ project_arr.each do |proj|
95
+ puts proj
96
+ puts "\n"
97
+ end
71
98
  elsif options[:calendar]
72
99
  # TODO: add a project filter method
73
100
  project_arr = pool.projects
@@ -140,6 +140,16 @@ module Plansheet
140
140
  type: seq
141
141
  sequence:
142
142
  - type: str
143
+ "setup_tasks":
144
+ desc: List of tasks needed before main project work begins
145
+ type: seq
146
+ sequence:
147
+ - type: str
148
+ "cleanup_tasks":
149
+ desc: List of tasks needed before main project work begins
150
+ type: seq
151
+ sequence:
152
+ - type: str
143
153
  "done":
144
154
  desc: List of tasks which have been completed
145
155
  type: seq
@@ -6,14 +6,17 @@ require_relative "project/yaml"
6
6
  require_relative "project/stringify"
7
7
  require_relative "./time"
8
8
 
9
- # Needed for Project#time_estimate, would be much happier *not* patching Array
10
- class Array
11
- def nil_if_empty
12
- count.zero? ? nil : self
9
+ module Plansheet
10
+ module PlansheetArray
11
+ # Needed for Project#time_estimate
12
+ refine Array do
13
+ def nil_if_empty
14
+ count.zero? ? nil : self
15
+ end
16
+ end
13
17
  end
14
- end
18
+ using PlansheetArray
15
19
 
16
- module Plansheet
17
20
  PROJECT_STATUS_PRIORITY = {
18
21
  "wip" => 1,
19
22
  "ready" => 2,
@@ -58,7 +61,7 @@ module Plansheet
58
61
  day_of_week frequency last_for lead_time].freeze
59
62
  DATE_PROPERTIES = %w[due defer paused_on dropped_on completed_on created_on starts_on last_done
60
63
  last_reviewed].freeze
61
- ARRAY_PROPERTIES = %w[dependencies externals urls tasks done tags].freeze
64
+ ARRAY_PROPERTIES = %w[dependencies externals urls tasks setup_tasks cleanup_tasks done tags].freeze
62
65
 
63
66
  ALL_PROPERTIES = STRING_PROPERTIES + DATE_PROPERTIES + ARRAY_PROPERTIES
64
67
 
@@ -121,11 +124,13 @@ module Plansheet
121
124
  end
122
125
 
123
126
  if done?
124
- @completed_on ||= Date.today unless recurring?
125
127
  remove_instance_variable("@status") if @status
126
- remove_instance_variable("@time_estimate") if @time_estimate
127
- remove_instance_variable("@time_estimate_minutes") if @time_estimate
128
- remove_instance_variable("@time_roi_payoff") if @time_roi_payoff
128
+ unless recurring?
129
+ @completed_on ||= Date.today
130
+ remove_instance_variable("@time_estimate") if @time_estimate
131
+ remove_instance_variable("@time_estimate_minutes") if @time_estimate
132
+ remove_instance_variable("@time_roi_payoff") if @time_roi_payoff
133
+ end
129
134
  elsif paused?
130
135
  @paused_on ||= Date.today
131
136
  remove_instance_variable("@status") if @status
@@ -332,16 +337,10 @@ module Plansheet
332
337
  !@frequency.nil? || !@day_of_week.nil? || !@last_done.nil? || !@last_for.nil?
333
338
  end
334
339
 
335
- def paused?
336
- status == "paused"
337
- end
338
-
339
- def dropped?
340
- status == "dropped"
341
- end
342
-
343
- def done?
344
- status == "done"
340
+ PROJECT_STATUS_PRIORITY.each_key do |stat|
341
+ define_method("#{stat}?".to_sym) do
342
+ status == stat
343
+ end
345
344
  end
346
345
 
347
346
  def dropped_or_done?
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: false
2
+
3
+ require "date"
4
+ module Plansheet
5
+ class DailyLaTeXSheet
6
+ include Plansheet::LaTeXMixins
7
+ def initialize(output_file, config)
8
+ @config = config
9
+ projects_str = sheet_header
10
+ projects_str << document do
11
+ sheet_body
12
+ end
13
+
14
+ puts "Writing to #{output_file}"
15
+ File.write(output_file, projects_str)
16
+ end
17
+
18
+ def dateline
19
+ <<~DATELINE
20
+ \\thispagestyle{empty}
21
+
22
+ Date \\(\\underline{\\hspace{4cm}}\\)
23
+
24
+ DATELINE
25
+ end
26
+
27
+ INCOLUMN_SPACING = "7mm".freeze
28
+ def sheet_body
29
+ [
30
+ dateline,
31
+ vspace("1cm"),
32
+ vbox do
33
+ @config["top"].map do |section|
34
+ minipage("8cm") do
35
+ section_output(section)
36
+ end
37
+ end.join
38
+ end,
39
+
40
+ %w[left_bar right_bar].map do |col|
41
+ minipage("8cm") do
42
+ @config[col].map do |section|
43
+ minipage("\\textwidth") do
44
+ section_output(section)
45
+ end
46
+ end.join(vspace(INCOLUMN_SPACING))
47
+ end
48
+ end
49
+ ].flatten.join
50
+ end
51
+
52
+ def section_output(section)
53
+ case section["type"]
54
+ when "checkboxes"
55
+ checkboxes(section["items"])
56
+ when "checkbox_and_lines"
57
+ checkbox_and_lines(section["items"])
58
+ when "multiline_checkbox_and_lines"
59
+ multiline_checkbox_and_lines(section["items"])
60
+ end
61
+ end
62
+
63
+ def checkboxes(items)
64
+ items.map do |l|
65
+ l.map do |i|
66
+ checkbox_item i
67
+ end.join(" ")
68
+ end.join(HARD_NL)
69
+ end
70
+
71
+ def checkbox_and_lines(items)
72
+ items.map do |i|
73
+ checkbox_item(i, line: "4cm")
74
+ end.join(HARD_NL)
75
+ end
76
+
77
+ def multiline_checkbox_and_lines(items)
78
+ items.map do |q|
79
+ "#{checkbox_item q} #{HARD_NL}#{writein_line("\\textwidth")}"
80
+ end.join(HARD_NL)
81
+ end
82
+ end
83
+ end
@@ -2,97 +2,39 @@
2
2
 
3
3
  require "date"
4
4
  module Plansheet
5
- # The Sheet class constructs a Markdown/LaTeX file for use with pandoc
5
+ # The Sheet class constructs a Markdown/LaTeX file for use with pdflatex
6
6
  class LaTeXSheet
7
+ include LaTeXMixins
7
8
  def initialize(output_file, project_arr)
8
- projects_str = String.new
9
- projects_str << sheet_header
10
-
11
- project_arr.each do |p|
12
- projects_str << project_minipage(p)
9
+ projects_str = sheet_header
10
+ projects_str << document do
11
+ "\\thispagestyle{empty}\n\n\\section{Date: #{Date.today}}\n".concat(
12
+ project_arr.map do |p|
13
+ project_minipage(p)
14
+ end.join
15
+ )
13
16
  end
14
17
  puts "Writing to #{output_file}"
15
- projects_str << sheet_footer
16
18
  File.write(output_file, projects_str)
17
19
  end
18
20
 
19
- def sheet_header
20
- # LaTeX used to be generated by pandoc
21
- <<~FRONTMATTER
22
- \\PassOptionsToPackage{unicode}{hyperref}
23
- \\PassOptionsToPackage{hyphens}{url}
24
- \\documentclass[]{article}
25
- \\author{}
26
- \\date{#{Date.today}}
27
- \\usepackage{amsmath,amssymb}
28
- \\usepackage{lmodern}
29
- \\usepackage{iftex}
30
- \\usepackage[T1]{fontenc}
31
- \\usepackage[utf8]{inputenc}
32
- \\usepackage{textcomp}
33
- \\IfFileExists{upquote.sty}{\\usepackage{upquote}}{}
34
- \\IfFileExists{microtype.sty}{
35
- \\usepackage[]{microtype}
36
- \\UseMicrotypeSet[protrusion]{basicmath}
37
- }{}
38
- \\makeatletter
39
- \\@ifundefined{KOMAClassName}{
40
- \\IfFileExists{parskip.sty}{
41
- \\usepackage{parskip}
42
- }{
43
- \\setlength{\\parindent}{0pt}
44
- \\setlength{\\parskip}{6pt plus 2pt minus 1pt}}
45
- }{
46
- \\KOMAoptions{parskip=half}}
47
- \\makeatother
48
- \\usepackage{xcolor}
49
- \\IfFileExists{xurl.sty}{\\usepackage{xurl}}{}
50
- \\IfFileExists{bookmark.sty}{\\usepackage{bookmark}}{\\usepackage{hyperref}}
51
- \\hypersetup{
52
- hidelinks,
53
- pdfcreator={LaTeX via plansheet}}
54
- \\urlstyle{same}
55
- \\usepackage[margin=1.5cm]{geometry}
56
- \\setlength{\\emergencystretch}{3em}
57
- \\providecommand{\\tightlist}{
58
- \\setlength{\\itemsep}{0pt}\\setlength{\\parskip}{0pt}}
59
- \\setcounter{secnumdepth}{-\\maxdimen}
60
-
61
- \\begin{document}
62
-
63
- \\thispagestyle{empty}
64
-
65
- \\section{Date: #{Date.today}}
66
- FRONTMATTER
67
- end
68
-
69
- def sheet_footer
70
- '\end{document}'
71
- end
72
-
73
21
  def project_minipage(proj)
74
- str = String.new
75
- str << "\\begin{minipage}{6cm}\n"
76
- str << project_header(proj)
77
- proj&.tasks&.each do |t|
78
- str << "$\\square$ #{sanitize_string(t)} \\\\\n"
22
+ minipage("6cm") do
23
+ project_header(proj)&.concat(
24
+ proj&.tasks&.map do |t|
25
+ "#{checkbox_item sanitize_string(t)}#{HARD_NL}"
26
+ end&.join("") || "" # empty string to catch nil
27
+ )
79
28
  end
80
- str << "\\end{minipage}\n"
81
- str
82
- end
83
-
84
- def sanitize_string(str)
85
- str.gsub("_", '\_')
86
29
  end
87
30
 
88
31
  def project_header(proj)
89
- str = String.new
90
- str << "#{sanitize_string(proj.namespace)}: #{sanitize_string(proj.name)}\\\\\n"
32
+ str = "#{sanitize_string(proj.namespace)}: #{sanitize_string(proj.name)}#{HARD_NL}"
91
33
  str << proj.status.to_s
92
34
  str << " - #{sanitize_string(proj.location)}" if proj.location
93
35
  str << " due: #{proj.due}" if proj.due
94
36
  str << " time: #{proj.time_estimate}" if proj.time_estimate
95
- str << " \\\\\n"
37
+ str << HARD_NL
96
38
  str
97
39
  end
98
40
  end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ module Plansheet
5
+ module LaTeXMixins
6
+ HARD_NL = " \\\\\n"
7
+ SQUARE = "$\\square$"
8
+
9
+ def vspace(space)
10
+ "\\vspace{#{space}}\n"
11
+ end
12
+
13
+ def writein_line(space)
14
+ "$\\underline{\\hspace{#{space}}}$"
15
+ end
16
+
17
+ def checkbox_item(str, line: nil)
18
+ "#{SQUARE} #{str}#{" #{writein_line(line)}" if line}"
19
+ end
20
+
21
+ def vbox
22
+ "\\vbox{\n#{yield}\n}\n"
23
+ end
24
+
25
+ %w[center document enumerate tabbing].each do |env|
26
+ define_method(env.to_sym) do |&e|
27
+ <<~ENV
28
+ \\begin{#{env}}
29
+ #{e.call}
30
+ \\end{#{env}}
31
+ ENV
32
+ end
33
+ end
34
+
35
+ def itemize_tightlist
36
+ <<~DOCUMENT
37
+ \\begin{itemize}
38
+ \\tightlist
39
+ #{yield}
40
+ \\end{itemize}
41
+ DOCUMENT
42
+ end
43
+
44
+ def itemline(item, opt: nil)
45
+ "\\item#{opt ? "[#{opt}]" : ""} #{item}\n"
46
+ end
47
+
48
+ def minipage(size)
49
+ <<~MINIPAGE
50
+ \\begin{minipage}{#{size}}
51
+ #{yield}
52
+ \\end{minipage}
53
+ MINIPAGE
54
+ end
55
+
56
+ def sanitize_string(str)
57
+ str.gsub("_", '\_')
58
+ end
59
+
60
+ def sheet_header
61
+ # LaTeX used to be generated by pandoc
62
+ <<~FRONTMATTER
63
+ \\PassOptionsToPackage{unicode}{hyperref}
64
+ \\PassOptionsToPackage{hyphens}{url}
65
+ \\documentclass[]{article}
66
+ \\author{}
67
+ \\date{#{Date.today}}
68
+ \\usepackage{amsmath,amssymb}
69
+ \\usepackage{lmodern}
70
+ \\usepackage{iftex}
71
+ \\usepackage[T1]{fontenc}
72
+ \\usepackage[utf8]{inputenc}
73
+ \\usepackage{textcomp}
74
+ \\IfFileExists{upquote.sty}{\\usepackage{upquote}}{}
75
+ \\IfFileExists{microtype.sty}{
76
+ \\usepackage[]{microtype}
77
+ \\UseMicrotypeSet[protrusion]{basicmath}
78
+ }{}
79
+ \\makeatletter
80
+ \\@ifundefined{KOMAClassName}{
81
+ \\IfFileExists{parskip.sty}{
82
+ \\usepackage{parskip}
83
+ }{
84
+ \\setlength{\\parindent}{0pt}
85
+ \\setlength{\\parskip}{6pt plus 2pt minus 1pt}}
86
+ }{
87
+ \\KOMAoptions{parskip=half}}
88
+ \\makeatother
89
+ \\usepackage{xcolor}
90
+ \\IfFileExists{xurl.sty}{\\usepackage{xurl}}{}
91
+ \\IfFileExists{bookmark.sty}{\\usepackage{bookmark}}{\\usepackage{hyperref}}
92
+ \\hypersetup{
93
+ hidelinks,
94
+ pdfcreator={LaTeX via plansheet}}
95
+ \\urlstyle{same}
96
+ \\usepackage[margin=1.5cm]{geometry}
97
+ \\setlength{\\emergencystretch}{3em}
98
+ \\providecommand{\\tightlist}{
99
+ \\setlength{\\itemsep}{0pt}\\setlength{\\parskip}{0pt}}
100
+ \\setcounter{secnumdepth}{-\\maxdimen}
101
+ FRONTMATTER
102
+ end
103
+
104
+ def config_file_sanity_check(options = {})
105
+ options.each do |opt, type|
106
+ unless @config[opt].is_a?(type) && !@config[opt]&.empty?
107
+ abort "Need to specify #{opt} #{type.to_s.downcase} in your ~/.plansheet.yml config"
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: false
2
+
3
+ require "date"
4
+ module Plansheet
5
+ class MonthlyLaTeXSheet
6
+ include LaTeXMixins
7
+ def initialize(output_file, config)
8
+ @config = config || {}
9
+ config_file_sanity_check({
10
+ "highlevel_items" => Array,
11
+ "tags" => Hash
12
+ })
13
+
14
+ str = sheet_header
15
+ str << document do
16
+ [
17
+ month_line,
18
+ highlevel_items,
19
+ center do
20
+ [
21
+ events_minipage,
22
+ tags_minipages
23
+ ].join
24
+ end,
25
+ monthly_calendar_grid
26
+ ].join
27
+ end
28
+ puts "Writing to #{output_file}"
29
+ File.write(output_file, str)
30
+ end
31
+
32
+ def highlevel_items
33
+ itemize_tightlist do
34
+ @config["highlevel_items"].map do |i|
35
+ itemline("#{i} #{writein_line("5cm")}", opt: SQUARE)
36
+ end.join.concat("\n")
37
+ end
38
+ end
39
+
40
+ def tags_minipages
41
+ minipage("6.5cm") do
42
+ @config["tags"].map { |k, v| tag_minipage k, v }.join
43
+ end.concat("\n")
44
+ end
45
+
46
+ def events_minipage
47
+ minipage("8cm") do
48
+ "Events:\n#{
49
+ itemize_tightlist do
50
+ 30.times.map do |n|
51
+ itemline(
52
+ writein_line("6cm"),
53
+ opt: (Date.today + n).strftime("%a %m-%d")
54
+ )
55
+ end.join.concat("\n")
56
+ end
57
+ }"
58
+ end
59
+ end
60
+
61
+ def pretty_tag_name(tag)
62
+ tag.capitalize.gsub("_", " ")
63
+ end
64
+
65
+ def tag_minipage(tag, items = 10)
66
+ "#{pretty_tag_name tag}:\n".concat(
67
+ itemize_tightlist do
68
+ items.times.map do
69
+ itemline(writein_line("6cm"), opt: SQUARE)
70
+ end.join
71
+ end
72
+ )
73
+ end
74
+
75
+ def month_line
76
+ "For the next month: #{Date.today} - #{Date.today + 30}\n\n"
77
+ end
78
+
79
+ def monthly_calendar_grid
80
+ <<~GRID
81
+ \\begin{tabular}{|#{7.times.map { "p{2cm}" }.join("|")}|}
82
+ \\hline
83
+ GRID
84
+ .concat(
85
+ %w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday].join(" & ").concat(HARD_NL)
86
+ ).concat(
87
+ 5.times.map do |n|
88
+ calendar_grid_line n
89
+ end.join(HARD_NL).concat(HARD_NL)
90
+ ).concat(
91
+ <<~GRID
92
+ \\hline
93
+ \\end{tabular}
94
+ GRID
95
+ )
96
+ end
97
+
98
+ private
99
+
100
+ def calendar_grid_line(week_multiplier = 0)
101
+ "\\hline\n#{
102
+ 7.times.map do |n|
103
+ (Date.today - Date.today.wday + (n + (week_multiplier * 7))).strftime("%d")
104
+ end.join(" & ")
105
+ } #{
106
+ vspace("5mm")
107
+ }"
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: false
2
+
3
+ require "date"
4
+ module Plansheet
5
+ class WeeklyLaTeXSheet
6
+ include LaTeXMixins
7
+ include TimeUtils
8
+ MINIPAGE_SIZE = "6cm".freeze
9
+ def initialize(output_file, projects, config)
10
+ @config = config || {}
11
+ config_file_sanity_check({
12
+ "namespaces" => Hash,
13
+ "tags" => Hash
14
+ })
15
+
16
+ @external_commits = projects.select(&:externals) || []
17
+ @projects = projects # TODO: remove finished?
18
+ str = sheet_header
19
+ str << document do
20
+ [
21
+ week_line,
22
+ [
23
+ event_writeins,
24
+ past_due,
25
+ upcoming_due
26
+ ].compact.join,
27
+ # Since the following are a lot more volatile in minipage length,
28
+ # sort by newline so more fits on the page
29
+ [
30
+ external_commits,
31
+ works_in_progress,
32
+ tag_minipages,
33
+ namespace_minipages,
34
+ recurring_defer
35
+ ].flatten.compact.sort_by { |x| x.count("\n") }.join
36
+ ].flatten.join
37
+ end
38
+ puts "Writing to #{output_file}"
39
+ File.write(output_file, str)
40
+ end
41
+
42
+ def namespace_minipages
43
+ @config["namespaces"].map do |namespace, limit|
44
+ projects = @projects.select { |x| x.namespace == namespace }
45
+ project_minipage namespace, projects_in_time(projects, limit)
46
+ end&.join
47
+ end
48
+
49
+ def tag_minipages
50
+ @config["tags"].map do |tag, limit|
51
+ projects = @projects.select { |x| x&.tags&.any?(tag) }
52
+ project_minipage pretty_tag_name(tag), projects_in_time(projects, limit)
53
+ end&.join
54
+ end
55
+
56
+ def works_in_progress
57
+ projects = @projects.select(&:wip?)
58
+ project_minipage "Works in progress", projects if projects
59
+ end
60
+
61
+ def past_due
62
+ projects = @projects.select { |x| x.due ? x.due < Date.today : false }
63
+ project_minipage "Past due", projects if projects
64
+ end
65
+
66
+ def recurring_defer
67
+ projects = @projects.select { |x| x.last_for ? x.last_for_deferral < Date.today + 8 : false }
68
+ project_minipage "Recurring defer", projects if projects
69
+ end
70
+
71
+ def upcoming_due
72
+ projects = @projects.select { |x| x.due ? (x.due >= Date.today && x.due < Date.today + 8) : false }
73
+ project_minipage "Upcoming due", projects if projects
74
+ end
75
+
76
+ def external_commits
77
+ project_minipage("Externals", @external_commits) unless @external_commits&.empty?
78
+ end
79
+
80
+ def event_writeins
81
+ minipage(MINIPAGE_SIZE) do
82
+ "Events:\n#{
83
+ itemize_tightlist do
84
+ 7.times.map do |n|
85
+ itemline(
86
+ writein_line("4cm"),
87
+ opt: (Date.today + n).strftime("%a %m-%d")
88
+ )
89
+ end.join.concat("\n")
90
+ end
91
+ }"
92
+ end
93
+ end
94
+
95
+ def pretty_tag_name(tag)
96
+ tag.capitalize.gsub("_", " ")
97
+ end
98
+
99
+ def project_minipage(thing, projects = [])
100
+ unless projects&.empty?
101
+ minipage(MINIPAGE_SIZE) do
102
+ "#{thing}:
103
+ #{itemize_tightlist do
104
+ projects&.map do |x|
105
+ itemline("#{x.name} #{x&.time_estimate}", opt: SQUARE)
106
+ end&.join("\n")
107
+ end
108
+ }"
109
+ end
110
+ end
111
+ end
112
+
113
+ DEFAULT_PROJECT_TIME_ESTIMATE_MIN = 120
114
+ def projects_in_time(projects, time)
115
+ projects ||= []
116
+ t = parse_time_duration(time)
117
+ p = []
118
+ projects.each do |proj|
119
+ e = proj&.time_estimate ? parse_time_duration(proj.time_estimate) : DEFAULT_PROJECT_TIME_ESTIMATE_MIN
120
+ if e <= t
121
+ p.append proj
122
+ t -= e
123
+ end
124
+ end
125
+ p
126
+ end
127
+
128
+ def week_line
129
+ "For the next week: #{Date.today} - #{Date.today + 7}\n\n"
130
+ end
131
+ end
132
+ end
@@ -1,3 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "sheet/latex_utils"
3
4
  require_relative "sheet/latex"
5
+ require_relative "sheet/daily"
6
+ require_relative "sheet/weekly"
7
+ require_relative "sheet/monthly"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Plansheet
4
- VERSION = "0.30.1"
4
+ VERSION = "0.34.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plansheet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.1
4
+ version: 0.34.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Crosby
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-28 00:00:00.000000000 Z
11
+ date: 2022-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dc-kwalify
@@ -63,7 +63,6 @@ files:
63
63
  - ".rubocop.yml"
64
64
  - CODE_OF_CONDUCT.md
65
65
  - Gemfile
66
- - Gemfile.lock
67
66
  - Guardfile
68
67
  - LICENSE.txt
69
68
  - README.md
@@ -76,7 +75,11 @@ files:
76
75
  - lib/plansheet/project/stringify.rb
77
76
  - lib/plansheet/project/yaml.rb
78
77
  - lib/plansheet/sheet.rb
78
+ - lib/plansheet/sheet/daily.rb
79
79
  - lib/plansheet/sheet/latex.rb
80
+ - lib/plansheet/sheet/latex_utils.rb
81
+ - lib/plansheet/sheet/monthly.rb
82
+ - lib/plansheet/sheet/weekly.rb
80
83
  - lib/plansheet/time.rb
81
84
  - lib/plansheet/version.rb
82
85
  homepage: https://dafyddcrosby.com
data/Gemfile.lock DELETED
@@ -1,108 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- plansheet (0.30.1)
5
- dc-kwalify (~> 1.0)
6
- diffy (= 3.4.2)
7
- rgl (= 0.5.8)
8
-
9
- GEM
10
- remote: https://rubygems.org/
11
- specs:
12
- ast (2.4.2)
13
- coderay (1.1.3)
14
- dc-devtools (0.0.202207232239)
15
- dc-rubocop (>= 0.0.3)
16
- guard (~> 2.18)
17
- guard-rake (~> 1.0)
18
- minitest
19
- rake (~> 13.0)
20
- rubocop (~> 1.30)
21
- rubocop-minitest
22
- rubocop-rake (~> 0.6)
23
- dc-kwalify (1.0.0)
24
- dc-rubocop (0.0.3)
25
- rubocop
26
- diffy (3.4.2)
27
- ffi (1.15.5)
28
- formatador (1.1.0)
29
- generator (0.0.1)
30
- guard (2.18.0)
31
- formatador (>= 0.2.4)
32
- listen (>= 2.7, < 4.0)
33
- lumberjack (>= 1.0.12, < 2.0)
34
- nenv (~> 0.1)
35
- notiffany (~> 0.0)
36
- pry (>= 0.13.0)
37
- shellany (~> 0.0)
38
- thor (>= 0.18.1)
39
- guard-rake (1.0.0)
40
- guard
41
- rake
42
- json (2.6.2)
43
- lazy_priority_queue (0.1.1)
44
- listen (3.7.1)
45
- rb-fsevent (~> 0.10, >= 0.10.3)
46
- rb-inotify (~> 0.9, >= 0.9.10)
47
- lumberjack (1.2.8)
48
- method_source (1.0.0)
49
- minitest (5.16.2)
50
- nenv (0.3.0)
51
- notiffany (0.1.3)
52
- nenv (~> 0.1)
53
- shellany (~> 0.0)
54
- parallel (1.22.1)
55
- parser (3.1.2.0)
56
- ast (~> 2.4.1)
57
- pry (0.14.1)
58
- coderay (~> 1.1)
59
- method_source (~> 1.0)
60
- psych (4.0.4)
61
- stringio
62
- rainbow (3.1.1)
63
- rake (13.0.6)
64
- rb-fsevent (0.11.1)
65
- rb-inotify (0.10.1)
66
- ffi (~> 1.0)
67
- rdoc (6.4.0)
68
- psych (>= 4.0.0)
69
- regexp_parser (2.5.0)
70
- rexml (3.2.5)
71
- rgl (0.5.8)
72
- lazy_priority_queue (~> 0.1.0)
73
- rexml (~> 3.2, >= 3.2.4)
74
- stream (~> 0.5.3)
75
- rubocop (1.32.0)
76
- json (~> 2.3)
77
- parallel (~> 1.10)
78
- parser (>= 3.1.0.0)
79
- rainbow (>= 2.2.2, < 4.0)
80
- regexp_parser (>= 1.8, < 3.0)
81
- rexml (>= 3.2.5, < 4.0)
82
- rubocop-ast (>= 1.19.1, < 2.0)
83
- ruby-progressbar (~> 1.7)
84
- unicode-display_width (>= 1.4.0, < 3.0)
85
- rubocop-ast (1.19.1)
86
- parser (>= 3.1.1.0)
87
- rubocop-minitest (0.20.1)
88
- rubocop (>= 0.90, < 2.0)
89
- rubocop-rake (0.6.0)
90
- rubocop (~> 1.0)
91
- ruby-progressbar (1.11.0)
92
- shellany (0.0.1)
93
- stream (0.5.4)
94
- generator
95
- stringio (3.0.2)
96
- thor (1.2.1)
97
- unicode-display_width (2.2.0)
98
-
99
- PLATFORMS
100
- x86_64-linux
101
-
102
- DEPENDENCIES
103
- dc-devtools
104
- plansheet!
105
- rdoc
106
-
107
- BUNDLED WITH
108
- 2.3.6