How to build a sync procedure between Google Fit and a new SuiteCRM Module

Hi …i have zero to little coding experience but im not exceedingly stupid -

I want to build a new HR module in SuiteCRM that stores my users health data(the user is an employee) and a procedure to sync data with the users Google Fit Data Store. Google Fit offers a guide on how to GET the data from the Google Fit Store and the steps for the authentication process.Could you please give me the main steps to follow which i can then research the detail of.I will list what i perceive the steps to be and which of them i have an understanding of - please rectify where i have it wrong and if possible point me to good sources to learn ( for a beginner).

1.Building a SuiteCRM module (Fields,Labels,Relationships,Layouts and Subpanels) i understand almost perfectly.So we can work on the assumption that i have a SUITECRM module(call it the EmployeeFIT module) with the MYSQL backend that stores data like sleep patterns,activities,heartrate and nutritional intake…
2.Using the Google FIT API on the OAuth 2.0 Playground i have a fair idea how to produce the required output.Underneath this summary ive pasted the Google FIT API Output.My question then is what do i do to produce this in a format(like CSV) that can be readily imported into the MySQL database(automatically and scheduled)
3.Finally,if the above format is produced,how do i automatically(and scheduled again) import that into my SuiteCRM MySQL backend.

HTTP/1.1 200 OK
Content-length: 14078
X-xss-protection: 1; mode=block
Content-location: https://www.googleapis.com/fitness/v1/users/me/dataSources
X-content-type-options: nosniff
Transfer-encoding: chunked
Expires: Fri, 05 Oct 2018 09:52:51 GMT
Vary: Origin, X-Origin
Server: GSE
Etag: "977qOFam2oE2uMtwc7J4i3egnsw/2WlhzJAMLfIG5awQLsnBZtGmmNI"
Cache-control: private, max-age=0, must-revalidate, no-transform
Date: Fri, 05 Oct 2018 09:52:51 GMT
X-frame-options: SAMEORIGIN
Alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
Content-type: application/json; charset=UTF-8
-content-encoding: gzip
{
  "dataSource": [
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "duration", 
            "format": "integer"
          }
        ], 
        "name": "com.google.active_minutes"
      }, 
      "dataStreamName": "from_activity<-merge_activity_segments", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.active_minutes:com.google.android.gms:from_activity<-merge_activity_segments", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "duration", 
            "format": "integer"
          }
        ], 
        "name": "com.google.active_minutes"
      }, 
      "dataStreamName": "from_steps<-merge_step_deltas", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.active_minutes:com.google.android.gms:from_steps<-merge_step_deltas", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "duration", 
            "format": "integer"
          }
        ], 
        "name": "com.google.active_minutes"
      }, 
      "dataStreamName": "merge_active_minutes", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.active_minutes:com.google.android.gms:merge_active_minutes", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "detailed", 
      "dataType": {
        "field": [
          {
            "name": "activity_confidence", 
            "format": "map"
          }
        ], 
        "name": "com.google.activity.samples"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:detailed", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "phone_default_dnn_v14", 
      "dataType": {
        "field": [
          {
            "name": "activity_confidence", 
            "format": "map"
          }
        ], 
        "name": "com.google.activity.samples"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:phone_default_dnn_v14", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "phone_relabel_bicycle_to_unknown_with_wifi", 
      "dataType": {
        "field": [
          {
            "name": "activity_confidence", 
            "format": "map"
          }
        ], 
        "name": "com.google.activity.samples"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:phone_relabel_bicycle_to_unknown_with_wifi", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "smd_gouda", 
      "dataType": {
        "field": [
          {
            "name": "activity_confidence", 
            "format": "map"
          }
        ], 
        "name": "com.google.activity.samples"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:smd_gouda", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "software_tilt_froyo", 
      "dataType": {
        "field": [
          {
            "name": "activity_confidence", 
            "format": "map"
          }
        ], 
        "name": "com.google.activity.samples"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:software_tilt_froyo", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataStreamName": "session_activity_segment", 
      "application": {
        "packageName": "com.google.android.apps.fitness"
      }, 
      "dataStreamId": "derived:com.google.activity.segment:com.google.android.apps.fitness:session_activity_segment", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "activity_from_steps", 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.segment:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:activity_from_steps", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "from_activity_samples<-derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:detailed", 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.activity.segment:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:from_activity_samples<-derived:com.google.activity.samples:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:detailed", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataStreamName": "merge_activity_segments", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.activity.segment:com.google.android.gms:merge_activity_segments", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataStreamName": "platform_activity_segments", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.activity.segment:com.google.android.gms:platform_activity_segments", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataStreamName": "session_activity_segment", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.activity.segment:com.google.android.gms:session_activity_segment", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.consumed"
      }, 
      "dataStreamName": "merge_calories_consumed", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.calories.consumed:com.google.android.gms:merge_calories_consumed", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.expended"
      }, 
      "dataStreamName": "from_activities", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.calories.expended:com.google.android.gms:from_activities", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.expended"
      }, 
      "dataStreamName": "from_bmr", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.calories.expended:com.google.android.gms:from_bmr", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.expended"
      }, 
      "dataStreamName": "merge_calories_expended", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.calories.expended:com.google.android.gms:merge_calories_expended", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.expended"
      }, 
      "dataStreamName": "platform_calories_expended", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.calories.expended:com.google.android.gms:platform_calories_expended", 
      "type": "derived"
    }, 
    {
      "dataStreamName": "derive_step_deltas<-raw:com.google.step_count.cumulative:HUAWEI:PRA-LX2:a0fb6093:step counter", 
      "dataType": {
        "field": [
          {
            "name": "steps", 
            "format": "integer"
          }
        ], 
        "name": "com.google.step_count.delta"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "derived:com.google.step_count.delta:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:derive_step_deltas<-raw:com.google.step_count.cumulative:HUAWEI:PRA-LX2:a0fb6093:step counter", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "steps", 
            "format": "integer"
          }
        ], 
        "name": "com.google.step_count.delta"
      }, 
      "dataStreamName": "estimated_steps", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "steps", 
            "format": "integer"
          }
        ], 
        "name": "com.google.step_count.delta"
      }, 
      "dataStreamName": "merge_step_deltas", 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "dataStreamId": "derived:com.google.step_count.delta:com.google.android.gms:merge_step_deltas", 
      "type": "derived"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "optional": true, 
            "name": "duration", 
            "format": "integer"
          }
        ], 
        "name": "com.google.active_minutes"
      }, 
      "dataStreamName": "user_input", 
      "application": {
        "packageName": "com.google.android.apps.fitness"
      }, 
      "dataStreamId": "raw:com.google.active_minutes:com.google.android.apps.fitness:user_input", 
      "type": "raw"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "activity", 
            "format": "integer"
          }
        ], 
        "name": "com.google.activity.segment"
      }, 
      "dataStreamName": "user_input", 
      "application": {
        "packageName": "com.google.android.apps.fitness"
      }, 
      "dataStreamId": "raw:com.google.activity.segment:com.google.android.apps.fitness:user_input", 
      "type": "raw"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.consumed"
      }, 
      "dataStreamName": "", 
      "application": {
        "packageName": "com.fatsecret.android"
      }, 
      "dataStreamId": "raw:com.google.calories.consumed:com.fatsecret.android:", 
      "type": "raw"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "calories", 
            "format": "floatPoint"
          }
        ], 
        "name": "com.google.calories.expended"
      }, 
      "dataStreamName": "", 
      "application": {
        "packageName": "com.fatsecret.android"
      }, 
      "dataStreamId": "raw:com.google.calories.expended:com.fatsecret.android:", 
      "type": "raw"
    }, 
    {
      "dataStreamName": "raw_sensor", 
      "dataType": {
        "field": [
          {
            "name": "sensor_type", 
            "format": "integer"
          }, 
          {
            "name": "timestamps", 
            "format": "integerList"
          }, 
          {
            "name": "sensor_values", 
            "format": "floatList"
          }
        ], 
        "name": "com.google.sensor.events"
      }, 
      "dataQualityStandard": [], 
      "application": {
        "packageName": "com.google.android.gms"
      }, 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "raw:com.google.sensor.events:com.google.android.gms:HUAWEI:PRA-LX2:a0fb6093:raw_sensor", 
      "type": "raw"
    }, 
    {
      "dataQualityStandard": [], 
      "dataType": {
        "field": [
          {
            "name": "steps", 
            "format": "integer"
          }
        ], 
        "name": "com.google.step_count.cumulative"
      }, 
      "dataStreamName": "step counter", 
      "device": {
        "model": "PRA-LX2", 
        "version": "", 
        "type": "phone", 
        "uid": "a0fb6093", 
        "manufacturer": "HUAWEI"
      }, 
      "dataStreamId": "raw:com.google.step_count.cumulative:HUAWEI:PRA-LX2:a0fb6093:step counter", 
      "type": "raw"
    }
  ]
}

