Simple Rails Report with for a count within a month - ruby-on-rails-3

I am producing a report where the user selects a month and the report shows the number of households that visited the agency during the Month stored as #month.
Each Household can have many visits with a visited_on attribute for the date of the visit. A Household may visit more than once in a month but I just want to know how many different households visited in the month
This is my best shot so far:
def house_count(month)
where(housholds.visits.visited_on.strftime('%M') == month ).count
end
This method is in my controller for households and of course it doesn't work
How do I produce a report that show eg. Month: June Total Households seen: 45

Move the code to the Household model:
class Household < ActiveRecord::Base
def visits_count(month)
visits.where("strftime('%m', visited_on) + 0 = ?", month).count
end
end
According to this answer the + 0 helps with formatting.
Other options to try, depending on your database type:
where("extract(month from visited_on) = ?", month)
where("MONTH(visited_on) = ?", month)

I was unclear in the question but households have visits so I moved the the method to the visits table and used:
class Visit < ActiveRecord::Base
def self.by_month(month)
where("select(to_char(visited_on, 'FMMonth')) = ?", month)
end
This gives me the number of visits per month - I still need to add DISTINCT for the household_id contained in the visits table so I am counting the different households in case a the same household visits twice

Related

Calculate Day 7 retention rate of all players acquired on specific data

I'm new to SQL and I'm struggling with few of the questions on my exercise. How to calculate Day 7 retention rate of all players acquired on 01 October 2019. The tables shown is just a sample of what the extended table is.
My answer was:
SELECT
Session_table,
COUNT(DISTINCT user_id) as active_users,
COUNT(DISTINCT future_ user_id) as retained_users,
CAST(COUNT(DISTINCT future_ user_id) / COUNT(DISTINCT user_id) AS float) retention
FROM inapp_purchase
LEFT JOIN user_id as future_id
ON user_id = future_ user_id
AND user_id. inapp_purchase = user_id.session_table - datetime(page_views.pv_ts, '+7 day')
GROUP BY 1
There are two tables:
session_table
When each user_id starts a session , the session will have a unique session_id. Session_length_seconds tells how long a session lasted in seconds. level tells what game level the player was at the end of that session. The session_table will have one line for each session each user has (users can have multiple sessions per day each with unique session_id).
inapp_purchase
The inapp_purchase table has one line for each product that the user (denoted by user_id) purchased. There can be multiple purchases in a day per user, per session. The session_id and user_id here can link to the session_table to track active users who also make a payment. Product_id tells which product was purchased and the purchase_value tells the amount the user paid in $.
There are also three requests for the inapp_purchase table:
Calculate daily average revenue per daily active user for last 3 months
Calculate the daily average revenue per paying user year to date?
Calculate the daily conversion rate over the last month? This measure is defined by the proportion of daily active users who make a purchase on the day in question.
Please let me know if the above information is sufficient.
Thank you for your help.

Is there a simple line (or two) of code that will pull records before a minimum date in another table?

I want to pull Emergency room visits before a members first treatment date. Everyone as a different first treatment date and none occur before Jan 01 2012.
So if a member has a first treatment date of Feb 24 2013, I want to know how many times they visited the ER one year prior to that date.
These min dates are located in another table and I can not use the Min date in my DATEADD function. Thoughts?
One possible solution is to use a CTE to capture the visits between the dates your interested in and then join to that with your select.
Here is an example:
Rextester
Edit:
I just completely updated my answer. Sorry for the confusion.
So you have at least two tables:
Emergency room visits
Treatment information
Let's call these two tables [ERVisits] and [Treatments].
I suppose both tables have some id-field for the patient/member. Let's call it [MemberId].
How about this conceptual query:
WITH [FirstTreatments] AS
(
SELECT [MemberId], MIN([TreatmentDate]) AS [FirstTreatmentDate]
FROM [Treatments]
GROUP BY [MemberId]
)
SELECT V.[MemberId], T.[FirstTreatmentDate], COUNT(*) AS [ERVisitCount]
FROM [ERVisits] AS V INNER JOIN [FirstTreatments] AS T ON T.[MemberId] = V.[MemberId]
WHERE DATEDIFF(DAY, V.[VisitDate], T.[FirstTreatmentDate]) BETWEEN 0 AND 365
GROUP BY V.[MemberId], T.[FirstTreatmentDate]
This query should show the number of times a patient/member has visited the ER in the year before his/her first treatment date.
Here is a tester: https://rextester.com/UXIE4263

