diff --git a/2355/2/README.md b/2355/2/README.md new file mode 100644 index 000000000..2be9e424f --- /dev/null +++ b/2355/2/README.md @@ -0,0 +1,65 @@ +## Для работы данной программы вам понадобиться: +* Установить ruby на свой ПК +* Установить gem "russian-obscenity". +Для этого воспользуйтесь командой ``gem install russian_obscenity`` +* Установить gem "terminal-table" +Для этого воспользуйтесь командой ``gem install terminal-table`` +* Скачать данную программу. Для этого нажмите зеленую кпопку __Clone or download__ и выберете __Download_ZIP__. +После этого распакуйте архив в любую удобную папку. +* Скачайть архив с текстами баттлов по данной [ссылке](https://yadi.sk/d/uz-52CQP3ZWCtE). +После этого распакуйте архив в папку с программой. +## Инструкция по применению + +Откройте папку с программой в консоли. Запустить файл __main.rb__ командой __ruby main.rb__, добавив в конце записи необходимые команды. +Список доступных команд: +* __--top-bad-words=__ +* __--top-words=__ +* __--name=__ + +### Команда __--top-bad-words=__ + +Использование данной команды приводит к тому, что на экране выводится топ исполнителей, в текста баттлов которых входит наибольшее количество нецензурных выражений. Принимает целочисленные агрументы и выводит столько исполнителей, сколько запросил пользователь. По умолчанию(если аргументы не были переданы) выводит только самого нецензурного исполнителя. Также в виде таблицы выводит данные о количестве поединков(баттлов), среднее количество нецензурных слов в поединке и среднее количество слов в раунде. + +Пример работы программы. + +``` +ruby main.rb --top-bad-words=6 + ++--------------------------+--------------+-------------------------+---------------------+----------------------+ +| Леха Медь | 3 баттла(ов) | 276 нецензурных слов(а) | 92 слов(а) на баттл | 626 слов(а) в раунде | +| Rickey F | 6 баттла(ов) | 268 нецензурных слов(а) | 44 слов(а) на баттл | 541 слов(а) в раунде | +| Хип-хоп одинокой старухи | 3 баттла(ов) | 237 нецензурных слов(а) | 79 слов(а) на баттл | 620 слов(а) в раунде | +| Букер | 4 баттла(ов) | 162 нецензурных слов(а) | 40 слов(а) на баттл | 322 слов(а) в раунде | +| Эмио Афишл | 2 баттла(ов) | 155 нецензурных слов(а) | 77 слов(а) на баттл | 574 слов(а) в раунде | ++--------------------------+--------------+-------------------------+---------------------+----------------------+ + + +``` + +### Команды __--top-words=__ и __--name=__ + +Данные команды используются вместе и их исполнение приводит к тому, что на экране выводится топ наиболее часто используемых слов заданного исполнителя. +Команда __--top-words=__ принимает целочисленные аргументы и по умолчанию имеет параметр 30, т.е. выводится топ-30 наиболее часто используемых слов. +Команда __--name=__ принимает строковые данные и является обязательной для заполнения, т.к. при отсутствии агрументов выдает ошибку. Также при отсутствии заданного исполнителя в списке баттлеров для анализа, программа выдаст список доступных исполнителей. + +Пример работы программы. + +``` + +ruby main.rb --top-words=10 --name=Толик + +Рэпер Толик не известен мне. Зато мне известны: +Гнойный +Oxxxymiron +Галат +... + +ruby main.rb --top-words=5 --name=Oxxxymiron + +просто - 32 раз(а) +рэп - 21 раз(а) +быть - 17 раз(а) +ты, - 16 раз(а) +против - 15 раз(а) +``` + diff --git a/2355/2/app.rb b/2355/2/app.rb new file mode 100644 index 000000000..0b78478b6 --- /dev/null +++ b/2355/2/app.rb @@ -0,0 +1,12 @@ +require './first_level.rb' +require './second_level.rb' +require 'optparse' + +OptionParser.new do |opts| + opts.on('--top-bad-words=') { |bad| FirstLevel.new(bad).print_table } + opts.on('--top-words=') do |most| + opts.on('--name=') do |battler_name| + SecondLevel.new(most, battler_name).name_check + end + end +end.parse! diff --git a/2355/2/battle.rb b/2355/2/battle.rb new file mode 100644 index 000000000..678c352ea --- /dev/null +++ b/2355/2/battle.rb @@ -0,0 +1,10 @@ +# This class describes battle +class Battle + attr_reader :file, :words_count + + def initialize(name, number) + @name = name + @file = File.new("./rap-battles/#{name}/#{number}") + @words_count = File.readlines("./rap-battles/#{name}/#{number}").join.split.length + end +end diff --git a/2355/2/first_level.rb b/2355/2/first_level.rb new file mode 100644 index 000000000..427c73252 --- /dev/null +++ b/2355/2/first_level.rb @@ -0,0 +1,32 @@ +require './top_bad_words.rb' +require 'terminal-table' + +# This class is needed for second level of task +class FirstLevel + def initialize(bad) + @bad = !bad.empty? ? bad.to_i : 1 + @first_level = TopBadWords.new + end + + def line(value, elem, table) + table << [elem, Rapper.new(elem).battle_count.to_s + ' баттла(ов)', + value[1].to_s + ' нецензурных слов(а)', + @first_level.obscenity_per_battle(elem).to_s + ' слов(а) на баттл', + @first_level.words_per_round(elem).to_s + ' слов(а) в раунде'] + end + + def table_content(table) + @bad.times do |index| + value = @first_level.top_obscenity_check[index] + elem = value[0] + line(value, elem, table) + end + end + + def print_table + table = Terminal::Table.new do |tb| + table_content(tb) + end + puts table + end +end diff --git a/2355/2/obscenity.rb b/2355/2/obscenity.rb new file mode 100644 index 000000000..12122f3df --- /dev/null +++ b/2355/2/obscenity.rb @@ -0,0 +1,63 @@ +require 'russian_obscenity' +require './rapper.rb' +require './battle.rb' + +# This class is needed to find and collect all obscenity from text files for one rapper +class Obscenity + attr_reader :rapper + + def initialize(name) + @rapper = Rapper.new(name) + @mistakes = [] + end + + def initialize_mistakes + File.new('./mistakes').each { |line| @mistakes << line.delete("\n") } + end + + def delete_mistakes_from_stars(line) + line.split.each do |word| + @rapper.obscenity << word if word =~ /.*\*.*[А-Яа-я.,]$/ + end + end + + def check_each_battle_for_star(number) + Battle.new(@rapper.name, number).file.each do |line| + delete_mistakes_from_stars(line) + end + end + + def first_check + 1.upto(@rapper.battle_count) do |number| + check_each_battle_for_star(number) + end + end + + def delete_mistakes_from_rus_obs(word) + @mistakes.each { |mis| @rapper.obscenity << word unless mis.casecmp(word) } + end + + def rus_obs_line(line) + RussianObscenity.find(line).each do |word| + delete_mistakes_from_rus_obs(word) + end + end + + def check_each_battle_for_rus_obs(number) + Battle.new(@rapper.name, number).file.each do |line| + rus_obs_line(line) + end + end + + def check_rus_obs + 1.upto(@rapper.battle_count) do |number| + check_each_battle_for_rus_obs(number) + end + end + + def check_battles_for_obscenity + initialize_mistakes + first_check + check_rus_obs + end +end diff --git a/2355/2/rapper.rb b/2355/2/rapper.rb new file mode 100644 index 000000000..c34528af0 --- /dev/null +++ b/2355/2/rapper.rb @@ -0,0 +1,11 @@ +# This class describes rapper +class Rapper + attr_reader :name, :battle_count, :obscenity, :favourite_words + + def initialize(name) + @name = name + @battle_count = Dir[File.join("./rap-battles/#{name}/", '**', '*')].count { |file| File.file?(file) } + @obscenity = [] + @favourite_words = {} + end +end diff --git a/2355/2/second_level.rb b/2355/2/second_level.rb new file mode 100644 index 000000000..e29575069 --- /dev/null +++ b/2355/2/second_level.rb @@ -0,0 +1,33 @@ +require './top_bad_words.rb' +require './top_words.rb' +require './rapper.rb' + +# This class is needed for second level of task +class SecondLevel + def initialize(most = 30, name = 'None') + @most = !most.empty? ? most.to_i : 30 + @name = name + @second_level = TopWord.new(name) + @check = TopBadWords.new + end + + def print_top_words + @second_level.ready_top_words + @second_level.res(@most) + end + + def battlers_list + @check.battlers_names + @check.battlers + end + + def name_check + battlers = battlers_list + if !battlers.include?(@name) + puts 'Я не знаю рэпера ' + @name + ', но знаю таких: ' + battlers.each { |battler| puts battler } + else + print_top_words + end + end +end diff --git a/2355/2/top_bad_words.rb b/2355/2/top_bad_words.rb new file mode 100644 index 000000000..faf22351e --- /dev/null +++ b/2355/2/top_bad_words.rb @@ -0,0 +1,44 @@ +require './obscenity.rb' + +# This class is needed for first level of Task 2 +class TopBadWords + attr_reader :battlers, :top_obscenity + + def initialize + @battlers = [] + @top_obscenity = {} + end + + def battlers_names + File.new('./battlers').each { |line| @battlers << line.delete("\n") } + end + + # I don't know how don't use "check." twice + # This method smells of :reek:FeatureEnvy + def top_obscenity_values(check, name) + check.check_battles_for_obscenity + @top_obscenity[name] = check.rapper.obscenity.size + end + + def top_obscenity_check + battlers_names + @battlers.each { |name| top_obscenity_values(Obscenity.new(name), name) } + (@top_obscenity.sort_by { |_key, val| val }).reverse + end + + def obscenity_per_battle(name) + battler = Rapper.new(name) + @top_obscenity[name] / battler.battle_count + end + + # This method is needed in TopBadWords class, so it doesn't depend on instance state + # This method smells of :reek:UtilityFunction + def words_per_round(name, words = 0) + battler = Rapper.new(name) + count = battler.battle_count + 1.upto(count) do |number| + words += Battle.new(battler.name, number).words_count + end + words / (count * 3) + end +end diff --git a/2355/2/top_words.rb b/2355/2/top_words.rb new file mode 100644 index 000000000..d55b85dd7 --- /dev/null +++ b/2355/2/top_words.rb @@ -0,0 +1,60 @@ +require './rapper.rb' +require './battle.rb' + +# This class is needed to find most popular words from text files +class TopWord + attr_reader :battler + + def initialize(name) + @battler = Rapper.new(name) + @pretexts = [] + @words = [] + @hash = @battler.favourite_words + end + + def clear_words(line) + line.split.each do |word| + word = word.delete '.', ',', '?»', '"', '!', ';' + @words << word + end + end + + def check_words_in_text(number) + Battle.new(@battler.name, number).file.each do |line| + clear_words(line) + end + end + + def check_all_words + 1.upto(@battler.battle_count) do |number| + check_words_in_text(number) + end + end + + def pretexts_value + File.new('./pretexts').each { |word| @pretexts << word.delete("\n") } + end + + def top_words_counter + while @words.any? + check = @words.first + @hash[check] = 0 + @words.each { |word| @hash[check] += 1 if word == check && !@pretexts.include?(word) } + @words.delete(check) + end + end + + def ready_top_words + check_all_words + pretexts_value + top_words_counter + end + + def res(value) + most_words = (@hash.sort_by { |_key, val| val }).reverse + 0.upto(value - 1) do |index| + word = most_words[index] + puts word[0] + ' - ' + word[1].to_s + ' раз(а)' + end + end +end