Rails advanced searching with datetimes - ruby-on-rails

I'm working on an app that will allow users to search for any recordings we have in a database, organized by three values: agent (name), phone, and date. date is recorded as a datetime value. I want to provide advanced search features that will allow users to select time ranges following this guideline:
Year: entered into a text field
Month: entered into a text field
Day: entered into a text field
Hours: two select lists representing a range upper and lower bound
Minutes: two select lists representing a range upper and lower bound
I watched this Railscast for help on how to get started with the search logic, but I'm concerned because I don't know how to pass in dateparts into Rails queries. Normally you would run something like select date_part(hh, date) as date_hour from table_name to get the value of hour, but how would you do that in Rails, or in the way Ryan Bates suggests in his Railscast?

You may concat the values in the controller accessing them by the params and then parse to a datetime
Then DateTime.strptime allows you to specify the format and convert a String to a DateTime.

Related

How do I get the nearest past date in a range for each entry in another list?

I collect customer feedback for my education business and add it to a Google Sheet. The feedback data has a submission date (A2:A) and some satisfaction metrics, which I visualize in a Google Data Studio dashboard.
The problem is that I want the feedback per cohort, but not everyone fills in the feedback form on the same day. I have a list of all courses with their respective dates (Cohorts!A2:A), and I want to assign each feedback submission to their respective cohort in a new column. It would be nice to also match it to the specific course type and country, but for now matching the cohort date would suffice.
I've tried using VLOOKUP and ARRAYFORMULA to go through the feedback dates and get the nearest past date to take it as the "course date" for that student. All the solutions I've tried either only take a single date or TODAY as a reference, but I have a whole list I'd like to fill in.
From my understanding, you are trying to round the timestamp, then match it to your course table?
To round a timestamp to a date:
=INT($A2)
When doing lookups like you're describing, I frequently end up calculating the nearest week as well - this formula returns the Sunday of the week start. Figured it might be helpful.
=text($A2+CHOOSE(WEEKDAY($A2),0,-1,-2,-3,-4,-5,-6),"m/d/yyyy")

Rails column type for moment.js formatted date and time

I am building a rails app, where the user picks up a date from a date picker and a time from the time picker. Both the date and time have been formatted using moment js to show the date and time in the following way:
moment().format('LL'); //January 23,2017
moment().format('LTS'); //1:17:54 PM
I read this answer with guidelines about selection of a proper column type.
Is there documentation for the Rails column types?
Ideally, I should be using :date, :time or :timestamp for this. But since the dates are formatted, should I be using :string instead?
Which would be the correct and appropriate column type to use in this situation?
If you want to store a time reference in your database you should use one of the types the database offers you. I'll explain this using MySQL (which is the one I have used the most) but the explanation should be similar in other database servers.
If you use a timestamp column you will be using just 4 bytes of storage, which is always a good new since it makes smaller indexes, uses less memory in temporal tables during the internal database operations and so on. However, timestamp has a smaller range than datetime so you will only be able to store values from year 1970 up to year 2038 more or less
If you use datetime you will be able to store a wider range (from year 1001 to year 9999) with the same precision (second). The bad consequence is that a higher range needs more memory, making it a bit slower.
There are some other differences between these two column types that don't fit in this answer, but you should keep an eye on before deciding.
If you use varchar, which is the default column type for text attributes in Ruby on Rails, you will be forced to convert from text to datetime and vice-versa every time you need to use that field. In addition, ordering or filtering on that column will be very inefficient because the database will need to convert all strings into dates before filtering or sorting, making it impossible to use indexes on that column.
If you need sub-second precision, you can use bigint to meet your requirements, as MySQL does not provide a date specific type for this purpose
In general, I recommend using timestamp if your application requirements fit the timestamp limitation. Otherwise, use datetime, but I strongly discourage you to use varchar for this purpose.
EDIT: Formatting
The way you store dates in database is completely different from the way you display it to the user. You can create a DateTime object using DateTime.new(year, month, day, hour, minute, second) and assign that object to your model. By the time you save it into database, ActiveRecord will be in charge of converting the DateTime object into the appropiate database format.
In order to display a value that is already stored in database in a specific format (in a view, API response, etc.) you can hava a look at other posts like this one.
You can have a timestamp column in your database, and then parse the request to a ruby datetime object like this:
d = Time.parse(params[:date])
t = Time.new(params[:time])
dt = DateTime.new(d.year, d.month, d.day, t.hour, t.min, t.sec, t.zone)
#now simply use dt to your datetime column
On Postgres you can save a ruby DateTime object straight into a postgres timestamp field, e.g
User.first.update_attribute('updated_at', dt )
Another option is to concatenate your date and time strings into one and then u can do a one-liner:
User.last.update_attribute('created_at', Time.parse('January 23,2017 1:17:54 PM'))
I'm pretty sure this will work on MySQL datetime or timestamp as well.
Credit to david grayson Ruby: combine Date and Time objects into a DateTime

How to filter result based on birthdate and min/max age values in Parse?

