Skip to content

Commit

Permalink
merge develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Goetz committed Oct 4, 2023
2 parents 75117b7 + 51606c2 commit 0eed598
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 183 deletions.
2 changes: 1 addition & 1 deletion Fit
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ rebuild_garmin:
build_garmin_monitoring:
garmindb_cli.py --monitoring --import --analyze

import_garmin_monitoring:
garmindb_cli.py --monitoring --import --latest

build_garmin_activities:
garmindb_cli.py --activities --import --analyze

Expand Down
11 changes: 7 additions & 4 deletions garmindb/GarminConnectConfig.json.example
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"garmin": {
"domain": "garmin.com"
},
"credentials": {
"user" : "[email protected]",
"secure_password" : false,
"password" : "yourpassword"
},
"data": {
"weight_start_date" : "01/01/2019",
"sleep_start_date" : "01/01/2019",
"rhr_start_date" : "01/01/2019",
"monitoring_start_date" : "01/01/2019",
"weight_start_date" : "12/31/2019",
"sleep_start_date" : "12/31/2019",
"rhr_start_date" : "12/31/2019",
"monitoring_start_date" : "12/31/2019",
"download_latest_activities" : 25,
"download_all_activities" : 1000
},
Expand Down
3 changes: 2 additions & 1 deletion garmindb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
from .plugin_manager import PluginManager
from .version import format_version, log_version, python_version_check

