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

2396 - 3 #279

Open
wants to merge 1 commit 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
3 changes: 3 additions & 0 deletions 2396/3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/config/secrets.yml
config/secrets.yml
app.rb
10 changes: 10 additions & 0 deletions 2396/3/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
source 'https://rubygems.org'

gem 'mechanize'
gem 'ohm'
gem 'rack'
gem 'redis'
gem 'shotgun'
gem 'sidekiq'
gem 'sinatra'
gem 'thin'
83 changes: 83 additions & 0 deletions 2396/3/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
GEM
remote: https://rubygems.org/
specs:
concurrent-ruby (1.0.5)
connection_pool (2.2.2)
daemons (1.2.6)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
eventmachine (1.2.5)
hiredis (0.6.1)
http-cookie (1.0.3)
domain_name (~> 0.5)
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)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_portile2 (2.3.0)
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
rack (2.0.5)
rack-protection (2.0.1)
rack
redic (1.5.0)
hiredis
redis (4.0.1)
shotgun (0.9.2)
rack (>= 1.0)
sidekiq (5.1.3)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
sinatra (2.0.1)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.1)
tilt (~> 2.0)
stal (0.3.0)
redic (~> 1.5)
thin (1.7.2)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
tilt (2.0.8)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
webrobots (0.1.2)

PLATFORMS
ruby

DEPENDENCIES
mechanize
ohm
rack
redis
shotgun
sidekiq
sinatra
thin

BUNDLED WITH
1.16.2
15 changes: 15 additions & 0 deletions 2396/3/config.ru
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'sinatra/base'
require 'sidekiq/api'
require 'sidekiq/web'
require 'ohm'

Dir.glob('./{controllers,helpers,models}/*.rb').sort.each do |file|
require file
end
require './lib/workers/post_worker'
Post.redis = Redic.new('redis://127.0.0.1:6379/0')
Comment.redis = Redic.new('redis://127.0.0.1:6379/1')

map('/posts') { run PostsController }
map('/sidekiq') { run Sidekiq::Web }
map('/') { run ApplicationController }
11 changes: 11 additions & 0 deletions 2396/3/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This class is the base for my controllers
class ApplicationController < Sinatra::Base
set :views, File.expand_path(File.join(__FILE__, '../../views'))
not_found do
erb :not_found, layout: false
end

get '/' do
redirect '/posts'
end
end
31 changes: 31 additions & 0 deletions 2396/3/controllers/posts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This is class PostsController
class PostsController < ApplicationController
get '/' do
@posts = Post.all
erb :'posts/index'
end
get '/new' do
Copy link
Member

Choose a reason for hiding this comment

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

добавь пустую строку перед этим блоком

erb :'posts/new'
end

post '/create' do
Post.all.each do |post|
post.delete if post.link == params[:link]
Copy link
Member

Choose a reason for hiding this comment

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

Можешь рассказать зачем ты это делаешь?
Потому что на первый взгляд это кажется совсем не верным решением.

Copy link
Author

Choose a reason for hiding this comment

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

Удаляю пост, если он уже был ранее создан. Обновить его данные было бы логичнее...

end
PostWorker.perform_async(params[:link])
sleep 2
Copy link
Member

Choose a reason for hiding this comment

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

Не, фигня :)

Copy link
Member

Choose a reason for hiding this comment

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

Я так понимаю, ты ждешь что твой парсер обработает страницу?

А что, если за 2 секунды PostWorker не отработает?
Если ты прям хочешь ждать до конца, то ты можешь вызывать PostWorker.perform(params[:link]).

Но вообще так тоже делать не круто (а делать через pub/sub), но наверное пока сойдет и так.

Copy link
Author

Choose a reason for hiding this comment

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

Изначально хотел добавить flash как в рельсах, что ссылка отправлена на анализ, но не успевал и сделал такой костыль.

redirect '/'
end

get '/delete/:id' do
@post = Post.all[params[:id]]
Copy link
Member

Choose a reason for hiding this comment

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

Ты сначала загружаешь все посты в память, а потом только ищешь по айди.

Если бы ты внимательно почитал документацию Ohm, то ты бы увидел что можно искать и так https://github.com/soveran/ohm#finding-records

@post.delete
redirect '/'
end

get '/:id' do
@posts = Post.all
Copy link
Member

Choose a reason for hiding this comment

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

Здесь то же самое.

@post = @posts[params[:id]]
erb :'posts/show'
end
end
35 changes: 35 additions & 0 deletions 2396/3/helpers/comments_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require 'json'
require 'mechanize'

# This is class handling comments for post across API onliner.by
class CommentsParser
LIMIT = 50
API_PATH = 'https://comments.api.onliner.by/news/tech.post/'.freeze
Copy link
Member

Choose a reason for hiding this comment

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

Я так понимаю из url, ты только технические статьи можешь парсить?

Copy link
Member

Choose a reason for hiding this comment

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

Хз отчего, но я бы мб вынес эти констаны в Setting.onliner_api и сделал хешом. Подумай об этом. Делать необязательно, но мб тебе понравится.

Copy link
Author

Choose a reason for hiding this comment

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

Согласен. Добавило бы больше гибкости

API_PARAMS = "/comments?limit=#{LIMIT}&_=0.9841189675826583".freeze
attr_reader :agent

def initialize
@agent = Mechanize.new
end

def perform(url)
page_post = agent.get(url)
post_id = page_post.search('span.news_view_count').first.values[1]
comment_list = agent.get(API_PATH + post_id + API_PARAMS)
handling_comments(comment_list)
end

