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 @@
+
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' %>
+
+
+
+
+ <%= 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
+
+
+ Links |
+ Scores |
+ Comments |
+
+ <% articles.each do |article|%>
+
+ <%= article.link%> |
+ <%= article.score%> |
+ comments |
+
+ <% end%>
+
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 |
+ Scores |
+
+ <% @comments.each do |comment|%>
+
+ <%= comment.comment_text%> |
+ <%= comment.score%> |
+
+ <% end%>
+