sifttter-redux 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +8 -0
- data/HISTORY.md +7 -0
- data/LICENSE +21 -0
- data/README.md +227 -0
- data/Rakefile +44 -0
- data/Sifttter-Redux.gemspec +30 -0
- data/bin/srd +212 -0
- data/lib/Sifttter-Redux/cli-message.rb +135 -0
- data/lib/Sifttter-Redux/config-manager.rb +136 -0
- data/lib/Sifttter-Redux/constants.rb +15 -0
- data/lib/Sifttter-Redux/date-range-maker.rb +102 -0
- data/lib/Sifttter-Redux/methods.rb +190 -0
- data/lib/Sifttter-Redux/os.rb +47 -0
- data/lib/Sifttter-Redux.rb +6 -0
- data/test/catch_up_test.rb +56 -0
- data/test/test_helper.rb +9 -0
- metadata +129 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
#| ======================================================
|
2
|
+
#| CliManager Module
|
3
|
+
#| Singleton to manage common CLI interfacing
|
4
|
+
#| ======================================================
|
5
|
+
module CliMessage
|
6
|
+
|
7
|
+
ERROR = 1
|
8
|
+
INFO = 2
|
9
|
+
SECTION = 3
|
10
|
+
WARNING = 4
|
11
|
+
|
12
|
+
#| ------------------------------------------------------
|
13
|
+
#| error method
|
14
|
+
#|
|
15
|
+
#| Outputs a formatted-red error message.
|
16
|
+
#| @param message The message to output
|
17
|
+
#| @return Void
|
18
|
+
#| ------------------------------------------------------
|
19
|
+
def self.error(message, addNewline = true)
|
20
|
+
if addNewline
|
21
|
+
puts "---> ERROR: #{message}".red
|
22
|
+
else
|
23
|
+
print "---> ERROR: #{message}".red
|
24
|
+
end
|
25
|
+
|
26
|
+
@@last_message_type = ERROR
|
27
|
+
end
|
28
|
+
|
29
|
+
#| ------------------------------------------------------
|
30
|
+
#| finish_message method
|
31
|
+
#|
|
32
|
+
#| Finishes a previous message by appending "DONE" in the
|
33
|
+
#| correct color.
|
34
|
+
#| @return Void
|
35
|
+
#| ------------------------------------------------------
|
36
|
+
def self.finish_message(message)
|
37
|
+
case @@last_message_type
|
38
|
+
when ERROR
|
39
|
+
puts message.red
|
40
|
+
when INFO
|
41
|
+
puts message.blue
|
42
|
+
when SECTION
|
43
|
+
puts message.green
|
44
|
+
when WARNING
|
45
|
+
puts message.yellow
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#| ------------------------------------------------------
|
50
|
+
#| info method
|
51
|
+
#|
|
52
|
+
#| Outputs a formatted-blue informational message.
|
53
|
+
#| @param message The message to output
|
54
|
+
#| @return Void
|
55
|
+
#| ------------------------------------------------------
|
56
|
+
def self.info(message, addNewline = true)
|
57
|
+
if addNewline
|
58
|
+
puts "---> INFO: #{message}".blue
|
59
|
+
else
|
60
|
+
print "---> INFO: #{message}".blue
|
61
|
+
end
|
62
|
+
|
63
|
+
@@last_message_type = INFO
|
64
|
+
end
|
65
|
+
|
66
|
+
#| ------------------------------------------------------
|
67
|
+
#| prompt method
|
68
|
+
#|
|
69
|
+
#| Outputs a prompt, collects the user's response, and
|
70
|
+
#| returns it.
|
71
|
+
#| @param prompt The prompt to output
|
72
|
+
#| @param default The default option
|
73
|
+
#| @return String
|
74
|
+
#| ------------------------------------------------------
|
75
|
+
def self.prompt(prompt, default)
|
76
|
+
print "#{prompt} [default: #{default}]: "
|
77
|
+
choice = $stdin.gets.chomp
|
78
|
+
if choice.empty?
|
79
|
+
return default
|
80
|
+
else
|
81
|
+
return choice
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
#| ------------------------------------------------------
|
86
|
+
#| section method
|
87
|
+
#|
|
88
|
+
#| Outputs a formatted-orange section message.
|
89
|
+
#| @param message The message to output
|
90
|
+
#| @return Void
|
91
|
+
#| ------------------------------------------------------
|
92
|
+
def self.section(message, addNewline = true)
|
93
|
+
if addNewline
|
94
|
+
puts "#### #{message}".green
|
95
|
+
else
|
96
|
+
print "#### #{message}".green
|
97
|
+
end
|
98
|
+
|
99
|
+
@@last_message_type = SECTION
|
100
|
+
end
|
101
|
+
|
102
|
+
#| ------------------------------------------------------
|
103
|
+
#| success method
|
104
|
+
#|
|
105
|
+
#| Outputs a formatted-green success message.
|
106
|
+
#| @param message The message to output
|
107
|
+
#| @return Void
|
108
|
+
#| ------------------------------------------------------
|
109
|
+
def self.success(message, addNewline = true)
|
110
|
+
if addNewline
|
111
|
+
puts "---> SUCCESS: #{message}".green
|
112
|
+
else
|
113
|
+
print "---> SUCCESS: #{message}".green
|
114
|
+
end
|
115
|
+
|
116
|
+
@@last_message_type = WARNING
|
117
|
+
end
|
118
|
+
|
119
|
+
#| ------------------------------------------------------
|
120
|
+
#| warning method
|
121
|
+
#|
|
122
|
+
#| Outputs a formatted-yellow warning message.
|
123
|
+
#| @param message The message to output
|
124
|
+
#| @return Void
|
125
|
+
#| ------------------------------------------------------
|
126
|
+
def self.warning(message, addNewline = true)
|
127
|
+
if addNewline
|
128
|
+
puts "---> WARNING: #{message}".yellow
|
129
|
+
else
|
130
|
+
print "---> WARNING: #{message}".yellow
|
131
|
+
end
|
132
|
+
|
133
|
+
@@last_message_type = WARNING
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
#| ======================================================
|
4
|
+
#| ConfigManager Class
|
5
|
+
#|
|
6
|
+
#| Singleton to manage the YAML config file
|
7
|
+
#| for this script
|
8
|
+
#| ======================================================
|
9
|
+
|
10
|
+
class ConfigManager
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
attr_accessor :configFile
|
14
|
+
|
15
|
+
#| ------------------------------------------------------
|
16
|
+
#| initialize method
|
17
|
+
#|
|
18
|
+
#| Initializes this singleton with data from the config
|
19
|
+
#| file. If the file doesn't exist, an empty hash is
|
20
|
+
#| created in anticipation of future config saves.
|
21
|
+
#| @return Void
|
22
|
+
#| ------------------------------------------------------
|
23
|
+
def initialize
|
24
|
+
@configFile = SifttterRedux::SRD_CONFIG_FILEPATH
|
25
|
+
|
26
|
+
if File.exists?(SifttterRedux::SRD_CONFIG_FILEPATH)
|
27
|
+
@data = YAML.load_file(SifttterRedux::SRD_CONFIG_FILEPATH)
|
28
|
+
@data.each do |k, v|
|
29
|
+
define_singleton_method(k) { return v }
|
30
|
+
end
|
31
|
+
else
|
32
|
+
@data = {}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#| ------------------------------------------------------
|
37
|
+
#| _dump method
|
38
|
+
#|
|
39
|
+
#| Convenience method that dumps the configuration hash's
|
40
|
+
#| data.
|
41
|
+
#| @return Void
|
42
|
+
#| ------------------------------------------------------
|
43
|
+
def _dump
|
44
|
+
puts @data
|
45
|
+
end
|
46
|
+
|
47
|
+
#| ------------------------------------------------------
|
48
|
+
#| add_to_section method
|
49
|
+
#|
|
50
|
+
#| Adds a hash to the configuration data.
|
51
|
+
#| @param hash The hash to add to configuration
|
52
|
+
#| @param section The section into which the hash goes
|
53
|
+
#| @return Void
|
54
|
+
#| ------------------------------------------------------
|
55
|
+
def add_to_section(hash, section)
|
56
|
+
unless @data.has_key?(section)
|
57
|
+
CliMessage.warning("Attempting to insert into a non-existing section: #{section}; skipping...")
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
@data[section].merge!(hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
#| ------------------------------------------------------
|
65
|
+
#| create_section method
|
66
|
+
#|
|
67
|
+
#| Creates a new section in the configuration hash.
|
68
|
+
#| @param section The section to create
|
69
|
+
#| @return Void
|
70
|
+
#| ------------------------------------------------------
|
71
|
+
def create_section(section)
|
72
|
+
if @data.has_key?(section)
|
73
|
+
CliMessage.warning("Attempting to create existing section (#{section}); skipping...")
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
define_singleton_method(section) { return @data[section] }
|
78
|
+
@data.merge!(section => {})
|
79
|
+
end
|
80
|
+
|
81
|
+
#| ------------------------------------------------------
|
82
|
+
#| delete_section method
|
83
|
+
#|
|
84
|
+
#| Deletes a section in the configuration hash.
|
85
|
+
#| @param section The section to delete
|
86
|
+
#| @return Void
|
87
|
+
#| ------------------------------------------------------
|
88
|
+
def delete_section(section)
|
89
|
+
unless @data.has_key?(section)
|
90
|
+
CliMessage.warning("Attempting to delete non-existing section (#{section}); skipping...")
|
91
|
+
return
|
92
|
+
end
|
93
|
+
|
94
|
+
remove_singleton_method(section)
|
95
|
+
@data.delete(section)
|
96
|
+
end
|
97
|
+
|
98
|
+
#| ------------------------------------------------------
|
99
|
+
#| remove_singleton_method method
|
100
|
+
#|
|
101
|
+
#| Removes a hash from the configuration data based on
|
102
|
+
#| its key.
|
103
|
+
#| @param hash The hash key remove
|
104
|
+
#| @param section The section from which the key comes
|
105
|
+
#| @return Void
|
106
|
+
#| ------------------------------------------------------
|
107
|
+
def remove_from_section(key, section)
|
108
|
+
unless @data.has_key?(section) && @data[section].has_key?(key)
|
109
|
+
CliMessage.warning("Attempting to remove a non-existing key: #{section}.#{key}; skipping...")
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
@data[section].delete(key)
|
114
|
+
end
|
115
|
+
|
116
|
+
#| ------------------------------------------------------
|
117
|
+
#| reset method
|
118
|
+
#|
|
119
|
+
#| Clears out the configuration data by resetting the hash.
|
120
|
+
#| @return Void
|
121
|
+
#| ------------------------------------------------------
|
122
|
+
def reset
|
123
|
+
@data = {}
|
124
|
+
end
|
125
|
+
|
126
|
+
#| ------------------------------------------------------
|
127
|
+
#| save_configuration method
|
128
|
+
#|
|
129
|
+
#| Saves the configuration data to the filesystem.
|
130
|
+
#| @return File
|
131
|
+
#| ------------------------------------------------------
|
132
|
+
def save_configuration
|
133
|
+
return File.open(@configFile, 'w') { |f| f.write(@data.to_yaml) }
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SifttterRedux
|
2
|
+
VERSION = "0.2.0"
|
3
|
+
|
4
|
+
# Sifttter and Sifttter Redux Constants
|
5
|
+
SRD_CONFIG_FILEPATH = File.join(ENV['HOME'], '.sifttter_redux')
|
6
|
+
SFT_LOCAL_FILEPATH = "/tmp/sifttter"
|
7
|
+
SFT_REMOTE_FILEPATH = "/Apps/ifttt/sifttter"
|
8
|
+
|
9
|
+
# Dropbox Upload Constants
|
10
|
+
DBU_LOCAL_FILEPATH = "/usr/local/opt"
|
11
|
+
|
12
|
+
# Day One Constants
|
13
|
+
DO_REMOTE_FILEPATH = "/Apps/Day\\ One/Journal.dayone/entries"
|
14
|
+
DO_LOCAL_FILEPATH = "/tmp/dayone"
|
15
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'chronic'
|
2
|
+
|
3
|
+
class DateRangeMakerError < StandardError
|
4
|
+
def initialize(msg = "You've triggered a DateRangeMakerError")
|
5
|
+
super
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class BadChronicDateError < DateRangeMakerError
|
10
|
+
def initialize(msg = "Invalid date provided to Chronic...")
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class BadDateOrderError < DateRangeMakerError
|
16
|
+
def initialize(msg = "The start date must be before or equal to the end date...")
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidFlagsError < DateRangeMakerError
|
22
|
+
def initialize(msg = "You can't specify -t without specifying -f...")
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#| ======================================================
|
28
|
+
#| DateRangeMaker Class
|
29
|
+
#|
|
30
|
+
#| Singleton to manage the YAML config file
|
31
|
+
#| for this script
|
32
|
+
#| ======================================================
|
33
|
+
class DateRangeMaker
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def last_seven_days(include_today = false)
|
40
|
+
if (include_today)
|
41
|
+
_r = (Date.today - 7..Date.today)
|
42
|
+
else
|
43
|
+
_r = (Date.today - 7...Date.today)
|
44
|
+
end
|
45
|
+
|
46
|
+
return _r
|
47
|
+
end
|
48
|
+
|
49
|
+
def range(options = {})
|
50
|
+
|
51
|
+
opts = {
|
52
|
+
:start_date => Date.today,
|
53
|
+
:end_date => nil,
|
54
|
+
:include_today => false
|
55
|
+
}
|
56
|
+
|
57
|
+
options.each do |k, v|
|
58
|
+
k = k.to_sym
|
59
|
+
raise ArgumentError, "Unknown property: #{k}" unless opts.key?(k)
|
60
|
+
opts[k] = v
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
chronic_start_date = Chronic.parse(opts[:start_date]).to_date
|
65
|
+
rescue
|
66
|
+
raise BadChronicDateError.new("Invalid date provided to Chronic: #{opts[:start_date]}") if !opts[:start_date].nil?
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
chronic_end_date = Chronic.parse(opts[:end_date]).to_date
|
72
|
+
rescue
|
73
|
+
raise BadChronicDateError.new("Invalid date provided to Chronic: #{opts[:end_date]}") if !opts[:end_date].nil?
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
raise InvalidFlagsError.new if (opts[:start_date].nil? && !opts[:end_date].nil?)
|
78
|
+
raise BadDateOrderError.new if (chronic_end_date && chronic_start_date > chronic_end_date)
|
79
|
+
|
80
|
+
if (!chronic_start_date.nil?)
|
81
|
+
if (chronic_end_date.nil?)
|
82
|
+
if (opts[:include_today])
|
83
|
+
dates = (chronic_start_date..Date.today)
|
84
|
+
else
|
85
|
+
dates = (chronic_start_date...Date.today)
|
86
|
+
end
|
87
|
+
else
|
88
|
+
dates = (chronic_start_date..chronic_end_date)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
return dates
|
93
|
+
end
|
94
|
+
|
95
|
+
def today
|
96
|
+
return (Date.today..Date.today)
|
97
|
+
end
|
98
|
+
|
99
|
+
def yesterday
|
100
|
+
return (Date.today - 1..Date.today - 1)
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
#| ======================================================
|
2
|
+
#| METHODS
|
3
|
+
#| ======================================================
|
4
|
+
|
5
|
+
#| ------------------------------------------------------
|
6
|
+
#| collect_preferences method
|
7
|
+
#|
|
8
|
+
#| Collects preferences from the user and stores the
|
9
|
+
#| entered values into a configuration file.
|
10
|
+
#| @return Void
|
11
|
+
#| ------------------------------------------------------
|
12
|
+
def collect_preferences
|
13
|
+
CliMessage.section('COLLECTING PREFERENCES...')
|
14
|
+
|
15
|
+
pref = CliMessage.prompt("Location for downloaded Sifttter files from Dropbox", SifttterRedux::SFT_LOCAL_FILEPATH)
|
16
|
+
$config.add_to_section({"sifttter_local_filepath" => pref}, "sifttter_redux")
|
17
|
+
|
18
|
+
pref = CliMessage.prompt("Location of Sifttter files in Dropbox", SifttterRedux::SFT_REMOTE_FILEPATH)
|
19
|
+
$config.add_to_section({"sifttter_remote_filepath" => pref}, "sifttter_redux")
|
20
|
+
|
21
|
+
pref = CliMessage.prompt("Location for downloaded Day One files from Dropbox", SifttterRedux::DO_LOCAL_FILEPATH)
|
22
|
+
$config.add_to_section({"dayone_local_filepath" => pref}, "sifttter_redux")
|
23
|
+
|
24
|
+
pref = CliMessage.prompt("Location of Day One files in Dropbox", SifttterRedux::DO_REMOTE_FILEPATH)
|
25
|
+
$config.add_to_section({"dayone_remote_filepath" => pref}, "sifttter_redux")
|
26
|
+
end
|
27
|
+
|
28
|
+
#| ------------------------------------------------------
|
29
|
+
#| download_sifttter_files method
|
30
|
+
#|
|
31
|
+
#| Downloads Sifttter files from Dropbox
|
32
|
+
#| @return Void
|
33
|
+
#| ------------------------------------------------------
|
34
|
+
def download_sifttter_files
|
35
|
+
# Download all Sifttter files from Dropbox.
|
36
|
+
CliMessage.info('Downloading Sifttter files...', false)
|
37
|
+
`#{$db_uploader} download #{$config.sifttter_redux["sifttter_remote_filepath"]} #{$config.sifttter_redux["sifttter_local_filepath"]}`
|
38
|
+
CliMessage.finish_message('DONE.')
|
39
|
+
end
|
40
|
+
|
41
|
+
#| ------------------------------------------------------
|
42
|
+
#| initialize_procedures method
|
43
|
+
#|
|
44
|
+
#| Initializes Sifttter Redux by downloading and collecting
|
45
|
+
#| all necessary items and info.
|
46
|
+
#| @return Void
|
47
|
+
#| ------------------------------------------------------
|
48
|
+
def initialize_procedures
|
49
|
+
$config.reset
|
50
|
+
$config.create_section("sifttter_redux")
|
51
|
+
$config.add_to_section({"config_location" => $config.configFile}, "sifttter_redux")
|
52
|
+
|
53
|
+
install_db_uploader
|
54
|
+
collect_preferences
|
55
|
+
|
56
|
+
CliMessage.section("INITIALIZATION COMPLETE!")
|
57
|
+
|
58
|
+
$config.save_configuration
|
59
|
+
end
|
60
|
+
|
61
|
+
#| ------------------------------------------------------
|
62
|
+
#| install_db_uploader method
|
63
|
+
#|
|
64
|
+
#| Installs Dropbox Uploader to a user-specified location
|
65
|
+
#| by cloning the git repository.
|
66
|
+
#| @return Void
|
67
|
+
#| ------------------------------------------------------
|
68
|
+
def install_db_uploader
|
69
|
+
valid_directory_chosen = false
|
70
|
+
|
71
|
+
CliMessage.section('DOWNLOADING DROPBOX UPLOADER...')
|
72
|
+
|
73
|
+
# Create a new configuration section for Dropbox-Uploader
|
74
|
+
$config.create_section("db_uploader")
|
75
|
+
|
76
|
+
until valid_directory_chosen
|
77
|
+
# Prompt the user for a location to save Dropbox Uploader. "
|
78
|
+
db_uploader_location = CliMessage.prompt("Location for Dropbox-Uploader", SifttterRedux::DBU_LOCAL_FILEPATH)
|
79
|
+
db_uploader_location.chop! if db_uploader_location.end_with?('/')
|
80
|
+
db_uploader_location = "/usr/local/opt" if db_uploader_location.empty?
|
81
|
+
|
82
|
+
# If the entered directory exists, clone the repository.
|
83
|
+
if File.directory?(db_uploader_location)
|
84
|
+
valid_directory_chosen = true
|
85
|
+
db_uploader_location << "/Dropbox-Uploader"
|
86
|
+
|
87
|
+
# If, for some reason, Dropbox Uploader alread exists at this location,
|
88
|
+
# skip the clone.
|
89
|
+
if File.directory?(db_uploader_location)
|
90
|
+
CliMessage.info("You seem to already have Dropbox Uploader at this location; skipping...")
|
91
|
+
else
|
92
|
+
%x{git clone https://github.com/andreafabrizi/Dropbox-Uploader.git #{db_uploader_location}}
|
93
|
+
end
|
94
|
+
|
95
|
+
# Save config data to YAML.
|
96
|
+
$config.add_to_section({"local_filepath" => db_uploader_location}, "db_uploader")
|
97
|
+
else
|
98
|
+
puts "Sorry, but #{db_uploader_location} isn't a valid directory."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#| ------------------------------------------------------
|
104
|
+
#| run_sifttter method
|
105
|
+
#|
|
106
|
+
#| Modified form of Sifttter
|
107
|
+
#|
|
108
|
+
#| Sifttter: An IFTTT-to-Day One Logger by Craig Eley 2014
|
109
|
+
#| Based on tp-dailylog.rb by Brett Terpstra 2012
|
110
|
+
#| @param date The date to use when scanning Sifttter files
|
111
|
+
#| @return Void
|
112
|
+
#| ------------------------------------------------------
|
113
|
+
def run_sifttter(date)
|
114
|
+
uuid_command = "uuidgen" if OS.mac?
|
115
|
+
uuid_command = "uuid" if OS.linux?
|
116
|
+
uuid = %x{#{uuid_command}}.gsub(/-/,'').strip
|
117
|
+
|
118
|
+
date_for_title = date.strftime('%B %d, %Y')
|
119
|
+
datestamp = date.to_time.utc.iso8601
|
120
|
+
starred = false
|
121
|
+
|
122
|
+
template = ERB.new <<-XMLTEMPLATE
|
123
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
124
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
125
|
+
<plist version="1.0">
|
126
|
+
<dict>
|
127
|
+
<key>Creation Date</key>
|
128
|
+
<date><%= datestamp %></date>
|
129
|
+
<key>Entry Text</key>
|
130
|
+
<string><%= entrytext %></string>
|
131
|
+
<key>Starred</key>
|
132
|
+
<<%= starred %>/>
|
133
|
+
<key>Tags</key>
|
134
|
+
<array>
|
135
|
+
<string>daily logs</string>
|
136
|
+
</array>
|
137
|
+
<key>UUID</key>
|
138
|
+
<string><%= uuid %></string>
|
139
|
+
</dict>
|
140
|
+
</plist>
|
141
|
+
XMLTEMPLATE
|
142
|
+
|
143
|
+
date_regex = "#{date.strftime('%B')} 0?#{date.strftime('%-d')}, #{date.strftime('%Y')}"
|
144
|
+
time_regex = "\d{1,2}:\d{1,2}\s?[AaPpMm]{2}"
|
145
|
+
|
146
|
+
files = %x{find #{$config.sifttter_redux["sifttter_local_filepath"]} -type f -name '*.txt' | grep -v -i daily | sort}
|
147
|
+
|
148
|
+
projects = []
|
149
|
+
files.split("\n").each do |file|
|
150
|
+
if File.exists?(file.strip)
|
151
|
+
f = File.open(file.strip, encoding: 'UTF-8')
|
152
|
+
lines = f.read
|
153
|
+
f.close
|
154
|
+
project = "### " + File.basename(file).gsub(/^.*?\/([^\/]+)$/,"\\1") + "\n"
|
155
|
+
|
156
|
+
found_completed = false
|
157
|
+
lines.each_line do |line|
|
158
|
+
if line =~ /&/
|
159
|
+
line.gsub!(/[&]/, 'and')
|
160
|
+
end
|
161
|
+
if line =~ /#{date_regex}/
|
162
|
+
found_completed = true
|
163
|
+
project += line.gsub(/@done/,'').gsub(/#{date_regex}\s(-|at)\s/, '').gsub(/#{time_regex}\s-\s/, '').strip + "\n"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
if found_completed
|
168
|
+
projects.push(project)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
if projects.length <=0
|
173
|
+
CliMessage.error("No entries found...")
|
174
|
+
exit!
|
175
|
+
end
|
176
|
+
|
177
|
+
if projects.length > 0
|
178
|
+
entrytext = "# Things done on #{date_for_title}\n\n"
|
179
|
+
projects.each do |project|
|
180
|
+
entrytext += project.gsub(/.txt/, ' ') + "\n\n"
|
181
|
+
end
|
182
|
+
|
183
|
+
Dir.mkdir($config.sifttter_redux["dayone_local_filepath"]) if !Dir.exists?($config.sifttter_redux["dayone_local_filepath"])
|
184
|
+
|
185
|
+
fh = File.new(File.expand_path($config.sifttter_redux["dayone_local_filepath"] + "/" + uuid + ".doentry"), 'w+')
|
186
|
+
fh.puts template.result(binding)
|
187
|
+
fh.close
|
188
|
+
CliMessage.success("Entry logged for #{date_for_title}...")
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#| ======================================================
|
2
|
+
#| OS Module
|
3
|
+
#|
|
4
|
+
#| Module to easily find the running operating system
|
5
|
+
#| ======================================================
|
6
|
+
module OS
|
7
|
+
|
8
|
+
#| ------------------------------------------------------
|
9
|
+
#| linux? method
|
10
|
+
#|
|
11
|
+
#| Returns true if the host OS is Linux (false otherwise).
|
12
|
+
#| @return Bool
|
13
|
+
#| ------------------------------------------------------
|
14
|
+
def OS.linux?
|
15
|
+
OS.unix? and not OS.mac?
|
16
|
+
end
|
17
|
+
|
18
|
+
#| ------------------------------------------------------
|
19
|
+
#| mac? method
|
20
|
+
#|
|
21
|
+
#| Returns true if the host OS is OS X (false otherwise).
|
22
|
+
#| @return Bool
|
23
|
+
#| ------------------------------------------------------
|
24
|
+
def OS.mac?
|
25
|
+
(/darwin/ =~ RUBY_PLATFORM) != nil
|
26
|
+
end
|
27
|
+
|
28
|
+
#| ------------------------------------------------------
|
29
|
+
#| unix? method
|
30
|
+
#|
|
31
|
+
#| Returns true if the host OS is Unix (false otherwise).
|
32
|
+
#| @return Bool
|
33
|
+
#| ------------------------------------------------------
|
34
|
+
def OS.unix?
|
35
|
+
!OS.windows?
|
36
|
+
end
|
37
|
+
|
38
|
+
#| ------------------------------------------------------
|
39
|
+
#| windows? method
|
40
|
+
#|
|
41
|
+
#| Returns true if the host OS is Windows (false otherwise).
|
42
|
+
#| @return Bool
|
43
|
+
#| ------------------------------------------------------
|
44
|
+
def OS.windows?
|
45
|
+
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'test_helper'
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'lib/Sifttter-Redux/date-range-maker.rb')
|
4
|
+
|
5
|
+
class DefaultTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
$drm = DateRangeMaker.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_today
|
12
|
+
assert_equal($drm.today, (Date.today..Date.today))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_yesterday
|
16
|
+
assert_equal($drm.yesterday, (Date.today - 1..Date.today - 1))
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_last_7_days
|
20
|
+
assert_equal($drm.last_seven_days, (Date.today - 7...Date.today))
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_last_7_days_include_today
|
24
|
+
assert_equal($drm.last_seven_days(true), (Date.today - 7..Date.today))
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_range_only_start_date
|
28
|
+
assert_equal($drm.range({:start_date => "2014-02-01"}), (Date.parse("2014-02-01")...Date.today))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_range_only_start_date_include_today
|
32
|
+
assert_equal($drm.range({:start_date => "2014-02-01", :include_today => true}), (Date.parse("2014-02-01")..Date.today))
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_range_start_date_and_end_date
|
36
|
+
assert_equal($drm.range({:start_date => "2014-02-01", :end_date => "2014-02-05"}), (Date.parse("2014-02-01")..Date.parse("2014-02-05")))
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_range_bad_dates
|
40
|
+
assert_raise BadChronicDateError do
|
41
|
+
$drm.range({:start_date => "Bad Start Date", :end_date => "Bad End Date"})
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_range_end_date_with_no_start_date
|
46
|
+
assert_raise InvalidFlagsError do
|
47
|
+
$drm.range({:start_date => nil, :end_date => Date.today})
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_range_end_date_before_start_date
|
52
|
+
assert_raise BadDateOrderError do
|
53
|
+
$drm.range({:start_date => Date.today, :end_date => Date.today - 1})
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|