Export to excel using csv library - ruby-on-rails

I have the following code to export to excel
on my controller:
def users_report
#users = Kid.where(confirmation_token: nil).paginate(:page => params[:page], :per_page => 30)
#userxls = Kid.where(confirmation_token: nil)
respond_to do |format|
format.html
format.xls { send_data #userxls.to_csv({col_sep: "\t"}) }
end
end
on my model:
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << ["Name", "Surname", "E-mail", "Birthday", "School", "Class", "Native Language", "Practised Language", "Conversations same Native", "Convserations different Native", "Message same Native", "Message different Native", "Posts", "Videos watched", "Clossed/Finished calls", "Missed calls", "Connections per Week", "Nb of foreign friends", "Nb of friends same country", "Activation Date", "Email Parent", "Parent Activated"]
all.each do |kid|
kids = Array.new
kid.name = kid.name rescue "No name"
kid.surname = kid.surname rescue "No surname"
kid.email = kid.email rescue "No email"
kid.birthday = kid.birthday rescue "No age"
kid.school.name = kid.school.name rescue "No School"
kid.courses.first.name = kid.courses.first.name rescue "No Course"
kid.native_languages = kid.native_languages rescue "No native language"
kid.practice_languages = kid.practice_languages rescue "No practise language"
kid.number_of_native_conversations = kid.number_of_native_conversations rescue "0"
kid.number_of_foreign_conversations = kid.number_of_foreign_conversations rescue "0"
kid.number_of_native_messages = kid.number_of_native_messages rescue "0"
kid.number_of_foreign_messages = kid.number_of_foreign_messages rescue "0"
kid.number_of_activity_posts = kid.number_of_activity_posts rescue "0"
kid.number_of_finished_closed_calls = kid.number_of_finished_closed_calls rescue "0"
kid.number_of_missed_calls = kid.number_of_missed_calls rescue "0"
kid.avg_of_connections_week = kid.avg_of_connections_week rescue "0"
kid.number_of_foreign_friends = kid.number_of_foreign_friends rescue "0"
kid.number_of_friends_same_country = kid.number_of_friends_same_country rescue "0"
kid.confirmed_at = kid.confirmed_at.try(:strftime, "%d/%m/%Y")
kid.tutor.email = kid.tutor.email rescue 'No parent email'
kid.tutor.confirmed = kid.tutor.confirmed rescue 'No parent email'
kids << [kid.name, kid.surname, kid.email, kid.birthday, kid.school.name, kid.courses.first.name, kid.native_languages, kid.practice_languages, kid.number_of_native_conversations, kid.number_of_foreign_conversations, kid.number_of_native_messages, kid.number_of_foreign_messages, kid.number_of_activity_posts, kid.number_of_finished_closed_calls, kid.number_of_missed_calls, kid.avg_of_connections_week, kid.number_of_foreign_friends, kid.number_of_friends_same_country, kid.confirmed_at.try(:strftime, "%d/%m/%Y"), kid.tutor.email, kid.tutor.confirmed? ]
kids.each do |k|
csv << k
end
end
end
end
I know this code is horrible, but I getting the following problem:
1) I can insert electments directo on my csv array, because when I open the file I get something like #kid<3248293028>, instead of name, surname, email, bla bla bla, this I why I have to insert first on a kids array and then using a each, insert in the csv array.
2)Inside my model I have several methods, like age, to get the age of the kids:
def age
now = Time.now.utc.to_date
(now.year - self.birthday.year - (self.birthday.to_date.change(:year => now.year) > now ? 1 : 0)) rescue (now.year - self.birthday.year)
end
but the t0_csv method does not see this method, I have the same problem with other methods like:
sentence_native_languages_of
sentence_practise_languages_of
number_of_native_conversations
number_of_foreign_conversations
number_of_native_messages
etc etc etc, all of then exist in the model but the to_csv never see this methods.
Why, help me to understand.
Thanks in advance.
UPDATE
here is one of the method that my to_csv method can't see
def number_of_native_conversations
na_messages = 0
KidConversation.all_for(self).each do |conversation|
na_messages += 1 unless conversation.foreign_conversation_for? self
end
return na_messages
end

