Sorting list of objects by object attribute? - ruby-on-rails

I have a list of objects that are pulled in from an API. Here is the snippet of output (as there's about 300 lines):
combo =>
ID: 6, Name:Thomas Partey, Club:1, Position: 3, Price: $4.7, Total Pts: 57
ID: 7, Name:Martin Ødegaard, Club:1, Position: 3, Price: $7.0, Total Pts: 128
ID: 8, Name:Kieran Tierney, Club:1, Position: 2, Price: $4.6, Total Pts: 23
ID: 12, Name:Emile Smith Rowe, Club:1, Position: 3, Price: $5.6, Total Pts: 5
I would like to change the order so that they are ranked by Total Points rather than ID
I have tried the following:
sorted = combo.sort_by(#totalpoints)
As well as: (but I assume I want to try and use #teampoints since I've defined that)
sorted = combo.sort_by(:totalpoints)
My Full code is:
class Player
attr_accessor :id, :firstname, :secondname, :club, :position, :price, :totalpoints,
:active
def initialize(id, firstname, secondname, club, position, price, totalpoints, active)
#id = id.to_i
#firstname = firstname.to_s
#secondname = secondname.to_s
#club = club.to_s
#position = position.to_i
#price = price / 10.to_f
#totalpoints = totalpoints.to_i
#active = active.to_i
end
def to_s()
" ID: " + #id.to_s + ", Name:" + #firstname.to_s + " " + #secondname.to_s + ", Club:" + #club.to_s + ", Position: " + #position.to_s + ", Price: $" + #price.to_s + ", Total Pts: " + #totalpoints.to_s + " "
end
def self.pull()
require 'net/http'
require 'json'
url = 'https://fantasy.premierleague.com/api/bootstrap-static/'
uri = URI(url)
response = Net::HTTP.get(uri)
object = JSON.parse(response)
elements = object["elements"]
elements.map! { |qb|
if qb["chance_of_playing_next_round"].to_f > 0
Player.new(
qb["id"], # ID
qb["first_name"], # First Name
qb["second_name"], # Surname
qb["team"], # Club
qb["element_type"], # Position
qb["now_cost"], # Current Price
qb["total_points"], # Total Points
qb["chance_of_playing_next_round"]) # Chance Of Playing
end
}
end
combo = Player.pull().map{|qb| qb}
sorted = combo.sort_by(#totalpoints)
puts sorted
end

Based on what you've got shown, this should do what you need:
sorted = combo.sort_by(&:totalpoints)
It's essentially a shortened version of this:
sorted = combo.sort_by { |_combo| _combo.totalpoints }

Related

Is there a simple way to calculate sumproduct in Rails?

I have a PositionGroup that has_many :positions.
Whenever a position_group object is touched, I would like to update pg.average_price like so:
# Average Price = ((position1.transaction_price * (position1.volume/total_volume) +
# position2.transaction_price * (position2.volume/total_volme))
In my callback method, I tried this:
def update_average_price
total_volume = positions.sum(:volume)
avg_price = positions.sum("transaction_price * (volume/#{total_volume})")
update_column(:average_price, avg_price)
end
But when I check the value of avg_price once multiple positions exist, I am getting 0.0.
This is my spec for this specific functionality:
it "should calculate the Weighted Average Purchase Price of all positions" do
# Position 3: Price = 20, volume = 200
# (Position 1 Price * (Position 1 Units/Total # of Units)) +
# (Position 2 Price * (Position 2 Units/Total # of Units)) +
# (Position 3 Price * (Position 3 Units/Total # of Units))
# ($10 * (100/400)) + ($15 * (100/400) + $20 * (100/400)) =
# ($2.50 + $3.75 + $5) = $11.25
price3 = 20
pos3 = create(:position, stock: stock, portfolio: portfolio, transaction_price: 20, current_price: price3, action: :buy, volume: 200, position_group: pg)
expect(pg.average_price).to eql 11.25
end
This is the result when I run it:
1) PositionGroup methods should calculate the Weighted Average Purchase Price of all positions
Failure/Error: expect(pg.average_price).to eql 11.25
expected: 11.25
got: 0.0
(compared using eql?)
I am pretty sure the issue is this line, from my callback method update_average_price on PositionGroup:
avg_price = positions.sum("transaction_price * (volume/#{total_volume})")
Is there a better way to approach this or is there something else giving me that 0.0 when I shouldn't be?
avg_price = positions.sum("transaction_price * (volume/#{total_volume.to_f})")
to_f is missing, converting to float to get a decimal to work with.
Example
irb(main):022:0> Position.all
Position Load (0.3ms) SELECT "positions".* FROM "positions" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Position id: 1, transaction_price: 0.5e2, volume: 150, position_group_id: 1>, #<Position id: 2, transaction_price: 0.1e1, volume: 50, position_group_id: 1>]>
irb(main):023:0> Position.all.sum("transaction_price * (volume/#{total_volume.to_f})")
(0.2ms) SELECT SUM(transaction_price * (volume/200.0)) FROM "positions"
=> 37.75

Creating a checksum for passed in parameters in Ruby

I am creating a checksum for my payment gateway. The checksum should be in this format:
key|txnid|amount|productinfo|firstname|email|||||||||||salt
I need to leave 11 pipes between the email and the salt.
In my controller I created a method:
require 'digest/sha2'
def getChecksum(key, txnid, amount, productinfo, firstname, email, phone, surl, furl)
String str = key + '|' + txnid + '|' + amount + '|' + productinfo + '|' + firstname + '|' + email + '|' + phone + '|' + surl + '|' + furl+ '|'+ '|'+ '|'+ '|'+ '|'+ '|'+ '|'+ '|' + 'salt'
Digest::SHA512.hexdigest( str )
end
I called this method in my controller:
hash: getChecksum(
key: 'key_value',
txnid: '4',
amount: '2000',
productinfo: 'first sales',
firstname: 'John',
email: 'johndepp#gmail.com',
phone: '123456789',
surl: 'http://someurl.io',
furl: 'http://someurl.io')
But now I am getting a "wrong number of arguments" error:
wrong number of arguments (1 for 9)
def getChecksum( key, txnid,
amount, productinfo, firstname, email, phone, surl, furl)
I think all the parameters are considered as a single one. Can someone tell me what I am doing wrong here?
You are sending a single hash as the parameter, instead you need to send the values only (without keys). Also no need to declare the type String str, you should review your Ruby fundamentals, I'm guessing you come from another language.
# You are calling this
getChecksum(key: 'key_value', txnid: '4', ...)
# which syntactically equals this, a single hash as a parameter
getChecksum({key: 'key_value', txnid: '4', ...})
# Instead, send the values alone
getChecksum('key_value', '4', ...)
Let's look at how you're writing your code and see if we can make it more Ruby-like and hopefully more production-worthy.
You wrote this:
def getChecksum(key, txnid, amount, productinfo, firstname, email, phone, surl, furl)
String str = key + '|' + txnid + '|' + amount + '|' + productinfo + '|' + firstname + '|' + email + '|' + phone + '|' + surl + '|' + furl+ '|'+ '|'+ '|'+ '|'+ '|'+ '|'+ '|'+ '|' + 'salt'
Digest::SHA512.hexdigest( str )
end
First, ick.
Methods and variables in Ruby are snake_case, not camelCase. ItsAReadabilityThing.
We don't define variables using String str =.... Ruby can figure out what type of variable it is 99.99% of the time, so str =... is sufficient.
Think about how you're using fixed-strings and values. '|' is your separator, so define it as such:
SEPARATOR = '|'
And you need eleven of them as a divider, so define that:
DIVIDER = SEPARATOR * 11
I'd write the code more like this:
require 'digest/sha2'
SEPARATOR = '|'
DIVIDER = SEPARATOR * 11 # => "|||||||||||"
def checksum_1(key, txnid, amount, productinfo, firstname, email, phone, surl, furl)
str = [key, txnid, amount, productinfo, firstname, email, phone, surl, furl].join(SEPARATOR) + DIVIDER + 'salt'
Digest::SHA512.hexdigest(str)
end
But even that can be improved upon.
When we have more than three parameters it's time to refactor and clean up the parameter list. There are many ways to do it, but using a hash is the most common:
def checksum_2(params)
str = params.values_at(
:key, :txnid, :amount, :productinfo, :firstname, :email, :phone, :surl, :furl
).join(SEPARATOR) + DIVIDER + 'salt'
Digest::SHA512.hexdigest( str )
end
Using values_at is a nice way to extract the values of the keys in a hash. They come out in an array in the same order of the keys.
The problem now is that a parameter could be missing and Ruby won't catch it. Named parameters are the new-hotness for dealing with that, but for older Rubies, or to get an idea how it might be done in other languages, we can look at the keys received, then their associated values, and if things don't look good raise an exception rather than return a bogus value.
To make things easier I'm going to use Active Support's blank? method, found in the Core Extensions:
require 'active_support/core_ext/object/blank'
def checksum_3(params)
required_values = [:key, :txnid, :amount, :productinfo, :firstname, :email, :phone, :surl, :furl]
missing_keys = required_values - params.keys
fail ArgumentError, "Missing keys: #{ missing_keys.map{ |k| ":#{ k }" }.join(', ') }" unless missing_keys.empty?
missing_values = required_values.select{ |k| params[k].blank? }
fail ArgumentError, "Missing values for: #{ missing_values.map{ |k| ":#{ k }" }.join(', ') }" unless missing_values.empty?
str = params.values_at(*required_values).join(SEPARATOR) + DIVIDER + 'salt'
Digest::SHA512.hexdigest(str)
end
Here's how the calls to the various versions of the method look, along with the associated checksum that's returned:
c1 = checksum_1('key_value', '4', '2000', 'first sales', 'John', 'johndepp#gmail.com', '123456789', 'http://someurl.io', 'http://someurl.io')
c2 = checksum_2(
key: 'key_value',
txnid: '4',
amount: '2000',
productinfo: 'first sales',
firstname: 'John',
email: 'johndepp#gmail.com',
phone: '123456789',
surl: 'http://someurl.io',
furl: 'http://someurl.io'
)
c3 = checksum_3(
key: 'key_value',
txnid: '4',
amount: '2000',
productinfo: 'first sales',
firstname: 'John',
email: 'johndepp#gmail.com',
phone: '123456789',
surl: 'http://someurl.io',
furl: 'http://someurl.io'
)
c1 # => "fcf4e21c6e711808a6984824f09552dccc5c4378b55720c88b3abd4a1904931b8d883beaef51edec705a9d8b0ccd3ba898a0d4c05f75bd41fa3b90f0df7b5c79"
c2 # => "fcf4e21c6e711808a6984824f09552dccc5c4378b55720c88b3abd4a1904931b8d883beaef51edec705a9d8b0ccd3ba898a0d4c05f75bd41fa3b90f0df7b5c79"
c3 # => "fcf4e21c6e711808a6984824f09552dccc5c4378b55720c88b3abd4a1904931b8d883beaef51edec705a9d8b0ccd3ba898a0d4c05f75bd41fa3b90f0df7b5c79"
While we still have to pass in the same amount of information, using the hash allows us to see what each parameter is being assigned to, resulting in self-documenting code, which is a very important thing for long-term maintenance. Also, the hash doesn't require a specific order of the parameters, making it easier to write the code calling it; I usually know I need certain things but can't remember what order they're in.
That's all my take on how to write this type of code. See "Ruby 2 Keyword Arguments" for a similar write-up on the same topic.
Here's how you would define the method with keyword arguments:
def getChecksum(key:, txnid:, amount:, productinfo:, firstname:, email:, phone:, surl:, furl:)
str = key + '|' + txnid + '|' + amount + '|' + productinfo + '|' + firstname + '|' + email + '|' + phone + '|' + surl + '|' + furl+ '|'+ '|'+ '|'+ '|'+ '|'+ '|'+ '|'+ '|' + 'salt'
Digest::SHA512.hexdigest( str )
end
Then you can call it with a hash like you are currently.
I think this looks a bit cleaner, though:
def get_checksum(key:, txnid:, amount:, productinfo:, firstname:, email:, phone:, surl:, furl:)
str = "#{key}|#{txnid}|#{amount}|#{productinfo}|#{firstname}|#{email}|#{phone}|#{surl}|#{furl}||||||||salt"
Digest::SHA512.hexdigest(str)
end
Or perhaps:
def get_checksum(key:, txnid:, amount:, productinfo:, firstname:, email:, phone:, surl:, furl:)
str = [ key, txnid, amount, productinfo, firstname, email, phone, surl, furl ].join("|")
Digest::SHA512.hexdigest("#{str}||||||||salt")
end
You can learn more about keyword arguments, including how to give keywords default values to make them optional, here: https://robots.thoughtbot.com/ruby-2-keyword-arguments

How to calculate the hash for a payment form?

I'm trying to calculate a hash for a payment form but I get the error:
no implicit conversion of Fixnum into String
so I interpret this that I'm trying to do math calculation on what is text. But how should I correct my code?
The instructions from the payment merchant are:
hash = SHA1(salt + "|" + description + "|" + amount + "|" + currency +
"|" + transaction_type)
So in my controller I have:
def checkout
#organization = Organization.new(organizationnew_params)
if #organization.save
#organization.members.each do |single_member|
single_member.send_activation_email
end
#amount = 100.00
#currency = "EUR"
#description = #organization.id
#transaction_description = "My description"
#transaction_type = "S"
### LINE BELOW HAS THE HASH, WHICH REFERS TO THE PRIVATE METHOD BELOW ###
#hash = hash(#description, #amount, #currency, #transaction_type)
render 'checkout'
else
render 'new_premium'
end
end
private
def hash(description, amount, currency, transaction_type)
#hash = SHA1(SALT + "|" + description + "|" + amount + "|" + currency + "|" + transaction_type)
end
In an initializer I have defined SALT (as well as my merchant_id which is used in the form that is posted to the merchant in the checkout view).
Writing this will be better.
hash = SHA1([salt, description, amount.to_s, currency,transaction_type].join("|"))
You interpreted it wrongly, it's the other way round. You're trying to use a number as a string.
Try to explicitely convert the number in to a string, like so:
#hash = SHA1(SALT + "|" + description + "|" + amount.to_s + "|" + currency + "|" + transaction_type)
As I don't know what all the variables' types are, you might have to add another to_s to a non-string.
The error indicates that you are adding a number to a string, I think the problem is that "amount" is a number which you are adding to a string. you just have to change that:
#hash = SHA1(SALT + "|" + description + "|" + amount.to_s + "|" + currency + "|" + transaction_type)
the :to_s method will convert amount into string. (if there are other numbers just do the same.)
Or you can do this with string interpolation:
#hash = SHA1("#{SALT}|#{description}|#{amount}|#{currency}|#{transaction_type}")

Generating a tree from nested set

I am looking to display a html tree from nested set data.
As the displayed tree does not always display all branches the leaf nodes cannot be identified by subtracting the lft & rgt values.
1 Drinks 27
/ | \
2 Coffee 3 4 Tea 20 21 Milk 26
/ \
5 Black 8 9 Green 19
/ \
10 China 14 15 Africa 18
I was looking to adapt the following code:
How to render all records from a nested set into a real html tree
Solution:
Happy to receive code improvement suggestions :)
def tree_from_set(set, start_level)
buf = "<ul>"
parent = []
prev = set[start_level]
set[start_level+1..-1].each do |node|
if node.lft.between?(prev.lft, prev.rgt)
# Previous was the parent
buf << open_parent_tag(prev)
parent.push(prev)
else
if node.lft.between?(parent.last.lft, parent.last.rgt)
#Previous was a child
buf << leaf_tag(prev)
else
buf << leaf_tag(prev)
begin
buf << "</ul></li>"
parent.pop
end until parent.empty? or node.lft.between?(parent.last.lft, parent.last.rgt)
end
end
prev = node
end
buf << leaf_tag(prev)
begin
buf << "</ul></li>"
parent.pop
end until parent.empty?
buf << "</ul>"
buf.html_safe
end
def open_parent_tag(node)
%{ <li>
#{link_to(node.name, node)}
<ul>
}
end
def leaf_tag(node)
content_tag(:li, link_to(node.name, node))
end
I used this function, but on php, not on ruby:
<?php
//nested sets data ordered by left
$data = array(
array("left" => 1, "right" => 10, "name" => "P0"),
array("left" => 2, "right" => 7, "name" => "P1"),
array("left" => 3, "right" => 4, "name" => "P11"),
array("left" => 5, "right" => 6, "name" => "P12"),
array("left" => 8, "right" => 9, "name" => "P2")
);
//Converter function gets nested sets array and returns nested php array
function nest($arrData){
$stack = array();
$arraySet = array();
foreach( $arrData as $intKey=>$arrValues) {
$stackSize = count($stack);
while($stackSize > 0 && $stack[$stackSize-1]['right'] < $arrValues['left']) {
array_pop($stack);
$stackSize--;
}
$link =& $arraySet;
for($i=0;$i<$stackSize;$i++) {
$link =& $link[$stack[$i]['id']]["children"]; //navigate to the proper children array
}
$tmp = array_push($link, array ('item'=>$arrValues,'children'=>array()));
array_push($stack, array('id' => $tmp-1, 'right' => $arrValues['right']));
}
return $arraySet;
}
//Print result
printArray(nest($data));
function printArray($array){
echo "<ul>";
foreach ($array as $row){
$children = $row['children'];
echo "<li>";
echo $row['item']['name'];
if (!empty($children)) printArray($children);
echo "</li>";
}
echo "</ul>";
}
?>
Managed to convert it to C++ for those who was looking for it like I was.
readDataBaseData(QVector<NodeData> &data, DBNode *node)
{
if(data.size() < 1){
return;
}
QVector<QPair<NodeData, int>> stack;
DBNode* arrayRes = node;
foreach (NodeData curArrDataItem, data) {
int stackSize = stack.size();
while(stackSize > 0 &&
stack.at(stackSize - 1).first.right < curArrDataItem.left){
stack.pop_back();
stackSize--;
}
DBNode* link = arrayRes;
for(int i = 0; i < stackSize; i++){
link = link->childAt(stack.at(i).second);
}
link = new DBNode(curArrDataItem, link);
stack.push_back(QPair<NodeData, int>(curArrDataItem, link->getParent()->childCount() - 1));
}
}

SQLite/Postgres/Heroku: Problem translating query

Heroku throws an error on my Postgres-Query stating:
ActiveRecord::StatementInvalid
(PGError: ERROR: syntax error at or
near "date" 2011-05-03T13:58:22+00:00
app[web.1]: LINE 1: ...2011-05-31')
GROUP BY EXTRACT(YEAR FROM TIMESTAMP
date)||EXT...
The SQLite query in development works as expected. Here is the code:
def self.calculate(year, month, user_id, partner_id)
case ActiveRecord::Base.connection.adapter_name
when 'SQLite'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('entries.date, ' +
'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group("strftime('%Y-%m', date)")
when 'PostgreSQL'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('entries.date, ' +
'sum(case when joint = "f" then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = "t" and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = "t" and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group("EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)")
else
raise 'Query not implemented for this DB adapter'
end
end
I would really appreciate any hints. And as I am already asking a question here, I am uncertain about the case when joint = "t" in the sums in both queries too, is there a better way to do this?
UPDATE
Thanks to both peufeu and a horse with no name the code now looks like:
when 'PostgreSQL'
where(':user_id = entries.user_id OR :partner_id = entries.user_id', {
:user_id => user_id,
:partner_id => partner_id
}).
where('entries.date <= :last_day', {
:last_day => Date.new(year, month, 1).at_end_of_month
}).
select('min(entries.date) as date, ' +
'sum(case when joint = false then amount_calc else 0 end) as sum_private, ' +
'sum(case when joint = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_joint, ' +
'sum(case when joint = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_joint, ' +
'sum(case when compensation = true and user_id = ' + user_id.to_s + ' then amount_calc else 0 end) as sum_user_compensation, ' +
'sum(case when compensation = true and user_id = ' + partner_id.to_s + ' then amount_calc else 0 end) as sum_partner_compensation '
).
group('EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")')
...and works like expected.
Another of my statement runs into troubles now and I edit it here as it seems related to the answer of peufeu. Model/Controller:
def self.all_entries_month(year, month, user_id, partner_id)
mydate = Date.new(year, month, 1)
where(':user_id = entries.user_id OR (:partner_id = entries.user_id AND entries.joint = :true)', {
:user_id => user_id,
:partner_id => partner_id,
:true => true
}).
where(':first_day <= entries.date AND entries.date <= :last_day', {
:first_day => mydate,
:last_day => mydate.at_end_of_month
})
end
# group by tag and build sum of groups named group_sum
def self.group_by_tag
group('tag').
select('entries.*, sum(amount_calc) as group_sum')
end
controller:
#income = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).income
#cost = Entry.all_entries_month(#year, #month, current_user.id, current_partner.id).cost
# group cost by categories
#group_income = #income.group_by_tag.order('group_sum desc')
#group_cost = #cost.group_by_tag.order('group_sum')
The error is:
ActionView::Template::Error (PGError: ERROR: column "entries.id" must appear in the GROUP BY clause or be used in an aggregate function
2011-05-03T18:35:20+00:00 app[web.1]: : SELECT entries.*, sum(amount_calc) as group_sum FROM "entries" WHERE (1 = entries.user_id OR (2 = entries.user_id AND entries.joint = 't')) AND ('2011-04-01' <= entries.date AND entries.date <= '2011-04-30') AND (amount_calc <= 0 AND compensation = 'f') GROUP BY tag ORDER BY group_sum):
2011-05-03T18:35:20+00:00 app[web.1]: 6: </thead>
2011-05-03T18:35:20+00:00 app[web.1]: 7: <tbody>
2011-05-03T18:35:20+00:00 app[web.1]: 8: <% if categories %>
2011-05-03T18:35:20+00:00 app[web.1]: 9: <% categories.each do |category| %>
2011-05-03T18:35:20+00:00 app[web.1]: 10: <tr>
2011-05-03T18:35:20+00:00 app[web.1]: 11: <td class="align-left"><%= category.tag %></td>
2011-05-03T18:35:20+00:00 app[web.1]: 12: <td class="align-right"><%= my_number_to_percentage (category.group_sum.to_f / total_cost) * 100 %></td>
UPDATE 2: I found the solution
# group by tag and build sum of groups named group_sum
def self.group_by_tag
group('tag').
select('tag, sum(amount_calc) as group_sum')
end
Problem is there :
EXTRACT(YEAR FROM TIMESTAMP date)||EXTRACT(MONTH FROM TIMESTAMP date)
Solution :
EXTRACT(YEAR FROM "date"), EXTRACT(MONTH FROM "date")
The word "TIMESTAMP" is a bit out of place there ;) ... also :
Since DATE is a reserved SQL keyword, it is a very bad idea to use it as a column name. Here, I quoted it using " so postgres doesn''t get confused.
And you don't need to waste CPU time building a string concatenating your two EXTRACTs, just remember GROUP BY can use several parameters.
Then of course the query will fail because you SELECT "date" but your dont' GROUP BY date. postgres has no way to know which date you want from all the rows which have the same Year-Month. MySQL will return a value at random from the rows, postgres likes correctness so it will throw an error. You could SELECT min(date) for instance, which would be correct.
I am uncertain about the case when joint = "t"
That depends on how you want to get your results, nothing wrong there.
Maybe using several queries would scan a smaller portion of the table (I don't know about your dataset) or maybe not.
I don't know ruby/heroku, but the expression
joint = "t"
refers to a column t in PostgreSQL because object names are quoted with double quotes. So unless Ruby/Heroku is replacing those double quotes with single quotes that will be an invalid condition.
If t should be string literal (the character t) then you need to use single quotes: joint = 't'
If joint is of type boolean then you should use joint = true in PostgreSQL

Resources