clicks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/clicks/clicks_management.rb +143 -0
- data/lib/clicks/serializer.rb +22 -0
- data/lib/clicks.rb +17 -0
- metadata +45 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 75c4fd4c1763c9eff3c4600079fa58e566a123075fe9077546e36a220f5bb3af
|
|
4
|
+
data.tar.gz: a5d53cfc61f59a979653fac9b66e8cb268f802e5182d90aaf014970180dcc790
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9aca8e6ca897216ae9c9c7e355ca517e2d3d92265ccd1669e353ccc849ab6a0c6cedbae00337ad7c4a8c592426f76e4b29dbaa1df4f088f31bef072b08a4091e
|
|
7
|
+
data.tar.gz: 1a51fbb6c1d0dedca61fecff44b4665ab9b3078c75a20e14cd3916b62a39c9bdf287040dfeab62089f1d5d1101448c1d1e024d432df77a74ae243ce8db71b7de
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/all'
|
|
4
|
+
require 'pry'
|
|
5
|
+
|
|
6
|
+
module Clicks
|
|
7
|
+
class ClicksManagement
|
|
8
|
+
def initialize(hash_clicks, options)
|
|
9
|
+
@result = []
|
|
10
|
+
@candidates = Array.new
|
|
11
|
+
@limit = if options.present? && options[:limit].present?
|
|
12
|
+
options[:limit].to_i
|
|
13
|
+
else
|
|
14
|
+
10
|
|
15
|
+
end
|
|
16
|
+
perform(hash_clicks)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def perform(hash_clicks)
|
|
20
|
+
clicks = filter_clicks(group_by_ip(hash_clicks))
|
|
21
|
+
clean_massive_clicks_per_ip(clicks)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def group_by_ip(clicks)
|
|
25
|
+
clicks.group_by {|record| record['ip']}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def filter_clicks(clicks)
|
|
29
|
+
clicks.each do |_k, v|
|
|
30
|
+
if v.size == 1
|
|
31
|
+
@result.push(v.first)
|
|
32
|
+
else
|
|
33
|
+
most_expensive_one_per_group(v)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
@result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def clean_massive_clicks_per_ip(clicks)
|
|
40
|
+
group_by_ip(clicks).delete_if{|k,v| v.size >= @limit}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def most_expensive_one_per_group(clicks_by_ip)
|
|
45
|
+
selected_record_within_one_hour(clicks_by_ip)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def selected_record_within_one_hour(clicks)
|
|
49
|
+
clicks.each_with_index do |record, index|
|
|
50
|
+
(index...clicks.size).each do |comparator_position|
|
|
51
|
+
if comparator_position > index
|
|
52
|
+
if eligible_diff_time?(clicks[comparator_position]["timestamp"], record["timestamp"])
|
|
53
|
+
push_to_collection(@candidates, record)
|
|
54
|
+
push_to_collection(@candidates, clicks[comparator_position])
|
|
55
|
+
else
|
|
56
|
+
if @candidates.blank?
|
|
57
|
+
break if manage_record_with_no_siblings(record, clicks, index)
|
|
58
|
+
else
|
|
59
|
+
manage_last_sibling(record)
|
|
60
|
+
break
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
elsif index==clicks.size-1
|
|
64
|
+
manage_last_click(record)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def manage_last_click(record)
|
|
71
|
+
return manage_candidates if array_contains?(@candidates, record)
|
|
72
|
+
|
|
73
|
+
push_to_collection(@result, record)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def manage_last_sibling(record)
|
|
77
|
+
push_to_collection(@candidates, record)
|
|
78
|
+
manage_candidates
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def manage_record_with_no_siblings(record, clicks, index)
|
|
82
|
+
if (!eligible_diff_time?(record["timestamp"], clicks[index-1]["timestamp"]) && !eligible_diff_time?(clicks[index+1]["timestamp"], record["timestamp"])) || index.zero?
|
|
83
|
+
push_to_collection(@result, record)
|
|
84
|
+
return true
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def records_from_different_ips?(record_1, record_2)
|
|
89
|
+
record_1["ip"] != record_2["ip"]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def push_to_collection(collection, record)
|
|
93
|
+
collection.push(record) unless array_contains?(collection, record)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def manage_candidates
|
|
97
|
+
duplicated_amount_clicks = select_duplicated_records
|
|
98
|
+
if duplicated_amount_clicks.count == 1
|
|
99
|
+
add_to_result(duplicated_amount_clicks.first)
|
|
100
|
+
else
|
|
101
|
+
winner = most_recent_one(duplicated_amount_clicks)
|
|
102
|
+
push_to_collection(@result, winner)
|
|
103
|
+
end
|
|
104
|
+
@candidates = []
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def select_duplicated_records
|
|
108
|
+
candidates_ordered_amount = candidates_by_amount
|
|
109
|
+
candidates_ordered_amount.select do |click|
|
|
110
|
+
click["amount"] == candidates_ordered_amount[0]["amount"]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def add_to_result(record)
|
|
115
|
+
push_to_collection(@result, record) unless compare_dates_diff(@result.last, record)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def compare_dates_diff(record_1, record_2)
|
|
119
|
+
eligible_diff_time?(record_1["timestamp"], record_2["timestamp"])
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def most_recent_one(clicks)
|
|
123
|
+
clicks.sort_by { |hsh| Time.parse(hsh["timestamp"]) }.reverse!.first
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def array_contains?(array, record)
|
|
127
|
+
array.each { |click| return true if click == record }
|
|
128
|
+
false
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def candidates_by_amount
|
|
132
|
+
@candidates.sort_by { |hsh| -hsh["amount"] }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def eligible_diff_time?(time_end, time_start)
|
|
136
|
+
puts "Hora final"
|
|
137
|
+
puts time_end
|
|
138
|
+
puts "hora comienzo"
|
|
139
|
+
puts time_start
|
|
140
|
+
time_end.to_time&.hour == time_start.to_time&.hour
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
module Clicks
|
|
5
|
+
class Serializer
|
|
6
|
+
def initialize(options = nil)
|
|
7
|
+
@path = if options.present? && options[:path].present?
|
|
8
|
+
options[:path].to_s
|
|
9
|
+
else
|
|
10
|
+
"./lib/clicks.json"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
def read_file
|
|
14
|
+
file = File.read(@path)
|
|
15
|
+
parsed_file = JSON.parse(file)
|
|
16
|
+
parsed_file
|
|
17
|
+
end
|
|
18
|
+
def write_file(clicks)
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/clicks.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "clicks/clicks_management"
|
|
4
|
+
require "clicks/serializer"
|
|
5
|
+
|
|
6
|
+
module Click
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
def self.call(options = nil)
|
|
9
|
+
serializer = if options.present?
|
|
10
|
+
Clicks::Serializer.new(options)
|
|
11
|
+
else
|
|
12
|
+
Clicks::Serializer.new
|
|
13
|
+
end
|
|
14
|
+
clicks_list = serializer.read_file
|
|
15
|
+
Clicks::ClicksManagement.new(clicks_list, options)
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clicks
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- María Teresa Simancas
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-02-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Given a json file, will get clicks records by following an specific criteria.
|
|
14
|
+
email: teresasimancasfdez@gmail.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/clicks.rb
|
|
20
|
+
- lib/clicks/clicks_management.rb
|
|
21
|
+
- lib/clicks/serializer.rb
|
|
22
|
+
homepage:
|
|
23
|
+
licenses:
|
|
24
|
+
- MIT
|
|
25
|
+
metadata: {}
|
|
26
|
+
post_install_message:
|
|
27
|
+
rdoc_options: []
|
|
28
|
+
require_paths:
|
|
29
|
+
- lib
|
|
30
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - ">="
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: 2.6.3
|
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
requirements: []
|
|
41
|
+
rubygems_version: 3.0.3
|
|
42
|
+
signing_key:
|
|
43
|
+
specification_version: 4
|
|
44
|
+
summary: Clicks gem
|
|
45
|
+
test_files: []
|