apple health data will soon be out in the world...
This commit is contained in:
		
							
								
								
									
										129
									
								
								inprep/applehealth-grafana/.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										129
									
								
								inprep/applehealth-grafana/.gitignore
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
 | 
					__pycache__/
 | 
				
			||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					*$py.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Distribution / packaging
 | 
				
			||||||
 | 
					.Python
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					develop-eggs/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					downloads/
 | 
				
			||||||
 | 
					eggs/
 | 
				
			||||||
 | 
					.eggs/
 | 
				
			||||||
 | 
					lib/
 | 
				
			||||||
 | 
					lib64/
 | 
				
			||||||
 | 
					parts/
 | 
				
			||||||
 | 
					sdist/
 | 
				
			||||||
 | 
					var/
 | 
				
			||||||
 | 
					wheels/
 | 
				
			||||||
 | 
					pip-wheel-metadata/
 | 
				
			||||||
 | 
					share/python-wheels/
 | 
				
			||||||
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					*.egg
 | 
				
			||||||
 | 
					MANIFEST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyInstaller
 | 
				
			||||||
 | 
					#  Usually these files are written by a python script from a template
 | 
				
			||||||
 | 
					#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
				
			||||||
 | 
					*.manifest
 | 
				
			||||||
 | 
					*.spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					pip-delete-this-directory.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					htmlcov/
 | 
				
			||||||
 | 
					.tox/
 | 
				
			||||||
 | 
					.nox/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					.coverage.*
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					coverage.xml
 | 
				
			||||||
 | 
					*.cover
 | 
				
			||||||
 | 
					*.py,cover
 | 
				
			||||||
 | 
					.hypothesis/
 | 
				
			||||||
 | 
					.pytest_cache/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					*.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Django stuff:
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					local_settings.py
 | 
				
			||||||
 | 
					db.sqlite3
 | 
				
			||||||
 | 
					db.sqlite3-journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Flask stuff:
 | 
				
			||||||
 | 
					instance/
 | 
				
			||||||
 | 
					.webassets-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Scrapy stuff:
 | 
				
			||||||
 | 
					.scrapy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx documentation
 | 
				
			||||||
 | 
					docs/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyBuilder
 | 
				
			||||||
 | 
					target/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Jupyter Notebook
 | 
				
			||||||
 | 
					.ipynb_checkpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IPython
 | 
				
			||||||
 | 
					profile_default/
 | 
				
			||||||
 | 
					ipython_config.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pyenv
 | 
				
			||||||
 | 
					.python-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pipenv
 | 
				
			||||||
 | 
					#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 | 
				
			||||||
 | 
					#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 | 
				
			||||||
 | 
					#   having no cross-platform support, pipenv may install dependencies that don't work, or not
 | 
				
			||||||
 | 
					#   install all needed dependencies.
 | 
				
			||||||
 | 
					#Pipfile.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PEP 582; used by e.g. github.com/David-OConnor/pyflow
 | 
				
			||||||
 | 
					__pypackages__/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Celery stuff
 | 
				
			||||||
 | 
					celerybeat-schedule
 | 
				
			||||||
 | 
					celerybeat.pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SageMath parsed files
 | 
				
			||||||
 | 
					*.sage.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Environments
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					.venv
 | 
				
			||||||
 | 
					env/
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					ENV/
 | 
				
			||||||
 | 
					env.bak/
 | 
				
			||||||
 | 
					venv.bak/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Spyder project settings
 | 
				
			||||||
 | 
					.spyderproject
 | 
				
			||||||
 | 
					.spyproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rope project settings
 | 
				
			||||||
 | 
					.ropeproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mkdocs documentation
 | 
				
			||||||
 | 
					/site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mypy
 | 
				
			||||||
 | 
					.mypy_cache/
 | 
				
			||||||
 | 
					.dmypy.json
 | 
				
			||||||
 | 
					dmypy.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Pyre type checker
 | 
				
			||||||
 | 
					.pyre/
 | 
				
			||||||
							
								
								
									
										6
									
								
								inprep/applehealth-grafana/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								inprep/applehealth-grafana/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					FROM python:3.9
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					COPY requirements.txt .
 | 
				
			||||||
 | 
					RUN pip install -r requirements.txt
 | 
				
			||||||
 | 
					COPY . .
 | 
				
			||||||
 | 
					CMD ["python", "app.py"]
 | 
				
			||||||
							
								
								
									
										21
									
								
								inprep/applehealth-grafana/LICENSE
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										21
									
								
								inprep/applehealth-grafana/LICENSE
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					MIT License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2021 Ivaylo Pavlov
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					SOFTWARE.
 | 
				
			||||||
							
								
								
									
										1
									
								
								inprep/applehealth-grafana/README.md
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1
									
								
								inprep/applehealth-grafana/README.md
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					A step by step setup guide is available in this [blog post on Ivo's Blog](https://www.ivaylopavlov.com/charting-apple-healthkit-data-in-grafana/).
 | 
				
			||||||
							
								
								
									
										107
									
								
								inprep/applehealth-grafana/app.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										107
									
								
								inprep/applehealth-grafana/app.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					import json
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import socket
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					from flask import request, Flask
 | 
				
			||||||
 | 
					from influxdb import InfluxDBClient
 | 
				
			||||||
 | 
					from geolib import geohash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DATAPOINTS_CHUNK = 80000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					logger = logging.getLogger("console-output")
 | 
				
			||||||
 | 
					logger.setLevel(logging.DEBUG)
 | 
				
			||||||
 | 
					handler = logging.StreamHandler(sys.stdout)
 | 
				
			||||||
 | 
					handler.setLevel(logging.DEBUG)
 | 
				
			||||||
 | 
					formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
 | 
				
			||||||
 | 
					handler.setFormatter(formatter)
 | 
				
			||||||
 | 
					logger.addHandler(handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app = Flask(__name__)
 | 
				
			||||||
 | 
					app.debug = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					client = InfluxDBClient(host='localhost', port=28086)
 | 
				
			||||||
 | 
					client.create_database('db')
 | 
				
			||||||
 | 
					client.switch_database('db')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route('/collect', methods=['POST', 'GET'])
 | 
				
			||||||
 | 
					def collect():
 | 
				
			||||||
 | 
					    logger.info(f"Request received")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    healthkit_data = None
 | 
				
			||||||
 | 
					    transformed_data = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        healthkit_data = json.loads(request.data)
 | 
				
			||||||
 | 
					    except:
 | 
				
			||||||
 | 
					        return "Invalid JSON Received", 400
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        logger.info(f"Ingesting Metrics")
 | 
				
			||||||
 | 
					        for metric in healthkit_data.get("data", {}).get("metrics", []):
 | 
				
			||||||
 | 
					            number_fields = []
 | 
				
			||||||
 | 
					            string_fields = []
 | 
				
			||||||
 | 
					            for datapoint in metric["data"]:
 | 
				
			||||||
 | 
					                metric_fields = set(datapoint.keys())
 | 
				
			||||||
 | 
					                metric_fields.remove("date")
 | 
				
			||||||
 | 
					                for mfield in metric_fields:
 | 
				
			||||||
 | 
					                    if type(datapoint[mfield]) == int or type(datapoint[mfield]) == float:
 | 
				
			||||||
 | 
					                        number_fields.append(mfield)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        string_fields.append(mfield)
 | 
				
			||||||
 | 
					                point = {
 | 
				
			||||||
 | 
					                    "measurement": metric["name"],
 | 
				
			||||||
 | 
					                    "time": datapoint["date"],
 | 
				
			||||||
 | 
					                    "tags": {str(nfield): str(datapoint[nfield]) for nfield in string_fields},
 | 
				
			||||||
 | 
					                    "fields": {str(nfield): float(datapoint[nfield]) for nfield in number_fields}
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                transformed_data.append(point)
 | 
				
			||||||
 | 
					                number_fields.clear()
 | 
				
			||||||
 | 
					                string_fields.clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logger.info(f"Data Transformation Complete")
 | 
				
			||||||
 | 
					        logger.info(f"Number of data points to write: {len(transformed_data)}")
 | 
				
			||||||
 | 
					        logger.info(f"DB Write Started")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in range(0, len(transformed_data), DATAPOINTS_CHUNK):
 | 
				
			||||||
 | 
					            logger.info(f"DB Writing chunk")
 | 
				
			||||||
 | 
					            client.write_points(transformed_data[i:i + DATAPOINTS_CHUNK])
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        logger.info(f"DB Metrics Write Complete")
 | 
				
			||||||
 | 
					        logger.info(f"Ingesting Workouts Routes")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        transformed_workout_data = []
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for workout in healthkit_data.get("data", {}).get("workouts", []):
 | 
				
			||||||
 | 
					            tags = {
 | 
				
			||||||
 | 
					                "id": workout["name"] + "-" + workout["start"] + "-" + workout["end"]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for gps_point in workout["route"]:
 | 
				
			||||||
 | 
					                point = {
 | 
				
			||||||
 | 
					                    "measurement": "workouts",
 | 
				
			||||||
 | 
					                    "time": gps_point["timestamp"],
 | 
				
			||||||
 | 
					                    "tags": tags,
 | 
				
			||||||
 | 
					                    "fields": {
 | 
				
			||||||
 | 
					                        "lat": gps_point["lat"],
 | 
				
			||||||
 | 
					                        "lng": gps_point["lon"],
 | 
				
			||||||
 | 
					                        "geohash": geohash.encode(gps_point["lat"], gps_point["lon"], 7),
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                transformed_workout_data.append(point)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for i in range(0, len(transformed_workout_data), DATAPOINTS_CHUNK):
 | 
				
			||||||
 | 
					                logger.info(f"DB Writing chunk")
 | 
				
			||||||
 | 
					                client.write_points(transformed_workout_data[i:i + DATAPOINTS_CHUNK])
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        logger.info(f"Ingesting Workouts Complete")
 | 
				
			||||||
 | 
					    except:
 | 
				
			||||||
 | 
					        logger.exception("Caught Exception. See stacktrace for details.")
 | 
				
			||||||
 | 
					        return "Server Error", 500
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return "Success", 200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    hostname = socket.gethostname()
 | 
				
			||||||
 | 
					    ip_address = socket.gethostbyname(hostname)
 | 
				
			||||||
 | 
					    logger.info(f"Local Network Endpoint: http://{ip_address}/collect")
 | 
				
			||||||
 | 
					    app.run(host='0.0.0.0', port=5353)
 | 
				
			||||||
@@ -1,9 +1,17 @@
 | 
				
			|||||||
services:
 | 
					services:
 | 
				
			||||||
 | 
					  web:
 | 
				
			||||||
 | 
					     build: .
 | 
				
			||||||
 | 
					     ports: 
 | 
				
			||||||
 | 
					      - 2007:2007
 | 
				
			||||||
 | 
					     depends_on: 
 | 
				
			||||||
 | 
					      - influxdbV1
 | 
				
			||||||
 | 
					     volumes:
 | 
				
			||||||
 | 
					       - .:/app
 | 
				
			||||||
  influxdbV1:
 | 
					  influxdbV1:
 | 
				
			||||||
    image: influxdb:1.8.4
 | 
					    image: influxdb:1.8.4
 | 
				
			||||||
    container_name: reachableceo-health-data
 | 
					    container_name: reachableceo-health-data
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "8086:8086"
 | 
					      - "28086:8086"
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - reachableceo-health-data:/var/lib/influxdb
 | 
					      - reachableceo-health-data:/var/lib/influxdb
 | 
				
			||||||
    restart: unless-stopped
 | 
					    restart: unless-stopped
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								inprep/applehealth-grafana/requirements.txt
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								inprep/applehealth-grafana/requirements.txt
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					flask
 | 
				
			||||||
 | 
					influxdb
 | 
				
			||||||
 | 
					geolib
 | 
				
			||||||
							
								
								
									
										37
									
								
								inprep/applehealth-grafana/sample-data.json
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										37
									
								
								inprep/applehealth-grafana/sample-data.json
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "data" : {
 | 
				
			||||||
 | 
					    "workouts" : [],
 | 
				
			||||||
 | 
					    "metrics" : [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        "data" : [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "qty" : 34,
 | 
				
			||||||
 | 
					            "date" : "2021-03-14 00:01:00 +0000"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "date" : "2021-03-14 00:51:00 +0000",
 | 
				
			||||||
 | 
					            "qty" : 25
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "qty" : 8.2796763649358063,
 | 
				
			||||||
 | 
					            "date" : "2021-03-14 01:13:00 +0000"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "date" : "2021-03-14 01:14:00 +0000",
 | 
				
			||||||
 | 
					            "qty" : 17.719295979506633
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "date" : "2021-03-14 01:15:00 +0000",
 | 
				
			||||||
 | 
					            "qty" : 17.719295979506636
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "date" : "2021-03-14 01:16:00 +0000",
 | 
				
			||||||
 | 
					            "qty" : 7.2817316760509208
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "name" : "step_count",
 | 
				
			||||||
 | 
					        "units" : "count"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -78,7 +78,7 @@ services:
 | 
				
			|||||||
    restart: always
 | 
					    restart: always
 | 
				
			||||||
    container_name: cleanslate-client
 | 
					    container_name: cleanslate-client
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - '2010:3000'
 | 
					      - '2006:3000'
 | 
				
			||||||
    depends_on:
 | 
					    depends_on:
 | 
				
			||||||
      - cleanslate-database
 | 
					      - cleanslate-database
 | 
				
			||||||
      - cleanslate-graphql-server
 | 
					      - cleanslate-graphql-server
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ services:
 | 
				
			|||||||
    image: ghcr.io/majorpeter/atomic-tracker:latest
 | 
					    image: ghcr.io/majorpeter/atomic-tracker:latest
 | 
				
			||||||
    container_name: reachableceo-atomichabits
 | 
					    container_name: reachableceo-atomichabits
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "2008:8080"
 | 
					      - "2005:8080"
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - tsys-atomichabits:/config
 | 
					      - tsys-atomichabits:/config
 | 
				
			||||||
    restart: always
 | 
					    restart: always
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,22 +38,22 @@
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "Reactive Resume",
 | 
					            "name": "Reactive Resume",
 | 
				
			||||||
            "category": "",
 | 
					            "category": "",
 | 
				
			||||||
            "url": "http://CharlesDevServer.knel.net:2007",
 | 
					            "url": "http://CharlesDevServer.knel.net:2003",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "Atomic Habits",
 | 
					            "name": "Atomic Habits",
 | 
				
			||||||
            "category": "",
 | 
					            "category": "",
 | 
				
			||||||
            "url": "http://CharlesDevServer.knel.net:2008"
 | 
					            "url": "http://CharlesDevServer.knel.net:2005"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "Cleanslate",
 | 
					            "name": "Cleanslate",
 | 
				
			||||||
            "category": "",
 | 
					            "category": "",
 | 
				
			||||||
            "url": "http://CharlesDevServer.knel.net:2010"
 | 
					            "url": "http://CharlesDevServer.knel.net:2006"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "name": "Apple Health Exporter",
 | 
					            "name": "Apple Health Exporter",
 | 
				
			||||||
            "category": "",
 | 
					            "category": "",
 | 
				
			||||||
            "url": "http://CharlesDevServer.knel.net:4000",
 | 
					            "url": "http://CharlesDevServer.knel.net:2007",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "notes": [
 | 
					    "notes": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,7 +52,7 @@ services:
 | 
				
			|||||||
    container_name: tsys-reactiveresume-app
 | 
					    container_name: tsys-reactiveresume-app
 | 
				
			||||||
    restart: unless-stopped
 | 
					    restart: unless-stopped
 | 
				
			||||||
    ports:
 | 
					    ports:
 | 
				
			||||||
      - "2007:3000"
 | 
					      - "2003:3000"
 | 
				
			||||||
    depends_on:
 | 
					    depends_on:
 | 
				
			||||||
      - reactiveresume-postgres
 | 
					      - reactiveresume-postgres
 | 
				
			||||||
      - reactiveresume-minio
 | 
					      - reactiveresume-minio
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user