def self.fetch_title(url)
agent = Mechanize.new
Copy link
Member

Choose a reason for hiding this comment

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

Мне не нравится то, что класс называется CommentsParser, а ты делаешь две разные вещи (фетчишь комменты и фетчишь тайтл).

По ООП тут правильно было бы вынести этот метод в отдельный класс. Либо (чтобы соптимизировать на запросе) фетчить заголовок вместе с комментами и возвращать хешом — {title: '...', comments: [...]}

Copy link
Author

Choose a reason for hiding this comment

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

Второй вариант рассматривал, но посчитал, что лучше вынести в отдельный метод.
Вынесу в отдельный класс.

agent.get(url).title
end

private

def handling_comments(comment_list)
body = comment_list.body
JSON.parse(body)['comments'].each_with_object([]) do |comment, comments|
comments << comment['text'].gsub("\n", '<br>')
end
end
end
31 changes: 31 additions & 0 deletions 2396/3/helpers/create_post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This is helper for create post from comments
class CreatePost
attr_reader :link
def initialize(link)
@link = link
end

def perform
create
end

private

def create
comments = CommentsParser.new.perform(link)
rating = RatingCounter.new.perform(comments)
post = Post.create title: fetch_title, link: link,
rating: rating.sum / rating.size
create_comments_for_post(post, comments, rating)
end

def fetch_title
CommentsParser.fetch_title(link)
end

def create_comments_for_post(post, comments, rating)
comments.zip(rating).each do |obj|
post.comments.add(Comment.create(text: obj.first, rating: obj.last))
end
end
end
58 changes: 58 additions & 0 deletions 2396/3/helpers/rating_counter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'net/https'
require 'uri'
require 'json'
require './helpers/setting'
# This is class conects to API AZURE, Analyze sentiment texts and
# fetch score for his
class RatingCounter
KEY_AZURE = Setting.get('key_azure').freeze
Copy link
Member

Choose a reason for hiding this comment

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

Вот это тоже бы почему-то вынес в Setting'и.

Copy link
Member

Choose a reason for hiding this comment

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

Или даже может сделал AzureSetting < Setting и так инкапсулировал конфиг Azure. То же самое с онлайнером.

URI_BASE = 'https://westcentralus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment'.freeze
PATH = URI(URI_BASE).freeze
attr_reader :data, :request

def initialize
@data = { documents: [] }
end

def perform(comments)
handling_data(comments)
@request = prepare_request
output_data(send_request)
end

private

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

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

def output_data(response)
body = response.body
JSON.parse(body)['documents'].each_with_object([]) do |data, rating|
rating << ((data['score'] * 200).to_i - 100)
end
end

def headers
heads = {}
heads['Content-Type'] = 'application/json'
heads['Ocp-Apim-Subscription-Key'] = KEY_AZURE
heads
end

def prepare_request
request = Net::HTTP::Post.new(PATH, headers)
request.body = data.to_json
request
end
end
21 changes: 21 additions & 0 deletions 2396/3/helpers/setting.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'yaml'

class Setting
CONFIG_FILE = 'secrets.yml'.freeze

class << self
def get(key)
load_settings[key.to_s]
end

def load_settings
YAML.safe_load(File.read(file_path))
Copy link
Member

Choose a reason for hiding this comment

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

закешируй эту загрузку файла

end

private

def file_path
"./config/#{CONFIG_FILE}"
end
end
end
13 changes: 13 additions & 0 deletions 2396/3/lib/workers/post_worker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This is class perform jobs in background
class PostWorker
include Sidekiq::Worker
def perform(link)
call(link)
Copy link
Member

Choose a reason for hiding this comment

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

А в чем смысл метода call?

Copy link
Author

Choose a reason for hiding this comment

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

reek ругался, что лучше вынести в отдельный класс.

end

private

def call(link)
CreatePost.new(link).perform
end
end
5 changes: 5 additions & 0 deletions 2396/3/models/comment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This is model for comments post
class Comment < Ohm::Model
attribute :text
attribute :rating
end
7 changes: 7 additions & 0 deletions 2396/3/models/post.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This is model post
class Post < Ohm::Model
attribute :title
attribute :link
attribute :rating
set :comments, Comment
end
17 changes: 17 additions & 0 deletions 2396/3/views/header.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ONLINER</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
15 changes: 15 additions & 0 deletions 2396/3/views/layout.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%= erb :header %>
Copy link
Member

Choose a reason for hiding this comment

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

header и head это разные вещи

Copy link
Author

Choose a reason for hiding this comment

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

принял

<header>
<%= erb :navbar %>
</header>
<div class="container">
Copy link
Member

Choose a reason for hiding this comment

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

Соблюдай отступы.

Copy link
Author

Choose a reason for hiding this comment

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

окей, накладочка.

<div class="row">
<div class="col-md-12"><%= yield %></div>
</div>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
Copy link
Member

Choose a reason for hiding this comment

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

JS и CSS скрипты мы обычно складываем в head.

Copy link
Author

Choose a reason for hiding this comment

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

шаблон из документации

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

</body>
</html>
22 changes: 22 additions & 0 deletions 2396/3/views/navbar.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Список статей </a>
</div>

<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/sidekiq" target="blank">Sidekiq</a></li>
</ul> <!--background:black !important; -->
<ul class="nav navbar-nav navbar-right">
<li><a href='/posts/new' class='btn btn-primary' style="color:white;background:#488CC7 !important;">Добавить статью</a></li>
</ul>
</div>
</div>
</nav>
Loading