How to create/save this database special calculated field - RoR - ruby-on-rails

I have a database field called sample_code. This field is composed on the following way: sample_id/year/lab_id
The lab_id is fixed, always the same. The year changes accordingly to the current year... The sample_id its incremental (resetting every year and not a database field).
What I want to do is every-time I create a new sample it generates this sample_code for me and saves it along with the rest of the sample fields...
My doubts are:
1 - How can I keep incrementing sample_id if it's not on the database?
2 - How can I reset the sample ID code each year?
3 - Where's the best place to put/call this code? Sample's controller?
Thanks for all the help you can give

If you're not using your database outside of your app, it should be fine to just store the sample_id as its own column. You can then put a method on your model that returns something like:
def sample_code
"#{sample_id}/#{Time.now.year}/<lab_id>"
end
Then you can just increment sample_id each time.
EDIT
Since you need to reset the id to 1 each year and the model is called Sample, you should avoid confusion by calling it something like annual_id instead of sample_id. sample_id would likely be confused with sample.id, which is a completely different thing.
With that change, you should just store the info in three columns on the model to make it easy: annual_id, year, and lab_id. Then for each record you can set:
annual_id = Sample.where(year: Time.now.year).pluck(:annual_id).max.to_i + 1
year = Time.now.year
lab_id = <however you are defining this>
This will use the current year for year and then reset the annual_id to 1 when there are no records because the year has changed (new year will give nil.to_i + 1 => 1).
Then you can just return the format you want for any given data point:
def sample_code
"#{annual_id}/#{year}/#{lab_id}"
end

Related

how to update a computed result of several columns in one record into postgresql database with rails