from .import_monitoring import GarminMonitoringFitData, GarminSleepFitData, GarminSummaryData, GarminProfile, GarminWeightData, GarminSleepData, GarminRhrData, GarminSettingsFitData, GarminHydrationData
from .import_monitoring import GarminMonitoringFitData, GarminSleepFitData, GarminSummaryData, GarminUserSettings, GarminSocialProfile, GarminPersonalInformation, GarminWeightData, \
GarminSleepData, GarminRhrData, GarminSettingsFitData, GarminHydrationData
from .activities_fit_data import GarminActivitiesFitData
from .garmin_tcx_data import GarminTcxData
from .garmin_json_data import GarminJsonSummaryData, GarminJsonDetailsData
230 changes: 81 additions & 149 deletions garmindb/download.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion garmindb/fit_file_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def _write_attribute(self, timestamp, message_fields, attribute_name, db_attribu
if attribute is not None:
if db_attribute_name is None:
db_attribute_name = attribute_name
root_logger.info("Writing attribute: %r -> %r at %r", attribute, db_attribute_name, timestamp)
Attributes.s_set_newer(self.garmin_db_session, db_attribute_name, attribute, timestamp)

def _write_attributes(self, timestamp, message_fields, attribute_names):
Expand All @@ -215,7 +216,7 @@ def _write_battery_entry(self, fit_file, message_fields):
root_logger.debug("battery message: %r", message_fields)

def _write_user_profile_entry(self, fit_file, message_fields):
root_logger.debug("user profile message: %r", message_fields)
root_logger.info("user profile message: %r", message_fields)
timestamp = fit_file.time_created_local
attribute_names = [
'gender', 'height', 'weight', 'age', 'year_of_birth', 'language', 'dist_setting', 'weight_setting', 'position_setting', 'elev_setting', 'sleep_time', 'wake_time',
Expand Down
4 changes: 4 additions & 0 deletions garmindb/garmin_connect_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ def get_password(self):
return self.get_secure_password()
return self.__get_node_value('credentials', 'password')

def get_garmin_base_domain(self):
"""Return the Garmin base domain to use for api calls."""
return self.__get_node_value_default('garmin', 'domain', "garmin.com")

def latest_activity_count(self):
"""Return the number of activities to download when getting the latest."""
return self.__get_node_value('data', 'download_latest_activities')
Expand Down
103 changes: 91 additions & 12 deletions garmindb/import_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,37 +265,116 @@ def _process_json(self, json_data):
class GarminProfile(JsonFileProcessor):
"""Class for importing JSON formatted Garmin Connect profile data into a database."""

def __init__(self, db_params, input_dir, debug):
def __init__(self, db_params, file_regex, input_dir, debug):
"""
Return an instance of GarminProfile.
Parameters:
----------
db_params (object): configuration data for accessing the database
file_regex (string): matches files to be processed
input_dir (string): directory (full path) to check for profile data files
debug (Boolean): enable debug logging
"""
logger.info("Processing profile data")
super().__init__(r'profile\.json', input_dir=input_dir, latest=False, debug=debug)
super().__init__(file_regex, input_dir=input_dir, latest=False, debug=debug)
self.garmin_db = GarminDb(db_params)
self.conversions = {'calendarDate': self._parse_date}

def _process_json(self, json_data):
measurement_system = fitfile.field_enums.DisplayMeasure.from_string(
json_data['measurementSystem'])
attributes = {
'name': json_data['displayName'].replace('_', ' '),
'time_zone': json_data['timeZone'],
'measurement_system': str(measurement_system),
'date_format': json_data['dateFormat']['formatKey']
}
attributes = self._process_attributes(json_data)
logger.info("Processing profile data: %r", attributes)
for attribute_name, attribute_value in attributes.items():
Attributes.set_newer(
self.garmin_db, attribute_name, attribute_value)
Attributes.set_newer(self.garmin_db, attribute_name, attribute_value)
return len(attributes)


class GarminUserSettings(GarminProfile):
"""Class for importing JSON formatted Garmin Connect user settings data into a database."""

def __init__(self, db_params, input_dir, debug):
"""
Return an instance of GarminProfile.
Parameters:
----------
db_params (object): configuration data for accessing the database
input_dir (string): directory (full path) to check for profile data files
debug (Boolean): enable debug logging
"""
logger.info("Processing user settings data")
super().__init__(db_params, r'^user-settings\.json', input_dir=input_dir, debug=debug)

def _process_attributes(self, json_data):
user_data = json_data['userData']
measurement_system = fitfile.field_enums.DisplayMeasure.from_string(user_data['measurementSystem'])
gender = fitfile.field_enums.Gender.from_string(user_data['gender'])
weight = fitfile.Weight.from_grams(user_data['weight'])
height = fitfile.Distance.from_cm(user_data['height'])
return {
'measurement_system': str(measurement_system),
'gender': str(gender),
'weight': weight.kgs_or_lbs(measurement_system),
'height': height.meters_or_feet(measurement_system),
'vo2max_running': user_data['vo2MaxRunning'],
'vo2max_cycling': user_data['vo2MaxCycling'],
'handedness': user_data['handedness'].lower()
}


class GarminPersonalInformation(GarminProfile):
"""Class for importing JSON formatted Garmin Connect user personal information data into a database."""

def __init__(self, db_params, input_dir, debug):
"""
Return an instance of GarminProfile.
Parameters:
----------
db_params (object): configuration data for accessing the database
input_dir (string): directory (full path) to check for profile data files
debug (Boolean): enable debug logging
"""
logger.info("Processing user personal information data")
super().__init__(db_params, r'^personal-information\.json', input_dir=input_dir, debug=debug)

def _process_attributes(self, json_data):
user_info = json_data['userInfo']
return {
'locale': user_info['locale'],
'time_zone': user_info['timeZone'],
'country_code': user_info['countryCode'],
}


class GarminSocialProfile(GarminProfile):
"""Class for importing JSON formatted Garmin Connect social profile data into a database."""

def __init__(self, db_params, input_dir, debug):
"""
Return an instance of GarminProfile.
Parameters:
----------
db_params (object): configuration data for accessing the database
input_dir (string): directory (full path) to check for profile data files
debug (Boolean): enable debug logging
"""
logger.info("Processing user settings data")
super().__init__(db_params, r'^social-profile\.json', input_dir=input_dir, debug=debug)

def _process_attributes(self, json_data):
return {
'id': json_data['id'],
'userName': json_data['userName'],
'name': json_data['fullName']
}


class GarminSummaryData(JsonFileProcessor):
"""Class for importing JSON formatted Garmin Connect daily summary data into a database."""

Expand Down
2 changes: 1 addition & 1 deletion garmindb/version_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

python_required = (3, 0, 0)
python_tested = (3, 11, 4)
version_info = (3, 4, 0)
version_info = (3, 5, 0)
prerelease = False


Expand Down
6 changes: 3 additions & 3 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ python-dateutil
cached-property
tqdm
matplotlib
cloudscraper
ipykernel
ipyleaflet
fitfile>=1.1.4
garth
fitfile>=1.1.5
tcxfile>=1.0.4
idbutils>=1.0.8
idbutils>=1.1.0
9 changes: 5 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ python-dateutil==2.8.2
cached-property==1.5.2
tqdm==4.65.1
matplotlib==3.7.2
cloudscraper==1.2.71
ipykernel==6.25.1
ipyleaflet==0.17.3
fitfile==1.1.4
tcxfile==1.0.4
idbutils==1.0.8
garth==0.4.34
fitfile>=1.1.5
tcxfile>=1.0.4
idbutils>=1.1.0

18 changes: 13 additions & 5 deletions scripts/garmindb_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

from garmindb import Download, Copy, Analyze
from garmindb import FitFileProcessor, ActivityFitFileProcessor, MonitoringFitFileProcessor, SleepFitFileProcessor
from garmindb import GarminProfile, GarminWeightData, GarminSummaryData, GarminMonitoringFitData, GarminSleepFitData, GarminSleepData, GarminRhrData, GarminSettingsFitData, \
GarminHydrationData
from garmindb import GarminUserSettings, GarminSocialProfile, GarminPersonalInformation, GarminWeightData, GarminSummaryData, GarminMonitoringFitData, GarminSleepFitData, GarminSleepData, GarminRhrData, \
GarminSettingsFitData, GarminHydrationData
from garmindb import GarminJsonSummaryData, GarminJsonDetailsData, GarminTcxData, GarminActivitiesFitData
from garmindb import ActivityExporter

Expand Down Expand Up @@ -163,9 +163,17 @@ def import_data(debug, latest, stats):

# Import the user profile and/or settings FIT file first so that we can get the measurement system and some other things sorted out first.
fit_files_dir = ConfigManager.get_or_create_fit_files_dir()
gp = GarminProfile(db_params_dict, fit_files_dir, debug)
if gp.file_count() > 0:
gp.process()
gus = GarminUserSettings(db_params_dict, fit_files_dir, debug)
if gus.file_count() > 0:
gus.process()

gpi = GarminPersonalInformation(db_params_dict, fit_files_dir, debug)
if gpi.file_count() > 0:
gpi.process()

gsp = GarminSocialProfile(db_params_dict, fit_files_dir, debug)
if gsp.file_count() > 0:
gsp.process()

gsfd = GarminSettingsFitData(fit_files_dir, debug)
if gsfd.file_count() > 0:
Expand Down
2 changes: 1 addition & 1 deletion test/test_profile_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_parse_uprofile(self):
gdb = GarminDb(db_params)
measurement_system = Attributes.measurements_type(gdb)
self.assertEqual(measurement_system, fitfile.field_enums.DisplayMeasure.statute,
'DisplayMeasure expected %r found %r' % (fitfile.field_enums.DisplayMeasure.statute, measurement_system))
'DisplayMeasure expected %r found %r from %r' % (fitfile.field_enums.DisplayMeasure.statute, measurement_system, gp.file_names))


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion utilities

0 comments on commit 0eed598

Please sign in to comment.