Class: Worklog::Storage

Inherits:
Object
  • Object
show all
Defined in:
lib/storage.rb

Overview

Handles storage of daily logs and people

Defined Under Namespace

Classes: LogNotFoundError

Constant Summary collapse

FILE_SUFFIX =
'.yaml'
LOG_PATTERN =

Regular expression to match daily log file names

/\d{4}-\d{2}-\d{2}#{FILE_SUFFIX}\z/

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Storage

Returns a new instance of Storage.



20
21
22
# File 'lib/storage.rb', line 20

def initialize(config)
  @config = config
end

Instance Method Details

#all_daysArray<DailyLog>

Return all logs for all available days

Returns:

  • (Array<DailyLog>)

    List of all logs



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/storage.rb', line 30

def all_days
  return [] unless folder_exists?

  logs = []
  Dir.glob(File.join(@config.storage_path, "*#{FILE_SUFFIX}")).map do |file|
    next unless file.match?(LOG_PATTERN)

    logs << load_log(file)
  end

  logs
end

#create_default_filesObject

This method assumes that the storage folder already exists. It creates default files like people.yaml if they do not exist.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/storage.rb', line 157

def create_default_files
  WorkLogger.info 'Creating default files in storage folder if they do not exist.'
  # projects_file = File.join(@config.storage_path, 'projects.yaml')
  # unless File.exist?(projects_file)
  #   File.write(projects_file, [].to_yaml)
  # end

  # Write the default config file if it does not exist
  if File.exist?(Configuration.config_file_path)
    WorkLogger.info "Configuration file (#{Configuration.config_file_path}) already exists, skipping creation."
  else
    WorkLogger.info "Creating default configuration file at #{Configuration.config_file_path}."
    File.write(Configuration.config_file_path,
               Configuration::CONFIGURATION_TEMPLATE.result)
  end
end

#create_default_foldervoid

This method returns an undefined value.

Create folder if not exists already.



143
144
145
146
147
148
149
150
151
152
153
# File 'lib/storage.rb', line 143

def create_default_folder
  WorkLogger.debug 'Creating storage folder if it does not exist.'

  # Do nothing if the storage path is not the default path
  unless @config.default_storage_path?
    WorkLogger.debug 'Custom storage path detected, skipping creation of default storage folder.'
    return
  end

  Dir.mkdir(@config.storage_path) unless Dir.exist?(@config.storage_path)
end

#create_file_skeleton(date) ⇒ Object

Create file for a new day if it does not exist

Parameters:

  • date (Date)

    The date, used as the file name.



96
97
98
# File 'lib/storage.rb', line 96

def create_file_skeleton(date)
  File.write(filepath(date), YAML.dump(DailyLog.new(date:, entries: []))) unless File.exist?(filepath(date))
end

#days_between(start_date, end_date = nil, epics_only = nil, tags_filter = nil) ⇒ Array<DailyLog>

Return days between start_date and end_date If end_date is nil, return logs from start_date to today

Parameters:

  • start_date (Date)

    The start date, inclusive

  • end_date (Date) (defaults to: nil)

    The end date, inclusive

  • epics_only (Boolean) (defaults to: nil)

    If true, only return logs with epic entries

  • tags_filter (Array<String>) (defaults to: nil)

    If provided, only return logs with entries that have at least one of the tags

Returns:



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/storage.rb', line 68

def days_between(start_date, end_date = nil, epics_only = nil, tags_filter = nil)
  return [] unless folder_exists?

  logs = []
  end_date = Date.today if end_date.nil?

  return [] if start_date > end_date

  while start_date <= end_date
    if File.exist?(filepath(start_date))
      tmp_logs = load_log!(filepath(start_date))
      tmp_logs.entries.keep_if { |entry| entry.epic? } if epics_only

      if tags_filter
        # Safeguard against entries without any tags, not just empty array
        tmp_logs.entries.keep_if { |entry| entry.tags&.intersect?(tags_filter) }
      end

      logs << tmp_logs if tmp_logs.entries.length > 0
    end

    start_date += 1
  end
  logs
end

#filepath(date) ⇒ String

Construct filepath for a given date.

Parameters:

  • date (Date)

    The date

Returns:

  • (String)

    The filepath



177
178
179
# File 'lib/storage.rb', line 177

def filepath(date)
  File.join(@config.storage_path, "#{date}#{FILE_SUFFIX}")
end

#folder_exists?Boolean

Returns:

  • (Boolean)


24
25
26
# File 'lib/storage.rb', line 24

def folder_exists?
  Dir.exist?(@config.storage_path)
end

#load_log(file) ⇒ Object



100
101
102
103
104
105
# File 'lib/storage.rb', line 100

def load_log(file)
  load_log!(file)
rescue LogNotFoundError
  WorkLogger.error "No work log found for #{file}. Aborting."
  nil
end

#load_log!(file) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/storage.rb', line 107

def load_log!(file)
  WorkLogger.debug "Loading file #{file}"
  begin
    yaml_content = File.read(file)
    cleaned_yaml = yaml_content.gsub(%r{!ruby/object:[^\s]+}, '')
    log = DailyLog.from_hash(YAML.safe_load(cleaned_yaml, permitted_classes: [Date, Time], symbolize_names: true))

    log.entries.each do |entry|
      entry.time = Time.parse(entry.time) unless entry.time.respond_to?(:strftime)
    end
    log
  rescue Errno::ENOENT
    raise LogNotFoundError
  end
end

#load_single_log_file(file, headline = true) ⇒ Object

Load a single log file and return its entries



135
136
137
138
139
# File 'lib/storage.rb', line 135

def load_single_log_file(file, headline = true)
  daily_log = load_log!(file)
  puts "Work log for #{Rainbow(daily_log.date).gold}:" if headline
  daily_log.entries
end

#tagsSet<String>

Return all tags as a set

Returns:

  • (Set<String>)

    Set of all tags



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/storage.rb', line 45

def tags
  logs = all_days
  tags = Set[]
  logs.each do |log|
    log.entries.each do |entry|
      next unless entry.tags

      entry.tags.each do |tag|
        tags << tag
      end
    end
  end
  tags
end

#write_log(file, daily_log) ⇒ Object



123
124
125
126
127
128
129
130
131
132
# File 'lib/storage.rb', line 123

def write_log(file, daily_log)
  WorkLogger.debug "Writing to file #{file}"

  File.open(file, 'w') do |f|
    # Sort entries by time before saving
    daily_log.entries.sort_by!(&:time)

    f.puts daily_log.to_yaml
  end
end