evertils 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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