You don’t need an intermediate file. You can just make SuiteCRM daily grab the data from the GoogleFIt API.

This would require a custom Scheduler job, which is basically some PHP that get’s launched on a schedule.

See the Docs about this: https://docs.suitecrm.com/developer/scheduled-tasks/

Then your PHP can Query the GoogleFit API and use Beans to cleanly put the data into SuiteCRM’s database.

This is all simple coding, but it is coding… I wish you good luck. :slight_smile:

1 Like

I’ve followed your SCHEDULED TASKS guide but are having issues.Herewith a dumb question.

In the INTRO it states :

“Scheduled tasks are performed in SuiteCRM by the scheduler module. Jobs are placed into the queue either through
the defined scheduled tasks or, for one off tasks, by code creating job objects. Note that both scheduled tasks and
using the job queue requires that you have the schedulers set up. This will vary depending on your system but usually
requires adding an entry either to Cron (for Linux systems) or to the windows scheduled tasks (for windows).
Opening the scheduled tasks page within SuiteCRM will let you know the format for the entry.”

It is unclear from the above what exactly the command line of the cron job should be.
My hosting service has CPanel which has a Cronjobs tool.This guide explains how to set up cron jobs :
https://www.namecheap.com/support/knowledgebase/article.aspx/9453/29/how-to-run-scripts-via-cron-jobs.

