Efficiently retrieving ice_cube schedules for a given time period - ruby-on-rails

I'm looking into using Ice Cube https://github.com/seejohnrun/ice_cube for recurring events.
My question is, if I then need to get any events that fall within a given time period (say, on a day or within a week), is there any better way than to loop through them all like this:
items = Records.find(:all)
items.each do |item|
schedule = item.schedule
if schedule.occurs_on?(Date.new)
#if today is a recurrence, add to array
end
end
This seems horribly inefficient but I'm not sure how else to go about it.

That's one approach - but what people do more often is end up denormalizing their schedules into a format that is conveniently queryable.
You may have a collection called something like ScheduleOccurrences - that you build each week / and then query that instead.
Its unfortunate it has to work this way, but sticking to the iCal way of managing schedules has led IceCube to need to format its data in certain ways (specifically ways that can line up with the requirements of the iCal RFC).
I've been doing some thinking recently about what a library would look like that shook away some of those restrictions, for greater flexibility like this - but its definitely still a bit off.
Hope this helps

I faced a similar problem and here was my approach:
Create a column on Event table to store the next occurrence date, and write a method which stores that value after_save. (method available through ice_cube. Perhaps index column too for faster querying.)
Then you can query the database for occurrences happening in the timeframe you need. See below:
Event.where(next_occurrence: Date.today.all_day)
Store EventOccurrences on a separate table.
Update the next_occurrence column for the rows returned to you by your query. Or something similar. This works for me because I'm running a daily job, so that update next_occurrence will run regularly. But you may need to tweak a bit.

Related

How to column sum in Airtable like excel?

I am designing airtable.
I met some issue.
I need to column total sum like excel.
For example:
Column1 Sum(Column1)
1 1
2 3
4 7
6 13
Like this. What is solution?
Thank you.
Airtable is not a spreadsheet but a relational database, the behavior you're looking for isn't straightforward to reproduce because it's not meant to work like rows matter and aren't anything but a temporary sort - they don't, so they're not.
I know you were probably hoping for a better answer than another question, but I simply have to ask - why do you need to do this inside Airtable? Why not just use one of dozens readily available and entirely free spreadsheet/table solutions?
Generally speaking and especially if you don't need to manipulate that data afterward (a big if), Airtable already does the calculation you want on the fly and has it stored as part of its metadata.
The row-by-row addition would be doable using a combination of one autonumber field, a linked record (to another table actually polling for values) and a rollup returning data. I've done this back in the day once or twiCe and it was always an overengineered mess, even if I only had to deal with small integers like from your example.
There's Google Drive sync beta ongoing at Airtable right now. Just get that and do the calculations you need elsewhere?
The alternative is the Scripting app, but that might prove crippling in terms of how it could affect your automations quota. And recalculating fields by "hand" is... not sophisticated, to put it mildly.
But hey, don't take my word for it; Curiosity and nostalgia got the better of me so I gave this futile effort another go on your behalf, here's my best take at such an overengineered mess of a field-wide sum function that's wildly annoying to use but at least doesn't take all day to update records, even if presented with hundreds or thousands of inputs.
So, yeah... right tool for the job and all that: this ain't it, chief, but be my guest. You can clone the base from the Universe and everything will be ready for testing, just keep creating new fields or deleting the exiting few ones, then hit the "Run Script" button of the only app hooked into the base to see it recalculate the sum.
Dumping the code here as we, if anyone wants to set up a new testing environment manually:
let table = base.getTable('Table 1');
let query = await table.selectRecordsAsync()
let cellsToAdd = [];
let sum = 0;
const keepSumming = x => sum += x ;
query.records.forEach( x => cellsToAdd.push(
{
"id": x.id,
fields:{
"Column1":x.getCellValue('Column1'),
"Sum":keepSumming(x.getCellValue('Column1'))
}
}
));
//Airtable limits us to 50 table mutations per request, hence the splicing
while(cellsToAdd.length>0){
await table.updateRecordsAsync(
cellsToAdd.splice(0,50)
)
};
There appears to be a feature built-in to Airtable for this now. In the desktop app, at least, take a look down at the bottom of each column below all rows. There is a context menu for the column, in which Sum is one of several options.

Best practices for events and date ranges

