Rails group_by query with parent & child records - sql

I am trying to get all the users who may Buy Sell or Create by counting the time like the below
I want to like this below example
2021 (0)
2020 (0)
2019 (4)
January (2)
-> Buy (1) // Specifically with records date if possible
-> Sell (1)
August (1)
-> Create (1)
September (1)
-> Buy (1)
2018 (3)
January (2)
-> Buy (1) // Specifically with records date if possible
-> Sell (1)
August (1)
-> Create (1)
And so on........
And I was trying something like below
// controller
#user = User.find(params[:user_id])
#records = #user.records.joins(:task)
.where(tasks: {name: ["Buy", "Sell", "Create"]})
.group_by {|t| t.created_at.beginning_of_year}
// view.html.erb
<% #records.each do |y, h| %>
<%= y.strftime("%Y") %>
<% h.each do |f| %>
<%= f.task.name %>
<% end %>
<% end %>
Output like below
2018
Buy
Sell
here are below my models & tables, I was trying several ways but still didn't get that.
//table users
has_many :records, dependent: :destroy
--------------------
id | name | etc... |
--------------------
1 | john | etc |
--------------------
2 | alex | etc |
--------------------
//table tasks
has_many :records, dependent: :destroy
-------------
id | name |
-------------
1 | Buy |
-------------
2 | Sell |
-------------
3 | Create |
-------------
//table records
belongs_to :task, required: true
belongs_to :user, required: true
-------------------------------------
id | user_id | task_id | created_at |
-------------------------------------
1 | 2 | 2 | timestamps |
-------------------------------------
2 | 1 | 1 | timestamps |
-------------------------------------
3 | 1 | 2 | timestamps |
-------------------------------------
Thanks in Advance

