dailyrep 1.0.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.
@@ -0,0 +1,77 @@
1
+ require 'open-uri'
2
+ require 'nokogiri'
3
+ require 'json'
4
+ require 'net/http'
5
+
6
+ require_relative 'Notificator'
7
+ require_relative 'IndexWriter'
8
+ require_relative 'Dbops'
9
+ require_relative 'Trackable'
10
+
11
+ module DailyRep
12
+ class IBrowser
13
+ include Dbops
14
+ include Trackable
15
+ attr_reader :entity
16
+
17
+ @@notificators = Notificator.get_clients
18
+ @@index_writer = IndexWriter.new
19
+ @@app_cycle_methods = ['process', 'write_to_db', 'web_reload', 'notify']
20
+
21
+ def initialize *inputs
22
+ @entity = self.class.to_s.downcase.split('::').last
23
+ end
24
+
25
+ def push_note note
26
+ @@notificators.each { |client|
27
+ client.push_note(note)
28
+ }
29
+ # @@client.push_note(nil, @@note_title, note)
30
+ end
31
+
32
+ def set_html entity, values, push=0
33
+ @@index_writer.set_html entity, values, push
34
+ end
35
+
36
+ def set_html_with_diff entity, scope, diff, reset=false
37
+ @@index_writer.set_html_with_diff entity, scope, diff, reset
38
+ end
39
+
40
+ def self.write_html
41
+ @@index_writer.write_html
42
+ end
43
+
44
+
45
+ def get_source_http source, val_scope
46
+ page = Nokogiri::HTML(open(source.to_s))
47
+ page.css(val_scope)
48
+ end
49
+
50
+ def get_source_json source
51
+ uri = URI(source)
52
+ out_json = Net::HTTP.get(uri)
53
+ JSON.parse(out_json)
54
+ end
55
+
56
+ def method_missing(meth, *args, &block)
57
+ if @@app_cycle_methods.include?(meth.to_s)
58
+ begin
59
+ self.log_msg meth.to_s
60
+ block.call
61
+ rescue Exception => e
62
+ log_error e
63
+ end
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ def run
70
+ self.process
71
+ self.write_to_db if Configer.is_phase_enabled?('write_to_db')
72
+ self.notify if Configer.is_phase_enabled?('notify')
73
+ self.web_reload if Configer.is_phase_enabled?('web_reload')
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,55 @@
1
+ module DailyRep
2
+ class IndexWriter
3
+
4
+ def initialize(index_path=Configer.index_path)
5
+ @out_html_path = index_path.to_s
6
+ @out_html = Nokogiri::HTML(File.open(@out_html_path))
7
+ end
8
+
9
+ def write_html
10
+ @out_html.css("#refresh")[0].content = Time.now
11
+ File.open(@out_html_path , 'w') { |file| file.write(@out_html.to_html) }
12
+ end
13
+
14
+ def set_html entity, params, notif=0
15
+ entity = entity.downcase
16
+ params.each do |key, val|
17
+ @out_html.css("##{entity}_#{key}")[0].content = val
18
+ end
19
+ @out_html.css("##{entity}_refresh")[0].content = Time.now
20
+ @out_html.css("##{entity}_notif")[0].content = Time.now if notif == 1
21
+ end
22
+
23
+ def set_html_with_diff entity, scope, diff, reset=false
24
+ entity = entity.downcase
25
+ target_div = @out_html.css(scope + " .list-group .list-group-item")
26
+ new_root_node = Nokogiri::XML::Node.new('div', @out_html)
27
+ new_root_node['class'] = 'list-group'
28
+
29
+
30
+ diff.each do |diff_el|
31
+ target_div.each { |node|
32
+ if diff_el[0] == '+' && node['href'].include?(diff_el[2].first[0]) then
33
+ target_div.delete(node)
34
+ end
35
+ }
36
+ end
37
+
38
+ new_root_node.add_child(target_div.unlink) if not reset
39
+
40
+ diff.each do |diff_el|
41
+ if diff_el[0] == '-' then
42
+ nodeel = Nokogiri::XML::Node.new('a', @out_html)
43
+ nodeel['href'] = diff_el[2].first[0]
44
+ nodeel['class'] = 'list-group-item'
45
+ nodeel.content = diff_el[2].first[1]
46
+ new_root_node.add_child(nodeel)
47
+ end
48
+ end
49
+
50
+ @out_html.at_css(scope + " .list-group").replace(new_root_node)
51
+
52
+ @out_html.css("##{entity}_refresh")[0].content = Time.now
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,135 @@
1
+
2
+ class String
3
+ def to_bool
4
+ return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
5
+ return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
6
+ raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
7
+ end
8
+ end
9
+
10
+ ActiveRecord::Base.establish_connection(
11
+ :adapter => 'sqlite3',
12
+ :database => DATABASE,
13
+ :pool => 5,
14
+ :timeout => 5000
15
+ )
16
+
17
+ def generate_database
18
+ ActiveRecord::Schema.define(version: 201502161646) do
19
+ create_table "Histories", force: false do |t|
20
+ t.text "entity"
21
+ t.text "parameter"
22
+ t.text "val"
23
+ t.datetime "created_at"
24
+ t.datetime "notified"
25
+ end
26
+ end
27
+ end
28
+
29
+ tables = ActiveRecord::Base.connection.execute(
30
+ "SELECT name FROM sqlite_master WHERE type='table' and name = 'Histories'; "
31
+ )
32
+
33
+ if tables.empty?
34
+ generate_database
35
+ else
36
+ generate_database unless tables[0]["name"] == 'Histories'
37
+ end
38
+
39
+
40
+ class History < ActiveRecord::Base
41
+ end
42
+
43
+ module DailyRep
44
+
45
+ START_TIME = Time.now
46
+
47
+ #constants
48
+ P_GO_FILE_PATH = 'GoFilePath'
49
+ P_PUSH_CLIENTS = 'Push clients'
50
+ P_PUSH_NOTE_TITLE = 'Push title'
51
+ P_RAISE_ERRORS = 'Raise errors'
52
+
53
+ PA_ENTITIES = 'Entities'
54
+ PA_APP_CYCLE = 'app_cycle'
55
+ P_INDEX_PATH = 'Index path'
56
+ P_NOTIF_DELTA = 'notification delta'
57
+ P_ENABLED = 'enabled'
58
+ P_TIME_FRAME = 'time frame'
59
+ P_SOURCE = 'source'
60
+ P_SEARCH_STRS = 'searchStr'
61
+
62
+
63
+
64
+
65
+
66
+ class Configer
67
+ def self.set_index_file file
68
+ @@config_json[P_INDEX_PATH] = file
69
+ end
70
+
71
+ File.open(CONFIG_FILE) {|file| @@config_json = JSON.parse(file.read) }
72
+
73
+ def self.index_path
74
+ @@config_json[P_INDEX_PATH]
75
+ end
76
+
77
+ def self.push_note_title
78
+ @@config_json[P_PUSH_NOTE_TITLE]
79
+ end
80
+
81
+ def self.raise_error?
82
+ @@config_json[P_RAISE_ERRORS].to_bool
83
+ end
84
+
85
+ def self.exctract_entity_params entity, parameter_name
86
+ @@config_json[PA_ENTITIES][entity][parameter_name]
87
+ end
88
+
89
+ def self.is_phase_enabled? phase
90
+ @@config_json[PA_APP_CYCLE][phase].to_bool
91
+ end
92
+ #+
93
+ def self.is_entity_enabled? entity
94
+ time_arr = @@config_json[PA_ENTITIES][entity][P_TIME_FRAME].split('..').map { |el|
95
+ el.to_i
96
+ }
97
+ (time_arr[0]..time_arr[1]).include?(Time.now.hour) &&
98
+ @@config_json[PA_ENTITIES][entity][P_ENABLED].to_bool
99
+ end
100
+ #+
101
+ def self.enabled_entities
102
+ @@config_json[PA_ENTITIES].keys.map { |entity|
103
+ entity.downcase.capitalize if is_entity_enabled?(entity)
104
+ }
105
+ end
106
+ #+
107
+ def self.get_client_tokens
108
+ @@config_json[P_PUSH_CLIENTS].values
109
+ end
110
+
111
+ # def self.include_search_strs? entity
112
+ # result = false
113
+ # result = true if @@config_json[PA_ENTITIES][entity][P_SEARCH_STRS]
114
+ # return result
115
+ # end
116
+
117
+ def self.get_search_strs entity
118
+ @@config_json[PA_ENTITIES][entity][P_SEARCH_STRS]
119
+ end
120
+
121
+ def self.method_missing(meth, *args, &block)
122
+ method_inputs = meth.to_s.split('_')
123
+ out_param = ''
124
+ @@config_json[PA_ENTITIES].keys.each { |key|
125
+ if method_inputs[0] == key.downcase
126
+ out_param = self.exctract_entity_params method_inputs[0], method_inputs[1]
127
+ end
128
+ }
129
+ super if not @@config_json[PA_ENTITIES].keys.include?(method_inputs[0])
130
+ return out_param
131
+ end
132
+
133
+ end
134
+
135
+ end
@@ -0,0 +1,23 @@
1
+ require 'washbullet'
2
+ module DailyRep
3
+
4
+ class Notificator
5
+ def self.get_clients
6
+ result = []
7
+ result = Configer.get_client_tokens.map { |token|
8
+ new token
9
+ }
10
+ end
11
+
12
+ def initialize(push_token, note_title=Configer.push_note_title)
13
+ @client = Washbullet::Client.new(push_token)
14
+ @note_title = note_title
15
+ end
16
+
17
+ def push_note note
18
+ @client.push_note(nil, @note_title, note)
19
+ end
20
+ end
21
+
22
+
23
+ end
@@ -0,0 +1,14 @@
1
+ module DailyRep
2
+ module Trackable
3
+ def log_msg entity, add_param='', phase
4
+ p "#{entity} (#{add_param}), stage: #{phase} at #{Time.now}"
5
+ end
6
+
7
+ def log_error e
8
+ # p " ERROR!: #{e.message}"
9
+ # p " ERROR_MSG: #{e.backtrace.inspect}"
10
+ raise
11
+ # if Configer.raise_error?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+ require_relative '../Dbops'
2
+
3
+ module DailyRep
4
+ module Entities
5
+
6
+
7
+ class Doujob < IBrowser
8
+ attr_reader :diff
9
+ include Dbops
10
+
11
+ def self.create
12
+ result = []
13
+ result = Configer.get_search_strs('doujob').map { |str|
14
+ new str
15
+ }
16
+ end
17
+
18
+ def initialize search, source=Configer.doujob_source
19
+ super
20
+ @search=search.gsub(' ','+')
21
+ @dou_search_path = source + @search
22
+ @vacancy_scope = 'div#vacancyListId li'
23
+ @parameter = 'title-href'
24
+ @diff =[]
25
+ end
26
+
27
+ def log_msg phase
28
+ super @entity, @search, phase
29
+ end
30
+
31
+ #overwriting
32
+ def process
33
+ super do
34
+ out_set = get_source_http @dou_search_path, @vacancy_scope
35
+ @out_vac = out_set.map do |a|
36
+ [a.css('.title .vt').attribute("href").text, "#{a.css('.company').text} : #{a.css('.title .vt').text}"]
37
+ end
38
+ out_hash = @out_vac.map { |el|
39
+ {el[0] => el[1]}
40
+ }
41
+ hist_hash = read_hist_hash
42
+ @diff = HashDiff.diff(out_hash, hist_hash)
43
+ end
44
+ end
45
+
46
+ #overwriting
47
+ def notify
48
+ super do
49
+ @diff.each { |lv1|
50
+ push_note("New vacancy at DOU: #{lv1[2]} - #{lv1[3]} ") if lv1[0] == "-"
51
+ push_note("DOU vacancy has gone: #{lv1[2]} - #{lv1[3]} ") if lv1[0] == "+"
52
+ }
53
+ end
54
+ end
55
+ #overwriting
56
+ def write_to_db
57
+ super do
58
+ @out_vac.each { |vac|
59
+ write_hist @entity, @parameter, "#{vac[0]}=>#{vac[1]}"
60
+ }
61
+ end
62
+ end
63
+ #overwriting
64
+ def web_reload
65
+ super do
66
+ set_html_with_diff @entity, '#doujobs', @diff
67
+ end
68
+ end
69
+
70
+ private
71
+ def read_hist_hash
72
+ maxtime = History.where(entity: @entity, parameter: @parameter).where("created_at > ?", Time.now - 6.hours).maximum("created_at")
73
+ maxtime||= Time.now
74
+ maxtime = maxtime.to_datetime
75
+ History.select("val").where(entity: @entity, parameter: @parameter).where("created_at > ?", maxtime - 1.minute).map { |row|
76
+ el_ar = row.val.split('=>')
77
+ {el_ar[0] => el_ar[1]}
78
+ }
79
+ end
80
+
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,87 @@
1
+ #encoding: utf-8
2
+ require 'active_support/core_ext'
3
+ module DailyRep
4
+ module Entities
5
+ class Kinozal < IBrowser
6
+ attr_reader :out_hash_clean, :to_notification
7
+
8
+ def self.create
9
+ result = []
10
+ result = Configer.get_search_strs('kinozal').map { |str|
11
+ new str
12
+ }
13
+ end
14
+
15
+ def initialize movie, source=Configer.kinozal_source, filters=Configer.kinozal_filters
16
+ super
17
+ @movie = movie
18
+ @search_path = source+@movie.gsub(' ','+') + filters
19
+ @scope = 'td.nam a'
20
+ @dirty_words = ['тизер', 'трейлер']
21
+ @out_hash_clean = {}
22
+ end
23
+
24
+ def log_msg phase
25
+ super @entity, @movie, phase
26
+ end
27
+
28
+ def process
29
+ super do
30
+ out_set = get_source_http @search_path, @scope
31
+ out_hash_dirty = {}
32
+ out_set.each { |el|
33
+ out_hash_dirty.merge!({ el['href'] => el.text.split(' / ')})
34
+ }
35
+ out_hash_dirty.each { |key, val|
36
+ exclude = 0
37
+ @dirty_words.each { |word|
38
+ if val[0].mb_chars.downcase.include?(word) then
39
+ exclude = 1
40
+ break
41
+ end
42
+ }
43
+ @out_hash_clean.merge!({key => val}) if exclude == 0
44
+ }
45
+ end
46
+ end
47
+
48
+ def write_to_db
49
+ super do
50
+ @to_notification = {}
51
+ @out_hash_clean.each { |key, val|
52
+ id = key.split('id=')[1]
53
+ if !check_row_exists(@entity, @movie, id) then
54
+ write_hist(@entity, @movie, id, 1)
55
+ @to_notification.merge!({key => [val[0], val.last]})
56
+ end
57
+ }
58
+ end
59
+ end
60
+
61
+ def notify
62
+ super do
63
+ @to_notification.each { |key, val|
64
+ note = "Kinozal
65
+ Movie appears: #{val[0]}
66
+ Quality: #{val.last}
67
+ Link: #{key}"
68
+ push_note note
69
+ }
70
+ end
71
+ end
72
+
73
+
74
+ def web_reload
75
+ super do
76
+ i = 0
77
+ diff = @to_notification.map { |key, val|
78
+ i = i+1
79
+ ['-', i , {'http://kinozal.tv/'+ key => val.join(' : ')} ]
80
+ }
81
+ set_html_with_diff @entity, '#kinozal', diff, true
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end