|
1 | | -# Deoptimized version of homework task |
| 1 | +# frozen_string_literal: true |
2 | 2 |
|
3 | 3 | require 'json' |
4 | | -require 'pry' |
5 | | -require 'date' |
6 | | -require 'minitest/autorun' |
| 4 | +require 'set' |
7 | 5 |
|
8 | | -class User |
9 | | - attr_reader :attributes, :sessions |
10 | | - |
11 | | - def initialize(attributes:, sessions:) |
12 | | - @attributes = attributes |
13 | | - @sessions = sessions |
14 | | - end |
| 6 | +def parse_line(line) |
| 7 | + type, id, *params = line.split(',') |
| 8 | + [type, id, *params] |
15 | 9 | end |
16 | 10 |
|
17 | | -def parse_user(id, first_name, last_name, age) |
| 11 | +def initialize_user(params) |
18 | 12 | { |
19 | | - 'id' => id, |
20 | | - 'first_name' => first_name, |
21 | | - 'last_name' => last_name, |
22 | | - 'age' => age, |
| 13 | + user_key: "#{params[0]} #{params[1]}", |
| 14 | + sessionsCount: 0, |
| 15 | + totalTime: 0, |
| 16 | + longestSession: 0, |
| 17 | + usedIE: false, |
| 18 | + alwaysUsedChrome: true, |
| 19 | + dates: [], |
| 20 | + browsers: [] |
23 | 21 | } |
24 | 22 | end |
25 | 23 |
|
26 | | -def parse_session(user_id, session_id, browser, time, date) |
27 | | - { |
28 | | - 'user_id' => user_id, |
29 | | - 'session_id' => session_id, |
30 | | - 'browser' => browser, |
31 | | - 'time' => time, |
32 | | - 'date' => date, |
33 | | - } |
| 24 | +def update_user_with_session(user, browser, time, date) |
| 25 | + user[:browsers] << browser |
| 26 | + user[:usedIE] ||= browser.include?('INTERNET EXPLORER') |
| 27 | + user[:alwaysUsedChrome] &&= browser.include?('CHROME') |
| 28 | + user[:totalTime] += time.to_i |
| 29 | + user[:sessionsCount] += 1 |
| 30 | + user[:longestSession] = [user[:longestSession], time.to_i].max |
| 31 | + user[:dates] << date |
34 | 32 | end |
35 | 33 |
|
36 | | -def collect_stats_from_users(report, users_objects, &block) |
37 | | - users_objects.each do |user| |
38 | | - user_key = "#{user.attributes['first_name']}" + ' ' + "#{user.attributes['last_name']}" |
39 | | - report['usersStats'][user_key] ||= {} |
40 | | - report['usersStats'][user_key] = report['usersStats'][user_key].merge(block.call(user)) |
41 | | - end |
| 34 | +def build_user_stat(user) |
| 35 | + { |
| 36 | + sessionsCount: user[:sessionsCount], |
| 37 | + totalTime: "#{user[:totalTime]} min.", |
| 38 | + longestSession: "#{user[:longestSession]} min.", |
| 39 | + browsers: user[:browsers].sort.join(', '), |
| 40 | + usedIE: user[:usedIE], |
| 41 | + alwaysUsedChrome: user[:alwaysUsedChrome], |
| 42 | + dates: user[:dates].sort.reverse |
| 43 | + }.to_json |
42 | 44 | end |
43 | 45 |
|
44 | | -def work(file_path, disable_gc: {}) |
| 46 | +def work(file_path, disable_gc = false) |
45 | 47 | GC.disable if disable_gc |
46 | 48 |
|
47 | | - file_lines = File.read(file_path).split("\n") |
48 | | - |
49 | | - users = [] |
50 | | - sessions = [] |
51 | | - |
52 | | - file_lines.each do |line| |
53 | | - type, id, param_1, param_2, param_3, param_4 = line.split(',') |
54 | | - users << parse_user(id, param_1, param_2, param_3) if type == 'user' |
55 | | - sessions << parse_session(id, param_1, param_2, param_3, param_4) if type == 'session' |
56 | | - end |
57 | | - |
58 | | - # Отчёт в json |
59 | | - # - Сколько всего юзеров + |
60 | | - # - Сколько всего уникальных браузеров + |
61 | | - # - Сколько всего сессий + |
62 | | - # - Перечислить уникальные браузеры в алфавитном порядке через запятую и капсом + |
63 | | - # |
64 | | - # - По каждому пользователю |
65 | | - # - сколько всего сессий + |
66 | | - # - сколько всего времени + |
67 | | - # - самая длинная сессия + |
68 | | - # - браузеры через запятую + |
69 | | - # - Хоть раз использовал IE? + |
70 | | - # - Всегда использовал только Хром? + |
71 | | - # - даты сессий в порядке убывания через запятую + |
72 | | - |
73 | | - report = {} |
74 | | - |
75 | | - report[:totalUsers] = users.count |
76 | | - |
77 | | - # Подсчёт количества уникальных браузеров |
78 | | - uniqueBrowsers = [] |
79 | | - sessions.each do |session| |
80 | | - browser = session['browser'] |
81 | | - uniqueBrowsers << browser if uniqueBrowsers.all? { |b| b != browser } |
82 | | - end |
83 | | - |
84 | | - report['uniqueBrowsersCount'] = uniqueBrowsers.count |
85 | | - |
86 | | - report['totalSessions'] = sessions.count |
87 | | - |
88 | | - report['allBrowsers'] = |
89 | | - sessions |
90 | | - .map { |s| s['browser'] } |
91 | | - .map { |b| b.upcase } |
92 | | - .sort |
93 | | - .uniq |
94 | | - .join(',') |
95 | | - |
96 | | - # Статистика по пользователям |
97 | | - |
98 | | - user_sessions = sessions.group_by { |session| session['user_id'] } |
99 | | - users_objects = users.map do |user| |
100 | | - User.new(attributes: user, sessions: user_sessions[user['id']] || []) |
101 | | - end |
102 | | - |
103 | | - report['usersStats'] = {} |
104 | | - |
105 | | - users_objects.each do |user| |
106 | | - user_key = "#{user.attributes['first_name']} #{user.attributes['last_name']}" |
107 | | - |
108 | | - # Подготовим данные для сессий |
109 | | - sessions = user.sessions |
110 | | - times = sessions.map { |s| s['time'].to_i } |
111 | | - browsers = sessions.map { |s| s['browser'].upcase } |
112 | | - |
113 | | - report['usersStats'][user_key] = { |
114 | | - # Количество сессий |
115 | | - 'sessionsCount' => sessions.count, |
116 | | - # Общее время |
117 | | - 'totalTime' => "#{times.sum} min.", |
118 | | - # Самая длинная сессия |
119 | | - 'longestSession' => "#{times.max} min.", |
120 | | - # Браузеры через запятую |
121 | | - 'browsers' => browsers.sort.join(', '), |
122 | | - # Хоть раз использовал IE? |
123 | | - 'usedIE' => browsers.any? { |b| b.include?('INTERNET EXPLORER') }, |
124 | | - # Всегда использовал только Chrome? |
125 | | - 'alwaysUsedChrome' => browsers.all? { |b| b.include?('CHROME') }, |
126 | | - # Даты сессий через запятую в обратном порядке в формате iso8601 |
127 | | - 'dates' => user.sessions.map { |s| s['date'] }.sort.reverse |
128 | | - } |
| 49 | + unique_browsers = Set.new |
| 50 | + total_users = 0 |
| 51 | + total_sessions = 0 |
| 52 | + current_user = nil |
| 53 | + is_first_user = true |
| 54 | + |
| 55 | + File.open('spec/fixtures/files/result.json', 'w') do |result| |
| 56 | + result.write('{"usersStats":{') |
| 57 | + |
| 58 | + File.foreach(file_path, chomp: true) do |line| |
| 59 | + type, _id, *params = parse_line(line) |
| 60 | + |
| 61 | + if type == 'user' |
| 62 | + if current_user |
| 63 | + result.write(',') unless is_first_user |
| 64 | + result.write("\"#{current_user[:user_key]}\":#{build_user_stat(current_user)}") |
| 65 | + is_first_user = false |
| 66 | + end |
| 67 | + |
| 68 | + current_user = initialize_user(params) |
| 69 | + total_users += 1 |
| 70 | + elsif type == 'session' |
| 71 | + browser = params[1].upcase |
| 72 | + update_user_with_session(current_user, browser, params[2], params[3]) |
| 73 | + unique_browsers.add(browser) |
| 74 | + total_sessions += 1 |
| 75 | + end |
| 76 | + end |
| 77 | + |
| 78 | + if current_user |
| 79 | + result.write(',') unless is_first_user |
| 80 | + result.write("\"#{current_user[:user_key]}\":#{build_user_stat(current_user)}") |
| 81 | + end |
| 82 | + |
| 83 | + result.write('},') |
| 84 | + result.write("\"totalUsers\":#{total_users},") |
| 85 | + result.write("\"uniqueBrowsersCount\":#{unique_browsers.count},") |
| 86 | + result.write("\"totalSessions\":#{total_sessions},") |
| 87 | + result.write("\"allBrowsers\":\"#{unique_browsers.sort.join(',')}\"") |
| 88 | + result.write('}') |
129 | 89 | end |
130 | 90 |
|
131 | | - File.write('spec/fixtures/files/result.json', "#{report.to_json}\n") |
132 | 91 | puts "MEMORY USAGE: %d MB" % (`ps -o rss= -p #{Process.pid}`.to_i / 1024) |
133 | 92 | end |
0 commit comments