bell 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.
- 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
|