bell 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +47 -0
- data/MIT-LICENSE +20 -0
- data/README.org +128 -0
- data/Rakefile +16 -0
- data/bell.gemspec +30 -0
- data/bin/bell +5 -0
- data/features/create_user.feature +16 -0
- data/features/full_report.feature +84 -0
- data/features/import_public_contacts.feature +20 -0
- data/features/import_user_contacts.feature +43 -0
- data/features/list_all_contacts.feature +19 -0
- data/features/list_user_contacts.feature +27 -0
- data/features/list_users.feature +22 -0
- data/features/remove_user.feature +21 -0
- data/features/rename_user.feature +25 -0
- data/features/step_definitions/cli_steps.rb +59 -0
- data/features/step_definitions/contact_steps.rb +104 -0
- data/features/step_definitions/database_steps.rb +3 -0
- data/features/step_definitions/report_steps.rb +17 -0
- data/features/step_definitions/user_steps.rb +71 -0
- data/features/support/env.rb +65 -0
- data/features/user_report.feature +67 -0
- data/lib/bell/cli.rb +12 -0
- data/lib/bell/commands/command.rb +58 -0
- data/lib/bell/commands/contact_command.rb +91 -0
- data/lib/bell/commands/implosion_command.rb +14 -0
- data/lib/bell/commands/report_command.rb +69 -0
- data/lib/bell/commands/user_command.rb +74 -0
- data/lib/bell/commands.rb +5 -0
- data/lib/bell/csv_parser.rb +52 -0
- data/lib/bell/dispatcher.rb +13 -0
- data/lib/bell/displayable.rb +13 -0
- data/lib/bell/full_report.rb +145 -0
- data/lib/bell/handlers/contacts_handler.rb +79 -0
- data/lib/bell/handlers/implosions_handler.rb +9 -0
- data/lib/bell/handlers/reports_handler.rb +41 -0
- data/lib/bell/handlers/users_handler.rb +50 -0
- data/lib/bell/handlers.rb +4 -0
- data/lib/bell/message.rb +115 -0
- data/lib/bell/public_contact.rb +17 -0
- data/lib/bell/user.rb +16 -0
- data/lib/bell/user_contact.rb +18 -0
- data/lib/bell/user_report.rb +88 -0
- data/lib/bell/util.rb +31 -0
- data/lib/bell.rb +157 -0
- data/spec/bell/cli_spec.rb +4 -0
- data/spec/bell/commands/command_spec.rb +59 -0
- data/spec/bell/commands/contact_command_spec.rb +112 -0
- data/spec/bell/commands/implosion_command_spec.rb +31 -0
- data/spec/bell/commands/report_command_spec.rb +71 -0
- data/spec/bell/commands/user_command_spec.rb +128 -0
- data/spec/bell/csv_parser_spec.rb +167 -0
- data/spec/bell/dispatcher_spec.rb +4 -0
- data/spec/bell/full_report_spec.rb +4 -0
- data/spec/bell/handlers/contacts_handler_spec.rb +160 -0
- data/spec/bell/handlers/implosions_handler_spec.rb +11 -0
- data/spec/bell/handlers/reports_handler_spec.rb +183 -0
- data/spec/bell/handlers/users_handler_spec.rb +94 -0
- data/spec/bell/public_contact_spec.rb +4 -0
- data/spec/bell/user_contact_spec.rb +4 -0
- data/spec/bell/user_report_spec.rb +4 -0
- data/spec/bell/user_spec.rb +4 -0
- data/spec/spec_helper.rb +21 -0
- metadata +230 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
module Bell
|
2
|
+
module CSVParser
|
3
|
+
extend self
|
4
|
+
include Displayable, Util::String
|
5
|
+
|
6
|
+
def parse_contacts(options)
|
7
|
+
contact_rows = CSV.read(options[:path])
|
8
|
+
rescue Errno::ENOENT
|
9
|
+
display(Message.no_such_file_or_directory(options[:path]))
|
10
|
+
rescue Errno::EISDIR
|
11
|
+
display(Message.path_is_a_directory(options[:path]))
|
12
|
+
rescue CSV::MalformedCSVError
|
13
|
+
display(Message.invalid_contacts_file(options[:path]))
|
14
|
+
else
|
15
|
+
user = User.find(:name => options[:user][:name]) if options[:user]
|
16
|
+
valid_contacts = []
|
17
|
+
contact_rows.each_with_index do |row, index|
|
18
|
+
if row.size < 2
|
19
|
+
display(Message.row_with_few_columns(row, index + 1))
|
20
|
+
elsif row.size > 2
|
21
|
+
display(Message.row_with_extra_columns(row, index + 1))
|
22
|
+
else
|
23
|
+
contact = if options[:user]
|
24
|
+
UserContact.new(:name => sanitize(row.first),
|
25
|
+
:number => row.last,
|
26
|
+
:user_id => user.id)
|
27
|
+
elsif options[:public]
|
28
|
+
PublicContact.new(:name => sanitize(row.first), :number => row.last)
|
29
|
+
end
|
30
|
+
if contact
|
31
|
+
if contact.valid?
|
32
|
+
valid_contacts << contact
|
33
|
+
else
|
34
|
+
display(Message.
|
35
|
+
formatted_contact_errors(contact,
|
36
|
+
:line_number => index + 1))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
if contact_rows.size > 0
|
42
|
+
if contact_rows.size == valid_contacts.size
|
43
|
+
valid_contacts
|
44
|
+
else
|
45
|
+
raise InvalidContacts
|
46
|
+
end
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Bell
|
4
|
+
class FullReport
|
5
|
+
def initialize(phone_bill)
|
6
|
+
@phone_bill = phone_bill
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
"[resumo]
|
11
|
+
#{formatted_total}
|
12
|
+
#{formatted_known_total}
|
13
|
+
#{formatted_public_total}
|
14
|
+
#{formatted_unknown_total}
|
15
|
+
#{formatted_fees_total}
|
16
|
+
|
17
|
+
[usuários]
|
18
|
+
#{users.map { |user| %{#{formatted_user(user)}} }.join("\n")}
|
19
|
+
|
20
|
+
[ligações públicas]
|
21
|
+
#{public_calls.map do |hash|
|
22
|
+
%{#{formatted_call(hash[:call], :contact => hash[:contact])}}
|
23
|
+
end.join("\n")}
|
24
|
+
|
25
|
+
[ligações desconhecidas]
|
26
|
+
#{unknown_calls.map do |call|
|
27
|
+
%{#{formatted_call(call)}}
|
28
|
+
end.join("\n")}".gsub(/^\ {7,}/, '')
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def formatted_total
|
34
|
+
%{#{"Total:"}}.ljust(25) << sprintf("%.2f", total)
|
35
|
+
end
|
36
|
+
|
37
|
+
def formatted_known_total
|
38
|
+
%{#{"Usuários:"}}.ljust(25) << sprintf("%.2f", known_total)
|
39
|
+
end
|
40
|
+
|
41
|
+
def formatted_public_total
|
42
|
+
%{#{"Públicas:"}}.ljust(25) << sprintf("%.2f", public_total)
|
43
|
+
end
|
44
|
+
|
45
|
+
def formatted_unknown_total
|
46
|
+
%{#{"Desconhecidas:"}}.ljust(25) << sprintf("%.2f", unknown_total)
|
47
|
+
end
|
48
|
+
|
49
|
+
def formatted_fees_total
|
50
|
+
%{#{"Taxas:"}}.ljust(25) << sprintf("%.2f", fees_total)
|
51
|
+
end
|
52
|
+
|
53
|
+
def formatted_user(user)
|
54
|
+
%{#{user[:name]}}.ljust(25) << sprintf("%.2f", user[:total])
|
55
|
+
end
|
56
|
+
|
57
|
+
def formatted_call(call, options = {})
|
58
|
+
contact = call.number_called << if options[:contact]
|
59
|
+
" (#{options[:contact].name})"
|
60
|
+
else
|
61
|
+
''
|
62
|
+
end
|
63
|
+
time = call.start_time.to_s
|
64
|
+
date = call.date[0, 8].to_s
|
65
|
+
cost = sprintf("%.2f", call.cost)
|
66
|
+
contact.ljust(30) << if time.empty?
|
67
|
+
date.empty? ? ' ' * 20 : ' ' * 10 << "#{date} "
|
68
|
+
else
|
69
|
+
date.empty? ? ' ' * 12 << "#{time}" : "#{time} #{date} "
|
70
|
+
end << cost
|
71
|
+
end
|
72
|
+
|
73
|
+
def calls
|
74
|
+
@calls ||= @phone_bill.calls
|
75
|
+
end
|
76
|
+
|
77
|
+
def fees
|
78
|
+
@fees ||= @phone_bill.fees
|
79
|
+
end
|
80
|
+
|
81
|
+
def known_calls
|
82
|
+
@known_calls ||= calls.inject([]) do |memo, call|
|
83
|
+
UserContact.find(:number => call.number_called) ? memo << call : memo
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def public_calls
|
88
|
+
@public_calls ||= calls.inject([]) do |memo, call|
|
89
|
+
if contact = PublicContact.find(:number => call.number_called)
|
90
|
+
memo << { :call => call, :contact => contact }
|
91
|
+
else
|
92
|
+
memo
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def unknown_calls
|
98
|
+
calls - (public_calls.map { |c| c[:call] } + known_calls)
|
99
|
+
end
|
100
|
+
|
101
|
+
def fees_total
|
102
|
+
@fees_total ||= fees.inject(0) { |memo, call| memo += call.cost.to_f }
|
103
|
+
end
|
104
|
+
|
105
|
+
def total
|
106
|
+
@total ||= @phone_bill.total
|
107
|
+
end
|
108
|
+
|
109
|
+
def known_total
|
110
|
+
@known_total ||= known_calls.inject(0) do |memo, call|
|
111
|
+
memo += call.cost.to_f
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def public_total
|
116
|
+
@public_total ||= public_calls.inject(0) do |memo, hash|
|
117
|
+
memo += hash[:call].cost.to_f
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def unknown_total
|
122
|
+
@unknown_total ||= unknown_calls.inject(0) do |memo, call|
|
123
|
+
memo += call.cost.to_f
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def user_total(user_name)
|
128
|
+
user = User.find(:name => user_name)
|
129
|
+
subtotal = calls.inject(0) do |total, call|
|
130
|
+
if UserContact.find(:number => call.number_called, :user_id => user.id)
|
131
|
+
total += call.cost.to_f
|
132
|
+
else
|
133
|
+
total
|
134
|
+
end
|
135
|
+
end
|
136
|
+
subtotal + (unknown_total + fees_total) / User.all.size
|
137
|
+
end
|
138
|
+
|
139
|
+
def users
|
140
|
+
User.all.inject([]) do |users_hash, user|
|
141
|
+
users_hash << { :name => user.name, :total => user_total(user.name) }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Bell
|
4
|
+
module Handlers
|
5
|
+
class ContactsHandler
|
6
|
+
include Displayable
|
7
|
+
|
8
|
+
def self.list(params = {})
|
9
|
+
if params.empty?
|
10
|
+
if UserContact.empty?
|
11
|
+
display Message.no_contacts_created
|
12
|
+
else
|
13
|
+
display formatted_contact_list(UserContact.all + PublicContact.all)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
if user = User.find(:name => params[:user][:name])
|
17
|
+
if user.contacts.empty?
|
18
|
+
display Message.contact_list_empty(user.name)
|
19
|
+
else
|
20
|
+
display formatted_contact_list(user.contacts,
|
21
|
+
:user_contacts => true,
|
22
|
+
:csv => params[:csv])
|
23
|
+
end
|
24
|
+
else
|
25
|
+
display Message.user_does_not_exist(params[:user][:name])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.import(params)
|
31
|
+
if params[:user]
|
32
|
+
user = User.find(:name => params[:user][:name])
|
33
|
+
if user
|
34
|
+
if contacts = CSVParser.parse_contacts(params)
|
35
|
+
contacts.each do |contact|
|
36
|
+
contact.save && display(Message.contact_created(contact))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
else
|
40
|
+
display(Message.user_does_not_exist(params[:user][:name]))
|
41
|
+
end
|
42
|
+
elsif params[:public]
|
43
|
+
if contacts = CSVParser.parse_contacts(params)
|
44
|
+
contacts.each do |contact|
|
45
|
+
contact.save &&
|
46
|
+
display(Message.contact_created(contact, :public => true))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
rescue InvalidContacts
|
51
|
+
display(Message.no_contacts_created)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def self.text_contact_list(contacts, options)
|
57
|
+
contacts.inject('') do |list, contact|
|
58
|
+
list << "#{contact.name} (#{contact.number})"
|
59
|
+
if contact.is_a?(PublicContact)
|
60
|
+
list << " - público"
|
61
|
+
else
|
62
|
+
list << " - #{contact.user.name}" unless options[:user_contacts]
|
63
|
+
end
|
64
|
+
list << "\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.csv_contact_list(contacts)
|
69
|
+
contacts.inject('') do |list, contact|
|
70
|
+
list << "\"#{contact.name}\",#{contact.number}\n"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.formatted_contact_list(contacts, options = {})
|
75
|
+
options[:csv] ? csv_contact_list(contacts) : text_contact_list(contacts, options)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Bell::Handlers
|
2
|
+
class ReportsHandler
|
3
|
+
include Bell::Displayable
|
4
|
+
|
5
|
+
def self.full_report(params = {})
|
6
|
+
phone_bill = Embratel::PhoneBill.new(params[:path])
|
7
|
+
rescue Errno::ENOENT
|
8
|
+
display Bell::Message.no_such_file_or_directory(params[:path])
|
9
|
+
rescue Errno::EISDIR
|
10
|
+
display Bell::Message.path_is_a_directory(params[:path])
|
11
|
+
rescue Bell::CSV::MalformedCSVError
|
12
|
+
display Bell::Message.malformed_csv_file(params[:path])
|
13
|
+
rescue Embratel::NonCSVFileError
|
14
|
+
display Bell::Message.non_csv_file(params[:path])
|
15
|
+
rescue Embratel::InvalidRowsError
|
16
|
+
display Bell::Message.invalid_rows(params[:path], $!.message)
|
17
|
+
else
|
18
|
+
display Bell::FullReport.new(phone_bill).to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.user_report(params = {})
|
22
|
+
phone_bill = Embratel::PhoneBill.new(params[:path])
|
23
|
+
rescue Errno::ENOENT
|
24
|
+
display Bell::Message.no_such_file_or_directory(params[:path])
|
25
|
+
rescue Errno::EISDIR
|
26
|
+
display Bell::Message.path_is_a_directory(params[:path])
|
27
|
+
rescue Bell::CSV::MalformedCSVError
|
28
|
+
display Bell::Message.malformed_csv_file(params[:path])
|
29
|
+
rescue Embratel::NonCSVFileError
|
30
|
+
display Bell::Message.non_csv_file(params[:path])
|
31
|
+
rescue Embratel::InvalidRowsError
|
32
|
+
display Bell::Message.invalid_rows(params[:path], $!.message)
|
33
|
+
else
|
34
|
+
if Bell::User.find(:name => params[:user][:name])
|
35
|
+
display Bell::UserReport.new(phone_bill, params[:user][:name]).to_s
|
36
|
+
else
|
37
|
+
display Bell::Message.user_does_not_exist(params[:user][:name])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Bell::Handlers
|
2
|
+
class UsersHandler
|
3
|
+
include Bell::Displayable
|
4
|
+
|
5
|
+
def self.create(params = {})
|
6
|
+
if Bell::User.find(:name => params[:user][:name])
|
7
|
+
display Bell::Message.user_already_exists(params[:user][:name])
|
8
|
+
else
|
9
|
+
Bell::User.create(:name => params[:user][:name])
|
10
|
+
display Bell::Message.user_created(params[:user][:name])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.rename(params = {})
|
15
|
+
if user = Bell::User.find(:name => params[:user][:source_name])
|
16
|
+
if Bell::User.find(:name => params[:user][:target_name])
|
17
|
+
display Bell::Message.user_already_exists(params[:user][:target_name])
|
18
|
+
else
|
19
|
+
user.name = params[:user][:target_name]
|
20
|
+
user.save
|
21
|
+
display Bell::Message.user_renamed(params[:user])
|
22
|
+
end
|
23
|
+
else
|
24
|
+
display Bell::Message.user_does_not_exist(params[:user][:source_name])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.list(params = {})
|
29
|
+
if Bell::User.empty?
|
30
|
+
display Bell::Message.no_created_users
|
31
|
+
else
|
32
|
+
display formatted_user_list
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.remove(params = {})
|
37
|
+
if user = Bell::User.find(:name => params[:user][:name])
|
38
|
+
user.destroy
|
39
|
+
display Bell::Message.user_removed(params[:user][:name])
|
40
|
+
else
|
41
|
+
display Bell::Message.user_does_not_exist(params[:user][:name])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def self.formatted_user_list
|
47
|
+
Bell::User.all.map(&:name).join("\n")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'handlers', 'users_handler')
|
2
|
+
require File.join(File.dirname(__FILE__), 'handlers', 'contacts_handler')
|
3
|
+
require File.join(File.dirname(__FILE__), 'handlers', 'reports_handler')
|
4
|
+
require File.join(File.dirname(__FILE__), 'handlers', 'implosions_handler')
|
data/lib/bell/message.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Bell
|
4
|
+
module Message
|
5
|
+
extend self
|
6
|
+
include Util
|
7
|
+
|
8
|
+
def no_created_users
|
9
|
+
"Nenhum usuário criado."
|
10
|
+
end
|
11
|
+
|
12
|
+
def user_created(user_name)
|
13
|
+
"Usuário '#{user_name}' criado."
|
14
|
+
end
|
15
|
+
|
16
|
+
def user_already_exists(user_name)
|
17
|
+
"erro: o usuário '#{user_name}' já existe."
|
18
|
+
end
|
19
|
+
|
20
|
+
def user_does_not_exist(user_name)
|
21
|
+
"erro: o usuário '#{user_name}' não existe."
|
22
|
+
end
|
23
|
+
|
24
|
+
def user_removed(user_name)
|
25
|
+
"Usuário '#{user_name}' removido."
|
26
|
+
end
|
27
|
+
|
28
|
+
def user_renamed(user)
|
29
|
+
"Usuário '#{user[:source_name]}' renomeado para '#{user[:target_name]}'."
|
30
|
+
end
|
31
|
+
|
32
|
+
def no_contacts_created
|
33
|
+
"Nenhum contato criado."
|
34
|
+
end
|
35
|
+
|
36
|
+
def contact_list_empty(user_name)
|
37
|
+
"A lista de contatos do usuário '#{user_name}' está vazia."
|
38
|
+
end
|
39
|
+
|
40
|
+
def contact_created(contact, options = {})
|
41
|
+
"'#{contact.name} (#{contact.number})' adicionado à lista de " <<
|
42
|
+
if options[:public]
|
43
|
+
"contatos públicos."
|
44
|
+
else
|
45
|
+
"contatos do usuário '#{contact.user.name}'."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def contact_removed(contact_name)
|
50
|
+
"Contato '#{contact_name}' removido."
|
51
|
+
end
|
52
|
+
|
53
|
+
def contact_name_taken(contact_name)
|
54
|
+
contact = UserContact.find(:name => contact_name)
|
55
|
+
"erro: este nome já é usado pelo contato '#{contact.name} (#{contact.number})' do usuário '#{contact.user.name}'.\nCrie um contato com nome diferente." if contact
|
56
|
+
end
|
57
|
+
|
58
|
+
def contact_number_taken(contact_number)
|
59
|
+
contact = UserContact.find(:number => contact_number)
|
60
|
+
"erro: este número já é usado pelo contato '#{contact.name} (#{contact.number})' do usuário '#{contact.user.name}'.\nCrie um contato com número diferente." if contact
|
61
|
+
end
|
62
|
+
|
63
|
+
def contact_number_bad_format(contact_number)
|
64
|
+
"erro: '#{contact_number}' não é um número de telefone válido.\nVeja 'bell --help' para saber mais sobre o formato aceito."
|
65
|
+
end
|
66
|
+
|
67
|
+
def no_such_file_or_directory(path)
|
68
|
+
"erro: o arquivo/diretório '#{path}' não existe."
|
69
|
+
end
|
70
|
+
|
71
|
+
def path_is_a_directory(path)
|
72
|
+
"erro: '#{path}' é um diretório."
|
73
|
+
end
|
74
|
+
|
75
|
+
def malformed_csv_file(path)
|
76
|
+
"erro: '#{path}' é um arquivo CSV danificado."
|
77
|
+
end
|
78
|
+
|
79
|
+
def non_csv_file(path)
|
80
|
+
"erro: '#{path}' não é um arquivo CSV."
|
81
|
+
end
|
82
|
+
|
83
|
+
def invalid_rows(path, message)
|
84
|
+
lines = message.match(/(?:\d+, )*(?:\d+)$/).to_a
|
85
|
+
"erro: '#{path}' tem " <<
|
86
|
+
lines.size == 1 ? 'erro na linha ' : 'erros nas linhas ' <<
|
87
|
+
lines.join(', ')
|
88
|
+
end
|
89
|
+
|
90
|
+
def invalid_contacts_file(path)
|
91
|
+
"erro: '#{path}' não é um arquivo de contatos válido."
|
92
|
+
end
|
93
|
+
|
94
|
+
def row_with_extra_columns(row, line_number)
|
95
|
+
"erro: a linha #{line_number}: '#{row.to_s}' tem mais de duas colunas.\nCada linha deve ter duas colunas, sendo a primeira o nome e a segunda o telefone do contato."
|
96
|
+
end
|
97
|
+
|
98
|
+
def row_with_few_columns(row, line_number)
|
99
|
+
"erro: a linha #{line_number}: '#{row.to_s}' tem mais de duas colunas.\nCada linha deve ter duas colunas, sendo a primeira o nome e a segunda o telefone do contato."
|
100
|
+
end
|
101
|
+
|
102
|
+
def row_with_short_number(row, line_number)
|
103
|
+
"erro: o número de telefone #{row.to_s} na linha '#{line_number}' é muito curto.\nNúmeros de telefone devem ter 10 dígitos, sendo os 2 primeiros o DDD."
|
104
|
+
end
|
105
|
+
|
106
|
+
def formatted_contact_errors(contact, options = {})
|
107
|
+
contact_error = contact.errors.first.last.first
|
108
|
+
if options[:line_number]
|
109
|
+
contact_error.split("\n").first.insert(4, " na linha #{options[:line_number]}")
|
110
|
+
else
|
111
|
+
contact_error
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Bell
|
2
|
+
class PublicContact < Sequel::Model
|
3
|
+
plugin :validation_helpers
|
4
|
+
|
5
|
+
def validate
|
6
|
+
super
|
7
|
+
validates_unique(:number,
|
8
|
+
:message => Message.contact_number_taken(number))
|
9
|
+
validates_format(/^\d{10}$/,
|
10
|
+
:number,
|
11
|
+
:message => Message.contact_number_bad_format(number))
|
12
|
+
if UserContact.find(:number => number)
|
13
|
+
errors.add(:number, Message.contact_number_taken(number))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/bell/user.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Bell
|
2
|
+
class User < Sequel::Model
|
3
|
+
one_to_many(:contacts, :class => UserContact)
|
4
|
+
plugin(:validation_helpers)
|
5
|
+
|
6
|
+
def after_destroy
|
7
|
+
super
|
8
|
+
contacts.each(&:delete)
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate
|
12
|
+
super
|
13
|
+
validates_unique(:name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Bell
|
2
|
+
class UserContact < Sequel::Model
|
3
|
+
many_to_one(:user)
|
4
|
+
plugin(:validation_helpers)
|
5
|
+
|
6
|
+
def validate
|
7
|
+
super
|
8
|
+
validates_unique(:number,
|
9
|
+
:message => Message.contact_number_taken(number))
|
10
|
+
validates_format(/^\d{10}$/,
|
11
|
+
:number,
|
12
|
+
:message => Message.contact_number_bad_format(number))
|
13
|
+
if PublicContact.find(:number => number)
|
14
|
+
errors.add(:number, Message.contact_number_taken(number))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Bell
|
4
|
+
class UserReport
|
5
|
+
include Bell::Util::String
|
6
|
+
|
7
|
+
def initialize(phone_bill, user_name)
|
8
|
+
@phone_bill = phone_bill
|
9
|
+
@user_name = user_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def user
|
13
|
+
Bell::User.find(:name => @user_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def calls
|
17
|
+
@phone_bill.calls.inject([]) do |calls, call|
|
18
|
+
if Bell::UserContact.find(:user_id => user.id, :number => call.number_called)
|
19
|
+
calls << call
|
20
|
+
else
|
21
|
+
calls
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def total
|
27
|
+
calls.inject(0) { |total, call| total += call.cost.to_f }
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"#{formatted_header}\n#{formatted_contact_calls}\n\n#{formatted_total}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def formatted_header
|
37
|
+
"Data".ljust(15) <<
|
38
|
+
"Contato".ljust(20) <<
|
39
|
+
"Número".ljust(15) <<
|
40
|
+
"Horário".ljust(20) <<
|
41
|
+
"Custo"
|
42
|
+
end
|
43
|
+
|
44
|
+
def contact_calls
|
45
|
+
calls.inject([]) do |contact_calls, call|
|
46
|
+
if contact = Bell::UserContact.find(:number => call.number_called)
|
47
|
+
contact_calls << { :contact => contact, :call => call }
|
48
|
+
else
|
49
|
+
contact_calls
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def formatted_total
|
55
|
+
"Total: R$ #{sprintf("%.2f", total)}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def formatted_contact_name(contact_name)
|
59
|
+
if contact_name.size > 20
|
60
|
+
shortened_contact_name = contact_name[0, 15].rstrip.concat('...')
|
61
|
+
else
|
62
|
+
shortened_contact_name = contact_name
|
63
|
+
end
|
64
|
+
|
65
|
+
multibyte_characters = shortened_contact_name.size -
|
66
|
+
multibyte_length(shortened_contact_name)
|
67
|
+
formatted_contact_name = shortened_contact_name.
|
68
|
+
ljust(20 + multibyte_characters)
|
69
|
+
end
|
70
|
+
|
71
|
+
def formatted_contact_call(contact_call)
|
72
|
+
contact = contact_call[:contact]
|
73
|
+
call = contact_call[:call]
|
74
|
+
|
75
|
+
|
76
|
+
call.date[0, 8].ljust(15) <<
|
77
|
+
formatted_contact_name(contact.name) <<
|
78
|
+
call.number_called.ljust(15) <<
|
79
|
+
call.start_time.ljust(20) <<
|
80
|
+
sprintf("%.2f", call.cost)
|
81
|
+
end
|
82
|
+
|
83
|
+
def formatted_contact_calls
|
84
|
+
contact_calls.
|
85
|
+
map { |contact_call| formatted_contact_call(contact_call) }.join("\n")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|