i am trying to add the images to excel. am able to send the different images when calling the method res_data. but after the execution i see the last image overwrites all the previous images captured.
here is my code. please help .am using axlsx gem in ruby
class abc
##row=1
##f = File.open('result.xlsx', 'w+')
##p = Axlsx::Package.new
##ws = ##p.workbook.add_worksheet
##ws.name="result"
def res_data(path)
##ws.add_image(:image_src => path, :noSelect => true, :noMove => true) do |image|
image.width=1366
image.height=768
image.start_at 0, ##row
end
##row=##row+42
##p.serialize(##f)
end
end
calling the method as (using capybara methods along with this)
require 'axlsx'
obj = abc.new
path='c:\test\image_file.png'
visit "http://google.com
save_screenshot(path, :full => true) # google home page screenshot is captured
obj.res_data(path)
click_button "btnI"
save_screenshot(path, :full => true) #clicks "i'm feeling lucky " and capture the screenshot again
obj.res_data(path)
am able to see the 'c:\test\image_file.png' file gets updated with the new image (when save screenshot is called)
but when i see the 'result_xlsx" file, it shows only the last image in all images.
Related
I'm trying to use the Wikipedia gem to run a rake task and match the first image that is either .jpg, .png or .gif to save to my institute instance. I'm using ruby 2.3 and Rails 5.
My current code is as follows:
namespace :import do
desc "Import images from Wikipedia"
task institutes: :environment do
require 'wikipedia'
Institute.all.each do |institute|
school = institute.name
page = Wikipedia.find(school)
next if page.content.nil?
accepted_formats = [".jpg", ".png", ".gif"]
images = page.image_urls
image = images.find {|i| i.image_type }
institute.update!(image_url: image)
end
def image_type
accepted_formats = File.extname(i)
end
end
end
This is giving the error NoMethodError: private method 'image_type' called for #<String....>
Is there a more efficient way (and one that works!) of doing this? Sorry, I'm not that experienced in Ruby! I can't work out what the best way to get this to work is; whether to include a method elsewhere or if there's some better way to do it?
I'll recommend you, at first to check is it need to update institute. Next, if you want to use accepted_formats you should define it in constant like: ACCEPTED_IMAGE_FORMATS or send it like argument.
Then you should move action that should return accepted image to method, something like first_valid_image(images, accepted_formats). On my opinion, it should looks like:
namespace :import do
desc "Import images from Wikipedia"
task institutes: :environment do
require 'wikipedia'
Institute.all.each do |institute|
school = institute.name
page = Wikipedia.find(school)
next if page.content.nil?
accepted_formats = [".jpg", ".png", ".gif"]
images = page.image_urls
image = first_valid_image(images, accepted_formats)
institute.update!(image_url: image) if image # this action would run only if image.ni? == false
end
def first_valid_image(images, accepted_formats)
images.find do |image|
File.extname(image).in? accepted_formats
end
end
end
end
I am trying to use a progress bar widget someone else created and posted on the internet (https://gist.github.com/mdirienzo/6716905).
I created a job file (Ruby) that extracts data from an Excel file and send it to the widget. It loads all the data into my variable (I can see that in the history.yml file) but the widget shows as a blank rectangle with a title only (no bars at all).
This is my job file progressbar.rb:
require 'date'
require 'roo'
require 'active_support'
require 'active_support/core_ext'
PROGRESS_FILENAME = 'progressbars.xlsx'
SCHEDULER.every '3s', :first_in => '1s' do
progress = []
progress_report = Roo::Excelx.new(PROGRESS_FILENAME)
(2..progress_report.last_row).each do |row|
name = progress_report.cell(row, 1)
name.sub!(/(\S+),\s*(\S+).*/, '\2 \1')
prog = progress_report.cell(row, 4)
progress.push({name: name, progress: prog})
end
# get next 10 timeline
progress = progress.sort{|a, b| a[:progress] <=> b[:progress]}[0..9]
# assemble data to send to client
progress_data = []
progress.each do |progress|
progress_data.push({
'label' => progress[:name],
'value' => progress[:progress].to_i
})
end
send_event('progress', items: progress_data)
end
The data is sending correctly however the widget is expecting a different name. From the site you linked the send event should be similar to send_event( 'progress_bars', {title: "", progress_items: []} so change your send event to send_event('progress', progress_items: progress_data).
I am trying to scrape a website and store data in XML using Mechanize and Nokogiri. I didn't set up a Rails project and I am only using Ruby and IRB.
I wrote this method:
def mechanize_club
agent = Mechanize.new
agent.get("http://www.rechercheclub.applipub-fft.fr/rechercheclub/")
form = agent.page.forms.first
form.field_with(:name => 'codeLigue').options[0].select
form.submit
page2 = agent.get('http://www.rechercheclub.applipub-fft.fr/rechercheclub/club.do?codeClub=01670001&millesime=2015')
body = page2.body
html_body = Nokogiri::HTML(body)
codeclub = html_body.search('.form').children("tr:first").children("th:first").to_i
#codeclubs << codeclub
filepath = '/davidgeismar/Documents/codeclubs.xml'
builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
xml.root {
xml.codeclubs {
#codeclubss.each do |c|
xml.codeclub {
xml.code_ c.code
}
end
}
}
end
puts builder.to_xml
end
My first problem is that I don't know how to test my code.
I call ruby webscraper.rb in my console, the file is treated I think, but it doesn't create an XML file in the specified path.
Then, more specifically I am quite sure this code is wrong as I didn't get a chance to test it.
Basically what I am trying to do is to submit a form several times:
agent = Mechanize.new
agent.get("http://www.rechercheclub.applipub-fft.fr/rechercheclub/")
form = agent.page.forms.first
form.field_with(:name => 'codeLigue').options[0].select
form.submit
I think this code is ok, but I dont want it to only select options[0], I want it to select an option, then scrape all the data I need, then go back to page, then select options[1]... until there are no more options (an iteration I guess).
the file is treated I think, but it doesnt create an xml file in the specified path.
There is nothing in your code that creates a file. You print some output, but don't do anything to open or write a file.
Perhaps you should read the IO and File documentation and review how you are using your filepath variable?
The second problem is that you don't call your method anywhere. Though it's defined and Ruby will see it and parse the method, it has no idea what you want to do with it unless you invoke the method:
def mechanize_club
...
end
mechanize_club()
Hi I'm having trouble downloading multiple files with axlsx. The problem is I'm sending an array of Id's to the controller and asking it to download the report using the render command. It raises an AbstractController::DoubleRenderError. I was thinking of overriding the error but realized it's a bad idea, I don't know what else to do... Any suggestions? Thanks.
My controller code looks like this:
def download_report
params[:user_id].each do |user_id|
#report = Report.find_by(:user_id => user_id)
render :xlsx => "download_report", :filename => "#{#report.user.last_name}.xlsx"
end
end
My axlsx template:
wb = xlsx_package.workbook
wb.add_worksheet(name: "Reports") do |sheet|
wb.styles do |s|
# template code
end
end
It is the built in expectation of Rails that you would call render once per request. And, the browser is going to expect one response per request. So, you are going to have to do something else!
You can use render_to_string, and combine the results into a zip file, serving that. See the bottom of this response.
Or, you could create a single spreadsheet and have each user's report show up on their own worksheet.
Or, on the client side, you could use javascript to request each spreadsheet and download each one separately.
The zip one would be something like this code, which uses render_to_string, rubyzip, and send_data:
def download_report
compressed_filestream = Zip::ZipOutputStream.write_buffer do |zos|
params[:user_id].each do |user_id|
#report = Report.find_by(:user_id => user_id)
content = render_to_string :xlsx => "download_report", :filename => "#{#report.user.last_name}.xlsx"
zos.put_next_entry("user_#{user_id}.xlsx")
zos.print content
end
end
compressed_filestream.rewind
send_data compressed_filestream.read, :filename => 'download_report.zip', :type => "application/zip"
end
Axlsx requires rubyzip, so you should have it already. And you probably want to lookup each user and use their name for the spreadsheet, unless you have it otherwise.
I'm trying to automate the process of searching for alternative telephone numbers using SayNoTo0870 . Every time one searches for an alternate number or name it brings up the '/companysearch.php' page.
Clearly this page has no reference, and in my mind you can't just link to this page.
What I'm hoping to do is use the code below, to automate the opening of a browser, searching of a name/number, stripping out the HTML and then providing the top 5 results. I've got the automation part down, but clearly when trying to save the webpage using Hpricot it only brings up the 'Sorry nothing can be found page' because I can't link directly to the search result page.
Here is my code thus far:
(I've removed comments to shorten it)
require 'rubygems'
require 'watir'
require 'hpricot'
require 'open-uri'
class OH870
def searchName(name)
browser = Watir::Browser.new
browser.goto 'http://www.saynoto0870.com/search.php'
browser.text_field(:name => 'search_name').set name
browser.button(:name => 'submit').click
end
def searchNumber(number)
browser = Watir::Browser.new
browser.goto 'http://www.saynoto0870.com/search.php'
browser.text_field(:name => 'number').set number
browser.button(:name => 'submit').click
end
def loadNew(website)
doc = Hpricot(open(website))
puts(doc)
end
def strip_tags
stripped = website.gsub( %r{</?[^>]+?>}, '' )
puts stripped
end
end # class
class Main < OH870
puts "What is the name of the place you want?"
website = 'http://www.saynoto0870.com/companysearch.php'
question = gets.chomp
whichNumber = OH870.new
whichNumber.searchName(question)
#result = OH870.new
#withoutTags = website.strip_tags
#result.loadNew(withoutTags)
end
Now I'm not sure whether there's a way of "asking watir to follow through to the companysearch.php page and dump the results without having to pass this page as a variable.
I wonder if anyone has any suggestions here?
With WATIR, minus the extraneous libraries, here's all it takes to accomplish what you've described (using the 'name' test case only). I've pulled it out of the function format since you already know how to do that, and this will be a clearer test case path.
require 'watir'
#browser = Watir::Browser.new :firefox #open a browser called #browser
#browser.goto "http://(your search page here)" #go to the search page
#browser.text_field(:name => 'name').value = "Awesome" #fill in the 'name' field
#browser.button(:name => 'submit').click #submit the form
If all goes well, we should now be looking at the search results. WATIR already knows it's on a new page - we don't have to specify a URL. In the case that the results are in a frame, we do need to access that frame before we can view its content. Let's pretend they're in a DIV element with an ID of "search_results":
results = #browser.div(:id => "search_results").text
resultsFrame = #browser.frame(:index => 1) #in the case of a frame
results = resultsFrame.div(id => "search_results).text
As you can see, you do not need to save the entire page to parse the results. They could be in table cells, they could be in a different div per line, or a new frame. All are easily accessible with WATIR to be stored in a variable, array, or immediately written to the console or log file.
#results = Array.new #create an Array to store our results
#browser.divs.each do |div| #for each div element on the page
if div.id == "search_results" #if the div ID equals "search_results"
#results << div.text #add it to our array named #results
end
end
Now, if you just wanted the top 5 there are many ways to access them.
#results[0] #first element
#results[0..4] #first 5 elements
I'd also suggest you look into a few programming principles like DRY (Don't Repeat Yourself). In your function definitions where you see that they share code, like opening the browser and visiting the same URL - you can consolidate those:
def search(how, what)
#browser = Watir::Browser.new :firefox
#browser.goto "(that search url again)"
#browser.text_field(:name => how).value = what
etc...
end
search("name", "Hilton")
search("number", "555555")
Since we know that the two available text_field names are "name" and "number", and those make good logical sense as a 'how', we can parameterize them and use a single function for both the Search by Name and Search by Number test cases. This is more efficient, as long as the test cases remain similar enough to be shared.