JSONP from forecast.io?

  • 1
  • Question
  • Updated 2 years ago
  • Answered
So, I'm attempting to reach way beyond my skill set in javascript. But before I begin on this, I'd like to know if it's even possible.

I've got an Apex Class I'd like to replace as it's Apex and fugly.

I've got an API setup and can successfully see JSON when entering the proper URL.

https://api.forecast.io/forecast/MY-API-KEY-HERE/45.421530,-75.697193,1977-10-05T12:00:00-0400

The developer URL is https://developer.forecast.io/docs/v2

JSON below

{"latitude":45.42153,"longitude":-75.697193,"timezone":"America/Toronto","offset":-4,"currently":{"time":244915200,"summary":"Partly Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":36.35,"apparentTemperature":34.23,"dewPoint":32.08,"humidity":0.84,"windSpeed":3,"windBearing":187,"visibility":10,"cloudCover":0.5,"pressure":1015.2},"hourly":{"summary":"Mostly cloudy starting overnight, continuing until night.","icon":"partly-cloudy-day","data":[{"time":244872000,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":46.12,"apparentTemperature":46.12,"dewPoint":39.67,"humidity":0.78,"windSpeed":2.34,"windBearing":235,"visibility":9.64,"cloudCover":0,"pressure":1014.41},{"time":244875600,"summary":"Partly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":44.64,"apparentTemperature":44.64,"dewPoint":39.34,"humidity":0.82,"windSpeed":3,"windBearing":260,"visibility":9.64,"cloudCover":0.27,"pressure":1014.5},{"time":244879200,"summary":"Partly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":44.55,"apparentTemperature":42.37,"dewPoint":39.66,"humidity":0.83,"windSpeed":4.24,"windBearing":230,"visibility":9.67,"cloudCover":0.38,"pressure":1014.85},{"time":244882800,"summary":"Mostly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":45.12,"apparentTemperature":41.31,"dewPoint":39.82,"humidity":0.82,"windSpeed":7,"windBearing":230,"visibility":9.64,"cloudCover":0.7,"pressure":1014.86},{"time":244886400,"summary":"Mostly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":43.24,"apparentTemperature":39.62,"dewPoint":39.84,"humidity":0.88,"windSpeed":6,"windBearing":260,"visibility":9.64,"cloudCover":0.66,"pressure":1014.98},{"time":244890000,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":43.36,"apparentTemperature":38.68,"dewPoint":39.96,"humidity":0.88,"windSpeed":8,"windBearing":230,"visibility":9.64,"cloudCover":0,"pressure":1015.01},{"time":244893600,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":41.61,"apparentTemperature":41.61,"dewPoint":39.97,"humidity":0.94,"windSpeed":2.49,"windBearing":230,"visibility":9.64,"cloudCover":0,"pressure":1015.22},{"time":244897200,"summary":"Mostly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":41.73,"apparentTemperature":38.94,"dewPoint":40.09,"humidity":0.94,"windSpeed":4.44,"windBearing":214,"visibility":9.64,"cloudCover":0.66,"pressure":1015.45},{"time":244900800,"summary":"Mostly Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":43.28,"apparentTemperature":43.28,"dewPoint":40.09,"humidity":0.88,"windSpeed":2.64,"windBearing":219,"visibility":9.67,"cloudCover":0.7,"pressure":1015.82},{"time":244904400,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":46.85,"apparentTemperature":43.38,"dewPoint":43.82,"humidity":0.89,"windSpeed":7.02,"windBearing":206,"visibility":10,"cloudCover":0.97,"pressure":1015.93},{"time":244908000,"summary":"Partly Cloudy","icon":"partly-cloudy-day","precipType":"snow","temperature":28.65,"apparentTemperature":23.47,"dewPoint":25.36,"humidity":0.87,"windSpeed":4.68,"windBearing":147,"visibility":10,"cloudCover":0.45,"pressure":1015.93},{"time":244911600,"summary":"Partly Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":33.53,"apparentTemperature":33.53,"dewPoint":29.8,"humidity":0.86,"windSpeed":2.52,"windBearing":178,"visibility":10,"cloudCover":0.45,"pressure":1015.38},{"time":244915200,"summary":"Partly Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":36.35,"apparentTemperature":34.23,"dewPoint":32.08,"humidity":0.84,"windSpeed":3,"windBearing":187,"visibility":10,"cloudCover":0.5,"pressure":1015.2},{"time":244918800,"summary":"Windy and Partly Cloudy","icon":"wind","precipType":"rain","temperature":40.08,"apparentTemperature":27.88,"dewPoint":33.6,"humidity":0.77,"windSpeed":34.21,"windBearing":295,"visibility":10,"cloudCover":0.51,"pressure":1014.71},{"time":244922400,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":60.56,"apparentTemperature":60.56,"dewPoint":46.54,"humidity":0.6,"windSpeed":10.49,"windBearing":231,"visibility":9.67,"cloudCover":1,"pressure":1014.25},{"time":244926000,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":59.49,"apparentTemperature":59.49,"dewPoint":40.28,"humidity":0.49,"windSpeed":14.76,"windBearing":240,"visibility":9.64,"cloudCover":1,"pressure":1014.66},{"time":244929600,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":54.97,"apparentTemperature":54.97,"dewPoint":48.3,"humidity":0.78,"windSpeed":4.94,"windBearing":219,"visibility":9.64,"cloudCover":1,"pressure":1014.36},{"time":244933200,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":54.47,"apparentTemperature":54.47,"dewPoint":48.14,"humidity":0.79,"windSpeed":5.08,"windBearing":190,"visibility":9.67,"cloudCover":1,"pressure":1013.64},{"time":244936800,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":54.49,"apparentTemperature":54.49,"dewPoint":48.24,"humidity":0.79,"windSpeed":5.52,"windBearing":170,"visibility":9.64,"cloudCover":1,"pressure":1013.39},{"time":244940400,"summary":"Partly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":43.67,"apparentTemperature":39.65,"dewPoint":32.26,"humidity":0.64,"windSpeed":6.84,"windBearing":182,"visibility":9.89,"cloudCover":0.52,"pressure":1013.04},{"time":244944000,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":54.83,"apparentTemperature":54.83,"dewPoint":48.08,"humidity":0.78,"windSpeed":7.92,"windBearing":205,"visibility":9.67,"cloudCover":1,"pressure":1012.54},{"time":244947600,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":55.61,"apparentTemperature":55.61,"dewPoint":50.21,"humidity":0.82,"windSpeed":12.86,"windBearing":211,"visibility":9.64,"cloudCover":1,"pressure":1012.48},{"time":244951200,"summary":"Partly Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":42.39,"apparentTemperature":36.53,"dewPoint":32.67,"humidity":0.68,"windSpeed":10.19,"windBearing":188,"visibility":9.89,"cloudCover":0.52,"pressure":1012.48},{"time":244954800,"summary":"Overcast","icon":"cloudy","precipType":"rain","temperature":55.35,"apparentTemperature":55.35,"dewPoint":49.89,"humidity":0.82,"windSpeed":12.58,"windBearing":218,"visibility":9.67,"cloudCover":0.98,"pressure":1012.07}]},"daily":{"data":[{"time":244872000,"summary":"Mostly cloudy throughout the day.","icon":"partly-cloudy-day","sunriseTime":244897674,"sunsetTime":244938987,"moonPhase":0.76,"precipType":"rain","temperatureMin":28.65,"temperatureMinTime":244908000,"temperatureMax":60.56,"temperatureMaxTime":244922400,"apparentTemperatureMin":23.47,"apparentTemperatureMinTime":244908000,"apparentTemperatureMax":60.56,"apparentTemperatureMaxTime":244922400,"dewPoint":40.32,"humidity":0.8,"windSpeed":6.02,"windBearing":228,"visibility":9.74,"cloudCover":0.64,"pressure":1014.38}]},"flags":{"sources":["isd"],"isd-stations":["710630-99999","716280-99999","717220-99999","725186-99999","726221-94725"],"units":"us"}}
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb

Posted 4 years ago

  • 1
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
Pat, 

Of course, this is possible.  Can you describe your use case?  In what context will the call to forecast.io be made and what are you going to do with the resulting JSON?

Irvin
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
I'm trying to collect the weather information for locations when field staff were working there. So, each day, a crew manager will update a series of tasks done at a site. Each day this happens on any site, I'd like to have a weather log for it.

I'm going to parse the JSON into a salesforce object. Then I'll use this data in Conga reports to produce daily and weekly reports with the data being neatly presented in a clean widget like weather report.
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
Based on your use case, this seems a better fit for scheduled batch APEX.  Why would you want to perform this in Skuid?  Are you expecting the field staff to click a button and get the latest forecast?
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
I'm not using APEX as I don't know APEX. Do you know APEX? Just looking at APEX makes my head heart for some reason. Drives me nuts. I simply prefer javascript.

Currently have an APEX class that retrieves for reporting purposes, but it doesn't save it to an object. Just displays it. It was made by someone before me.

This is not intended to provide weather forecasts. It's to provide a historical account of the weather on locations when tasks were being performed.
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
The APEX class could be enhanced to save the forecast data to an object.  How is the APEX class being invoked?  
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
Give me a few minutes and I will code it up and post here....
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Wha ... seriously?
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
Steps to create:
  • Create a custom object named Location
  • Add two custom fields
  • Add new Skuid page
  • Add https://api.forecast.io to Setup | Security | Remote Site Settings

Custom object & fields





Skuid page XML:


<skuidpage unsavedchangeswarning="yes" showsidebar="false" showheader="true" tabtooverride="Location__c">   <models>
      <model id="Location" limit="100" query="true" createrowifnonefound="false" sobject="Location__c">
         <fields>
            <field id="Name"/>
            <field id="CreatedDate"/>
            <field id="Forecast_io_JSON__c"/>
            <field id="Geo__c"/>
            <field id="Geo__Latitude__s"/>
            <field id="Geo__Longitude__s"/>
            <field id="LastModifiedDate"/>
         </fields>
         <conditions/>
         <actions/>
      </model>
   </models>
   <components>
      <pagetitle model="Location" uniqueid="locationsPageTitle">
         <maintitle>
            <template>{{Model.labelPlural}}</template>
         </maintitle>
         <subtitle>
            <template>Home</template>
         </subtitle>
         <actions/>
      </pagetitle>
      <skootable showconditions="true" showsavecancel="true" searchmethod="server" searchbox="true" showexportbuttons="false" pagesize="10" createrecords="true" model="Location" mode="read">
         <fields>
            <field id="Name" allowordering="true" valuehalign="" type="" readonly="true"/>
            <field id="Geo__Latitude__s" decimalplaces="" valuehalign="" type="" readonly="true"/>
            <field id="Geo__Longitude__s" decimalplaces="" valuehalign="" type="" readonly="true"/>
            <field id="LastModifiedDate" valuehalign="" type="" readonly="true" allowordering="true">
               <label>Last Forecast</label>
            </field>
         </fields>
         <rowactions>
            <action type="edit"/>
            <action type="delete"/>
            <action type="drawer" label="Show Forecast Data" icon="ui-silk-zoom-in" openicon="ui-silk-zoom-out">
               <drawer title="Drawer Area" width="90%" closehandle="true">
                  <components>
                     <basicfieldeditor showheader="true" showsavecancel="false" model="Location" buttonposition="" mode="readonly" layout="above">
                        <columns>
                           <column width="100%">
                              <sections>
                                 <section title="Forecast Data" collapsible="no" showheader="true">
                                    <fields>
                                       <field id="Forecast_io_JSON__c" valuehalign="" type="" readonly="true" maxdisplaycharacters="5000">
                                          <label> </label>
                                       </field>
                                    </fields>
                                 </section>
                              </sections>
                           </column>
                        </columns>
                     </basicfieldeditor>
                  </components>
               </drawer>
            </action>
            <action type="multi" label="Get forecast" icon="ui-silk-weather-clouds">
               <drawer title="Drawer Area" width="90%" closehandle="true">
                  <components/>
               </drawer>
               <actions>
                  <action type="blockUI" message="Getting latest forecast..."/>
                  <action type="custom" snippet="getForecast">
                     <onerroractions>
                        <action type="blockUI" message="There was an error" timeout="3000"/>
                     </onerroractions>
                  </action>
                  <action type="save">
                     <models>
                        <model>Location</model>
                     </models>
                     <onerroractions>
                        <action type="blockUI" message="There was an error" timeout="3000"/>
                     </onerroractions>
                  </action>
                  <action type="unblockUI"/>
               </actions>
               <popup width="80%" title="Viewing {{Model.label}}: {{Name}}">
                  <components>
                     <basicfieldeditor showheader="true" showsavecancel="false" mode="readonly" model="Location" buttonposition="" layout="">
                        <conditions>
                           <condition type="contextrow" field="Id" mergefield="Id" autocreated="true"/>
                        </conditions>
                        <columns>
                           <column width="50%">
                              <sections>
                                 <section title="Location Information" collapsible="no">
                                    <fields>
                                       <field id="Name" valuehalign="" type="" readonly="true"/>
                                       <field id="LastModifiedDate" valuehalign="" type="" readonly="true">
                                          <label>Last Forcecast</label>
                                       </field>
                                    </fields>
                                 </section>
                              </sections>
                           </column>
                           <column width="50%">
                              <sections>
                                 <section title="Geo Information" collapsible="no">
                                    <fields>
                                       <field id="Geo__Latitude__s" decimalplaces="" valuehalign="" type="" readonly="true"/>
                                       <field id="Geo__Longitude__s" decimalplaces="" valuehalign="" type="" readonly="true"/>
                                    </fields>
                                 </section>
                              </sections>
                           </column>
                        </columns>
                     </basicfieldeditor>
                     <basicfieldeditor showheader="true" showsavecancel="false" model="Location" buttonposition="" mode="readonly" layout="above">
                        <columns>
                           <column width="100%">
                              <sections>
                                 <section title="Forecast Data" collapsible="no">
                                    <fields>
                                       <field id="Forecast_io_JSON__c" valuehalign="" type="" maxdisplaycharacters="4000" readonly="true">
                                          <label> </label>
                                       </field>
                                    </fields>
                                 </section>
                              </sections>
                           </column>
                        </columns>
                        <conditions>
                           <condition type="contextrow" field="Id" mergefield="Id" autocreated="true"/>
                        </conditions>
                     </basicfieldeditor>
                  </components>
               </popup>
            </action>
         </rowactions>
         <massactions usefirstitemasdefault="true">
            <action type="massupdate"/>
            <action type="massdelete"/>
         </massactions>
         <views>
            <view type="standard"/>
         </views>
      </skootable>
   </components>
   <resources>
      <labels/>
      <css/>
      <javascript>
         <jsitem location="inlinesnippet" name="getForecast" cachelocation="false">var params = arguments[0],
$ = skuid.$;
var item = params.item;
var list = params.list;
var model = params.model;
var row = item.row;
// Create a jQuery Deferred
var dfd = $.Deferred();
// forecast.io URL format:
// https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME
var url = 'https://api.forecast.io/forecast/';
var apiKey = 'YOURAPIKEY';  // YOUR APIKEY GOES HERE
var latitude = row.Geo__Latitude__s;
var longitude = row.Geo__Longitude__s;
var forecastUrl = url + apiKey + '/' + latitude + ',' + longitude;
console.log('URL: ' + forecastUrl);
var sessionId = skuid.utils.userInfo.sessionId;
sforce.connection.remoteFunction({
   url : forecastUrl, 
   requestHeaders: {
      "Authorization": "Bearer "+ sessionId, 
      "Content-Type": "application/json"
   }, 
   method: "GET", 
   onSuccess : function(response) { 
      row.Forecast_io_JSON__c = response;
      dfd.resolve(response); 
   }, 
   onFailure : function(response) { 
       var pageTitle = $('#locationsPageTitle');
       var editor = pageTitle.data('object').editor;
       editor.handleMessages(
           [
               {
                  message: response,
                  severity: 'ERROR'
               }
           ]
       );
      dfd.reject(response);
   } 
});
// Return the Deferred's Promise
return dfd.promise();</jsitem>
      </javascript>
   </resources>
</skuidpage>


Screenshot 1 Locations List




Screenshot 2 Get Forecast





Screenshot 3 Display Forecast


Hope this helps.  Enjoy!
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Give me a minute. I've got to pick up my jaw off the ground. It's simply not responding appropriately!

Wish I were able to dive into this right now. Next up for sure though!
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Yup. Looked over the code. I knew it was going to be something that was month's in my javascript capabilities.
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
How many decimals for the Geolocation field? I went with 6.
(Edited)
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
Glad to help.  Still think that this is better suited as an async batch job that runs in off hours especially if you have lots of locations.  One less thing for a project manager to have to do.
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Well. I'm not going to require anyone to press any buttons for this to run particular.

I do agree that the best best way would be to have the async batch. Issue is the associated cost to make this happen.

Right now, with javascript, I can set it to run when the model is saved.
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Ok. Odd thing in the OnSuccess.

This line updates the field but the field.
row.RECON__Forecast_io_JSON__c = response;

Works every time, but the model doesn't saves as per the action framework no errors. It's as if the Save Action thinks there aren't any changes made to the model, so the command commits zero changes. Only once I've made an edit to the field does it save.
Photo of mB Pat Vachon

mB Pat Vachon, Champion

  • 42,714 Points 20k badge 2x thumb
Photo of Irvin Waldman

Irvin Waldman, Champion

  • 9,006 Points 5k badge 2x thumb
Hey, I never claimed that I actually tested the code!  Here ya' go:

var params = arguments[0],	
$ = skuid.$;

var item = params.item;
var list = params.list;
var model = params.model;
var row = item.row;
// Create a jQuery Deferred
var dfd = $.Deferred();
// forecast.io URL format:
// https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME
var url = 'https://api.forecast.io/forecast/';
var apiKey = '2f8da7905860d2d6fca0117ee440d3d7';  // YOUR APIKEY 
var latitude = row.Geo__Latitude__s;
var longitude = row.Geo__Longitude__s;
var forecastUrl = url + apiKey + '/' + latitude + ',' + longitude;
var sessionId = skuid.utils.userInfo.sessionId;
sforce.connection.remoteFunction({
   url : forecastUrl, 
   requestHeaders: {
      "Authorization": "Bearer "+ sessionId, 
      "Content-Type": "application/json"
   }, 
   method: "GET", 
   onSuccess : function(response) { 
      model.updateRow(
        { Id: row.Id },
        { Forecast_io_JSON__c: response }
      );
      dfd.resolve(response); 
   }, 
   onFailure : function(response) { 
       var pageTitle = $('#locationsPageTitle');
       var editor = pageTitle.data('object').editor;
       editor.handleMessages(
           [
               {
                  message: response,
                  severity: 'ERROR'
               }
           ]
       );
      dfd.reject(response);
   } 
});
// Return the Deferred's Promise
return dfd.promise();
Photo of Dipika Singh

Dipika Singh

  • 60 Points
http://printablecalendartemplates.com... the weather information for locations when field staff were working there. So, each day, a crew manager will update a series of tasks done at a site. Each day this happens on any site, I'd like to have a http://printablecalendartemplates.com/ weather log for it