Related

How to hold the checkbox true value in rails after search

The search is working fine but the problem is that when i print the excel report according to the result.it is showing all the values in the database, filter is not working then Checkbox true values are gone. How to hold the 'All address' checkbox params after refreshing the page.
This is my view
.col-md-3.small_scale_margin-top2
= check_box_tag "all_address"
= label_tag "Show All Addresses"
This is my controller
if params[:search].present? or params[:excel_report].present?
search_customer_supplier = params[:search_customer_supplier]
if params[:organization_children].present? and search_customer_supplier["id"].present?
organization_id = search_customer_supplier["id"]
organization = Organization.find(organization_id)
anchestor_ids = organization.anchestors.map{|o| o[:member].id }
search_customer_supplier["id"] = "(#{anchestor_ids.join(' OR ')})" if anchestor_ids.any?
end
#puts "======================================================================"
# puts params[:search_customer_supplier]['accounts_dealer_types.dealer_code']
params[:search_customer_supplier]['accounts_dealer_types.dealer_code'] = params[:search_customer_supplier]['accounts_dealer_types.dealer_code'].join(" OR ") if params[:search_customer_supplier]['accounts_dealer_types.dealer_code'].present?
# puts params[:search_customer_supplier]['accounts_dealer_types.dealer_code']
customer_report = params[:search_customer_supplier].map { |k, v| "#{k}:#{v}" if v.present? }.compact
else
customer_report = ["accounts_dealer_types.dealer_code:(CUS OR SUP OR INDCUS OR INDSUP)"]
end
#all_address = params[:all_address].to_bool if params[:all_address].present?
refined_query += customer_report.join(" AND ")
params[:query] = refined_query
# params[:per_page] = 500
#customer_reports = Organization.search(params)
puts "========================================================="
puts #customer_reports
puts "========================================================="
#account_managers = User.where(active: true)
respond_to do |format|
if params[:excel_report].present?
request.format = "xls"
format.xls { set_attachment_name "customer_supplier_report.xls" }
else
format.html
end
end
end
In my controller this is the relevant part for the checkbox
#all_address = params[:all_address].to_bool if params[:all_address].present?
Just use #all_address to set the checked attribute of check_box_tag
= check_box_tag "all_address", "1", #all_address
Note: Because the checked attribute is the third attribute in the list, you must provide a second attribute too. The "1" is just the default that would be used for the second argument anyway.

Rspec bypass inner function and return mock data

In rails I am writing a test for a controller method search_backups with Rspec:
def elastic_mongo_lookup(search_term)
devices_ids_from_elastic = ConfigTextSearch.search search_term
puts devices_ids_from_elastic
device_ids = devices_ids_from_elastic.map { |device| device._source.device_id }
csv_string = CSV.generate do |csv|
Device.where(:_id.in => device_ids).each do |device|
csv << [device.logical_name, device.primary_ip]
end
end
return csv_string
end
def search_backups
authorize! :read, :custom_report
csv_string = elastic_mongo_lookup params[:search_term]
if csv_string.blank?
flash[:notice] = "No results were found"
redirect_to reports_path
else
render text: "DeviceID, primary_ip\n" + csv_string
end
end#search_backups
describe "try controller method" do
let(:reports_controller) { ReportsController.new }
before do
allow(CSV).to receive(:generate).and_return("1234", "blah")
allow(ConfigTextSearch).to receive(:search).and_return(['"hits": [ {"_source":{"device_id":"54afe167b3000006"}]'])
allow(:devices_ids_from_elastic).to receive(:map).and_return('54afe167b3000006')
stub_request(:get, "http://localhost:9200/mongo_index/config_files/_search?q=").
with(:headers => {'Expect'=>'', 'User-Agent'=>'Faraday v0.9.1'}).
to_return(:status => 200, :body => '', :headers => {})
end
it "allows people to search backups" do
reports = double(ReportsController)
post 'search_backups'
end
end
The issue is that ConfigTextSearch.search search_term returns a elasticsearch ORM object.. which means I can't stub it because the .map() method on devices_ids_from_elastic.map is unique with it's nested _source method.
How could I bypass elastic_mongo_lookup entirely and just return a mocked csv_string to search_backups?
In an RSpec controller test, controller is defined as the controller under test. You can therefore achieve what you're asking about with the following:
allow(controller).to receive(:elastic_mongo_lookup).and_return('whatever string you choose')

