I am running tasks to scrap a website to get some concert dates.
My tasks are finding/updating the concerts (date and city), and record them in the data base. I can find them in my rails console and also they can be displayed in my index and show views.
BTW it seems it doesn't go through the create method in the ConcertController (I tried with binding.pry)
After the task is run, I want to get an email alert if a new record was found. When I receive the email it's not complete:
concert_mailer/new_dates.html.erb
<p>The boys are playing at <%= #concert.city %> on <%= #concert.date %></p>
#returns The boys are playing at on
<p>The boys are playing at <%= #concert.inspect %> </p>
#returns: The boys are playing at <Concert id: 1, date: nil, city: nil, created_at: "2017-12-05 20:32:09", updated_at: "2017-12-05 20:32:09">
In rails console I have the record:
<Concert id: 1, date: "2017-12-08 00:00:00", city: "ECHANGE CULTUREL CAMEROUN (0) | au 18 décembre 20...", created_at: "2017-12-05 20:32:09", updated_at: "2017-12-05 20:32:10">
Btw here are my tasks:
namespace :scrap do
desc "This get MM concerts"
url = "http://mountain-men.fr/concerts/"
doc = Nokogiri::HTML(open(url))
data = doc.search('#dateconcert table')
data = data.css('.jaunec' ).map { |tr| tr.css('td').map(&:text) } + doc.css('.jaunef' ).map { |tr| tr.css('td').map(&:text) }
task :find_concerts => :environment do
data.each do |concert|
c = Concert.create!
c.date = concert[0]
c.city = concert[1]
c.save
end
end
task :update_concerts => :environment do
existing_date = Concert.all.map { |c| [c.date, c.city] }
data.each do |concert|
c = Concert.create!
c.date = concert[0]
c.city = concert[1]
c.save unless existing_date.include?([concert[0], concert[1]])
end
Concert.where(city: nil, date:nil).destroy_all
end
end
concert.rb
class Concert < ApplicationRecord
after_create :send_notification
def send_notification
ConcertMailer.new_dates(self).deliver_now
end
end
concert_mailer.rb
class ConcertMailer < ApplicationMailer
default from: "concert#mountain-men.fr"
def new_dates(concert)
#concert = concert
mail( to: "email#exemple.com",
subject: "The boys are back in town"
)
end
end
Your after_create callback to trigger the email is being called before you update your Concert object with data. Instead of creating a empty object, try adding values to your fields before you save the Concert object.
Something like this:
data.each do |concert|
c = Concert.new
c.date = concert[0]
c.city = concert[1]
c.save
end
Or this :
data.each do |concert|
Concert.create(date: concert[0], city: concert[1])
end
This way you will have values saved before the after_create callback is triggered.
Related
I am trying to create a filter for my revenue_controller show action, but its not working very well.
This is what I have done in the code below, but it picks the end_datetime alone when I filter by date.
revenue_controller.rb
What am trying to do in the show action is default my search to 30days from now, and if params filled with date, use the selected datetime.
module Admin
module Statistic
class RevenuesController < BaseController
def show
#revenues_reports = Currency.all.map do |currency|
puts params
start_datetime = if params[:created_at].nil? then
30.days.ago
else
params[:created_at].slice(0..15)
end
end_datetime = if params[:created_at].nil? then
Time.now
else
params[:created_at].slice(0..15)
end
get_revenue_report_for_currency(currency, start_datetime, end_datetime)
end
end
def get_revenue_report_for_currency(currency, start_datetime, end_datetime)
puts "start: #{start_datetime}" # I added this to see from the log the start_datetime it picks
puts "end: #{end_datetime}" # I added this to see from the log the end_datetime it picks
total_withdraw_fees_charged = Withdraw.where(:currency => currency.id, :created_at => start_datetime..end_datetime).sum(:fee)
total_withdraw_network_fees_charged = 0.3 * total_withdraw_fees_charged
net_income_from_withdraws = total_withdraw_fees_charged - total_withdraw_network_fees_charged
total_sell_order_fees_charged = Order.where(:type => 'OrderAsk', :bid => currency.id, :created_at => start_datetime..end_datetime).sum(:fee)
total_buy_order_fees_charged = Order.where(:type => 'OrderBid', :ask => currency.id, :created_at => start_datetime..end_datetime).sum(:fee)
total_trading_fees = total_sell_order_fees_charged + total_buy_order_fees_charged
total_fees = net_income_from_withdraws + total_trading_fees
{
currency: currency.code.upcase,
total_withdraw_fees_charged: total_withdraw_fees_charged,
total_withdraw_network_fees_charged: total_withdraw_network_fees_charged,
net_income_from_withdraws: net_income_from_withdraws,
total_sell_order_fees_charged: total_sell_order_fees_charged,
total_buy_order_fees_charged: total_buy_order_fees_charged,
total_trading_fees: total_trading_fees,
total_fees: total_fees
}
end
end
end
end
show.html.erb
.panel.panel-primary
.panel-heading
h4.panel-title = t('admin.statistic.filter')
.panel-body
= form_tag(admin_statistic_revenue_path, :method => 'get', class: 'form-horizontal') do |f|
.row
.col.col-xs-3
label[for="#"]
| Start Date:
input.form-control.created_at.date_time_filter.from[name="[created_at]" type="#" value=""]
.col.col-xs-3
label[for="#"]
| End Date:
input.form-control.created_at.date_time_filter.to[name="[created_at]" type="#" value=""]
= button_tag "Search", :class => 'btn btn-info', :name => nil
browser params url
http://localhost:3000/admin/statistic/revenue?utf8=%E2%9C%93&%5Bcreated_at%5D=2018-08-01+00%3A00&%5Bcreated_at%5D=2018-10-05+00%3A00
development_logs
You are trying to use single parameter name for two different parameters created_at_from and created_at_to:
.col.col-xs-3
label[for="#"]
| Start Date:
input.form-control.created_at.date_time_filter.from[name="created_at_from" type="#" value=""]
.col.col-xs-3
label[for="#"]
| End Date:
input.form-control.created_at.date_time_filter.to[name="created_at_to" type="#" value=""]
created_at is usually datetime, so will have non-zero time for most records.
And 2018-10-05 12:34:56 +0100 is greater than 2018-10-05 00:00:00.
Correct filter for 'records created at specific date' is 'records created after that day has started, but before and of day':
start_datetime = if params[:created_at_from].present?
Time.zone.parse(params[:created_at_from]).beginning_of_day
else
30.days.ago
end
end_datetime = if params[:created_at_to].present?
Time.zone.parse(params[:created_at_to]).end_of_day
else
Time.zone.now
end
Also note zone, because depending on timezone day may start in different universal time.
I use PayPal-SDK-Rest gem. I have the next def inside of feed.rb(model):
def self.paypal_url(return_path)
values = {
business: "team#team.com",
cmd: "_xclick",
upload: 1,
return: "#{Rails.application.secrets.app_host}#{return_path}",
#invoice: id,
amount: 0.01,
item_name: "9dt9",
#item_number: course.id,
quantity: '1',
notify_url: "#{Rails.application.secrets.app_host}/hook"
}
#"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
"https://www.paypal.com/cgi-bin/webscr?" + values.to_query
end
Inside of view file I did put:
<%= link_to "Checkout", #feed.paypal_url("http://my_website.com/en") %>
For the notify_url I did use the next function in feed_controller:
def hook
params.permit! # Permit all Paypal input params
#status = params[:payment_status]
status = params[:st]
if status == "Completed"
Transaction.create(:status => params[:st], :transaction_id => params[:tx], :purchased_at => Time.now)
puts "Data: #{params[:st]} :: #{params[:tx]}"
else
puts "NothingHere"
end
render nothing: true
end
So, it must add my parameters to the table, but it doesn't. What is the problem, who knows? Why it doesn't insert anything into table?
Who can help me with it?
UPDATE
In the MGINX log I get:
Parameters: {"tx"=>"9BK361abcdefg473M", "st"=>"Completed", "amt"=>"0.01", "cc"=>"USD", "cm"=>"", "item_number"=>"", "locale"=>"en"}
I have a date field that is not a required field. I am using Chronic to format the user input string to a valid rails format for a date field. If Chronic is unable to parse the date, I would like to raise an error, rendering the edit view with the respective error message and the originally input value. Currently the update is successful if an invalid date is entered but nothing is updated for the service_date field.
new.html.erb
<%= f.text_field :service_date_text %>
bill.rb
require 'chronic'
class Bill < ActiveRecord::Base
def service_date_text
service_date.try(:strftime, "%m/%d/%Y")
end
def service_date_text=(date)
if date.present?
if Chronic.parse(date)
self.service_date = Chronic.parse(date)
else
self.errors.add(:service_date_text, "invalid date format hello.")
end
else
self.service_date = ''
end
end
end
bills_controller.rb
def update
#bill = current_account.bills.find(params[:id])
if #bill.update_attributes(bill_params)
redirect_to #bill, notice: 'Bill has been successfully updated.'
else
render :edit
end
end
private
def bill_params
params.require(:bill).permit(:description, :notes, :po_number, :service_date_text)
end
errors is cleared whenever you run valid?, which update_attributes does.
Example:
irb(main):001:0> album = Album.new
=> #<Album id: nil, name: nil, release_date: nil, rating: nil, genre_id: nil,
artist_id: nil, created_at: nil, updated_at: nil>
irb(main):004:0> album.errors.add :artist, "You've selected Justin Bieber (!!!)"
=> ["You've selected Justin Bieber (!!!)"]
irb(main):006:0> album.errors.messages
=> {:artist=>["You've selected Justin Bieber (!!!)"]}
irb(main):007:0> album.valid?
=> true
irb(main):008:0> album.errors.messages
=> {}
Don't abuse setters, use proper validations. For example (not tested):
require 'chronic'
class Bill < ActiveRecord::Base
validate :service_date_validation
def service_date_text
service_date.try(:strftime, "%m/%d/%Y")
end
def service_date_text=(date)
if date.present?
if Chronic.parse(date)
self.service_date = Chronic.parse(date)
else
self.service_date = false
end
else
self.service_date = ''
end
end
private
def service_date_validation
if self.service_date == false
self.errors.add(:service_date_text, "invalid date format hello.")
end
end
end
... There are also some gems which provide date validations, such as:
https://rubygems.org/gems/validates_timeliness
https://rubygems.org/gems/date_validator
https://rubygems.org/gems/rails_validations (disclaimer: I am the author)
... as well as some others...
I'll bet the issue is that Rails doesn't expect setter methods to add errors. I would make service_date_text just an attr_accessor and then call a validate method which sets service_date or adds an error.
attr_accessor :service_date_text
validate :service_date_text_format
private
def service_date_text_format
return unless service_date_text # or self.service_date ||= '' and then return
if date = Chronic.parse(date)
self.service_date = date
else
errors.add(:service_date, 'invalid format')
end
end
I use this logic in my app:
controller
#current_user = User.find_or_create_from_oauth(auth_hash)
user.rb
def self.find_or_create_from_oauth(auth_hash)
provider = auth_hash["provider"]
uid = auth_hash["uid"].to_s
case provider
when 'twitter'
if user = self.find_by_twitter_uid(uid)
return user
else
return self.create_user_from_twitter(auth_hash)
end
end
end
def self.create_user_from_twitter(auth_hash)
a = self.create({
:twitter_uid => auth_hash["uid"],
:name => auth_hash["info"]["name"]
})
puts a.inspect
user = User.find_by_twitter_uid(a.twitter_uid)
puts '---'
puts user.inspect
end
Immediately after self.create I would need to run this line:
Assignment.create(:user_id => a.id, :role_id => 2)
The problem is, that the line puts user.inspect return something like this:
#<User id: nil, name: "...name...", twitter_uid: "96580821", provider: "twitter", created_at: nil, updated_at: nil>
Why is in the hash returned id: nil?
Or, is there any other way, how to get the ID of last created record?
If the user has been correctly saved, you can use directly a:
a.assignments.create(:role_id => 2)
Otherwise (check using create! instead of create) there may be a validation error.
I have this method
def last_board
user = current_user #current_user
boards = current_user.boards #return every boards that belongs to current_user e.g. [#<Board _id: 4f2968ac1d41c81c7c000063, _type: "Board", created_at...]
followers = user.all_followers #return every followers of user [#<User _id: 4f2862b21d41c847e200005b, _type: "User" reset_password_sent_at: nil, confirmation_token: nil,...]
followers.each do |follower|
boards.each do |board|
# I want to be a follower of user, if I am following at least one board of this user
#I want run this code, "follower.unfollow(user)", only if follower does not following any user's board.
#this method "follower.follower_of?(board)" return true or false if follower follow board
end
end
you can something like this
followers.each do |follower|
is_having_any_board = false
follower.boards.each do |follower_board|
boards.each do |board|
if(follower_board.id == board.id)#delete last )
is_having_any_board = true
break;
end
end
end
if(is_having_any_board)
follower.follow(user)
else
follower.unfollow(user)
end
end