From fa4b31950e68ec7c1dd67c8a4a5ed3d304299dd1 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 27 Jul 2018 11:33:22 +0300 Subject: [PATCH 1/4] Load project --- 2355/3/config.ru | 5 +++ 2355/3/controllers/index_controller.rb | 53 ++++++++++++++++++++++++++ 2355/3/models/article.rb | 13 +++++++ 2355/3/models/article_rate.rb | 31 +++++++++++++++ 2355/3/models/comment.rb | 15 ++++++++ 2355/3/models/comment_parser.rb | 20 ++++++++++ 2355/3/models/text_analytics.rb | 40 +++++++++++++++++++ 2355/3/views/annihilate.slim | 15 ++++++++ 2355/3/views/header_footer.slim | 38 ++++++++++++++++++ 2355/3/views/index.slim | 46 ++++++++++++++++++++++ 2355/3/views/new.slim | 13 +++++++ 2355/3/views/show.slim | 29 ++++++++++++++ 12 files changed, 318 insertions(+) create mode 100644 2355/3/config.ru create mode 100644 2355/3/controllers/index_controller.rb create mode 100644 2355/3/models/article.rb create mode 100644 2355/3/models/article_rate.rb create mode 100644 2355/3/models/comment.rb create mode 100644 2355/3/models/comment_parser.rb create mode 100644 2355/3/models/text_analytics.rb create mode 100644 2355/3/views/annihilate.slim create mode 100644 2355/3/views/header_footer.slim create mode 100644 2355/3/views/index.slim create mode 100644 2355/3/views/new.slim create mode 100644 2355/3/views/show.slim diff --git a/2355/3/config.ru b/2355/3/config.ru new file mode 100644 index 000000000..2601f422d --- /dev/null +++ b/2355/3/config.ru @@ -0,0 +1,5 @@ +require 'sinatra/base' + +Dir.glob('./{controllers,models}/*.rb').each { |file| require file } + +map('/') { run IndexController } diff --git a/2355/3/controllers/index_controller.rb b/2355/3/controllers/index_controller.rb new file mode 100644 index 000000000..3e6d4473f --- /dev/null +++ b/2355/3/controllers/index_controller.rb @@ -0,0 +1,53 @@ +require 'sinatra' +require 'slim' +require 'ohm' + +# Main controller +class IndexController < Sinatra::Base + set :views, File.expand_path(File.join(__FILE__, '../../views')) + + get '/' do + slim :header_footer do + slim :index + end + end + + get '/add' do + slim :header_footer do + slim :new + end + end + + post '/add' do + Ohm.redis.call 'SET', 'article' + ((Ohm.redis.call 'GET', 'articles_count').to_i + 1).to_s, params[:link].to_s + Ohm.redis.call 'SET', 'articles_count', ((Ohm.redis.call 'GET', 'articles_count').to_i + 1).to_s + require_relative './models/article_rate.rb' + rating = ArticleRate.new(params[:link].to_s) + coment_count = rating.article.comments.size + Ohm.redis.call 'SET', 'article' + (Ohm.redis.call 'GET', 'articles_count') + '_comments_count', coment_count.to_s + rating.article_rate + rating.article.comments.size.times do |index| + text = rating.article.comments[index].text + Ohm.redis.call 'SET', 'article' + (Ohm.redis.call 'GET', 'articles_count') + '_comment' + (index + 1).to_s, text + end + Ohm.redis.call 'SET', 'article' + (Ohm.redis.call 'GET', 'articles_count') + '_rate', rating.rate.to_i.to_s + redirect '/' + end + + # I use 'id' in show.slim, so it needed here + # rubocop:disable Lint/UselessAssignment + get '/show/:id' do + slim :header_footer do + slim :show do + id = params['id'] + end + end + end + # rubocop:enable Lint/UselessAssignment + + get '/annihilate' do + slim :header_footer do + slim :annihilate + end + end +end diff --git a/2355/3/models/article.rb b/2355/3/models/article.rb new file mode 100644 index 000000000..cd645bc13 --- /dev/null +++ b/2355/3/models/article.rb @@ -0,0 +1,13 @@ +require_relative './comment_parser.rb' + +# This class describes article +class Article + attr_reader :url, :comments + + def initialize(url) + @url = url + parser = CommentParser.new + parser.parse(url) + @comments = parser.comments + end +end diff --git a/2355/3/models/article_rate.rb b/2355/3/models/article_rate.rb new file mode 100644 index 000000000..e36a5ce2d --- /dev/null +++ b/2355/3/models/article_rate.rb @@ -0,0 +1,31 @@ +require_relative './text_analytics.rb' +require_relative './article.rb' + +# This class is needed to get article rate +class ArticleRate + attr_reader :rate, :article + + def initialize(url) + @rate = 0 + @article = Article.new(url) + end + + def all_comments_rate + docs = [] + @article.comments.each do |comment| + docs << { 'id' => 1, + 'language' => 'ru', + 'text' => comment.text.to_s } + end + documents = { 'documents': docs } + TextAnalytics.new(documents).analyze['documents'] + end + + def article_rate + comments_rate = all_comments_rate + comments_rate.each do |comment| + @rate += ((comment['score'] * 200) - 100) + end + @rate /= comments_rate.size + end +end diff --git a/2355/3/models/comment.rb b/2355/3/models/comment.rb new file mode 100644 index 000000000..1ca8b5f5d --- /dev/null +++ b/2355/3/models/comment.rb @@ -0,0 +1,15 @@ +require_relative './text_analytics.rb' + +# This class describes comment +class Comment + attr_reader :text, :rate + + def initialize(text) + @text = text + docs = { 'id' => 1, + 'language' => 'ru', + 'text' => text } + documents = { 'documents': [docs] } + @rate = (TextAnalytics.new(documents).analyze['documents'][0]['score'] * 200 - 100).to_i + end +end diff --git a/2355/3/models/comment_parser.rb b/2355/3/models/comment_parser.rb new file mode 100644 index 000000000..4a9766b2f --- /dev/null +++ b/2355/3/models/comment_parser.rb @@ -0,0 +1,20 @@ +require 'selenium-webdriver' + +# This class parse web page for comments +class CommentParser + attr_reader :comments + + def initialize + @comments = [] + @driver = Selenium::WebDriver.for :chrome + end + + def parse(url) + @driver.get url + begin + @driver.find_element(:class, 'news-form__button').click + ensure + @comments = @driver.find_elements(:class, 'news-comment__speech') + end + end +end diff --git a/2355/3/models/text_analytics.rb b/2355/3/models/text_analytics.rb new file mode 100644 index 000000000..14241fa9c --- /dev/null +++ b/2355/3/models/text_analytics.rb @@ -0,0 +1,40 @@ +require 'net/https' +require 'uri' +require 'json' + +# This class responsible for analyzing comments +class TextAnalytics + attr_reader :documents, :access_key, :uri + + def initialize(documents) + @documents = documents + @access_key = '7614507e97184bfd9f38e1d93fa130e8' # use it, if you want... I don't mind + path = '/text/analytics/v2.0/sentiment' + @uri = URI('https://westcentralus.api.cognitive.microsoft.com' + path) + end + + # This method is written in accordance with the documentation of Azura, + # so it's not my fault that reek swears on TooManyStatements and Rubocop swears on EVERYTHING! + # This method smells of :reek:TooManyStatements + # rubocop:disable Style/HashSyntax + # rubocop:disable Style/NestedParenthesizedCalls + # rubocop:disable Style/ColonMethodCall + # rubocop:disable Style/RedundantParentheses + # rubocop:disable Lint/ParenthesesAsGroupedExpression + def analyze + request = Net::HTTP::Post.new(uri) + request['Content-Type'] = 'application/json' + request['Ocp-Apim-Subscription-Key'] = @access_key + request.body = @documents.to_json + + response = Net::HTTP.start(@uri.host, @uri.port, :use_ssl => @uri.scheme == 'https') do |http| + http.request(request) + end + JSON.parse(JSON::pretty_generate (JSON (response.body))) + end + # rubocop:enable Style/HashSyntax + # rubocop:enable Style/NestedParenthesizedCalls + # rubocop:enable Style/ColonMethodCall + # rubocop:enable Style/RedundantParentheses + # rubocop:enable Lint/ParenthesesAsGroupedExpression +end diff --git a/2355/3/views/annihilate.slim b/2355/3/views/annihilate.slim new file mode 100644 index 000000000..42aa50028 --- /dev/null +++ b/2355/3/views/annihilate.slim @@ -0,0 +1,15 @@ +main[role="main"] + section.jumbotron.text-center + .container + - Ohm.redis.call "FLUSHALL" + - Ohm.redis.call "SET" "articles_count" "0" + .alert.alert-success[role="alert"] + h3.jumbotron-heading + | All articles have been deleted! + p.lead.text-muted + | Choose what you want to do + p + a.btn.btn-primary.my-2[href="/add"] + | Add new article + a.btn.btn-primary.my-2[href="/"] + | Return to main page \ No newline at end of file diff --git a/2355/3/views/header_footer.slim b/2355/3/views/header_footer.slim new file mode 100644 index 000000000..5a81de153 --- /dev/null +++ b/2355/3/views/header_footer.slim @@ -0,0 +1,38 @@ +| +html[lang="en"] + head + meta[charset="utf-8"] + meta[name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"] + title + | Onliner analyzer + link[rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"] + body + header + #navbarHeader.collapse.bg-dark + .container + .row + .col-sm-8.col-md-7.py-4 + h4.text-white + | About + p.text-muted + | This application will help you to analyze the articles from the portal "Onliner.by" and find out their rating based on the analysis of comments. You can also find out the rating of each individual comment by viewing them in the appropriate section. It supports adding a new article for analysis, viewing the list of all added articles and rating comments. + .navbar.navbar-dark.bg-dark.shadow-sm + .container.d-flex.justify-content-between + a.navbar-brand.d-flex.align-items-center[href="/"] + svg.mr-2[xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"] + path[d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"] + circle[cx="12" cy="13" r="4"] + strong + | Onliner Analyzer + button.navbar-toggler[type="button" data-toggle="collapse" data-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation"] + span.navbar-toggler-icon + == yield + footer.text-muted + .container + p.float-right + | ©WhiteNiceFlower + p.float-left + | 27.07.2018 + script[src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"] + script[src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"] + script[src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"] \ No newline at end of file diff --git a/2355/3/views/index.slim b/2355/3/views/index.slim new file mode 100644 index 000000000..468f05673 --- /dev/null +++ b/2355/3/views/index.slim @@ -0,0 +1,46 @@ +main[role="main"] + section.jumbotron.text-center + .container + h1.jumbotron-heading + | There are analyzed articles: + table.table.table-striped.table-bordered + thead + tr + th + | # + th + | Article(Link) + th + | Raiting + th + | About + tbody + - if "#{Ohm.redis.call 'GET', 'articles_count'}".to_i == 0 + tr + th[scope="row"] + | There + td + | are + td + | NO + td + | articles + - "#{Ohm.redis.call 'GET', 'articles_count'}".to_i.times do |j| + tr + th[scope="row"] + | #{j + 1} + td + a[href="#{Ohm.redis.call 'GET', 'article' + "#{j + 1}"}"] + | #{Ohm.redis.call 'GET', 'article' + "#{j + 1}"} + td + | #{Ohm.redis.call 'GET', 'article' + "#{j + 1}" + '_rate'} + td + a[href="/show/#{j + 1}"] + | > + p.lead.text-muted + | Choose what you want to do + p + a.btn.btn-primary.my-2[href="/add"] + | Add new article + a.btn.btn-primary.my-2[href="/annihilate"] + | Delete all articles \ No newline at end of file diff --git a/2355/3/views/new.slim b/2355/3/views/new.slim new file mode 100644 index 000000000..27e5a9f48 --- /dev/null +++ b/2355/3/views/new.slim @@ -0,0 +1,13 @@ +main[role="main"] + section.jumbotron.text-left + .container + h3.jumbotron-heading + | Add new article + form.form-inline[method="post"] + .form-group.row + label.col-sm-2.col-form-label[for="link"] + | Link + .col-sm-10 + input#link.form-control[type="text" name="link" placeholder="https://onliner.by/..."] + button.btn.btn-primary[type="submit" href="/"] + | Add diff --git a/2355/3/views/show.slim b/2355/3/views/show.slim new file mode 100644 index 000000000..db4e1575e --- /dev/null +++ b/2355/3/views/show.slim @@ -0,0 +1,29 @@ +main[role="main"] + section.jumbotron.text-center + .container + h1.jumbotron-heading + - id = yield + | #{Ohm.redis.call "GET", "article" + "#{id}"} + table.table.table-striped.table-bordered + thead + tr + th + | # + th + | Comment + th + | Rate + tbody + - "#{Ohm.redis.call "GET", "article" + "#{id}" + "_comments_count"}".to_i.times do |i| + - require_relative '../models/comment.rb' + - comment = Comment.new("#{Ohm.redis.call "GET", "article" + "#{id}" + "_comment" + "#{i + 1}"}") + tr + th + | #{i + 1} + th + - if i == 0 + | #{'Лучший комментарий: '} #{comment.text} + - else + | #{comment.text} + th + | #{comment.rate} From 3bfde0dbfc1aebc90c2ca0ea99794cf6f65047ea Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 27 Jul 2018 11:57:13 +0300 Subject: [PATCH 2/4] Try to fix unexpected tokens tCOLON and tRCURLY --- 2355/3/models/article_rate.rb | 4 +--- 2355/3/models/comment.rb | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/2355/3/models/article_rate.rb b/2355/3/models/article_rate.rb index e36a5ce2d..db5be29ea 100644 --- a/2355/3/models/article_rate.rb +++ b/2355/3/models/article_rate.rb @@ -13,9 +13,7 @@ def initialize(url) def all_comments_rate docs = [] @article.comments.each do |comment| - docs << { 'id' => 1, - 'language' => 'ru', - 'text' => comment.text.to_s } + docs << { 'id' => 1, 'language' => 'ru', 'text' => comment.text.to_s } end documents = { 'documents': docs } TextAnalytics.new(documents).analyze['documents'] diff --git a/2355/3/models/comment.rb b/2355/3/models/comment.rb index 1ca8b5f5d..41ae71b6f 100644 --- a/2355/3/models/comment.rb +++ b/2355/3/models/comment.rb @@ -6,9 +6,7 @@ class Comment def initialize(text) @text = text - docs = { 'id' => 1, - 'language' => 'ru', - 'text' => text } + docs = { 'id' => 1, 'language' => 'ru', 'text' => text } documents = { 'documents': [docs] } @rate = (TextAnalytics.new(documents).analyze['documents'][0]['score'] * 200 - 100).to_i end From 8a1f37eb94d8f3694e6d9f74b04b2a69454b0cc2 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 27 Jul 2018 12:05:07 +0300 Subject: [PATCH 3/4] Fix reek --- 2355/3/models/article_rate.rb | 2 +- 2355/3/models/comment.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/2355/3/models/article_rate.rb b/2355/3/models/article_rate.rb index db5be29ea..054506484 100644 --- a/2355/3/models/article_rate.rb +++ b/2355/3/models/article_rate.rb @@ -15,7 +15,7 @@ def all_comments_rate @article.comments.each do |comment| docs << { 'id' => 1, 'language' => 'ru', 'text' => comment.text.to_s } end - documents = { 'documents': docs } + documents = { 'documents'=> docs } TextAnalytics.new(documents).analyze['documents'] end diff --git a/2355/3/models/comment.rb b/2355/3/models/comment.rb index 41ae71b6f..9b4c676fd 100644 --- a/2355/3/models/comment.rb +++ b/2355/3/models/comment.rb @@ -7,7 +7,7 @@ class Comment def initialize(text) @text = text docs = { 'id' => 1, 'language' => 'ru', 'text' => text } - documents = { 'documents': [docs] } + documents = { 'documents'=> [docs] } @rate = (TextAnalytics.new(documents).analyze['documents'][0]['score'] * 200 - 100).to_i end end From d42a3e69081b797b2e04422922eaf8a9431b9fe1 Mon Sep 17 00:00:00 2001 From: Aleksei Date: Fri, 27 Jul 2018 12:06:35 +0300 Subject: [PATCH 4/4] Fix reek v2 --- 2355/3/models/article_rate.rb | 2 +- 2355/3/models/comment.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/2355/3/models/article_rate.rb b/2355/3/models/article_rate.rb index 054506484..c04792315 100644 --- a/2355/3/models/article_rate.rb +++ b/2355/3/models/article_rate.rb @@ -15,7 +15,7 @@ def all_comments_rate @article.comments.each do |comment| docs << { 'id' => 1, 'language' => 'ru', 'text' => comment.text.to_s } end - documents = { 'documents'=> docs } + documents = { 'documents' => docs } TextAnalytics.new(documents).analyze['documents'] end diff --git a/2355/3/models/comment.rb b/2355/3/models/comment.rb index 9b4c676fd..42f7a8d96 100644 --- a/2355/3/models/comment.rb +++ b/2355/3/models/comment.rb @@ -7,7 +7,7 @@ class Comment def initialize(text) @text = text docs = { 'id' => 1, 'language' => 'ru', 'text' => text } - documents = { 'documents'=> [docs] } + documents = { 'documents' => [docs] } @rate = (TextAnalytics.new(documents).analyze['documents'][0]['score'] * 200 - 100).to_i end end