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

2345 - 3 #292

Open
wants to merge 4 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 2345/3/.ruby-gemset
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Onliner2345
1 change: 1 addition & 0 deletions 2345/3/.ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby-2.5.0
13 changes: 13 additions & 0 deletions 2345/3/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
source 'https://rubygems.org'

gem 'mechanize', '~> 2.7', '>= 2.7.6'
gem 'ohm', '~> 3.1', '>= 3.1.1'
gem 'redis', '~> 3.3', '>= 3.3.1'
gem 'sinatra', '~> 1.4', '>= 1.4.7'

group :development do
gem 'pry'
gem 'reek'
gem 'rubocop', '~> 0.54.0'
gem 'shotgun', '~> 0.9.2'
end
113 changes: 113 additions & 0 deletions 2345/3/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1)
codeclimate-engine-rb (0.4.1)
virtus (~> 1.0)
coderay (1.1.2)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
connection_pool (2.2.2)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
equalizer (0.0.11)
hiredis (0.6.1)
http-cookie (1.0.3)
domain_name (~> 0.5)
ice_nine (0.11.2)
kwalify (0.7.2)
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)
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
parallel (1.12.1)
parser (2.5.1.2)
ast (~> 2.4.0)
powerpack (0.1.2)
pry (0.11.3)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
rack (1.6.10)
rack-protection (1.5.5)
rack
rainbow (3.0.0)
redic (1.5.0)
hiredis
redis (3.3.5)
reek (5.0.2)
codeclimate-engine-rb (~> 0.4.0)
kwalify (~> 0.7.0)
parser (>= 2.5.0.0, < 2.6, != 2.5.1.1)
rainbow (>= 2.0, < 4.0)
rubocop (0.54.0)
parallel (~> 1.10)
parser (>= 2.5)
powerpack (~> 0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
shotgun (0.9.2)
rack (>= 1.0)
sinatra (1.4.8)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
stal (0.3.0)
redic (~> 1.5)
thread_safe (0.3.6)
tilt (2.0.8)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.4.0)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
webrobots (0.1.2)

PLATFORMS
ruby

DEPENDENCIES
mechanize (~> 2.7, >= 2.7.6)
ohm (~> 3.1, >= 3.1.1)
pry
redis (~> 3.3, >= 3.3.1)
reek
rubocop (~> 0.54.0)
shotgun (~> 0.9.2)
sinatra (~> 1.4, >= 1.4.7)

BUNDLED WITH
1.16.3
6 changes: 6 additions & 0 deletions 2345/3/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bundler.require

Dir.glob('./{controllers,models}/*.rb').each { |file| require_relative file }

map('/articles') { run ArticlesController }
map('/') { run ApplicationController }
12 changes: 12 additions & 0 deletions 2345/3/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class ApplicationController < Sinatra::Base
set :views, File.expand_path(File.join(__FILE__, '../../views'))
set :method_override, true

not_found do
erb :not_found
end

get '/' do
redirect to '/articles'
end
end
34 changes: 34 additions & 0 deletions 2345/3/controllers/articles_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require_relative './application_controller.rb'

class ArticlesController < ApplicationController
get '/' do
@articles = Article.all
erb :'/articles/index'
end

get '/new' do
erb :'/articles/create'
end

post '/' do
@article = ArticleCreater.new(params).create
redirect to '/'
end

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

delete '/:id' do
Article[params[:id]].delete
redirect to '/'
end

post '/:id' do
@article = Article[params[:id]]
@article.update(link: params[:article])
redirect to "/#{params[:id]}"
end
end
7 changes: 7 additions & 0 deletions 2345/3/models/article.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Article < Ohm::Model
attribute :link
attribute :title
attribute :rating
unique :link
collection :comments, :Comment
end
28 changes: 28 additions & 0 deletions 2345/3/models/article_creater.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class ArticleCreater
attr_reader :article
Copy link

Choose a reason for hiding this comment

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

You break Command Query separation principle:
when you say ArticleCreater#create - it is a command method - it COMMANDs to do smth
when you need article - it is a QUERY for a state of the object - so you should have 2 separate method for it
(I use attr_reader to create this method see example below)
so you should split the #create method into 2 methods

class ArticleCreator
   attr_reader :options   #input
   attr_reader :article     #output

  # helper method not to break command/query separation principle 
  # you should replace all places where you use ArticleCreater.new(options).create to ArtcleCreator.create(options)
  def self.create(options) 
    article_creator = new(options)
    article_creator.create(options)
    article_creator.article
  end

  def initialize(options)
  end
 
  def create
    create_article
    create_comments
  end

  private

  def create_article
    @article = Article.create(link: @article_link, title: title, rating: article_rating)
  end

  def create_comments
    CommentsCreater.new(comments_with_rating, article).create
  end
end


def initialize(options)
@article_link = options[:article]
@comments = ArticleParser.new(@article_link).comments
end

