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

2333-3 #281

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions 2333/3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.rdb
config.yml
20 changes: 20 additions & 0 deletions 2333/3/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

source 'https://rubygems.org'
ruby '2.5.1'

group :development do
gem 'pry'
gem 'shotgun'
end

group :development, :production do
gem 'config'
gem 'json'
gem 'mechanize'
gem 'ohm'
gem 'puma'
gem 'rake'
gem 'sinatra'
gem 'sinatra-config-file'
end
139 changes: 139 additions & 0 deletions 2333/3/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (5.2.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
backports (3.11.3)
coderay (1.1.2)
concurrent-ruby (1.0.5)
config (1.7.0)
activesupport (>= 3.0)
deep_merge (~> 1.2.1)
dry-validation (>= 0.10.4)
connection_pool (2.2.2)
deep_merge (1.2.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
dry-configurable (0.7.0)
concurrent-ruby (~> 1.0)
dry-container (0.6.0)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (0.4.7)
concurrent-ruby (~> 1.0)
dry-equalizer (0.2.1)
dry-inflector (0.1.2)
dry-logic (0.4.2)
dry-container (~> 0.2, >= 0.2.6)
dry-core (~> 0.2)
dry-equalizer (~> 0.2)
dry-types (0.13.2)
concurrent-ruby (~> 1.0)
dry-container (~> 0.3)
dry-core (~> 0.4, >= 0.4.4)
dry-equalizer (~> 0.2)
dry-inflector (~> 0.1, >= 0.1.2)
dry-logic (~> 0.4, >= 0.4.2)
dry-validation (0.12.1)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.1, >= 0.1.3)
dry-core (~> 0.2, >= 0.2.1)
dry-equalizer (~> 0.2)
dry-logic (~> 0.4, >= 0.4.0)
dry-types (~> 0.13.1)
hiredis (0.6.1)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (1.0.1)
concurrent-ruby (~> 1.0)
json (2.1.0)
mechanize (2.7.6)
domain_name (~> 0.5, >= 0.5.1)
http-cookie (~> 1.0)
mime-types (>= 1.17.2)
net-http-digest_auth (~> 1.1, >= 1.1.1)
net-http-persistent (>= 2.5.2)
nokogiri (~> 1.6)
ntlm-http (~> 0.1, >= 0.1.1)
webrobots (>= 0.0.9, < 0.2)
method_source (0.9.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_portile2 (2.3.0)
minitest (5.11.3)
multi_json (1.13.1)
mustermann (1.0.2)
nest (3.1.1)
redic
net-http-digest_auth (1.4.1)
net-http-persistent (3.0.0)
connection_pool (~> 2.2)
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
ntlm-http (0.1.1)
ohm (3.1.1)
nest (~> 3)
redic (~> 1.5.0)
stal
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
puma (3.12.0)
rack (2.0.5)
rack-protection (2.0.3)
rack
rake (12.3.1)
redic (1.5.0)
hiredis
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)
sinatra-config-file (1.0)
sinatra-contrib
sinatra-contrib (2.0.3)
activesupport (>= 4.0.0)
backports (>= 2.8.2)
multi_json
mustermann (~> 1.0)
rack-protection (= 2.0.3)
sinatra (= 2.0.3)
tilt (>= 1.3, < 3)
stal (0.3.0)
redic (~> 1.5)
thread_safe (0.3.6)
tilt (2.0.8)
tzinfo (1.2.5)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
webrobots (0.1.2)

PLATFORMS
ruby

DEPENDENCIES
config
json
mechanize
ohm
pry
puma
rake
shotgun
sinatra
sinatra-config-file

RUBY VERSION
ruby 2.5.1p57

BUNDLED WITH
1.16.3
11 changes: 11 additions & 0 deletions 2333/3/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

require 'bundler'
Bundler.require(:development)

Dir.glob('./{controllers,models,helpers}/*.rb').each { |file| require file }
Article.redis = Redic.new('redis://127.0.0.1:6379/0')
Comment.redis = Redic.new('redis://127.0.0.1:6379/0')
use Rack::MethodOverride

map('/') { run ArticlesController }
11 changes: 11 additions & 0 deletions 2333/3/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

require 'erb'
require 'sinatra/config_file'

# application controller
class ApplicationController < Sinatra::Base
set :views, File.expand_path(File.join(__FILE__, '../../views'))
register Sinatra::ConfigFile
config_file '../config.yml'
end
45 changes: 45 additions & 0 deletions 2333/3/controllers/articles_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require_relative 'application_controller.rb'
require 'erb'

# articles controller
class ArticlesController < ApplicationController
get '/' do
redirect '/articles'
end

get '/articles' do
@articles = Article.all
erb :index
end

get '/articles/add' do
erb :add_article
end

post '/articles/add' do
Copy link
Collaborator

Choose a reason for hiding this comment

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

Stick to RESTful routes.
e.g.

  • POST '/articles'
  • GET '/article/:id' (as you've done already)

Article.all.each do |article|
article.delete if article.link == params[:link]
end
ArticleBuilder.new(params[:link], settings.access_key).create_article
redirect '/articles'
end

get '/articles/:id' do
@articles = Article.all
@article = @articles[params[:id]]
erb :comments
end

delete '/articles/:id' do
@article = Article.all[params[:id]]
@article.delete
redirect '/articles'
end

delete '/articles' do
Article.all.each(&:delete)
redirect '/articles'
end
end
24 changes: 24 additions & 0 deletions 2333/3/helpers/article_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

# the class that creates an article
class ArticleBuilder
def initialize(link, key)
Copy link
Collaborator

Choose a reason for hiding this comment

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

You pass key through all of the classes. Why?
You could just use it in one class responsible for comments analysis.

@link = link
@key = key
end

def create_article
article = Article.create(link: @link, rating: article_rating)
CommentsBuilder.new(comments_with_rating, article).create
end

private

def article_rating
comments_with_rating.map(&:last).reduce(:+) / comments_with_rating.size
end

def comments_with_rating
@comments_with_rating ||= CommentsParser.new(@link, @key).texts_with_rating
end
end
32 changes: 32 additions & 0 deletions 2333/3/helpers/article_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

# The class that parse url and take the comments
class ArticleParser
LIMIT = 50
def initialize(url)
@page = Mechanize.new.get(url)
@agent = Mechanize.new
end

def comments
@comments = JSON.parse(@agent.get(api_url + api_path).body)['comments']
end

private

def api_url
"https://comments.api.onliner.by/news/#{article_category}.post/"
end

def api_path
"#{article_id}/comments?limit=#{LIMIT}"
end

def article_id
@page.search('.news_view_count').attr('news_id').value
end

def article_category
@page.uri.to_s.slice(/people|tech|auto|realt/)
end
end
16 changes: 16 additions & 0 deletions 2333/3/helpers/comments_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal:true

# the class that creates a comments from articles
class CommentsBuilder
def initialize(comments, article)
@comments = comments
@article = article
end

def create
@comments.each do |comment|
@article.comments.add(Comment.create(text: comment.first,
rating: comment.last))
end
end
end
32 changes: 32 additions & 0 deletions 2333/3/helpers/comments_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require_relative 'article_parser.rb'
require_relative 'rating_counter.rb'

# The class that parse comments from article
class CommentsParser
def initialize(url, key)
@page = url
@key = key
end

def texts_with_rating
texts_from_comments.flatten.zip(comments_rating)
end

private

def comments_rating
RatingCounter.new(texts_from_comments.flatten, @key).rating
end

def texts_from_comments
@texts_from_comments ||= comments_from_article.map do |comment|
comment.slice('text').values
end
end

def comments_from_article
ArticleParser.new(@page).comments
end
end
45 changes: 45 additions & 0 deletions 2333/3/helpers/rating_counter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

require 'json'
require 'uri'
require 'net/http'

# the class that send request to Azure and get comments rating
class RatingCounter
URI = 'https://westeurope.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment'

attr_reader :request, :uri
def initialize(comments, key)
@uri = URI(URI)
@data = { documents: [] }
@comments = comments
@request = create_post_request(key)
end

def rating
JSON.parse(send_request.body)['documents'].map { |data| (data['score'] * 200).to_i - 100 }
end

private

def send_request
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(request)
end
end

def prepare_data_for_request
@comments.each_with_index do |comment, index|
@data[:documents] << { id: index.to_s, language: 'ru', text: comment }
end
end

def create_post_request(key)
prepare_data_for_request
request = Net::HTTP::Post.new(uri,
'Content-Type' => 'application/json',
'Ocp-Apim-Subscription-Key' => key)
request.body = @data.to_json
request
end
end
10 changes: 10 additions & 0 deletions 2333/3/models/article.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require_relative 'comment.rb'

# article model for ohm
class Article < Ohm::Model
attribute :link
attribute :rating
set :comments, :Comment
end
Loading