Active Scaffold: field search on column with timestamp - ruby-on-rails

I enabled field search functionality in my controller according the FieldSearch-API-Documentation.
I have enabled field search on two columns as below
class FooController < ApplicationController
active_scaffold 'foo' do |config|
# allowed actions
config.actions = [:create, :update, :delete, :field_search, :list, :nested, :show]
# columns
config.columns = [:date, :created_at]
# searchable columns
config.field_search.columns = :date, :created_at
config.list.always_show_search = true
# list columns
config.list.columns = [:date, :created_at]
[...]
end
end
The column date is of type date and the column created_at is of type timestamp within the postgresql database.
With the configuration parameters above I get successfully a search form on the view rendered by active scaffold. On the search form there is an dropdown with operators (e.g. BETWEEN) and two input controls for selecting the range.
The Problem now is, that searching by date works fine and searching by created_at doesn't work.
After an exhaustive research I found a hint which describes that active scaffold uses by default the datetime format which is configured under ./config/locales/en.yml and shows in my case like this.
en:
time:
formats:
default: "%Y%m%d%H%M%S"
Obviously it does matter how the input format is. The search input for searching by created_at is e.g: from 20180101000000 to 20181231000000.
On the Column-API-Documentation I found the following:
options[:format] can be set for:
date and time columns, and it will be used as the format argument of I18n.localize to format them.
I not understand what kind of value I have to define in the option parameter within the controller?
config.columns[:created_at].options[:format] = ???

Just found the answer while writing the question :-)
If I define in ./config/locales/en.yml a new entry e.g. timestamp as below
en:
time:
formats:
timestamp: "%Y%m%d%H%M"
and do in my controller the following
config.columns[:created_at].options[:format] = "timestamp"
then I am successfully able to search by created_at with the input 201801010000 and 201812310000.

Related

rails simple form date input - separate integer inputs

I have a date column in my schema called dob.
I'm using a simple form to render the dob.
I want to format the input as 3 separate integer inputs.
The closest i have gotten is this code:
<%= f.input :dob, order: [:day, :month, :year] %>
However it produces this:
which is close. However i need the inputs to be integer inputs, and not dropdowns. Also i would like the month the be an integer as well.
If i use as: :integer, it just returns one single input.
Any ideas? The simple form documentation is not particularly helpful.
Thanks
you will need 3 integer inputs:
f.integer :day
f.integer :month
f.integer :year
This requires you to have day, month, and year attribute accessors on your model, and to assemble the date from the component integers, perhaps in the controller, e.g.
# users_controller.rb
def create
params[:dob] = Date.new(params[:year], params[:month], params[:day])
#... the rest of the method
end
in order to populate the integer fields for editing, you'll need methods on your model, e.g.
# in User.rb
def day
dob.day
end
# ...etc
You will also need to check that the data that the user inputs is within range.
All this special handling is why this is not normally done.

Issue with check times overlaps

There are two models:
# Table name: activities_people
#
# activity_id :integer not null
# person_id :integer not null
# date :date not null
# id :integer not null, primary key
# == Schema Information
#
# Table name: activities
#
# id :integer not null, primary key
# name :string(20) not null
# description :text
# active :boolean not null
# day_of_week :string(20) not null
# start_on :time not null
# end_on :time not null
Relations:
activity.rb
has_many :activities_people
has_many :people, through: :activities_people
activity_people.rb
belongs_to :person
belongs_to :activity
I try to create validation that person can join to one activity taking place in specific date and time(start_on, end_on). If I will try sign up to another activity while before I joined to other exercises(same date, and times overlap) should throw error.
What I try:
def check_join_client
activities_date = person.activities_people.where('date = date', date: date)
if activities_date.exists && person.activities.where('id IN (?)', activities_date)
end
I don't know how to use create query(person.activities.where ...) to getting person activities related with activies_people. activities_date check if we joined to activities taking place in same date. Second I want get check start_on and end_on.
Thanks in advance.
If I'm understanding you correctly, you want to find the activites_people for a user that match a query by the date array and then raise an error unless an associated activity for those matched activities_people.
Your original code for check_join_client uses if incorrectly:
def check_join_client
activities_date = person.activities_people.where('date = date', date: date)
if activities_date.exists && person.activities.where('id IN (?)', activities_date)
end
To translate this to pseudocode, you're essentially saying:
result = query if result.condition_met?
However the if condition (the expression after the if) will be evaluated before you define results. It might be more clear if I show a correct approach:
result = query
return result if result.condition_met?
Now to go back to your question about loading associated records, try something like this:
activities_people_ids_matching_date = person.activities_people
.where(date: self.date)
.pluck(:id)
# use pluck to get an array of ids because that's all that's needed here
# not sure how you're getting the date variable, so I'm assuming self.date will work
# I can use `.any?` to see if the ids list is not empty.
condition_met = activities_people_ids_matching_date.any? &&\
person.activities
.where(activities_people_id: activities_people_ids_matching_date)
.any?
condition_met ? true : raise(StandardError, "my error")
There surely is a way to get this done with one query instead of two, but it seems like where you're at with Ruby concepts it's more important to focus on syntax and core functionality than SQL optimization.
The correct syntax (one of several options) is:
person.activities_people.where(date: date)

