JSONP from forecast.io?

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"}}
```

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

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.

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?

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.

The APEX class could be enhanced to save the forecast data to an object.  How is the APEX class being invoked?  

Give me a few minutes and I will code it up and post here…

Wha … seriously?

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:
// <a target="_blank" rel="nofollow" href="https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME" title="Link httpsapiforecastioforecastAPIKEYLATITUDELONGITUDETIME">https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME</a>
var url = '<a target="_blank" rel="nofollow" href="https://api.forecast.io/forecast/'" title="Link httpsapiforecastioforecast">https://api.forecast.io/forecast/'</a>;
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!

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!

Yup. Looked over the code. I knew it was going to be something that was month’s in my javascript capabilities.

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.

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.

How many decimals for the Geolocation field? I went with 6.

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.


Hey, I never claimed that I actually tested the code!  Here ya’ go:

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

http://printablecalendartemplates.com/printable-calendars-2017-holidays/ 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