I'm using the Parse iOS SDK. I want to filter users based on their specified age ranges.
I have two tables:
1st, tableUser which has a field titled birthdate with a String data type.
2nd, tableSettings which has two fields minAge and maxAge, both of which are Number types
I want to fetch users from the tableUser class who's age, calculated from birthdate field, falls between the age range specified in the tableSettings class. For example, if the minAge value is 20 and the maxAge value is 25, then I only want to retrieve users with an age between this range.
Is this possible? How would I make such a query?
Your requirement sounds non-trivial with that suboptimal data structure. I'd probably go for cloud code to hide the required logic from the app. This logic would be to query the tableSettings and calculate the date range that applies.
Now that you have this range, it's still hard to use because your other table uses a string representation of the date rather than a true Date type. This really sucks. If you can you should change the date to the correct type, or at least add another column with a correct representation of the date (but then you have to keep them in sync).
Working with dates you can add specific range criteria to your query and life is easy.
Working with strings is compounded in difficulty because you have the day first, so you can't even use BEGINSWITH to filter the query on year and then process the content. It really is a terrible data model for the problem. So this basically leaves you paging through everything doing an explicit conversion of the string to a date and then comparing that to the range.
If you at all can, change the data model. Even if you create a new class (table) specifically for this data and use an afterSave hook to keep them in sync.

Rails data modeling question

I am creating a simple todo app where I have 2 types of tasks.
1) regular tasks - These have a due date
2) recurring tasks -These are poped up as reminders on specified date. They can be created either as weekly or monthly reminders. If created for a week, it will be poped up on each week (on a specified date on the week). Likewise for a month it need to be specified the week and the date.
What will be the best way to model this scenario?
I would have two columns for the reminder object - remind_at (date) and repeat_frequency (something to identify different re-occurrences by). That way, you could index the remind_at column and search by it quite quickly. Each time a reminder is shown to user, it would look at repeat_frequency - if it contains directions for repeating, set remind_at to next date, if not, delete/archive the reminder.
You could model a Task to have a due_date. But if a task is recurring, due_date will be null and you would use the recurrence field to compute the next_due_date. recurrence would be a string field holding a parsable string like "tuesday" (for weekly) or "17" (a day number for monthly).
def next_due_date
if due_date
due_date
else
# compute next due date using the 'recurrence' field and today's date
end
end
This may or may not be the "best way" for you, depending on your requirements, and the future needs of the model.

How would I store a date that can be partial (i.e. just the year, maybe the month too) and output it later with the same specifity?

I want to let users specify a date that may or may not include a day and month (but will have at least the year.) The problem is when it is stored as a datetime in the DB; the missing day/month will be saved as default values and I'll lose the original format and meaning of the date.
My idea was to store the real format in a column as a string in addition to the datetime column. Then I could use the string column whenever I have to display the date and the datetime for everything else. The downside is an extra column for every date column in the table I want to display, and printing localized dates won't be as easy since I can't rely on the datetime value... I'll probably have to parse the string.
I'm hoping I've overlooked something and there might be an easier way.
(Note I'm using Rails if it matters for a solution.)
As proposed by Jhenzie, create a bitmask to show which parts of the date have been specified. 1 = Year, 2 = Month, 4 = Day, 8 = Hour (if you decide to get more specific) and then store that into another field.
The only way that I could think of doing it without requiring extra columns in your table would be to use jhenzie's method of using a bitmask, and then store that bitmask into the seconds part of your datetime column.
in your model only pay attention to the parts you care about. So you can store the entire date in your db, but you coalesce it before displaying it to the user.
The additional column could simple be used for specifying what part of the date time has been specified
1 = day
2 = month
4 = year
so 3 is day and month, 6 is month and year, 7 is all three. its a simple int at that point
If you store a string, don't partially reinvent ISO 8601 standard which covers the case you describe and more:
http://en.wikipedia.org/wiki/ISO_8601
Is it really necessary to store it as a datetime at all ? If not stored it as a string 2008 or 2008-8 or 2008-8-1 - split the string on hyphens when you pull it out and you're able to establish how specific the original input was
I'd probably store the datetime and an additional "precision" column to determine how to output it. For output, the precision column can map to a column that contains the corresponding formatting string ("YYYY-mm", etc) or it can contain the formatting string itself.
I don't know a lot about DB design, but I think a clean way to do it would be with boolean columns indicating if the user has input month and day (one column for each). Then, to save the given date, you would:
Store the date that the user input in a datetime column;
Set the boolean month column if the user has picked a month;
Set the boolean day column if the user has picked a day.
This way you know which parts of the datetime you can trust (i.e. what was input by the user).
Edit: it also would be much easier to understand than having an int field with cryptic values!
The informix database has this facility. When you define a date field you also specify a mask of the desired time & date attributes. Only these fields count when doing comparisons.
With varying levels of specificity, your best bet is to store them as simple nullable ints. Year, Month, Day. You can encapsulate the display logic in your presentation model or a Value Object in your domain.
Built-in time types represent an instant in time. You can use the built in types and create a column for precision (Year, Month, Day, Hour, Etc.) or you can create your own date structure and use nulls (or another invalid value) for empty portions.
For ruby at least - you could use this gem - partial-date
https://github.com/58bits/partial-date

Resources