evertils 0.0.1

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.
@@ -0,0 +1,33 @@
1
+ module Granify
2
+ module Helper
3
+ class EvernoteENML
4
+ attr_reader :element, :embeddable_element
5
+
6
+ def initialize(file = nil)
7
+ @file = file
8
+ @element = enml_element
9
+
10
+ if !@element.nil?
11
+ @embeddable_element = "<hr/>Attachment with hash #{@element.data.bodyHash}<br /><en-media type=\"#{@element.mime}\" hash=\"#{@element.data.bodyHash}\" /><br /><br />"
12
+ end
13
+ end
14
+
15
+ private
16
+ def enml_element
17
+ if @file
18
+ read_file = File.open(@file, 'rb') { |io| io.read }
19
+
20
+ el = ::Evernote::EDAM::Type::Resource.new()
21
+ el.mime = MIME::Types.type_for(@file)[0].content_type
22
+ el.data = ::Evernote::EDAM::Type::Data.new()
23
+ el.data.size = read_file.size
24
+ el.data.bodyHash = Digest::MD5.hexdigest(read_file)
25
+ el.data.body = read_file
26
+ el.attributes = ::Evernote::EDAM::Type::ResourceAttributes.new()
27
+ el.attributes.fileName = @file # temporary for now, the actual file name
28
+ el
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,210 @@
1
+ require 'rexml/document'
2
+ require 'benchmark'
3
+ include REXML
4
+ include Benchmark
5
+
6
+ # reverse markdown for ruby
7
+ # author: JO
8
+ # e-mail: xijo@gmx.de
9
+ # date: 14.7.2009
10
+ # version: 0.1
11
+ # license: GPL
12
+ # edited by: Daniel Spencer
13
+ # https://github.com/indspenceable/en-cli/blob/master/lib/en/markdown_parser.rb
14
+
15
+ # TODO
16
+ # - ol numbering is buggy, in fact doesn't matter for markdown code
17
+
18
+ module Granify
19
+ module Helper
20
+ class EvernoteMD
21
+ # set basic variables:
22
+ # - @li_counter: numbering list item (li) tags in an ordered list (ol)
23
+ # - @links: hold the links for adding them to the bottom of the @output
24
+ # this means 'reference style', please take a look at http://daringfireball.net/projects/markdown/syntax#link
25
+ # - @outout: fancy markdown code in here!
26
+ # - @indent: control indention level for nested lists
27
+ # - @errors: appearing errors, like unknown tags, go into this array
28
+ def initialize()
29
+ @li_counter = 0
30
+ @links = []
31
+ @output = ""
32
+ @indent = 0
33
+ @errors = []
34
+ end
35
+
36
+ # Invokes the HTML parsing by using a string. Returns the markdown code in @output.
37
+ # To garantuee well-formed xml for REXML a <root> element will be added, but has no effect.
38
+ # After parsing all elements, the 'reference style'-links will be inserted.
39
+ def parse_string(string)
40
+ doc = Document.new("<root>\n"+string+"\n</root>")
41
+ root = doc.root
42
+ root.elements.each do |element|
43
+ parse_element(element, :root)
44
+ end
45
+ insert_links()
46
+ @output
47
+ end
48
+
49
+ # Parsing an element and its children (recursive) and writing its markdown code to @output
50
+ # 1. do indent for nested list items
51
+ # 2. add the markdown opening tag for this element
52
+ # 3a. if element only contains text, handle it like a text node
53
+ # 3b. if element is a container handle its children, which may be text- or element nodes
54
+ # 4. finally add the markdown ending tag for this element
55
+ def parse_element(element, parent)
56
+ name = element.name.to_sym
57
+ # 1.
58
+ @output << indent() if name.eql?(:li)
59
+ # 2.
60
+ @output << opening(element, parent)
61
+
62
+ # 3a.
63
+ if (element.has_text? and element.children.size < 2)
64
+ @output << text_node(element, parent)
65
+ end
66
+
67
+ # 3b.
68
+ if element.has_elements?
69
+ element.children.each do |child|
70
+ # increase indent if nested list
71
+ @indent += 1 if element.name=~/(ul|ol)/ and parent.eql?(:li)
72
+
73
+ if child.node_type.eql?(:element)
74
+ parse_element(child, element.name.to_sym)
75
+ else
76
+ if parent.eql?(:blockquote)
77
+ @output << child.to_s.gsub("\n ", "\n>")
78
+ else
79
+ @output << child.to_s
80
+ end
81
+ end
82
+
83
+ # decrease indent if end of nested list
84
+ @indent -= 1 if element.name=~/(ul|ol)/ and parent.eql?(:li)
85
+ end
86
+ end
87
+
88
+ # 4.
89
+ @output << ending(element, parent)
90
+ end
91
+
92
+ # Returns opening markdown tag for the element. Its parent matters sometimes!
93
+ def opening(type, parent)
94
+ case type.name.to_sym
95
+ when :h1
96
+ "# "
97
+ when :li || :"en-todo"
98
+ parent.eql?(:ul) ? " - " : " "+(@li_counter+=1).to_s+". "
99
+ when :ol
100
+ @li_counter = 0
101
+ ""
102
+ when :ul
103
+ ""
104
+ when :h2
105
+ "## "
106
+ when :em
107
+ "*"
108
+ when :strong
109
+ "**"
110
+ when :blockquote
111
+ # remove leading newline
112
+ type.children.first.value = ""
113
+ "> "
114
+ when :code
115
+ parent.eql?(:pre) ? " " : "`"
116
+ when :a
117
+ "["
118
+ when :img
119
+ "!["
120
+ when :hr
121
+ "----------\n\n"
122
+ else
123
+ @errors << "unknown start tag: "+type.name.to_s
124
+ ""
125
+ end
126
+ end
127
+
128
+ # Returns the closing markdown tag, like opening()
129
+ def ending(type, parent)
130
+ case type.name.to_sym
131
+ when :h1
132
+ " #\n\n"
133
+ when :h2
134
+ " ##\n\n"
135
+ when :p
136
+ parent.eql?(:root) ? "\n\n" : "\n"
137
+ when :ol
138
+ parent.eql?(:li) ? "" : "\n"
139
+ when :ul
140
+ parent.eql?(:li) ? "" : "\n"
141
+ when :em
142
+ "*"
143
+ when :strong
144
+ "**"
145
+ when :li
146
+ ""
147
+ when :blockquote
148
+ ""
149
+ when :code
150
+ parent.eql?(:pre) ? "" : "`"
151
+ when :a
152
+ @links << type.attribute('href').to_s
153
+ "][" + @links.size.to_s + "] "
154
+ when :img
155
+ @links << type.attribute('src').to_s
156
+ "" + type.attribute('alt').to_s + "][" + @links.size.to_s + "] "
157
+ "#{type.attribute('alt')}][#{@links.size}] "
158
+ else
159
+ @errors << " unknown end tag: "+type.name.to_s
160
+ ""
161
+ end
162
+ end
163
+
164
+ # Perform indent: two space, @indent times - quite simple! :)
165
+ def indent
166
+ str = ""
167
+ @indent.times do
168
+ str << " "
169
+ end
170
+ str
171
+ end
172
+
173
+ # Return the content of element, which should be just text.
174
+ # If its a code block to indent of 4 spaces.
175
+ # For block quotation add a leading '>'
176
+ def text_node(element, parent)
177
+ if element.name.to_sym.eql?(:code) and parent.eql?(:pre)
178
+ element.text.gsub("\n","\n ") << "\n"
179
+ elsif parent.eql?(:blockquote)
180
+ element.text.gsub!("\n ","\n>")
181
+ else
182
+ element.text
183
+ end
184
+ end
185
+
186
+ # Insert the mentioned reference style links.
187
+ def insert_links
188
+ @output << "\n"
189
+ @links.each_index do |index|
190
+ @output << " [#{index+1}]: #{@links[index]}\n"
191
+ end
192
+ end
193
+
194
+ # Print out all errors, that occured and have been written to @errors.
195
+ def print_errors
196
+ @errors.each do |error|
197
+ puts error
198
+ end
199
+ end
200
+
201
+ # Perform a benchmark on a given string n-times.
202
+ def speed_benchmark(string, n)
203
+ initialize()
204
+ bm(15) do |test|
205
+ test.report("reverse markdown:") { n.times do; parse_string(string); initialize(); end; }
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,260 @@
1
+ module Granify
2
+ module Helper
3
+ class Evernote
4
+ @@developer_token = ENV["EVERTILS_TOKEN"]
5
+
6
+ def initialize
7
+ authenticate
8
+ end
9
+
10
+ def authenticate
11
+ if @@developer_token.nil?
12
+ Notify.error("Evernote developer token is not configured properly!\n$EVERTILS_TOKEN == nil")
13
+ end
14
+
15
+ @evernoteHost = "www.evernote.com"
16
+ userStoreUrl = "https://#{@evernoteHost}/edam/user"
17
+
18
+ userStoreTransport = Thrift::HTTPClientTransport.new(userStoreUrl)
19
+ userStoreProtocol = Thrift::BinaryProtocol.new(userStoreTransport)
20
+ @@user = ::Evernote::EDAM::UserStore::UserStore::Client.new(userStoreProtocol)
21
+ @@shardId = user.shardId
22
+
23
+ versionOK = @@user.checkVersion("evernote-data",
24
+ ::Evernote::EDAM::UserStore::EDAM_VERSION_MAJOR,
25
+ ::Evernote::EDAM::UserStore::EDAM_VERSION_MINOR)
26
+
27
+ @version = "#{::Evernote::EDAM::UserStore::EDAM_VERSION_MAJOR}.#{::Evernote::EDAM::UserStore::EDAM_VERSION_MINOR}"
28
+
29
+ if !versionOK
30
+ Notify.error("Evernote API requires an update. Latest version is #{@version}")
31
+ end
32
+
33
+ noteStoreUrl = @@user.getNoteStoreUrl(@@developer_token)
34
+
35
+ noteStoreTransport = Thrift::HTTPClientTransport.new(noteStoreUrl)
36
+ noteStoreProtocol = Thrift::BinaryProtocol.new(noteStoreTransport)
37
+ @@store = ::Evernote::EDAM::NoteStore::NoteStore::Client.new(noteStoreProtocol)
38
+ end
39
+
40
+ def info
41
+ {
42
+ :user => "#{user.name} (#{user.username}) - ID##{user.id}",
43
+ :shard => user.shardId,
44
+ :api_version => @version,
45
+ }
46
+ end
47
+
48
+ def notebooks
49
+ @@store.listNotebooks(@@developer_token)
50
+ end
51
+
52
+ def tags
53
+ @@store.listTags(@@developer_token)
54
+ end
55
+
56
+ def user
57
+ @@user.getUser(@@developer_token)
58
+ end
59
+
60
+ def notebook_by_name(name = $request.command)
61
+ output = {}
62
+ notebooks.each do |notebook|
63
+ if notebook.name == name.to_s.capitalize
64
+ output = notebook
65
+ end
66
+ end
67
+
68
+ output
69
+ end
70
+
71
+ def notes_by_notebook(name)
72
+ output = {}
73
+ notebooks.each do |notebook|
74
+ if notebook.name.to_s == name.capitalize.to_s
75
+ filter = ::Evernote::EDAM::NoteStore::NoteFilter.new
76
+ filter.notebookGuid = notebook.guid
77
+
78
+ result = ::Evernote::EDAM::NoteStore::NotesMetadataResultSpec.new
79
+ result.includeTitle = true
80
+ result.includeUpdated = true
81
+ result.includeTagGuids = true
82
+
83
+ #output = @@store.findNotesMetadata(@@developer_token, filter, 0, 400, result)
84
+ notes(nil, notebook.guid).notes.each do |note|
85
+ output[note.guid] = @@store.getNoteContent(@@developer_token, note.guid)
86
+ end
87
+ end
88
+ end
89
+
90
+ output
91
+ end
92
+
93
+ def notebooks_by_stack(stack)
94
+ output = {}
95
+ notebooks.each do |notebook|
96
+ if notebook.stack == stack
97
+ #output[notebook.name] = []
98
+
99
+ filter = ::Evernote::EDAM::NoteStore::NoteFilter.new
100
+ filter.notebookGuid = notebook.guid
101
+
102
+ result = ::Evernote::EDAM::NoteStore::NotesMetadataResultSpec.new
103
+ result.includeTitle = true
104
+ result.includeUpdated = true
105
+ result.includeTagGuids = true
106
+
107
+ notes = @@store.findNotesMetadata(@@developer_token, filter, 0, 400, result)
108
+ output[notebook.name] = notes
109
+ end
110
+ end
111
+
112
+ output
113
+ end
114
+
115
+ def note(title_filter = nil, notebook_filter = nil)
116
+ filter = ::Evernote::EDAM::NoteStore::NoteFilter.new
117
+ filter.words = "intitle:#{title_filter}" if title_filter
118
+ filter.notebookGuid = notebook_filter if notebook_filter
119
+
120
+ @@store.findNotes(@@developer_token, filter, nil, 1)
121
+ end
122
+
123
+ def notes(title_filter = nil, notebook_filter = nil)
124
+ filter = ::Evernote::EDAM::NoteStore::NoteFilter.new
125
+ filter.words = "intitle:#{title_filter}" if title_filter
126
+ filter.notebookGuid = notebook_filter if notebook_filter
127
+
128
+ @@store.findNotes(@@developer_token, filter, nil, 300)
129
+ end
130
+
131
+ def note_exists
132
+ note = note(date_templates[$request.command])
133
+ note.totalNotes > 0
134
+ end
135
+
136
+ def create_note(title = date_templates[$request.command], body = template_contents, p_notebook_name = nil, file = nil, share_note = false)
137
+ if $request.command == :weekly && !Date.today.monday?
138
+ Notify.error("Sorry, you can only create new weekly logs on Mondays")
139
+ end
140
+
141
+ # Create note object
142
+ our_note = ::Evernote::EDAM::Type::Note.new
143
+ our_note.resources = []
144
+ our_note.tagNames = []
145
+
146
+ # only join when required
147
+ if body.is_a? Array
148
+ body = body.join
149
+ end
150
+
151
+ # a file was requested, lets prepare it for storage
152
+ if !file.nil?
153
+ media_resource = EvernoteENML.new(file)
154
+ body.concat(media_resource.embeddable_element)
155
+ our_note.resources << media_resource.element
156
+ end
157
+
158
+ n_body = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
159
+ n_body += "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">"
160
+ n_body += "<en-note>#{body}</en-note>"
161
+
162
+ # setup note properties
163
+ our_note.title = title
164
+ our_note.content = n_body
165
+
166
+ # properly tag logs
167
+ case $request.command
168
+ when :weekly
169
+ our_note.tagNames << "week-#{Time.now.strftime('%V').to_i}"
170
+ when :monthly
171
+ our_note.tagNames << "month-#{Time.now.strftime('%-m').to_i}"
172
+ end
173
+
174
+ if p_notebook_name.nil?
175
+ parent_notebook = notebook_by_name
176
+ else
177
+ parent_notebook = notebook_by_name(p_notebook_name)
178
+ end
179
+
180
+ ## parent_notebook is optional; if omitted, default notebook is used
181
+ if parent_notebook.is_a? ::Evernote::EDAM::Type::Notebook
182
+ our_note.notebookGuid = parent_notebook.guid
183
+ end
184
+
185
+ ## Attempt to create note in Evernote account
186
+ begin
187
+ output = {}
188
+ output[:note] = @@store.createNote(@@developer_token, our_note)
189
+
190
+ if share_note
191
+ shareKey = @@store.shareNote(@@developer_token, output[:note].guid)
192
+ output[:share_url] = "https://#{@evernoteHost}/shard/#{@@shardId}/sh/#{output[:note].guid}/#{shareKey}"
193
+ end
194
+ rescue ::Evernote::EDAM::Error::EDAMUserException => edue
195
+ ## Something was wrong with the note data
196
+ ## See EDAMErrorCode enumeration for error code explanation
197
+ ## http://dev.evernote.com/documentation/reference/Errors.html#Enum_EDAMErrorCode
198
+ Notify.error "EDAMUserException: #{edue}"
199
+ rescue ::Evernote::EDAM::Error::EDAMNotFoundException => ednfe
200
+ ## Parent Notebook GUID doesn't correspond to an actual notebook
201
+ Notify.error "EDAMNotFoundException: Invalid parent notebook GUID"
202
+ end
203
+
204
+ # A parent notebook object exists, otherwise it was saved to the default
205
+ # notebook
206
+ if parent_notebook.is_a? ::Evernote::EDAM::Type::Notebook
207
+ Notify.success("#{parent_notebook.stack}/#{parent_notebook.name}/#{our_note.title} created")
208
+ else
209
+ Notify.success("DEFAULT_NOTEBOOK/#{our_note.title} created")
210
+ end
211
+
212
+ output
213
+ end
214
+
215
+ def generate_stats
216
+ {
217
+ "Statistic description" => 9845.3894
218
+ }
219
+ end
220
+
221
+ private
222
+ # Legacy notes will have single/double character denotations for day of
223
+ # week, this maps them.
224
+ def day_of_week
225
+ case Date.today.strftime('%a')
226
+ when 'Mon'
227
+ :M
228
+ when 'Tue'
229
+ :Tu
230
+ when 'Wed'
231
+ :W
232
+ when 'Thu'
233
+ :Th
234
+ when 'Fri'
235
+ :F
236
+ end
237
+ end
238
+
239
+ def template_contents
240
+ if Date.today.friday? && $request.command == :daily
241
+ # Friday uses a slightly different template
242
+ IO.readlines("#{Granify::TEMPLATE_DIR}#{$request.command}-friday.enml").join("").gsub!("\n", '')
243
+ else
244
+ IO.readlines("#{Granify::TEMPLATE_DIR}#{$request.command}.enml").join("").gsub!("\n", '')
245
+ end
246
+ end
247
+
248
+ def date_templates
249
+ now = DateTime.now
250
+ end_of_week = now + 4 # days
251
+
252
+ {
253
+ :daily => "Daily Log [#{now.strftime('%B %-d')} - #{day_of_week}]",
254
+ :weekly => "Weekly Log [#{now.strftime('%B %-d')} - #{end_of_week.strftime('%B %-d')}]",
255
+ :monthly => "Monthly Log [#{now.strftime('%B %Y')}]"
256
+ }
257
+ end
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,39 @@
1
+ module Granify
2
+ module Helper
3
+ class Generate
4
+ def self.format_date(title)
5
+ if title =~ /Daily/
6
+ resp = /Daily Log \[([A-Z].*) \- [A-Z]\]/.match(title)
7
+
8
+ if resp
9
+ Time.parse($1)
10
+ end
11
+ elsif title =~ /Weekly/
12
+ resp = /Weekly Log \[([A-Z].*) (\d+) \- (\d+)\]/.match(title)
13
+
14
+ if resp
15
+ first = Time.parse($1 +" "+ $2)
16
+ second = Time.parse($1 +" "+ $3)
17
+
18
+ [first, second]
19
+ end
20
+ elsif title =~ /Monthly/
21
+ resp = /Monthly Log \[([A-Z].*) (\d+)\]/.match(title)
22
+
23
+ if resp
24
+ Time.parse($1 +" "+ $2)
25
+ end
26
+ elsif title =~ /Quarterly/
27
+ resp = /Quarterly Log \[([A-Z].*) \- ([A-Z].*) (\d+)\]/.match(title)
28
+
29
+ if resp
30
+ first = Time.parse($1 +" "+ $3)
31
+ second = Time.parse($2 +" "+ $3)
32
+
33
+ [first, second]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ module Granify
2
+ module Helper
3
+ class Time
4
+ def self.human_readable(start, finish)
5
+ seconds = finish.to_f - start.to_f
6
+
7
+ if seconds < 60
8
+ "No time at all!"
9
+ else
10
+ minutes = (seconds / 60).round(1)
11
+ if minutes < 1
12
+ "#{minutes} minute"
13
+ else
14
+ "#{minutes} minutes"
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.formatted(time = nil)
20
+ if time.nil?
21
+ time = ::Time.now
22
+ end
23
+
24
+ time.strftime("%e/%-m/%Y @ %I:%M:%S%P")
25
+ end
26
+ end
27
+ end
28
+ end
data/lib/log.rb ADDED
@@ -0,0 +1,111 @@
1
+ module Granify
2
+ class Log
3
+ attr_accessor :path, :total_files_processed
4
+ attr_reader :template
5
+
6
+ def initialize(*args)
7
+ if args.length == 0
8
+ # default log
9
+ @template = "#{Granify::LOG_DIR}/%s"
10
+ @path = sprintf(@template, "default.log")
11
+ else
12
+ @template = "#{Granify::LOG_DIR}/%s/%s-%s.log"
13
+
14
+ format(args)
15
+ end
16
+
17
+ @path
18
+ end
19
+
20
+ def stale?
21
+ Time.now - last_write > 60
22
+ end
23
+
24
+ def exists?
25
+ File.exist? @path
26
+ end
27
+
28
+ def delete
29
+ if exists?
30
+ File.delete @path
31
+ end
32
+
33
+ Notify.sinfo("Deleting truncated log file #{@path}")
34
+ @path = nil
35
+ end
36
+
37
+ def num_lines
38
+ File.foreach(@path).inject(0) {|c, line| c+1}
39
+ end
40
+
41
+ def faults
42
+ matchdata = { :errors => 0, :warnings => 0, :total => 0 }
43
+
44
+ begin
45
+ case @log_type
46
+ when :js
47
+ last_line = IO.readlines(@path)[-5].chomp
48
+ matches = last_line.match(/(\d+) example, (\d+) failure/)
49
+
50
+ if matches
51
+ matchdata[:errors] += matches[2].to_i
52
+ end
53
+ when :coffeelint
54
+ total = 0
55
+ File.foreach(@path) do |line|
56
+ matches = line.match(/Lint\! » (\d+) errors and (\d+)/)
57
+
58
+ if matches
59
+ matchdata[:errors] += matches[1].to_i
60
+ matchdata[:warnings] += matches[2].to_i
61
+ end
62
+ end
63
+ when :ruby
64
+ last_line = IO.readlines(@path)[-1].chomp
65
+ matches = last_line.match(/(\d+) files inspected\, (\d+)/)
66
+
67
+ if matches
68
+ matchdata[:errors] += matches[2].to_i
69
+ matchdata[:total] += matches[1].to_i
70
+ end
71
+ when :goliath
72
+
73
+ else
74
+ raise ArgumentError, "Unknown log type - #{log_type}"
75
+ end
76
+ rescue => e
77
+ Notify.error(e.message)
78
+ end
79
+
80
+ matchdata
81
+ end
82
+
83
+ def to_s
84
+ @path
85
+ end
86
+
87
+ private
88
+ def format(args)
89
+ @identifier = args[2]
90
+
91
+ @path = sprintf(@template,
92
+ @identifier,
93
+ args[0],
94
+ args[1].strftime('%Y-%m-%d-%T')
95
+ )
96
+
97
+ if !File.exists? @path
98
+ Utils.generate_path(args[0], args[1].strftime('%Y-%m-%d-%T'), @identifier)
99
+ end
100
+
101
+ # create the log file, populate it with temporary data
102
+ File.open(@path, 'w+') do |f|
103
+ f.write("Command output will be logged below when it finishes running\n")
104
+ end
105
+ end
106
+
107
+ def last_write
108
+ File.mtime(@path)
109
+ end
110
+ end
111
+ end