Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

2199 - 3 #277

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 2199/3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
12 changes: 12 additions & 0 deletions 2199/3/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
source 'https://rubygems.org'

gem 'capybara'
gem 'dotenv'
gem 'ohm'
gem 'poltergeist'
gem 'redis'
gem 'shotgun'
gem 'sinatra'
group :development do
gem 'pry'
end
75 changes: 75 additions & 0 deletions 2199/3/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
GEM
remote: https://rubygems.org/
specs:
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
capybara (3.4.1)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
xpath (~> 3.1)
cliver (0.3.2)
coderay (1.1.2)
dotenv (2.4.0)
hiredis (0.6.1)
method_source (0.9.0)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
mustermann (1.0.2)
nest (3.1.1)
redic
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
ohm (3.1.1)
nest (~> 3)
redic (~> 1.5.0)
stal
poltergeist (1.18.1)
capybara (>= 2.1, < 4)
cliver (~> 0.3.1)
websocket-driver (>= 0.2.0)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
public_suffix (3.0.2)
rack (2.0.5)
rack-protection (2.0.3)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
redic (1.5.0)
hiredis
redis (4.0.1)
shotgun (0.9.2)
rack (>= 1.0)
sinatra (2.0.3)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.3)
tilt (~> 2.0)
stal (0.3.0)
redic (~> 1.5)
tilt (2.0.8)
websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
xpath (3.1.0)
nokogiri (~> 1.8)

PLATFORMS
ruby

DEPENDENCIES
capybara
dotenv
ohm
poltergeist
pry
redis
shotgun
sinatra

BUNDLED WITH
1.16.1
8 changes: 8 additions & 0 deletions 2199/3/app/capybara_initializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require 'capybara'
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, js_errors: false)
end

# Configure Capybara to use Poltergeist as the driver
Capybara.default_driver = :poltergeist
52 changes: 52 additions & 0 deletions 2199/3/app/comment_analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'dotenv/load'
# analyze comment
class CommentsAnalyzer
ACCESS_KEY = ENV['ACCESS_KEY']
AZURE_ENDPOINT = 'https://westcentralus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment'.freeze

def initialize(texts)
@texts = texts
end

# :reek:NestedIterators
# rubocop:disable Metrics/AbcSize
def analyze
JSON.parse(run_request.body)['documents'].each_with_object([]) do |result, store|
document = documents.find { |doc| doc[:id].to_s == result['id'] }
store << {
text: document[:text],
rating: result['score'] * 200 - 100
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formula is too magical for me (where do the numbers (*200 - 100 ) come from?)
Let's move it to separate method and describe a little bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We get a decimal number and this formula needed to lead it to necessary range

}
end
end
# rubocop:enable Metrics/AbcSize

def azure_endpoint
@azure_endpoint ||= URI(AZURE_ENDPOINT)
end

# :reek:FeatureEnvy
def request
Net::HTTP::Post.new(azure_endpoint).tap do |request|
request['Content-Type'] = 'application/json'
request['Ocp-Apim-Subscription-Key'] = ACCESS_KEY
request.body = serialized_texts_json
end
end

def documents
@documents ||= @texts.map do |text|
{ id: text.object_id, language: 'ru', text: text }
end
end

def serialized_texts_json
{ documents: documents }.to_json
end

def run_request
https = Net::HTTP.new(azure_endpoint.host, azure_endpoint.port)
https.use_ssl = true
https.request(request)
end
end
14 changes: 14 additions & 0 deletions 2199/3/app/comment_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# parse node and find text and rating
class CommentParser
def initialize(node)
@node = node
end

def text
@text ||= @node.all('.news-comment__speech > div > p').map(&:text).reduce(&:+)
end

def rating
@rating ||= @node.all('.like-control span').sum { |mark| mark.text.to_i }
end
end
53 changes: 53 additions & 0 deletions 2199/3/app/onliner_page_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'pry'
require_relative '../app/comment_parser'
require_relative 'capybara_initializer'

# class for parsing
class OnlinerPage
COMMENTS_TO_TAKE = 50

def initialize(link)
@link = link
end

def browser
@browser ||= Capybara.current_session
end

# :reek:TooManyStatements
def visit_page
attempts = 0
begin
browser.visit(@link)
rescue StandardError => exception
attempts += 1
sleep(2 * attempts)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you choose 2*attempts formula?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use exponential backoff. I did something wrong??