How to access an instance variable from a do block?

The following code does not work. It says undefined method 'table_name' for nil:NilClass
#members = Members.all
table member_list_rows do
if #members.table_name == members
row(0).background_color = '3498db'
end
end
Full code
class MemberPdf < Prawn::Document
def initialize(members, view, allcount)
super(top_margin: 50)
if members.size != allcount
#warn = " (Not all members)"
else
#all = " All"
end
text "Showing#{#all} #{members.size} Members", size: 18, style: :bold, align: :center, color: "636363"
text "#{#warn}", size: 11, align: :center, color: "858585"
#members = members
#view = view
member_list
end
def member_list
move_down 20
table member_list_rows do
self.row(0).align = :center
if #members.table_name == "members"
row(0).background_color = '3498db'
else
end
row(0).text_color = "FFFFFF"
self.row_colors = ["DDDDDD", "FFFFFF"]
self.header = true
#self.cell.text_color = "B3B3B3"
row(0).columns(0).style size: 20
end
end
def member_list_rows
[["Name", "Awardunit", "Address", "Contact", "Level of Entry", "Current Award", "Disabled?" ]] +
#members.map do |member|
[member.name, member.awardunit.name, member.address, member.name, member.entrylvl, member.currentaward, #view.yesno(member.disabled)]
end
end
end
Members controller
if params[:commit] == "Clear"
params[:q] = nil
end
respond_to do |format|
format.html
format.pdf do
pdf = MemberPdf.new(Member.search(params[:q]).result.order( 'name ASC' ), view_context, Member.all.size)
send_data pdf.render, filename: "Members_List.pdf", type: "application/pdf", disposition: "inline"
end
end
It is due to #members is nil.You are doing it wrong.
Change this
#members = Members.all #Wrong
to
#members = Member.all #Right
Always remember,the Model name should be singular.
Those are called Naming Conventions. For more information,read these Style guides(Ruby and Rails)
Most likely table method is changing context, in which you don't have access to the #members instance variable anymore. This can be achieved easily by this sample code:
def do_stuff(&block)
cls = Class.new
cls.instance_eval(&block)
end
#test_var = "test_var"
do_stuff { puts #test_var }
You will receive nothing, because #test_var does not exist in the cls.
Am not sure what you are doing with "table member_list_rows". Didn't get that.
In the third line though, it should be
if #members.table_name == "members"

export to excel french character

I have the following method on on my action to export to excel a list of user:
def users_report
#users = Kid.where(confirmation_token: nil).paginate(:page => params[:page], :per_page => 30)
#userxls = Kid.where(confirmation_token: nil)
respond_to do |format|
format.html
format.xls { send_data #userxls.to_csv({col_sep: "\t"}) }
end
end
On my model the to_csv method:
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << ["Name", "Surname", "E-mail", "Age", "School", "Class", "Native Language", "Practised Language", "Conversations same Native", "Convserations different Native", "Message same Native", "Message different Native", "Posts", "Clossed/Finished calls", "Missed calls", "Connections per Week", "Nb of foreign friends", "Nb of friends same country", "Activation Date", "Email Parent", "Parent Activated"]
kids = Array.new
all.each do |kid|
if kid.user_role != "admin"
k = Array.new
kid.name = kid.name rescue "No name"
k << kid.name
kid.surname = kid.surname rescue "No surname"
k << kid.surname
kid.email = kid.email rescue "No email"
k << kid.email
k << kid.age rescue "No age"
if !kid.school.nil?
k << kid.school.name
else
k << "No School"
end
if kid.courses.empty?
k << "No Courses"
else
k << kid.courses.first.name
end
if !kid.native_languages.empty?
languages = Array.new
kid.native_languages.each do |lang|
languages << Language.find(lang).name
end
k << languages
else
k << "No native language"
end
if !kid.practice_languages.empty?
languages = Array.new
kid.practice_languages.each do |lang|
languages << Language.find(lang).name
end
k << languages
else
k << "No practise language"
end
k << kid.number_of_native_conversations rescue "0"
k << kid.number_of_foreign_conversations rescue "0"
k << kid.number_of_native_messages rescue "0"
k << kid.number_of_foreign_messages rescue "0"
k << kid.number_of_activity_posts rescue "0"
k << kid.number_of_finished_closed_calls rescue "0"
k << kid.number_of_missed_calls rescue "0"
k << kid.avg_of_connections_week rescue "0"
k << kid.number_of_foreign_friends rescue "0"
k << kid.number_of_friends_same_country rescue "0"
k << kid.confirmed_at.try(:strftime, "%d/%m/%Y") rescue "0"
k << kid.tutor.email rescue "No parent email"
k << kid.tutor.confirmed? rescue "No parent email"
kids << k
end
end
kids.each do |k|
csv << k
end
end
end
But on my excel file I'm getting names like Jérôme instead of Jérôme. I tried:
# encoding: utf-8
on my view also tried for every field
.force_encoding("UTF-8")
But I still have this problem. Please I really need help with this.
Thanks in advance
CVS::generate understands an option :encoding (see Ruby API).
So use
format.xls { send_data #userxls.to_csv({col_sep: "\t", encoding: 'UTF-8'}) }
You may also think about separating representation and business logic. I use csv_builder that provides views like user_report.csv.csvbuilder to define the csv output.
cvs_builder uses the instance variable #encoding to specify the output character encoding.
Edit
It seems, like your generated csv is already encoded in UTF-8 but you read it as if it were ISO-8859-1 aka. LATIN-1.
You may want to try to generate the csv in LATIN-1 as excel has issues importing UTF-8 csv files.
Depending on the Ruby or Rails version, you have to use ISO-8859-1 instead of LATIN-1.

Resque require 'csv' not working

I am using resque to process a file in the background. It's a CSV file however I get the following error: uninitialized constant ImportFileHelper::CSV
I have tried to require 'csv' and also include CSV neither will work.
require 'csv'
module ImportFileHelper
HOST = ""
USER_NAME = ""
PASSWORD = ""
def self.process_file(file_data, file_name)
init
#file_name = file_name
begin
csv = CSV.parse(file_data, :headers => true)
csv.each do |row|
#first_name = row["FirstName"]
#last_name = row["LastName"]
#email = row["Email"]
#password = "ch#ngeM3!"
#user_group_name = row["GroupName"].split(",")
#store_name = row["StoreName"]
#external_id = row["ExternalID"]
add_user unless #first_name.nil? || #last_name.nil? || #email.nil? || #password.nil? || #first_name.empty? || #last_name.empty? || #email.empty?
end
rescue NoMethodError => no_method_error
log_error_to_db no_method_error
rescue IOError => error
log_error_to_db error
#errors << error.to_s
rescue Exception => ex
log_error_to_db ex
end
prep_soap_responses_for_output
end
def self.init
HTTPI.log = false
#body = { username: USER_NAME, password: PASSWORD }
#errors = []
#existing_users = []
configure_savon
get_all_groups
get_all_stores
end
def self.prep_soap_responses_for_output
[#existing_users, #errors]
end
def self.log_error_to_db(error)
error.backtrace ||= "Not Available"
if error.message.length > 250
error_message = "There was an error"
else
error_message = error.message
end
ErrorLog.create(message: error_message, trace: error.backtrace, file_name: #file_name)
end
def self.get_store_id
#store_id = #stores[#store_name.to_sym]
end
def self.get_all_stores
#stores = { }
client = Savon::Client.new(HOST + "Storews.asmx?wsdl")
body_data = { mall_id: 1, is_return_offline_store: :false }
#body.merge! body_data
begin
response = client.request :get_store_list, body: #body
if response
hash_response = response.to_hash
stores = hash_response[:get_store_list_response][:get_store_list_result][:store]
stores.each do |s|
store = { s[:name].to_sym => s[:store_id] }
#stores.merge! store
end
end
rescue Savon::Error => ex
log_error_to_db error
#errors << error.to_s
end
end
def self.create_adbuilder_user_object
AdbuilderUser.new(#first_name, #last_name, #email, #user_id, #store_id, #store_name, #user_group_name, #group_id, #external_id)
end
def self.configure_savon
Savon.configure do |configure|
configure.log = false
end
end
def self.add_user
body_data = { first_name: #first_name, last_name: #last_name, user_password: #password, email: #email, external_id: #external_id }
#body.merge! body_data
begin
client = Savon::Client.new(HOST + "UserWS.asmx?wsdl")
response = client.request :add_user, body: #body
if response
#user_id = response.body[:add_user_response][:add_user_result]
if #user_group_name
get_group_id
end
if #store_name
#store_id = get_store_id
unless #store_id.to_s =~ /^0$/
adbuilder_user = create_adbuilder_user_object
UserMailer.create_password(adbuilder_user).deliver if adbuilder_user
end
end
end
rescue Savon::Error => error
log_error_to_db error
if error.message == "(soap:Client) 3: A user with the same email login already exists. Please choose a different login."
#existing_users << #email
else
#errors << error.to_s
end
rescue Exception => error
log_error_to_db error
#errors << error.message.to_s
end
end
def self.get_group_id
begin
#user_group_name.each do |group_name|
user_group_id = #groups_info[group_name.downcase.to_sym]
add_user_to_group user_group_id if user_group_id
end
rescue Exception => error
log_error_to_db error
#errors << error.message.to_s
end
end
def self.get_all_groups
#groups_info = {}
begin
client = Savon::Client.new(HOST + "Usergroupws.asmx?wsdl")
response = client.request :get_user_group_list, body: #body
if response
group = response.to_hash
groups = group[:get_user_group_list_response][:get_user_group_list_result][:user_group]
groups.each do |g|
new_hash = { g[:name].gsub(/\s/, "_").downcase.to_sym => g[:user_group_id] }
#groups_info.merge! new_hash
end
end
rescue Savon::Error => error
log_error_to_db
#errors << error.to_s
end
end
def self.add_user_to_group(group_id)
body_data = { user_id: #user_id, user_group_id: group_id }
#body.merge! body_data
begin
client = Savon::Client.new(HOST + "Usergroupws.asmx?wsdl")
response = client.request :add_user_to_group, body: #body
rescue Savon::Error => error
log_error_to_db error
#errors << error.to_s
end
end
end
So as a work around for this I am doing the csv parsing in the resque job file. This is now allowing it to run. Not sure if this is the best way to do it though.
class ProcessFile
#queue = :rts_file_parser
def self.perform(file_data, file_name)
csv = CSV.parse(file_data, :headers => true)
csv.each do |row|
row_data = { first_name: row["FirstName"], last_name: row["LastName"], email: row["Email"], password: "ch#ngeM3!", user_group_name: row["GroupName"].split(","), store_name: row["StoreName"], external_id: row["ExternalID"] }
ImportFileHelper.process_file file_name, row_data
end
end
end
Mind if I claim the answer (via my comment)?
It looks like it might be a scope resolution issue.
Try ::CSV instead of CSV.
Try adding the gem to the gemfile.

Resources