Finding users that have made a reservation within 1 month of signing up

I am trying to figure out how a query that will allow me to compare the number of users that have signed up in a given week to the number of users that have signed up and made a restaurant reservation within that same week. This is to help me understand activation rates of users on our app.
So far I have the following code which gives me the number of users that have signed up within a given week as well as the total number of reservations placed in that week. But what I am now trying to find is the number of users that have placed a reservation within their first week of signing up. Any help with this would be great.
SELECT
(DATE_TRUNC('week', (users.created_at::timestamptz))) AS week,
COUNT(users.created_at) AS new_users,
COUNT(reservations.covers) AS reservations_placed,
FROM
users
LEFT JOIN reservations ON users.id = reservations.user_id
LEFT JOIN restaurants ON reservations.restaurant_id = restaurants.id
LEFT JOIN regions ON regions.id = restaurants.region_id
WHERE reservations.denied_at IS NULL
AND reservations.canceled_at IS NULL
AND reservations.no_show_at IS NULL
AND reservations.origin != 'in_house'
AND reservations.origin != 'web'
GROUP BY
week
ORDER BY
week

Rails 3 query specific data from multiple models

Here are my two models:
class Account < ActiveRecord::Base
has_many :values
end
class Value < ActiveRecord::Base
belongs_to :account
end
On my account model I have a :name attribute and a :type attribute where I choose either 'checking' or 'savings'. Every quarter of the year I record an amount for each account, this goes on the value model. The value model has a :quarter attribute (either 1, 2, 3, or 4), a :year attribute, and an :amount attribute. Some accounts get updated for every quarter of the year, and some don't. So for example I could have a checking account that has values for the 1,2,3,and 4th quarters of 2010, and then another checking account with values for only the 1st and 2nd quarters. Every time a quarter is entered, it is just an update of the previous quarter, not a running cumulative total.
What I want to do is to run a query that will show me my average account value for last year for a certain account type, 'checking' or 'savings'. The thing I'm struggling with is that I can't just run a query and total the whole year, because only the last entered value (ordered by :quarter and :year) is the actual value, not a total of all the quarters. So how do I loop through all of the 'checking' accounts for example and pull the :amount from most recently entered value entry, whether that was the 1st quarter or the 4th?
I would use the created_at timestamp of the value record to order the results. It sounds like you want the most recent value record within a given range. If you can't order the results by date can you simply order by :quarter DESC and use the first result?
Don't forget about utilizing AVG() functions on the SQL server side: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_avg
Also check out http://ar.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html

How to create "Upcoming birthdays" module in Rails?