instead of querying the user query the records table directly with the user_id and the task names then perform 3 group_by(s) first group_by is on the year the
the task was created the second group_by is for the month and the third is for the task name
The code to look up the task
records = Record.joins(:user, :task).where(users: {id: #user.id}, tasks: {name: ["Buy", "Sell", "Create"]})
records_grouped_by_year = records.group_by { |e| e.created_at.year }.sort.to_h
# now you will get a list of tasks in a hash with keys as years { 2018 => [record1, record2, record3, record4]}
records_grouped_by_year_month = records_grouped_by_year.transform_values { |records| records.group_by { |r| r.created_at.strftime("%B") }) }
# now you will get a hash inside a hash first key is year second key is month the record was created in { 2018 => {"January"=>[record1, record2, record4], "August" => [record3]}
records_grouped_by_year_month_name = records_grouped_by_year_month.transform_values { |records| records.transform_values { |record| tasks.group_by { |r| r.taks.name } } }
# now you will get 3 nested hashes first key is year second key is month and third key is the record name { 2018 => {"January"=>{"Sell=>[record1, record2], "Buy=>[record4]}, "August" => { "Buy" => [record3]}}}
(records.key.first..Time.current.year).each { |year| records_grouped_by_year_month_name[year] ||= {} }
# now it will add an empty hash for each year that didn't exists in the records
In your view:
<% task_count_grouped_by_year_grouped_by_name.each do |year, data_by_month| %>
<h1> <%= year %> </h1>
<% if data_by_month.blank? %>
<%= there was not sale on this year %>
<% else %>
<% data_by_month.each do |month, data_by_name| %>
<h2> <%= month %> </h2>
<% data_by_name.each do |task_name, records| %>
<br /> <bold><%= task_name %></bold> -> (<%= records.count %>) <% #
you have the records here as an array so you can do whatever you want with them %>
<% end %>
<% end %>
<% end %>
<% end %>

Related

Rails active record query to retrive all records that contains ALL IDS in the array of IDS

A video can have multiple categories.
video.rb
has_many :video_categories
has_many :categories, through: :video_categories
category.rb
has_many :video_categories
has_many :videos, through: :video_categories
I have this simple form that allows the user to select the categories he wants to combine in order to find specific videos. For example, there's a "python" category and "advanced" category. If he selected these two categories, it should show videos that has both categories.
Video A - Categories [1,4,7] (this is the categories ids)
Video B - Categories [1,2,7,9]
Video C - Categories [7,9]
If the user select the categories [1,7], the output should be video A and B. The current scope that I have its returning ALL videos that has category 1, ALL videos that has category 7 and videos that has BOTH of them.
I want just the videos that has BOTH of them. How do I do that ?
Current scope:
video.rb
scope :for_categories, -> (category_ids) {
joins(:video_categories).where(
video_categories: { category_id: category_ids }
)
}
pages_controller.rb
def search
#results = Video.for_categories(params[:category_ids])
end
My form
<%= form_with url: "/search", method: :get do |form| %>
<%= form.collection_check_boxes(:category_ids, Category.all,
:id, :title, { prompt: 'None'}, { multiple: true} ) do |cat| %>
<label class="text-capitalize checkbox-inline mr-2">
<%= cat.check_box %>
<%= cat.label %>
</label>
<% end %>
<%= form.submit "Search" %>
<% end %>
I was able to find the solution with the following code
video.rb
scope :by_categories, -> (category_ids) {
joins(:categories).where(categories: [category_ids] )
}
scope :by_all_categories, -> (category_ids) {
by_categories(category_ids).
group("videos.id").
having('count(videos.id) >= ?', category_ids.size)
}
pages_controller.rb
def search
#results = Video.by_all_categories(params[:category_ids].reject(&:empty?))
end
You want apply a GROUP and use HAVING to set a condition on the group to ensure that the number of categories joined matches the number of ids:
class Video
def self.for_categories(*category_ids)
ids = category_ids.flatten # handles a single array as input
group(:id)
.joins(:categories)
.where(categories: { id: ids })
.having(
Category.arel_table[:id]
.count
.gteq(ids.length)
)
end
end
In terms of SQL this will give you something like:
SELECT videos.*
FROM videos
INNER JOIN video_categories ON video_categories.video_id = videos.id
INNER JOIN categories ON video_categories.category_id = categories.id
WHERE categories.id IN (1, 2, 3)
GROUP BY videos.id
HAVING COUNT(categories.id) >= 3
categories_to_search = [1, 7]
conditions = categories_to_search.map do |c|
{ viedo_categories: { category_id: c } }
do
Video.where conditions
# this is the same as: Video.where viedo_categories: { category_id: 1 }, viedo_categories: { category_id: 7 }

display data to table horizontally asp sql

So I have a data that I got from the database that I want to display horizontally in my asp page. It is like a transpose option in Excel.
Here is how my data is currently displayed.
Call # | Greetings # | Verifying Information # | Hold Protocol # |
Call 1 | Greetings 1 | Verifying Information 1 | Hold Protocol 1 |
Call 2 | Greetings 2 | Verifying Information 2 | Hold Protocol 2 |
Call 3 | Greetings 3 | Verifying Information 3 | Hold Protocol 3 |
But I want to display it like this.
Call # | Call 1 | Call 2 | call 3
Greeting # | Greeting 1 | Greeting 2 | Greeting 3
Verifying# | Verifying 1 | Verifying 2 | Verifying 3
Hold Protocol# | Hold Protocol 1 | Hold Protocol 2 | Hold Protocol 3
And here is my code for displaying the data
<table class="table-striped table-bordered table-scores">
<tr>
<td>Call #</td>
<td>Greetings</td>
<td>Verifying Information</td>
<td>Hold Protocol</td>
</tr>
<%
if not RsAgent.eof then
do while not RsAgent.eof
%>
<tr>
<td> <% response.write("Call ") %>> </td>
<td> <% response.write(greeting) %> </td>
<td> <% response.write(verify) %> </td>
</tr>
<%
ctr =ctr+1
RsAgent.movenext
loop
end if
end if
%>
</table>
You can use the GetRows Recordset's method to retrieve the records into an array and then print the data as you like.
This is an example:
<table>
<%
titles = Array("Call #", "Greetings #", "Verifying Information #", "Hold Protocol #")
data = rsAgent.GetRows()
For i=0 To 3
%>
<tr>
<th><%=titles(i)%></th>
<% For j=0 To UBound(data,2) %>
<td><%=data(i,j)%></td>
<% Next j %>
</tr>
<% Next i %>
</table>
What you might want to do is something like this.
<tr>
<td>Call#</td>
<% while not RsAgent.eof %>
<td><% response.write("call") %></td>
<% RsAgent.movenext %>
<% wend %>
</tr>
<% RsAgent.movefirst %>
<tr>
<td>Greetings</td>
<% while not RsAgent.eof %>
<td><% response.write("greeting") %></td>
<% RsAgent.movenext %>
<% wend %>
</tr>
and so on...

Trouble with Paperclip and Ajax with Rails 3.2.8

I am writting an application 100% ajax with rails 3.2.8
Everything was going great until I tried to upload photos of the employees to the employee management module.
This is part of my code:
At the Controller:
class EmpleadosController < ApplicationController
respond_to :js
before_filter :require_user
def index
#empleados = Empleado.paginate( page: params[ :page ],
joins: [:user,:contacto],
select: 'empleados.id as id, empleados.user_id, empleados.contratado_el, empleados.despedido_el, empleados.nss, empleados.sueldo_base_semanal, empleados.comentarios, empleados.foto_file_name, empleados.foto_content_type, empleados.foto_file_size, empleados.foto_updated_at, users.username as username, contactos.salutacion_id as salutacion_id, contactos.id as contacto_id, contactos.nombres as nombres, contactos.apellidos as apellidos'
).sorted( params[ :sort ] )
end
def new
#empleado = Empleado.new
end
def create
#empleado = Empleado.new(params[:empleado])
#empleado.sueldo_base_semanal = params[:empleado][:sueldo_base_semanal].gsub(',','').gsub('$','').to_f
if #empleado.save
redirect_to empleados_path
else
render action: 'new'
end
end
...
Now for the view (I am using HAML):
= form_for( #empleado, remote: true, html: {multipart: true} ) do |f|
= show_me_the_errors #empleado
%table.captura
%tr
%td.etiqueta
= f.label :user_id
%td.campo
= f.select :user_id,
User.usuarios_no_asignados,
{ include_blank: true },
{ tabindex: 1 }
= image_tag 'nuevo_24.png',
style: 'vertical-align: bottom;',
id: 'empleado_nuevo_usuario'
... ( 5 more text fields ) ...
%tr
%td.etiqueta
= f.label :foto
%td.campo
= f.file_field :foto,
accept: 'image/png,image/gif,image/jpeg'
The thing is that while there is not selected file for upload, everything works fine, the command respond_to :js works as it should be in all interactions between create with index and new.
But, when you select an image, all the interactions after create with index and new become HTML completely ignoring the respond_to :js, I mean, the form for behaves just like if the remote: true wasn't there
When everything works well, the URL is localhost:3000 and never changes, but when I select an image to upload, the URL after crete becomes localhost:3000/empleados
Does any one have any clue about this? I have been trying to solve this for the last 3 dyas and failed.
Thaks in advance.
OK, after several days trying to find the problem I started working on a workaround. So this is it:
1) I created a DB Table for temprary storage, with the ID field set without autoincrement, and with the normal field names:
mysql> describe imagens;
+---------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+--------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| imagen_file_name | varchar(500) | YES | | NULL | |
| imagen_content_type | varchar(500) | YES | | NULL | |
| imagen_file_size | int(11) | YES | | NULL | |
| imagen_updated_at | datetime | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| lock_version | int(11) | NO | | 0 | |
+---------------------+--------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
2) at the view (_form.html.haml):
-# this variable will be used as primary key for identifying the image to upload in the database
- ale = Time.now.to_i
3) at the view (_form.html.haml):
I separated the file_field tag
-# FORM for general data input
= form_for( #empleado, remote: true ) do |f|
...
then, inside that form I add, a hidden file with the variable ale and a pretty small iframe
-# I use it in order to find out for wich record to search at the temporary DBTable
= hidden_field_tag :ale, ale
-# this IFRAME will be used to catch the error message returned by rails when uploading the image, it will be invisible
%iframe{src: '#', frameborder:0, height: 0, width: 0, id: 'nulo', name: 'nulo'}
and encapsulated in its own form_for tag, but without any kind of submit or commit button.
= form_for( #imagen, remote: true, html: {multipart: true, target: 'nulo'} ) do |f|
-# used to set the primary key to this value
= hidden_field_tag :ale, ale
= f.file_field :imagen, {style: 'vertical-align: middle;'}
then a little javascript to upload the image when the user choose one, it does the submit by an event trigger:
:javascript
$('#imagen_imagen').change(function(){
$('#new_imagen').submit();
});
4) this is the content of the model for imagen (models/imagen.rb):
class Imagen < ActiveRecord::Base
attr_accessible :imagen
has_attached_file :imagen,
styles: { t100: '100x100>', t300: '300x300X', t600: '600x600' },
convert_options: { all: '-strip' },
path: ":rails_root/public/system/:class/:attachment/:id/:style/:filename",
url: "/system/:class/:attachment/:id/:style/:filename",
hash_secret: 'topsecret'
validates_attachment_size :imagen,
less_than: 250.kilobytes,
message: (I18n.t :mensajes)[:paperclip][:grande]
end
5) this is the code at controllers/imagens_controller.rb :
class ImagensController < ApplicationController
respond_to :js
# by the way, this sline is from authlogic, a great gem to control the access to your site
before_filter :require_user
def create
#imagen = Imagen.new(params[:imagen])
#imagen.id = params[:ale]
#imagen.save
render text: ''
end
end
6) now, in controller/empleados_controller.rb the code is:
def create
#empleado = Empleado.new(params[:empleado])
if #empleado.save
imagen = Imagen.find(params[:ale].to_i)
if imagen
#empleado.foto = imagen.imagen
#empleado.save
imagen.destroy
end
redirect_to empleados_path
else
#imagen = Imagen.new
render action: 'new'
end
end
7) Glosary:
imagen = image
empleado = employee
empleados = staff
ale = short name for aleatorio, I mean, random
foto = picture o photograpy
8) Conclussion:
It works !!!
9) To-Do:
The drawback is that after you upload an image, it will be stored at the database, and then you can cancel the form or change page, that will leave the record and picture alive.
At the beginning I will make a link in my admin module that destroys all records from the Table holding the temporary images, with something like Imagen.all.each{|x| x.destroy }
Later on, I will write a script that at 2:00 a.m. executes that code.

