# 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.