# Events Endpoint
Major Revisions:
Changes in details method:
1. Time is returned from Meetup as milliseconds since epoch. This needs to be converted to seconds, converted to a time unit, then converted to datetime. ✅
2. Meetup does not provide an end time, only a duration. The duration is not required, so if duration is nil the duration/1000 crashes the program. I added an if/else statement at the start of the method to make sure duration exists. ✅
3. Meetup does not provide a scholarship available option (commented it out). ✅
<hr/>
### Code Review
I added comments in the code block below:
<hr/>
Details_for method in meetup.rb:
```ruby
def details_for(event)
# Perhaps extract this duration logic into a private method
# that is dedicated to calculating event duration.
# And then call that private method in this method.
# That way, each method has their own discrete, single purpose.
start_date = Time.at(event['time']/1000)
if event['duration'].exists?
duration = event['duration']/1000
else
duration = 0
end
{
meetup_id:event['id'],
meetup_updated: Time.at(event['updated']).to_datetime,
name:event['name'],
description: event['description'],
url:event['link'],
start_date:start_date.to_datetime,
end_date: (start_date+duration).to_datetime, #end time is not provided, only start time and duration.
address1:event['venue']['address_1'],
address2:event['venue']['address_2'],
city: event['venue']['city'],
state: event['venue']['state'],
zip: event['venue']['zip']
#scholarship_available: event[]
}
end
```
Changes in database:
Note: I wrote the original database table based on feedback from Rick, but that was before we knew what Meetup provided.
1. Created a migration to add the columns meetup_id and meetup_updated to the events table. Added an index to meetup_id because that is how I'm searching for a record.
<hr/>
### Code Review
Instead of adding these columns unique to Meetups, perhaps go the [polymorphic association route](http://guides.rubyonrails.org/association_basics.html#polymorphic-associations). This will allow you to keep the door open to capturing similar information, for other `Event` sources. For example, from Facebook, etc.
So instead of creating a `meetup_id` column, you would create two new generic columns like:
```ruby
:source_id, :string
:source_type, :string
```
And for the Meetup case, the `source_id` would be the Meetup's event ID, and the `source_type` would be "Meetup". And if this were a Facebook `Event`, the `source_id` would be the Facebook's event ID, and the `source_type` would be "Facebook".
You'll note that this is not exactly a polymorhpic association, because the `source_id` is a `string` instead of an `integer`, but it fits the desired behavior well.
And then add a third `source_updated` column if you like.
Feel free to use another term than `source_*`. Whatever works best.
<hr/>
add_columns_to_events.rb
```ruby
class AddColumnsToEvents < ActiveRecord::Migration[5.0]
def change
add_column :events, :meetup_id, :string, index: true, unique: true
add_column :events, :meetup_updated, :datetime
end
end
```
Changes in Event Class Validations (I wrote this as well originally)
1. Problem - Meetup doesn't require all the fields I originally required. So I commented out almost all the validations. I did add validations for meetup_id and meetup_updated. Meetup_id has a uniqueness validation so we don't write duplicate meetings to our database.
```ruby
class Event < ApplicationRecord
#Allowing URL, end_date, and address2 to be empty
validates :name, presence: true
# validates :description, presence: true
validates :start_date, presence: true
validates :meetup_id, presence: true, uniqueness: {case_sensitive: false}
validates :meetup_updated, presence: true
# validates :address1, presence: true
# validates :city, presence: true
# validates :state, presence: true, length: {is: 2} #Dropdown selection on front end?
# VALID_ZIP_REGEX= /\A\d{5}-?\d{4}?\z/
# #validates :zip, presence: true, format:{with:VALID_ZIP_REGEX}
# #validates :scholarship_available, inclusion: {in: [true, false]}
# VALID_URL_REGEX = URI::regexp
# validates :url, format: {with:VALID_URL_REGEX}
end
```
<hr/>
### Code Review
Good call. Based on the above polymorphic'ish implementation, you'll want to consider validating uniqueness against the combination of two fields. For example:
```
#This is ok:
#event 1
source_id: "22"
source_type: "Meetup"
#event 2
source_id: "22"
source_type: "Facebook"
#This is not ok:
#event 3
source_id: "33"
source_type: "Meetup"
#event 4
source_id: "33"
source_type: "Meetup"
```
Here is a [stackoverflow post](https://stackoverflow.com/a/5659281/3899985) that talks about how to do that.
<hr/>
Here is the good stuff! This is the rake task to actually create and write the events to the database. events is a hash with the group url as the key. The value can be an array (if a group has multiple meetups scheduled)
meetup.rake
<hr/>
### Code Review
I added comments in the code block below:
<hr/>
```ruby
namespace :meetup do
desc "Rake task to get Meetup API data"
task :fetch => :environment do
# Perhaps change this method to return a flattened array of hashes,
# and add an attribute in each hash for the url? For example,
# [
# { url: group_url_1, ...remaining event attributes... },
# { url: group_url_1, ...remaining event attributes... },
# { url: group_url_2, ...remaining event attributes... },
# ...
# ]
# This way, you won't have to do nested looping inside of a loop.
# You can just take action directly on each hash in the array.
events = Meetup.new.event_details_by_group
events.each do |group, event|
event.each do |event|
# Instead of creating the event, and then if something goes wrong
# because the Event already existed, update attributes, you can try
# the find_or_create_by! method here:
# https://apidock.com/rails/v4.0.2/ActiveRecord/Relation/find_or_create_by
# And you would want to use the "!" version of this method so that
# if there is an issue saving the record, it will throw an error,
# and not fail silently.
# Then you can catch the error in a rescue block, so you can
# easily troubleshoot the issues, should any appear.
# Here is an example of that:
# https://github.com/OperationCode/operationcode_backend/blob/master/lib/tasks/users.rake#L18-L27
new_event = Event.new(event) #Validation requires meetup_id be unique
new_event.save
if !new_event.valid? #It didn't save, so the meetup id exists
temp_event = Event.find_by(meetup_id: event[:meetup_id])
temp_event.update_attributes(event)
end
end
end
end
end
```
IT WORKS!!! If I run docker-compose run web rake meetup:fetch all the events are pulled from Meetup and populated in the Events database. If I then load local host I can see the events in my browser as json.