How to make parts of Profile searchable or not

In my Rails 3 app, I want to allow a user to specify which parts of their profile can be searchable by others. I know how to do this if I wanted to make the entire user invisible, but how can I set it up so multiple fields can be designated searchable or not separately?
More info:
In terms of functionality, I want to limit searches based on what parts of their profile a user chooses to be searchable in /settings. Parts of the profile would be, for example, #user.profile.hometown or #user.profile.current_city. I'm working off of a Combination of Railscasts #52 and Trevor Turk's tutorial to set what others can search through checkboxes in the settings.
When searchability is defined in settings, when a user searches (or filters) the /users index, what isn't hidden will be public and searchable. In terms of how this works in the DB as far as hiding table columns or grouping, I thought about hiding tables but maybe that's not the best solution. I'm as beginner as can be and hadn't really thought much about that to be honest.
Method 1 - show/hide specific columns
So, the most direct way (and this will work if there are only a handful of things you want to show/hide), is just to create a boolean column for every thing you need to show/hide. So, if you had a phone number field, you could have a column called "show_phone_number", and when true it would show it.
Method 2 - show/hide whole sections
The next level that you might need is, rather than showing/hiding particular columns, have your show/hide boolean columns something like show_contact_info, show_photos, etc. for each logical section that a user would show or hide.
Then in your view, you'd have something like:
app/views/user/show.html.erb (or .haml or whatever you're using)
....
<% if #user.show_contact_info %>
<%= render :partial => "user_contact_info", :locals => {:user => #user} %>
<% end %>
app/views/partials/_user_contact_info.html.erb
<%=h user.email %><br />
<%=h user.phone_number %><br />
<%= user.blog_url %><br />
...
Method 3 - show/hide sections based on who is viewing it
Finally (and the code here is untested, but I think you'll get the idea) let's say your site has a social structure, and you want to show information to some people, but not to others. Basically you'll need the following in some form or another:
Section visibilities (who can view what sections)
Roles (friends, followers, public, private)
a few methods to make these relationships clear/easy to understand
So, in your User model you'd have something like:
class User < ActiveRecord::Base
has_many :friends, :through => "friendships" # or whatever construct you have
has_many :followers, :through => "followings" # or whatever construct you have
has_many :profile_visibilities
...
def is_friends_with(user)
friends.include?(user)
end
def is_a_follower_of(user)
user.followers.include?(self)
end
def can_see(visibilities)
visibilities.each do |v|
v.user == self || v.is_public || can_see_because_we_are_friends(v) || can_see_because_i_follow(v)
end
end
private:
def can_see_because_we_are_friends(visibility)
visibility.is_friend && is_friends_with(visibility.user)
end
def can_see_because_i_follow(visibility)
visibility.is_follower && is_follower_of(visibility.user)
end
end
Then a class called ProfileVisibilities:
class ProfileVisibilities < ActiveRecord::Base
belongs_to :user
...
def is_public
visibility == "public"
end
def is_friend
visibility == "friends"
end
def is_follower
visibility == "followers"
def is_private
!is_public && !is_friend && !is_follower
end
end
Then a table called profile_visibilities
id | user_id | profile_section | visibility
----------------------------------------------
1 | 1 | contact_info | public # <= visible to everyone
2 | 1 | personal_info | friends # <= visible only to "friends"
3 | 1 | blog_posts | friends # <= visible to "friends"
4 | 1 | blog_posts | followers # <= ...and followers
5 | 1 | photos | friends # <= visible only to "friends"
Then in your controller, something like:
app/controllers/users_controller.rb
...
def show
#user = User.find(params[:id])
#contact_info_visibilities = ProfileVisibilities.find(:all, :conditions = ['user_id = ? AND profile_section = "contact_info"', #user.id]
#photo_visibilities = ProfileVisibilities.find(:all, :conditions = ['user_id = ? AND profile_section = "photos"', #user.id]
# ... and more for each section visibility you need
end
...
And in your view:
app/views/user/show.html.erb
...
<% if current_user.can_see(#contact_info_visibilities) %>
<%= render :partial => "user_contact_info", :locals => {:user => #user}
<% end %>
<% if current_user.can_see(#photo_visibilities) %>
<%= render :partial => "user_photos", :locals => {:user => #user}
<% end %>
...

Implement a Rails 3 nested form with has-many through association

I'm trying to implement a Rails3 nested form with has-many through association.
My model relationships are as follows (My models are Project ProjectDuration, ProjectFee). Project can have many project duration through project_fees.
Following are my models/table
mysql> desc projects;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
class Project < ActiveRecord::Base
has_many :project_durations, :through => :project_fees
has_many :project_fees
accepts_nested_attributes_for :project_fees
end
mysql> desc project_durations;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| duration | varchar(255) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
class ProjectDuration < ActiveRecord::Base
has_many :projects, :through => :project_fees
has_many :project_fees
end
mysql> desc project_fees;
+---------------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| projec_id | int(11) | YES | | NULL | |
| project_duration_id | int(11) | YES | | NULL | |
| fee | float | YES | | NULL | |
+---------------------+----------+------+-----+---------+----------------+
class ProjectFee < ActiveRecord::Base
belongs_to :projects
belongs_to :project_durations
end
And my projects_controllers new action is as follows
class ProjectsController < AdminsController
def new
#project = Project.new
#project_durations = ProjectDuration.find(:all)
project_fees = #project.project_fees.build()
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #project }
end
end
end
And I finally I have the following view (new.erb)
<%= form_for(#project, :html => { :class => :form }) do |f| -%>
<% #project_durations.each do |duration| %>
<%= f.fields_for :project_fees do |builder| %>
<%= render 'fee_fields', :f => builder, :project => #project, :duration => duration %>
<% end %>
<% end %>
<% end -%>
and 'fee_fields' is as
<ul>
<li>
<%= duration.duration %>
<%= f.text_field :fee %>
<%= f.hidden_field :project_duration_id, :value => duration.id %>
</li>
</ul>
Even though this saves data to 'project_fees' table, it does not save data in the project_id field of the project_fees table.
I'm using Rails 3 with Ruby 1.8.7 on Linux.
Assuming your MySQL output is cut and pasted, you probably have a typo in a migration somewhere. The column
projec_id
in your project_fees table should be
project_id
This was the life saver for me
http://iqbalfarabi.net/2011/01/20/rails-nested-form-with-has-many-through-association/