Assuming we use the further example in your guide - should the cronjobs commandline
launch the cleanmeetingscheduler.php? if not what should the cronjob command be?

Question 2 :

On the Create Scheduler Screen under BASIC theres a Job dropdown…it seems one either chooses URL and populate the Job URL field next to it
or select another choice from the dropdown.My question is how/where do i populate that dropdown - because in all of the Schedulers that my installation
was populated with that dropdown isnt populated with anything other than “URL”

QUESTION 3 :

I have zero PHP knowledge and very little coding experience whatsoever.Ive found some PHP courses
online yesterday.In your reply you stated "This is all simple coding, but it is coding…

3a) Roughly how much time do you think i would need to invest in learning PHP in order to just
learn how to use the mentioned Google FIT API and get the data into my SuiteCRM Module?

3b) In your mind - how many hours of coding do you envisage my stated requirement would take to build for an experienced developer(not myself
then) in SuiteCRM/PHP/MySQL?

3c) If its too long for me to learn and build ill hunt for an experienced developer - is there somewhere around suirtecrm.com where i can contact a list
of developers to do that for me?..or should i just post that need here in the forum?

I thank you and hope to hear from you soon

One issue is to make the entire “Scheduler jobs” system work. It always involves launching a single php file from cron, it’s called cron.php. See

https://pgorod.github.io/Scheduler-Jobs/

Once that is working, you can work on getting your own specific job to launch. That’s where the section on “Custom Scheduler jobs” comes in, the one from the link I gave you a few posts ago.

About the development, you can contact (or ask me to be contacted) by SalesAgility for a quote. There are also other developers than frequent these forums and are willing to work.

I can’t specify how many hours it will take, I can only say it doesn’t sound too complicated, it’s a simple integration (since GoogleFit provides an API).

It’s obvious that learning PHP will take a much longer time, but it really depends on your previous experience with other languages, and your ability to code (varies greatly from person to person).

1 Like