Date filter not displaying

I created a new datetime row for a page, it works well. The column displays all the specific dates, but when I want to show specific rows using a date filter, it doesn't display a thing.
In the model:
before_validation :payment_date
def payment_date
self.payment_date = sales_order.payment_transactions.last.date
end
The date filter from the controller file:
filter :payment_date, :as => :date_range, :collection => proc { Complaint.all.map{|c| c.sales_order.try(:payment_transactions).try(:last).try(:date)} }
I suppose I did a couple of mistakes and i'm curious where the bad coding is.
In AA filtering is based on Ransack, so your filter would look as follows (assuming sales_order is an association on your model):
filter :sales_order_payment_transactions_date, label: 'Some label', as: :date_range
Here you basically go along the association chain - it is acceptable with ransack.

Search integers with Sunspot

I managed to get Sunspot working in my rails setup. My rails setup renders graphs (Chartwell) with input from my database (integers), example: "design: 80, art: 20, code: 40".
Is there a way that I can search for "design" and get all elements with design > 70 (integer, for instance) as output?
You're not giving code, so I'll improvise.
Since you have everything working, I assume you already have defined your design field indexed in searchable definition in your model.
After you've done that, you will have a .search block for Sunspot (or Model) in your active code (most probably in a controller).
So, let's suppose your model name is Graph.
design = params[:design] # guessing, again.
Graph.search do
with(:design).greater_than(design)
# .... other conditions
end
This should work for you.
UPDATE:
Assuming that you have a design:integer column in your db
In your event.rb:
searchable do
text :name, :location, :date_search
integer :design
end
in your events_controller.rb:
#search = Event.search do
fulltext params[:search]
with(:design).greater_than(params[:design].to_i)
end
Note: You should pass :design as a parameter with your search form

How to index translations of one item in Sunspot

I am using Sunspot/Solr to index my Rails website. I index City name by doing the following:
class City < ActiveRecord::Base
searchable do
text :name
...
end
...
Now I am internationalizing the whole site, using the Globalize3 gem. It saves translations in another table, and get these translations out using the normal accessors.
From the example here (http://osdir.com/ml/rails-oceania/2011-11/msg00047.html) they have:
searchable do
# sorting
string(:job_title) { title }
# keyword / fulltext searching
I18n.available_locales.each do |locale|
text(("title_" + locale.to_s).to_sym, :default_boost => 2)
{ eval("title_" + locale.to_s) }
end
end
So essentially for each locale there is an indexing column in Sunspot, like title_en and title_fr. I am wondering if there is a better approach? (too many columns sounds bad to me)
One alternative I am thinking is to concatenate translations of one item as a single string and put it in another text index column.
Also I was thinking if there is something similar to integer :ids, :multiple => true for texts?
So what's a better way to index multiple translations of the same item name?
I implemented single column language index:
LANGUAGES = {
'en' => 'English',
'fr' => 'Français'
}
#inside city model
text :name_alt do
LANGUAGES.keys.reject{|l| l=='en'}.map do |locale|
read_attribute(:name, locale:locale)
end
end
This uses Rails I18n module and Globalize3. 'read_attribute' is part of Globalize3.
This avoids creating columns per language.
I am not sure whether this is better or the multi column approach.

Resources