def create
@article = Article.create(link: @article_link, title: title, rating: article_rating)
CommentsCreater.new(comments_with_rating, article).create
article
end

private

def article_rating
comments_with_rating.map { |comment| comment[:rating] }.sum / @comments.size
end

def comments_with_rating
CommentsRating.new(@comments).put_ratings
end

def title
ArticleParser.new(@article_link).title
end
end
38 changes: 38 additions & 0 deletions 2345/3/models/article_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'mechanize'

class ArticleParser
def initialize(link = 'https://people.onliner.by/2018/07/23/vodol')
@agent = Mechanize.new
@article_link = link
end

def title
@agent.get(@article_link).search('.news-header__title').text.strip
end

def comments
data['comments'].reverse.map do |comment|
{ author: CommentParser.author(comment),
text: CommentParser.text(comment),
votes: CommentParser.votes(comment) }
end
end

private

def data
JSON.parse(@agent.get(comment_link).body)
end

def comment_link
"https://comments.api.onliner.by/news/#{section}.post/#{article_id}/comments?limit=9999"
end

def section
@article_link[%r{https://(\w+)}, 1]
end

def article_id
@agent.get(@article_link).search('.news_view_count').attr('news_id').value
end
end
6 changes: 6 additions & 0 deletions 2345/3/models/comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class Comment < Ohm::Model
attribute :author
attribute :text
attribute :rating
reference :article, :Article
end
13 changes: 13 additions & 0 deletions 2345/3/models/comment_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module CommentParser
Copy link

Choose a reason for hiding this comment

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

Looks like it should have the same structure as ArticleParser
Do you have some reason to use module in this case?

I would do smth like this:

class CommentParser
  def initialize(comment_data)
  end
  
  def votes
  end
  
  def author
  end

  def text
  end
end

def self.votes(options)
options['marks'].values.reduce(:+)
end

def self.author(options)
options['author']['name']
end

def self.text(options)
options['text']
end
end
23 changes: 23 additions & 0 deletions 2345/3/models/comments_rating.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class CommentsRating
LIMIT = 50

def initialize(comments)
@comments = comments
end

# :reek:FeatureEnvy
Copy link

Choose a reason for hiding this comment

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

this is bad practice.
It means that code get too complex
I could show how to fix it - but there are no specs for the class - so I will take much time for me (other developer) to understand what it does and refactor or extend.

If you want - you could add specs for this particular class and I'll show how to fix the 'FeatureEnvy' issue
or you can fix it yourself

def put_ratings
selected_for_evaluation = sort_comments_by_votes
ratings = RatingCounter.new(selected_for_evaluation).calculate
selected_for_evaluation.zip(ratings).map do |record|
record.first[:rating] = record.last
record[0]
end
end

private

def sort_comments_by_votes
@comments.sort_by { |comment| comment[:votes] }.reverse.first(LIMIT)
end
end
15 changes: 15 additions & 0 deletions 2345/3/models/create_comments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class CommentsCreater
LIMIT = 50
Copy link

Choose a reason for hiding this comment

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

LIMIT = 50 - never used

Copy link

Choose a reason for hiding this comment

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

delete it


def initialize(comments, article)
@comments = comments
@article = article
end

# :reek:FeatureEnvy
def create
@comments.each do |comment|
Comment.create(author: comment[:author], text: comment[:text], rating: comment[:rating], article: @article)
end
end
end
45 changes: 45 additions & 0 deletions 2345/3/models/rating_counter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'net/https'
require 'uri'
require 'json'

class RatingCounter
URI = 'https://westcentralus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment'.freeze
ACCESS_KEY = IO.read(File.dirname(__FILE__) + '/pass').freeze
attr_reader :request

def initialize(comments)
@uri = URI(URI)
@data = { documents: [] }
@comments = comments
end

def calculate
prepare_comments
@request = create_request
response = send_request
JSON.parse(response.body)['documents'].map do |data|
(data['score'] * 200).to_i - 100
end
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_comments
@comments.each_with_index do |comment, index|
@data[:documents] << { 'id' => index.to_s, 'language' => 'ru', 'text' => comment[:text] }
end
end

def create_request
request = Net::HTTP::Post.new(@uri, 'Content-Type' => 'application/json',
'Ocp-Apim-Subscription-Key' => ACCESS_KEY)
request.body = @data.to_json
request
end
end
16 changes: 16 additions & 0 deletions 2345/3/views/articles/create.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<html>
<head>
<title> Adding an article </title>
</head>

<h1>New article</h1>
<body>
<style> #article {width: 450px;} </style>
<form action="/articles" method="post">
<div class="form-group">
<input type="text" class="form-control" id="article" title="Example: https://tech.onliner.by/2018/07/30/startup-51" placeholder="Input link" name="article" pattern="^(http|https):\/\/\w+\.onliner.+" autofocus>
</div>
<button type="submit" class="btn btn-success">Add</button>
</form>
</body>
</html>
Loading