I have a table called "Scores" which has 4 columns, "first", "second", "third", and "average" for keeping record of user's score.
When a user create the record initially, he can leave "average" column blank. Then he can edit all 3 scores later.
After editing, the user can see the computed average (or sum, or any calculation result.) in his show page, since I have
def show
#ave = (#score.first + #score.second + #score.third)/3
end
However, #ave is not in the database, how can I update #ave into the column of "average" of my database?
Ideally, it would be the best if the computing takes place before updating into database, so all 4 values can be updated into database together. It might have something to do with Active Record Callbacks, but I don't know how to do that.
Second approach, I think i need a "trigger" in database so that it can compute and update "average" column as soon as other 3 columns got updated. If this is how you do it, please let me know and the advantage of comparing with solution number 1.
Last approach, since the user already know the average in his show page, I don't have to update the computed average into "average" column immediately. I think i can leave this to a delayed_job or background job. If this is how you do it, please let know me how.
Thank you in advance!(ruby 2.3, rails 5.0.1, postgresql 9.5
Unless you really do need the average stored in the database for some reason, I would add an attribute to the Score model:
def average
(first + second + third)/3.0
end
If one or more might not be present, I would:
def average
actual_scores = [first, second, third].compact
return nil if actual_scores.empty?
actual_scores.sum / actual_scores.size
end
If you do need the average saved, then I would add a before_validate callback:
before_validation do
self.average = (first + second + third)/3.0
end
Ideas 1 and 2 are perfectly valid approaches. Idea 3 is overkill and I would strongly recommend against that approach.
In idea 1, all you need to do (in any language) is simply look at each individual value put in (not including average) and generate the average value to be included in your insert statement. It's really as simple as that.
Idea 2 requires making a trigger as follows:
CREATE OR REPLACE FUNCTION update_average()
RETURNS trigger AS
$BODY$
BEGIN
NEW.AVERAGE=(NEW.first+NEW.second+NEW.third)/3;
RETURN NEW;
END;
$BODY$
Then assign it to run on update or insert of your table:
CREATE TRIGGER last_name_changes
BEFORE INSERT or UPDATE
ON scores
FOR EACH ROW
EXECUTE PROCEDURE update_average();

How to sum columns and group by date column in Rails

I have a model with following columns
Charges Model
Date
fee
discount
Data
1/1/15, 1, 1
1/1/15, 2, 1
2/2/15, 3, 3
I have a few named scopes like this_year
I want to do something like Charges.this_year.summed_up
How do I make a named scope for this.
The returned response then should be:
1/1/15, 3, 2
2/2/15, 3, 3
Assuming you have a model with a date field(eg. published_at) and 2 integer fields(eg. fee, discount). You can use "group" method to run GROUP BY on published_at. Then just use sum method if you want only sum of one fields. If you want more than one field, you have to run a select with SQL SUMs inside, to get multiple column sums. Here is an example.
Charge..group(published_at)
.select("published_at, SUM(fee) AS sum_fee, SUM(discount) AS sum_discount")
.order("published_at")
Note: Summarized fields won't show up in rails console return value prompt. But they are there for you to use.
Depending upon what end result you want, you may want to look at .group(:attribute) rather than .group_by:
Charge.group(:date).each do |charge|
charge.where('date = ?', charge.date).sum(:fee)
charge.where('date = ?', charge.date).sum(:discount)
end
I found this approach easier, especially if setting multiple conditions on the data you want to extract from the table.
In any case, I had an accounting model that presented this kind of issue where I needed credit and debit plus type of payment info on a single table and spent a fruitful few hours learning all about group_by before realizing that .group() offered a simple solution.

How to update thousands of records

I have to update an age column based on the value in a date of birth column. There are thousands of records to update.
How do I do this using rails?
Is this the right way to do it?
User.update_all(:age => some_method);
def some_method
age = Date.today.year - dob.year
end
Yes, update_all is the right method but no, you can't do it like this. Your some_method will only get called once to set up a database call (I assume you're persisting to a database). You'll then get an error because dob won't be recognised in the scope of the User class.
You'll need to translate your date logic to SQL functions.
Something like (for mysql):
User.update_all("age = year(now()) -
year(dob) -
(DATE_FORMAT(now(), '%m%d') < DATE_FORMAT(dob, '%m%d'))")
(NB. the date_format stuff is so that you get the right age for people who's birthdays are later in the year than the current date - see this question for more details)
The other option is to use one of the batches functionality in rails.
User.where(some_condition).find_in_batches do |group_of_users|
# some logic
# e.g. group_of_users.update_all(:age => some_logic)
end
This would lock your db for less time. Note that you should pretty much always update with a condition in mind. I can't think of many cases you would want to update an entire table every time something happens.
There are a few options checkout the rails docs or the api.
your query is right.
There are many way to update record in a batch/lot.
But, I think that your query is best. Because it is rails query that will support every condition for all database.
for updating more than one attributes
Model.update_all(:column1 => value1, :column2 => value2, ........)
or
you can use :
Model.update_all("column1 = value1, column2 = value2, ........")

Tracking Followers Over Time

I want to build functionality in my Rails application that shows follower trends over time.
Currently, my following methodology involves creating and destroying relationship objects - following creates an object with the IDs of the follower and followed and unfollowing deletes that relationship object.
Since the relationship object is deleted upon an unfollow, it's impossible to go back and look at how many followers existed for a followed at any given time.
To solve this, the best solution I can think of is this:
Instead of deleting a relationship object upon unfollowing, create a new object with a negative value of, say, -1. Following would create an object with a positive value of +1. Therefore, adding up the total values for a given pair would yield whether or not they were currently following (1 or 0), while historical trends could also be calculated by adding up the total following values for a given followed.
My question is: Is this the most elegant solution this problem? Is there an easier way to do it? I realize that it's possible to use cron jobs to output a daily number, but that seems like it would duplicate data. Any other suggestions?
Any help would be greatly appreciated!
I would add an active field then instead of deleting the relationship record I would set the record to inactive. Then you'll have to update all of your user facing queries to reflect active = 1. Then you can use the records with active = 0 for reporting purposes. You can also add a deactivated_at field that stores the date that the record was deactivated.
An example scenario would be user 1 follows user 2, follows user 3, follows user 4, un-follows user 2, re-follows user 2, un-follows user 4.
follower_id followed_id active created_at deactivated_at
1 2 0 9/10/2012 9/13/2012
1 3 1 9/10/2012 NULL
1 4 0 9/10/2012 9/17/2012
1 2 1 9/16/2012 NULL
just use paranoia
https://github.com/radar/paranoia
class Relationship < ActiveRecord::Base
acts_as_paranoid
...
end
(if you have a unique index over the two numeric ID columns, remove it, use a plain index)
Then you can have
def currently_following_count(uid)
Relationship.where(:followed_id => uid).count
end
def historical_following_count(uid)
Relationship.unscoped.where(:followed_id => uid).count
end

updating wrong value to database

I'm just trying to increment a record by 1 starting at 2000, when a new record is created upon clicking on the create action to create a record:
if resource_model == Student then #resource.testing_id = id + 2000 end
So if the record has an id of 1, I assume that the testing_id will be 2001. But instead it returns:
2147483647 (maximum mysql limit?)
Any suggestions on how to address this? Thanks.
You can't know record ID during create. ID is known after saving record do database.
You can't relay on ID to give you values like 1, 2, 3 ... and so on.
Don't store value like ID+2000, becouse you can get it at any time by calculating id+2000.
You can get next testing_id by something like this:
if resource_model == Student then
#resource.testing_id = Student.first(:order => "testing_id DESC").testing_id + 1
end
But if two processes at the same time will fetch the same value then you will have duplicate testing_id.
Object.idf is a (deprecated) method that returns the Ruby object ID, which uniquely identifies the object. Rails has overridden id so that in models it refers to the database primary key. I'm going to take a guess that your code snippet is from a controller, and that's why id is returning a strange and large number: It's the Ruby object id of the controller, not the primary key of the object. Give id a receiver, e.g. #resource.id, and see how that works.
2147483647 = 2 ^(32-1)
Could you show some of your code here?
From what i'm guessing here is that you are comparing apples with strawberries :P.
I think you are adding a "1" on a string so that means for example:
2000 + 1 = 20001
20001 + 1 = 200001
So if you do that a couple of times this means you will get to the maximum size of an int (21475483647). I don't know this for a 100% sure, but this has to do with the way you ask your question and the way you don't post code etc...
I hope this edit was helpfull tho.

Resources