Rails data modeling question - ruby-on-rails

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.

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")

What is the correct type I should use to store a date up to the minute?

I want to store the date of an event in my database, but I want to do so without storing informations about seconds or anything smaller than seconds. Using Rails, in my migration I have the option to create a date column or a datetime column, the first one of which is too less accurate, and the second one is too much (up to the second and less). Which type should I choose to store such a date? Currently I'm using datetime and setting the seconds to a fixed value (e.g. 0) manually each time some date is set in the model.
Something like this:
self.date ||= Time.now.change(:sec => 0)
Am I totally out of track? Should I just use an integer field for each component of the date instead? (year, month, day, etc...) Or is datetime the correct type but I'm not understanding the purpose of it? (I think it's meant for timestamps and such things where seconds matter)
datetime is the correct type. And be sure to store it without time zone at time zone UTC:
http://derickrethans.nl/storing-date-time-in-database.html
At your option, use an SQL trigger to round your date to the minute on insert/update. It'll simplify your ruby code.

Availability Scheme in database

I'm currently designing a website which can help my rowing team plan training times and such. The basic idea is that every rower can set the times they can train. Coaches can then see the availability of all the rowers in a handy table and can use this to plan a training.
My question is, how should I represent availability in the class diagram and database?
The idea that I had was to divide days into time blocks: Block 1 stands for 7:00 - 7:30, block 2 stand for 7:30 - 8:00. Then I will create a table 'timeblocks' with the following attributes:
block_id
user_id
date (day, month and year)
block_number
availability
Is this a efficient way of storing availability data?\
Another way you can normalization this table into two piece. a special block table and availability table.
block :
Block_id
block_range
Time_Block
Time_blockId
Block_ID
user_ID
Date
Availability

How would you build this daily class schedule?

What I want to do is very simple but I'm trying to find the best or most elegant way to do this. The Rails application I'm building now will have a schedule of daily classes. For each class the fields relevant to this question are:
Day of the week
Starting time
Ending time
A single entry could be something such as:
day of week: Wednesday
starting time: 10:00 am
ending time: Noon
Also I must mention that it's a bi-lingual Rails 2.2 app and I'm using the native i18n Rails feature. I actually have several questions.
Regarding the day of the week, should I create an extra table with list of days, or is there a built-in way to create that list on the fly? Keep in mind these days of the week will have to be rendered in English or Spanish in the schedule view depending on the locale variable.
While querying the schedule I will need to group and order the results by weekday, from Monday to Sunday, and of course order the classes within each day by starting time.
Regarding the starting time and ending time of each class would you use datetime fields or integer fields? If the latter how would you implement this exactly?
Looking forward to read the different suggestions you guys will come up with.
I would just store the day of the week as an integer. 0 => Monday ... 6 => Sunday (or any way you want. ie. 0 => Sunday). Then store the start time and end time as Time.
That would make grouping really easy. All you would have to do is sort by the day of the week and the start time.
You can display this in multiple ways, but here is what I would do.
Have functions like: #sunday_classes = DailyClass.find_sunday_classes that returns all the classes for Sunday sorted by start time. Then repeat for each day.
def find_sunday_classes
find_by_day_of_week(1, :order -> 'start_time')
end
Note: find_by probably should have id at the end but that's just preference in how you want to name the column.
If you want the full week then call all seven from the controller and loop trough them in the view. You could even create detail pages for each day.
Translation is the only tricky part. You can create a helper function that takes an integer and returns the text for the appropriate day of the week based on local.
That's very basic. Nothing complicated.
If your data is a Time then I would store that as a Time - otherwise you will always have to convert it out of the database when you do date and time related operations on it. The day is redundant data, as it will be part of the time object.
This should mean that you don't need to store a list of days.
If t is a time then
t.strftime('%A')
will always give you the day as a string in English. This could then be translated by i18n as required.
So you only need to store starting time and ending time, or starting time and duration. Both should be equivalent. I would be tempted to store ending time myself, in case you need to do data manipulations on ending times, which therefore won't have to be calculated.
I think most of the rest of what you describe should also fall out of storing time data as instances of Time.
Ordering by week day and time will just be a matter of ordering by your time column. i.e.
daily_class.find(:all, :conditions => ['whatever'], :order => :starting_time)
Grouping by day is a little more tricky. However this is an excellent post on how to group by week. Grouping by day will be analogous.
If you are dealing with non-trivial volumes of data, it may be better to do it in the database, with a find_by_sql and that may depend on your database's time and date functionality, but again storing the data as a Time will also help you here. For example in Postgresql (which I use), getting the week of a class is
date_trunc('week', starting_time)
which you can use in a Group By clause, or as a value to use in some loop logic in rails.
Re days-of-week, if you need to have e.g. classes that meet 09:00-10:00 on MWF, then you could either use a separate table for days a class meets (keyed by both class ID and DOW) or be evil (i.e. non-normalized) and keep the equivalent of an array of DOW in each class. The classic argument is this:
The separate table can be indexed in a way to support either class-oriented or DOW-oriented selects, but takes a bit more glue to put the entire picture together for a class.
The array-of-DOW is simpler to visualize for beginning programmers and slightly simpler to code about, but means that reasoning about DOW requires looking at all classes.
If this is only for your personal class schedule, do what gets you the value you're looking for, and live with the consequences; if you're trying to build a real system for multiple users, I'd go with a separate table. All those normalization rules are there for a reason.
As far as (human-readable) DOW names, that's a presentation-layer issue, and shouldn't be in the core concept of DOW. (Suppose you decided to move to Montreal, and needed French? That should be another "face" and not a change to the core implementation.)
As for starting/ending times, again the issue is your requirements. If all classes begin and end at hour (x:00) boundaries, you could certainly use 0..23 as the hours of the day. But then your life would be miserable as soon as you had to accommodate that 45-minute seminar. As the old commercial said, "Pay me now or pay me later."
One approach would be to define your own ClassTime concept and partition all reasoning about times to that class. It could start with a simplistic representation (integral hours 0..23, or integral minutes after midnight 0..1439) and then "grow" as needed.

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