Search code examples
rubyjsonparsingiterationpivotaltracker

How can I retrieve information for only a specific type of activity from the Pivotal Tracker API?


I am getting JSON information from the Pivotal Tracker API, and I need to get certain info, instead of all the raw data. To do this, I used JSON.parse() to convert the JSON to a Ruby array of hashes.

Now, I need to iterate through this array and only return the relevant hashes.

For example, I just want to return the primary_resources hash that has a nested hash of:

"story_type" => "feature"

To do this, I wrote this code (response.body is the data returned from the GET request):

@data = response.body
parsed = JSON.parse(@data)
puts parsed['primary_resources']['story_type']['feature']

When I run the script, I get this error:

no implicit conversion of String into Integer (TypeError)

It seems that it is iterating through an array of hashes and looking for an integer number of the array (like array[3] or array[0]), but that doesn't help me. I need to return all the hashes that have a nested hash :kind => story in the primary resources hash.

I also tried to do this:

parsed.each do |entry|
  puts entry['primary_resources']['story_Type']['feature']
end

and I got the same error.

Here is my full code:

require 'json'
require 'net/http'
require 'open-uri'
require 'openssl'
require 'active_support'

prompt = '> '
puts "What is the id of the Project you want to get data from?"
print prompt
project_id = STDIN.gets.chomp()
puts "What is the beginning date you want to return from? ex: 2014-07-02"
print prompt
date1 = STDIN.gets.chomp()
puts "What is the end date you want to return from? ex: 2014-07-16"
print prompt
date2 = STDIN.gets.chomp()



def scope(project_id, date1, date2)
  uri = URI.parse("https://www.pivotaltracker.com/services/v5/projects/#{project_id}/activity?occurred_after=#{date1}T01:00:15Z&occurred_before=#{date2}T01:00:15Z&fields=primary_resources")
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  request = Net::HTTP::Get.new(uri.request_uri)
  request.add_field("X-TrackerToken", "*****************")
  response = http.request(request)
  @data = response.body
  parsed = JSON.parse(@data)
  puts parsed['primary_resources']['story_type']['feature']
  # parsed.each do |entry|
    # puts entry['primary_resources']['kind']['story']
  # end

  # puts parsed['primary_resources'].select { |r| r['story_type'].eql? 'feature' }
end

scope(project_id, date1, date2)

Here is some of the JSON response, without parsing (full response is too long, and really just the same response for like 15 other user stories):

What is the id of the Project you want to get data from?
> 961142
What is the beginning date you want to return from? ex: 2014-07-02
> 2014-07-02
What is the end date you want to return from? ex: 2014-07-16
> 2014-07-03
[
   {
    "primary_resources": [
      {
        "kind": "story",
        "id": 74313670,
        "name": "User can make an image fit inside the grid when viewing image detail and save it to case template. BUILD 146",
        "story_type": "bug",
        "url": "https://www.pivotaltracker.com/story/show/74313670"
      }
    ],
    "guid": "961142_3419",
    "project_version": 3419
  },

Here is some of the JSON response, after parsing (only showing first story for same reason as above):

What is the id of the Project you want to get data from?
> 961142
What is the beginning date you want to return from? ex: 2014-07-02
> 2014-07-02
What is the end date you want to return from? ex: 2014-07-16
> 2014-07-03
{"primary_resources"=>[{"kind"=>"story", "id"=>74313670, "name"=>"User can make an image fit inside the grid when viewing image detail and save it to case template. BUILD 146", "story_type"=>"bug", "url"=>"https://www.pivotaltracker.com/story/show/74313670"}], "guid"=>"961142_3419", "project_version"=>3419}

How can I iterate through this array of hashes and return only the ones of "story_type"=>"feature" ?


Solution

  • So according to the API documentation, what your code is receiving is an array of activities (or "activity-type resources"), each of which may contain a subarray (in "primary_resources") of the resources it affects.

    I assume what you want as output from your program is an array of the activities that relate to a "feature"-type story, not an array of the affected stories themselves. If that's true, try this:

    feature_story_activities = parsed.select do |activity|
      activity.has_key?('primary_resources') &&
        activity['primary_resources'].find do |resource|
        resource['story_type'].eql?('feature')
      end
    end
    
    puts feature_story_activities
    

    This places in feature_story_activities all the activities that

    • Specify a list of resources they affect, and

    • Affect at least one feature-type story.