diff --git a/2216/3/.gitignore b/2216/3/.gitignore new file mode 100644 index 000000000..769c24400 --- /dev/null +++ b/2216/3/.gitignore @@ -0,0 +1 @@ +secrets.yml diff --git a/2216/3/Gemfile b/2216/3/Gemfile new file mode 100644 index 000000000..d7f50e902 --- /dev/null +++ b/2216/3/Gemfile @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +gem 'json' +gem 'mechanize' +gem 'ohm' +gem 'sass' +gem 'sinatra' +gem 'sinatra-asset-pipeline' +gem 'sinatra-static-assets' diff --git a/2216/3/Gemfile.lock b/2216/3/Gemfile.lock new file mode 100644 index 000000000..0443968a0 --- /dev/null +++ b/2216/3/Gemfile.lock @@ -0,0 +1,98 @@ +GEM + remote: https://rubygems.org/ + specs: + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.12.2) + concurrent-ruby (1.0.5) + connection_pool (2.2.2) + domain_name (0.5.20180417) + unf (>= 0.0.5, < 1.0.0) + execjs (2.7.0) + ffi (1.9.25) + hiredis (0.6.1) + http-cookie (1.0.3) + domain_name (~> 0.5) + 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) + 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.3) + rack + rake (12.3.1) + rb-fsevent (0.10.3) + rb-inotify (0.9.10) + ffi (>= 0.5.0, < 2) + redic (1.5.0) + hiredis + sass (3.5.7) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + sinatra (2.0.3) + mustermann (~> 1.0) + rack (~> 2.0) + rack-protection (= 2.0.3) + tilt (~> 2.0) + sinatra-asset-pipeline (2.1.0) + coffee-script (~> 2.4) + rake (~> 12.3) + sass (~> 3.5) + sinatra (~> 2.0) + sprockets (~> 3.7) + sprockets-helpers (~> 1.2) + sinatra-static-assets (1.0.4) + sinatra (>= 1.1.0) + sprockets (3.7.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-helpers (1.2.1) + sprockets (>= 2.2) + stal (0.3.0) + redic (~> 1.5) + tilt (2.0.8) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.5) + webrobots (0.1.2) + +PLATFORMS + ruby + +DEPENDENCIES + json + mechanize + ohm + sass + sinatra + sinatra-asset-pipeline + sinatra-static-assets + +BUNDLED WITH + 1.15.1 diff --git a/2216/3/Rakefile b/2216/3/Rakefile new file mode 100644 index 000000000..7a48572a9 --- /dev/null +++ b/2216/3/Rakefile @@ -0,0 +1,4 @@ +require 'sinatra/asset_pipeline/task' +require_relative './controllers/application_controller' + +Sinatra::AssetPipeline::Task.define! ApplicationController diff --git a/2216/3/app.rb b/2216/3/app.rb new file mode 100644 index 000000000..21bab8633 --- /dev/null +++ b/2216/3/app.rb @@ -0,0 +1,2 @@ +require_relative './controllers/application_controller' +ApplicationController.run! diff --git a/2216/3/assets/stylesheets/style.scss b/2216/3/assets/stylesheets/style.scss new file mode 100644 index 000000000..70a387c2d --- /dev/null +++ b/2216/3/assets/stylesheets/style.scss @@ -0,0 +1,95 @@ +$body-color: rgb(219, 232, 175); +$header-and-footer-color: rgb(158, 203, 168); +$text-color: rgb(251, 255, 201); +$error-text-color: rgb(255, 0, 0); +$table-color: rgb(0, 0, 0); + +html { + background-color: $body-color; + height: 100%; + min-height: 100%; + position: relative; + width: 100%; +} + +body { + background-color: $body-color; + height: 100%; + margin: 0; + padding: 0; + width: 100%; +} + +header { + background-color: $header-and-footer-color; + height: 10%; + margin: 0; + padding: 0; + text-align: center; +} + +footer { + background-color: $header-and-footer-color; + bottom: 0; + height: 10%; + margin: 0; + padding: 0; + position: fixed; + text-align: center; + width: 100%; +} + +main { + background-color: $body-color; + height: 100%; + text-align: center; + width: 100%; +} + +.copyright { + color: $text-color; + padding: 2%; +} + +h1 { + color: $text-color; + font-size: 200%; + margin: 0; + padding: 0; + padding-top: 1%; +} + +.content { + margin: 15%; +} + +th { + border: 1px solid $table-color; +} + +td { + border: 1px solid $table-color; +} + +table { + border: 1px solid $table-color; + border-collapse: collapse; + margin: 0 auto; +} + +form { + padding-top: 5%; +} + +.look-sub { + width: 30%; +} + +.address { + width: 50%; +} + +.error-message { + color: $error-text-color; + padding-top: 3%; +} diff --git a/2216/3/config.ru b/2216/3/config.ru new file mode 100644 index 000000000..c664a2176 --- /dev/null +++ b/2216/3/config.ru @@ -0,0 +1,4 @@ +require_relative './controllers/application_controller' +Article.redis = Redic.new('redis://127.0.0.1:6379/0') +Comment.redis = Redic.new('redis://127.0.0.1:6379/1') +run ApplicationController diff --git a/2216/3/controllers/application_controller.rb b/2216/3/controllers/application_controller.rb new file mode 100644 index 000000000..288091158 --- /dev/null +++ b/2216/3/controllers/application_controller.rb @@ -0,0 +1,41 @@ +require 'sinatra' +require 'ohm' +require 'mechanize' +require 'sass' +require 'sinatra/asset_pipeline' +require 'sinatra/static_assets' +require 'json' +require 'open-uri' +require 'net/https' +require 'uri' +require_relative './helpers/helpers' + +class ApplicationController < Sinatra::Base + set :root, File.dirname(__FILE__) + set :assets_precompile, %w[*.css] + set :assets_paths, ['assets/stylesheets'] + set :assets_css_compressor, :sass + + register Sinatra::AssetPipeline + + get '/' do + error_text = '' + erb :index, locals: { error_text: error_text } + end + + post '/' do + error_text = LinkVerificator.new(params['address']).check_link + RecordMaker.new(params['address']).make_record if error_text == '' + erb :index, locals: { error_text: error_text } + end + + post '/posts/articles' do + articles = Article.all.to_a + erb :'posts/articles', locals: { articles: articles } + end + + get '/posts/comments/:id' do + @comments = Comment.all.to_a.select { |comment| comment.article_id == params[:id] } + erb :'posts/comments' + end +end diff --git a/2216/3/helpers/article.rb b/2216/3/helpers/article.rb new file mode 100644 index 000000000..6f7b030c6 --- /dev/null +++ b/2216/3/helpers/article.rb @@ -0,0 +1,5 @@ +class Article < Ohm::Model + attribute :link + attribute :score + collection :comment, :Comment +end diff --git a/2216/3/helpers/article_parser.rb b/2216/3/helpers/article_parser.rb new file mode 100644 index 000000000..ae3593c35 --- /dev/null +++ b/2216/3/helpers/article_parser.rb @@ -0,0 +1,35 @@ +class ArticleParser + attr_reader :link + MAX_NUM_OF_COMMENTS = 50 + + def initialize(link) + @link = link + end + + def parse + parse_json + end + + private + + def make_post_id + agent = Mechanize.new + page = agent.get(link) + page.search('app[entity-id]').to_s.match(/\d+/).to_s + end + + def make_objs + url = "https://comments.api.onliner.by/news/tech.post/#{make_post_id}/comments?limit=#{MAX_NUM_OF_COMMENTS}" + json = IO.open(url).read + JSON.parse(json) + end + + def parse_json + comments = [] + objs = make_objs + objs['comments'].each do |comment_inf| + comments << comment_inf['text'] + end + comments + end +end diff --git a/2216/3/helpers/azure_text_analytics.rb b/2216/3/helpers/azure_text_analytics.rb new file mode 100644 index 000000000..1b1752e04 --- /dev/null +++ b/2216/3/helpers/azure_text_analytics.rb @@ -0,0 +1,40 @@ +class AzureTextAnalytics + attr_reader :comments + + def initialize(comments) + @comments = comments + end + + def pull_out_score + objs = generate_json_array + objs.map { |obj| obj['documents'][0]['score'] } + end + + private + + def make_uri + uri = 'https://westcentralus.api.cognitive.microsoft.com' + path = '/text/analytics/v2.0/sentiment' + URI(uri + path) + end + + def open_connection(uri) + Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') + end + + def recieve_one_comment_json(comment, uri) + response = open_connection(uri) do |http| + http.request RequestMaker.make_request(uri, comments.index(comment), comment) + end + JSON.pretty_generate(JSON(response.body)) + end + + def recieve_json + uri = make_uri + comments.map { |comment| recieve_one_comment_json(comment, uri) } + end + + def generate_json_array + recieve_json.map { |json| JSON.parse(json) } + end +end diff --git a/2216/3/helpers/comment.rb b/2216/3/helpers/comment.rb new file mode 100644 index 000000000..f156bbeaf --- /dev/null +++ b/2216/3/helpers/comment.rb @@ -0,0 +1,5 @@ +class Comment < Ohm::Model + attribute :comment_text + attribute :score + reference :article, :Article +end diff --git a/2216/3/helpers/helpers.rb b/2216/3/helpers/helpers.rb new file mode 100644 index 000000000..509980599 --- /dev/null +++ b/2216/3/helpers/helpers.rb @@ -0,0 +1,8 @@ +require_relative './helpers/link_verificator' +require_relative './helpers/article_parser' +require_relative './helpers/article' +require_relative './helpers/comment' +require_relative './helpers/rating' +require_relative './helpers/request_maker' +require_relative './helpers/azure_text_analytics' +require_relative './helpers/record_maker' diff --git a/2216/3/helpers/link_verificator.rb b/2216/3/helpers/link_verificator.rb new file mode 100644 index 000000000..bf32fc997 --- /dev/null +++ b/2216/3/helpers/link_verificator.rb @@ -0,0 +1,13 @@ +class LinkVerificator + attr_reader :link + + def initialize(link) + @link = link + end + + def check_link + error_message = '' + error_message = 'Invalid link. Please, enter another link.' unless link.include? 'https://tech.onliner.by/' + error_message + end +end diff --git a/2216/3/helpers/rating.rb b/2216/3/helpers/rating.rb new file mode 100644 index 000000000..d103eb7cf --- /dev/null +++ b/2216/3/helpers/rating.rb @@ -0,0 +1,31 @@ +require 'active_support' +require 'active_support/core_ext' + +class Rating + attr_reader :comment_scores, :comments + + def initialize(comment_scores, comments) + @comment_scores = comment_scores + @comments = comments + end + + def comment_score(comment) + convert_score(comment_scores[comments.index(comment)]) + end + + def article_score + count_article_score + end + + private + + def convert_score(azure_score) + ((azure_score - 0.5) * 200).round + end + + def count_article_score + convert_scores = comment_scores.map { |score| convert_score(score) } + total_score = convert_scores.sum + (total_score / comments.size).round + end +end diff --git a/2216/3/helpers/record_maker.rb b/2216/3/helpers/record_maker.rb new file mode 100644 index 000000000..bfa877b18 --- /dev/null +++ b/2216/3/helpers/record_maker.rb @@ -0,0 +1,26 @@ +class RecordMaker + attr_reader :url + + def initialize(url) + @url = url + end + + def make_record + comments = fetch_comments + score = Rating.new(AzureTextAnalytics.new(comments).pull_out_score, comments) + article = Article.create link: url, score: score.article_score + make_comments_link(comments, article, score) + end + + private + + def fetch_comments + ArticleParser.new(url).parse + end + + def make_comments_link(comments, article, score) + comments.each do |comment| + Comment.create comment_text: comment, score: score.comment_score(comment), article: article + end + end +end diff --git a/2216/3/helpers/request_maker.rb b/2216/3/helpers/request_maker.rb new file mode 100644 index 000000000..4396e12a4 --- /dev/null +++ b/2216/3/helpers/request_maker.rb @@ -0,0 +1,11 @@ +class RequestMaker + def self.make_request(uri, id, comment) + request = Net::HTTP::Post.new(uri) + request['Content-Type'] = 'application/json' + request['Ocp-Apim-Subscription-Key'] = YAML.load_file(File.join(Dir.pwd, 'secrets.yml'))['azure']['ACCES_KEY'] + request.body = { 'documents': [ + { 'id' => id, 'language' => 'ru', 'text' => comment } + ] }.to_json + request + end +end diff --git a/2216/3/views/index.erb b/2216/3/views/index.erb new file mode 100644 index 000000000..c727cc564 --- /dev/null +++ b/2216/3/views/index.erb @@ -0,0 +1,11 @@ +
+
+ + + +
+
<%= error_text%>
+
+ +
+
diff --git a/2216/3/views/layout.erb b/2216/3/views/layout.erb new file mode 100644 index 000000000..a1f6b7d34 --- /dev/null +++ b/2216/3/views/layout.erb @@ -0,0 +1,19 @@ + + + + + Article Analysis + <%= stylesheet_tag 'style' %> + + +
+

Article Analysis

+
+
+ <%= yield %> +
+ + + diff --git a/2216/3/views/posts/articles.erb b/2216/3/views/posts/articles.erb new file mode 100644 index 000000000..5f05b841a --- /dev/null +++ b/2216/3/views/posts/articles.erb @@ -0,0 +1,15 @@ +

Articles

+ + + + + + + <% articles.each do |article|%> + + + + + + <% end%> +
LinksScoresComments
<%= article.link%><%= article.score%>comments
diff --git a/2216/3/views/posts/comments.erb b/2216/3/views/posts/comments.erb new file mode 100644 index 000000000..4ef0aef14 --- /dev/null +++ b/2216/3/views/posts/comments.erb @@ -0,0 +1,13 @@ +

Comments

+ + + + + + <% @comments.each do |comment|%> + + + + + <% end%> +
CommentsScores
<%= comment.comment_text%><%= comment.score%>