I’m trying to find the best practice for how to store and then query a event like this. User has purchased 3 items on separate dates.
Over that period there were two events that were held (events added in well after the user purchased the items as a retrospect, so at the time of purchase, event was not known). I’m trying to see how many items were purchased during each event by that user. How should I do that?
One solution but it sounds weird to me: When inserting a event, scan and add a relationship to all vertices that match
Manage date-time types isn't exactly an easy task on Neo4j, even in 3.2 version.
You have two options:
Hard way: convert all date to unix "timestamp" format ('s' or 'ms' from 1970), in order to calc date ranges.
Easy, convenient way: use "APOC" (here), a set of procedures and functions available as plugin for Neo4j; installation can be a bit tricky but it's worth it, indeed. It has a good number of 'date-time' functions.

Storing large amount of boolean values in Rails

I am to store quite large amount of boolean values in database used by Rails application - it needs to store 60 boolean values in single record per day. What is best way to do this in Rails?
Queries that I will need to program or execute:
* CRUD
* summing up how many true values are for each day
* possibly (but not nessesarily) other reports like how often true is recorded in each of field
UPDATE: This is to store events that may or may not occur in 5 minute intervals between 9am and 1pm. If it occurs, then I need to set it to true, if not then false. Measurements are done manually and users will be reporting these information using checkboxes on the website. There might be small updates, but most of the time it's just one time entry and then queries as listed above.
UPDATE 2: 60 values per day is per one user, there will be between 1000-2000 users. If there isn't some library that helps with that, I will go for simplest approach and deal with it later if I will get issues with performance. Every day user reports events by checking desired checkboxes on the website, so there is normally a single data entry moment per day (or few if not done on daily basis).
This is dependent on a lot of different things. Do you need callbacks to run? Do you need AR objects instantiated? What is the frequency of these updates? Is it done frequently but not many at a time or rarely but a bunch at once? Could you represent these booleans as a mask instead? We definitely need more context.
Why do these need to be in a single record? Can't you use a 'days' table to tie them all together, then use a day_id column in your 'events' table?
Specify in the Day model that it 'has_many :events' and specify in the Event model file that it 'belongs_to :day'. Then you can find all the events for a day with just the id for the day.
For the third day record, you'd do this:
this_day = Day.find 3
Then you can you use 'this_day.events' to get all the events for that day.
You'll need to decide what you wish to use to identify each day so you query for a day's events using something that you understand. The id column I used above to find it probably won't work.
You could use the timestamp first moment of each day to do that, for example. Or you could rely upon the 'created_at' column of the table to be between the start and end of a day
And you'll want to be sure to thing about what time zone you are using and how this will be stored in the database.
And if your data will be stored close to midnight, daylight savings time could also be an issue. I find it best to use GMT to avoid that issue.
Good luck.

How to model and store recurring tasks in rails?

Cron solutions in rails are numerous and pretty good. That's not what I'm struggling with here.
Instead, what I'm having trouble with is letting users create their own recurring tasks (like reminders) - specifically how to model and store these in the DB (a good UI for it is non-trivial too - would be awesome if there was code out there for that). Google calendar is a great example here (the UI to add an event, not the entire calendar)...they should be able to do daily at 1pm CST, or mon/wed/fri, or weekly, etc. Whatever cron solution is being used would then need to poll the database to see which reminders needed to be sent at that hour, etc.
Anyone seen a good plugin/gem for this in rails? Seems like there would be something out there for it, but I haven't found it yet.
Thanks!
I ended up rolling my own solution since I didn't need anything super fancy.
Basically I added a next_run datetime and interval string column to the db which was one of (day, week, etc). Then setup a cron job to run that looks for any next_run dates that have passed. It runs them and then sets next_run to some point in the future based on the interval column.
Simple but worked for my needs.
Currently thinking about using a plugin like this to store recurrences in a table
http://github.com/fnando/recurrence/tree/master
each reminder would have one recurrence object, and the reminder would also keep a datetime field when it's supposed to send it's next one. Then the cron could...
get all reminder's whose "next_send" date has passed
for each reminder
send it
update the "next_send" field using the recurrence object
end
If there are better solutions or I'm going down the wrong path, input always appreciated.
I have always found ical (the RFC, not the program) solutions to be the best approach for working with recurring events. There are a few good Ruby libraries for dealing with ical, and the newest kid on the block is ri_cal.
I'm currently having this problem and the solution that I'm considering is as follows:
class AllowReoccuringTasks < ActiveRecord::Migration
def self.up
add_column :tasks, :reoccuring, :boolean
add_column :tasks, :period, :integer
end
end
where period can be either 1 (every day), 7 (every week), or 14 (every other week).
If you wanted to support other types of schedules like every month, weekdays, weekends, etc., you could instead add a column called "schedule" and use constants to represent different schedule types. You could also use the enum plugin.

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.

Resources