retry if attempts <= 3
raise exception
end
end

def show_all_comments
browser.find('.button-style.button-style_subsidiary.button-style_big.news-form__button'\
'.news-form__button_width_full.news-form__button_font-weight_semibold').click
sleep(5)
end

def top_comment_texts
visit_page
show_all_comments
top_comments.map(&:text)
end

def comment_nodes
browser.all('[id^="comment-"]').drop(1)
end

def comments
comment_nodes.map { |comment_node| CommentParser.new(comment_node) }
end

def top_comments
comments.sort_by(&:rating).reverse.first(COMMENTS_TO_TAKE)
end
end
10 changes: 10 additions & 0 deletions 2199/3/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'sinatra/base'
require 'ohm'
require_relative 'app/onliner_page_parser'
require_relative 'app/comment_analyzer'
require_relative 'controllers/application_controller'
require_relative 'models/page'
require_relative 'models/comment'

# Dir.glob('./{helpers, controllers}/*.rb').each { |file| require file }
map('/') { run ApplicationController }
38 changes: 38 additions & 0 deletions 2199/3/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'sinatra'
require 'pry'
# class ApplicationController
class ApplicationController < Sinatra::Base
set views: 'views/'

get '/' do
redirect '/pages'
end

get '/pages' do
@pages = Page.all
erb :index
end

get '/pages/new' do
erb :new
end

get '/pages/:id' do
@page = Page[params[:id]]
erb :show
end

post '/pages' do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's meditate on this endpoint.

I'd like to have less code in here.
Something like this:

#app.rb
post '/pages' do
   if Page.exists?(link: link)
     @page = Page.find(link: link)
   else
     @page = Page.create_from(link: link)
   end
   erb :show
end

It's just an idea, but it will make your post action more verbose and will hide all Page and Comment creation logic.

link = params[:link]
@page = Page.find(link: link).first
unless @page
comment_texts = OnlinerPage.new(link).top_comment_texts
comments_with_score = CommentsAnalyzer.new(comment_texts).analyze
@page = Page.create(link: link)
comments_with_score.each do |comment_data|
Comment.create(comment_data.merge(page: @page))
end
end
erb :show
end
end
6 changes: 6 additions & 0 deletions 2199/3/models/comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Comment model
class Comment < Ohm::Model
attribute :text
attribute :rating
reference :page, 'Page'
end
10 changes: 10 additions & 0 deletions 2199/3/models/page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Page model
class Page < Ohm::Model
attribute :link
collection :comments, 'Comment'
index :link

def rating
comments.sum { |comment| comment.rating.to_f } / comments.count
end
end
20 changes: 20 additions & 0 deletions 2199/3/views/index.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<a href="/pages/new">Analyse new link!! </a>
<table class="table table-striped table-hover">
<thead>
<tr>
<th> Number </th>
<th> Link </th>
<th> Rating </th>
</tr>
</thead>
<tbody>
<% @pages.each do |page| %>
<tr>
<td><a href="<%= url("/pages/#{page.id}") %>"><%= page.id %></a></td>
<td><a href="<%= page.link %>"><%= page.link %></a></td>
<td><%= page.rating %></td>
</tr>
<% end %>
</tbody>
</tbody>
</table>
10 changes: 10 additions & 0 deletions 2199/3/views/layout.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>Analyzer</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" />
</head>
<body>
<%= yield %>
</body>
</html>
6 changes: 6 additions & 0 deletions 2199/3/views/new.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<form method="post" action=<%= url('/pages') %>>
<label for="link">Link:</label>
<input type="text" name="link">
<input type="submit">
</form>
<a href="/">Back to Index</a>
30 changes: 30 additions & 0 deletions 2199/3/views/show.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<table>
<tr>
<td><strong>Link:</strong></td>
<td><%= @page.link %></td>
</tr>
<tr>
<td><strong>Average rating:</strong></td>
<td><%= @page.rating.to_i %></td>
</tr>
</table>

<table class="table table-striped table-hover">
<thead>
<tr>
<th> Rating </th>
<th> Comment </th>
</tr>
</thead>
<tbody>
<% @page.comments.each do |comment| %>
<tr>
<td><%= "#{comment.rating.to_i}" %></td>
<td><%= "#{comment.text}" %></td>
</tr>
<% end %>
</tbody>
</table>


<a href="/">Back to Index</a>