I have a table "users" with a column "date_of_birth" (DATE format with day, month, year).
In frontend I need to list 5 upcoming birthdays.
Spent ages trying to work out the logic.. also browsed every possible article in Google with no luck..
Any suggestions how to do this in RoR?
Thanks!
Several answers have suggested calculating/storing day of year and sorting on that, but this alone won't do much good when you're close to the end of the year and need to consider people with birthdays in the beginning of the next year.
I'd go for a solution where you calculate the full date (year, month, day) of each person's next birthday, then sort on that. You can do this in Ruby code, or using a stored procedure in the database. The latter will be faster, but will make your app harder to migrate to a different db platform later.
It would be reasonable to update this list of upcoming birthdays once per day only, which means you can use some form of caching. Thus the speed of the query/code needed is less of an issue, and something like this should work fine as long as you cache the result:
class User
def next_birthday
year = Date.today.year
mmdd = date_of_birth.strftime('%m%d')
year += 1 if mmdd < Date.today.strftime('%m%d')
mmdd = '0301' if mmdd == '0229' && !Date.parse("#{year}0101").leap?
return Date.parse("#{year}#{mmdd}")
end
end
users = User.find(:all, :select => 'id, date_of_birth').sort_by(&:next_birthday).first(5)
Edit: Fixed to work correctly with leap years.
Thanks to this post in my rails 3 app i use:
u = User.where("strftime('%m%d', date_of_birth) = ?", Date.today.strftime('%m%d'))
Update:
To use this with Postgresql:
u = User.where("extract(month from date_of_birth) = ? AND extract(day from date_of_birth) = ?", Date.today.strftime('%m'), Date.today.strftime('%d'))
It's better to use SQL to make this query:
next_month = (Date.today + 1.month).month # or use any other integer
users_having_birhtday_next_month =
User.where("EXTRACT(MONTH FROM date_of_birth) = ?", next_month)
Note: EXTRACT - PostgreSQL function
I'd have a before_save callback that calculates and stores to the day of the year in the database alongside the birthday.
You then have a simple query to pull back the next 5 birthdays. Make sure to handle the boundary condition where you are at the end of the year (I'd check if you don't get 5 results in RoR and then run a new query for the 1st Jan to get some extra birthdays to make it up to 5).
You will probably want to cache the results so you don't keep rerunning the query if it is on a common page.
I too thought that day of year would be the way to go, but the fact that it is different for most of the year depending on whether it is a leap year or not makes it tricky.
Better is to store the month and day as a string: d.strftime('%m%d'). You can then use that as (possibly) two queries (assuming new column is 'monthday')
First,
User.find(:all,
:condition => [:monthday > Date.now.strftime('%m%d')],
:select => "DISTINCT monthday",
:limit => 5)
If you don't get 5 results, do the query again, except use "0101" instead of the date calculation and lower the limit.
This gets you a list of monthday strings that you then have to turn back into dates.
If you want users, remove the :select line.
If you're on Oracle, you can do it without creating a new column. IMO it's a smell to create a column that contains data you already have.
The SQL's a bit ugly - I'm sure there's a more elegant way to do it. Generally in these cases I'd ask my DBA friends for advice.
User.find(:all,
:conditions =>
"TO_NUMBER(TO_CHAR(dob, 'MMDD')) >= TO_NUMBER(TO_CHAR(SYSDATE, 'MMDD'))",
:order => "TO_NUMBER(TO_CHAR(dob, 'MMDD'))",
:limit => 5)
Some people think a duplicate column is faster, but if you have enough user data that speed's an issue, you should benchmark the duplicate column against a table without it that has a functional index on TO_NUMBER(TO_CHAR(dob, 'MMDD')).
Here's how I find today's birthdays:
User.find_by_sql("select * from users where date_format(date_of_birth, '%m%d') = date_format(now(), '%m%d')")
I run this once a day. It takes less than a second from about 100,000 rows. (It doesn't properly handle people born on Feb 29th.)
If your database is mysql, the following is probably faster:
scope :birthday_next_week, (lambda {
where('DayOfYear(date_of_birth) >= 7
And DayOfYear(date_of_birth) - DayOfYear(curdate()) Between 0 and 6) Or
(MOD(YEAR(curDate()),4) = 0) And MOD(YEAR(curDate()),100) != 0
And (DayOfYear(date_of_birth) + 366 - DayOfYear(curdate())) % 366 < 7) Or
(DayOfYear(date_of_birth) + 365 - DayOfYear(curdate())) % 365 < 7').
order('DATE_FORMAT( date_of_birth, "%m-%d" ) ASC')
})
Edit: changed to make it work in the week before new years eve / leap years.
I found this worked for me. I didn't require to filter the most recent five results, so I used month is same as current.
def upcoming_birthdays
#resident = current_business.residents.where("extract(month from date_of_birth) = ?", Date.today.strftime('%m')).paginate(:page => params[:page])
end