├── __init__.py ├── runtime.txt ├── personal_dashboard ├── __init__.py ├── quotes.py ├── chess.py ├── personal_info_template.py ├── todoist.py ├── spotify.py ├── withings.py ├── books.py ├── articles.py ├── toggl.py ├── meditation.py ├── moves.py └── rescuetime.py ├── Procfile ├── .env ├── static ├── favicon.ico ├── js │ └── canvasjs │ │ ├── assets │ │ ├── fonts │ │ │ ├── roboto-v15-latin-300.woff │ │ │ ├── roboto-v15-latin-500.woff │ │ │ ├── roboto-v15-latin-700.woff │ │ │ ├── roboto-v15-latin-300.woff2 │ │ │ ├── roboto-v15-latin-500.woff2 │ │ │ ├── roboto-v15-latin-700.woff2 │ │ │ ├── roboto-v15-latin-regular.woff │ │ │ ├── roboto-v15-latin-regular.woff2 │ │ │ └── font-roboto.css │ │ ├── font-awesome │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── less │ │ │ │ ├── screen-reader.less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── core.less │ │ │ │ ├── stacked.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── path.less │ │ │ │ ├── animated.less │ │ │ │ └── mixins.less │ │ │ ├── scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _screen-reader.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── font-awesome.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _animated.scss │ │ │ │ └── _mixins.scss │ │ │ └── HELP-US-OUT.txt │ │ ├── economy-dashboard │ │ │ ├── images │ │ │ │ └── paper-texture.png │ │ │ └── styles.css │ │ ├── jquery.scrollspeed │ │ │ ├── jquery.scrollspeed.min.js │ │ │ └── jquery.scrollspeed.js │ │ ├── jquery.inview │ │ │ └── jquery.inview.min.js │ │ ├── no-ui-slider │ │ │ └── nouislider.min.css │ │ └── web-analytics │ │ │ └── style.css │ │ ├── instruction.txt │ │ ├── canvasjs-non-commercial-1.9..textClipping │ │ ├── examples │ │ ├── 04-pie-chart │ │ │ ├── basic-pie-chart.html │ │ │ └── pie-chart-with-index-label.html │ │ ├── 06-doughnut-chart │ │ │ ├── basic-doughnut-chart.html │ │ │ ├── doughnut-chart-with-explode-on-click-disabled.html │ │ │ └── doughnut-chart-with-index-label.html │ │ ├── 03-area-chart │ │ │ ├── area-chart-with-opacity.html │ │ │ ├── area-chart-with-line-thickness.html │ │ │ ├── basic-area-chart.html │ │ │ ├── area-chart-with-date-time-axis.html │ │ │ └── area-chart-with-xy-zoom-type.html │ │ ├── 07-spline-chart │ │ │ ├── basic-spline-chart.html │ │ │ └── spline-chart-with-marker-customization.html │ │ ├── 20-range-area-chart │ │ │ ├── basic-range-area-chart.html │ │ │ └── range-area-with-null-data.html │ │ ├── 01-line-chart │ │ │ ├── line-chart-with-stripline.html │ │ │ ├── line-chart-with-null(empty)-data.html │ │ │ ├── line-chart-with-line-customization.html │ │ │ ├── basic-line-chart.html │ │ │ ├── line-chart-with-zooming.html │ │ │ ├── line-chart-with-date-time-axis.html │ │ │ ├── line-chart-with-xy-zoom-type.html │ │ │ └── multi-series-line-chart.html │ │ ├── 05-bar-chart │ │ │ ├── basic-bar-chart.html │ │ │ └── bar-chart-with-stripline.html │ │ ├── 21-range-spline-area-chart │ │ │ ├── basic-range-spline-area_-chart.html │ │ │ └── range-spline-area-with-line-chart.html │ │ ├── 17-spline-area-chart │ │ │ ├── spline-area-chart-with-stripline.html │ │ │ └── basic-spline-area-chart.html │ │ ├── 25 -jquery-plugin │ │ │ └── basic-jquery-line-chart.html │ │ ├── 02-column-chart │ │ │ ├── column-chart-with-index-label-customization.html │ │ │ ├── basic-column-chart.html │ │ │ └── multi-series-column-chart.html │ │ ├── 24-stacked-column-100-chart │ │ │ ├── basic-stacked-column-100-chart.html │ │ │ └── stacked-column-100-with-index-label.html │ │ ├── 18-range-bar-chart │ │ │ ├── basic-range-bar-chart.html │ │ │ └── range-bar-with-index-label.html │ │ ├── 15-step-line-chart │ │ │ ├── basic-step-line-chart.html │ │ │ └── multi-series-step-line-chart.html │ │ ├── 16-step-area-chart │ │ │ └── basic-step-area-chart.html │ │ ├── 26-some-common-options-across-all-chart-types │ │ │ ├── export-chart.html │ │ │ ├── culture.html │ │ │ ├── chart-with-animation.html │ │ │ ├── hide-and-unhide-data-series-from-legend.html │ │ │ └── combination-charts.html │ │ ├── 19-range-column-chart │ │ │ ├── basic-range-column-chart.html │ │ │ └── range-column-with-index-label.html │ │ ├── 08-bubble-chart │ │ │ ├── basic-bubble-chart.html │ │ │ └── bubble-chart-with-tooltip-customization.html │ │ ├── 13-candlestick-chart │ │ │ ├── candle-stick-chart-with-rising-color-and-tooltip-customization.html │ │ │ └── basic-candle-stick-chart.html │ │ ├── 09-stacked-column-chart │ │ │ ├── basic-stacked-column-chart.html │ │ │ └── stacked-column-with-total-shown-on-top.html │ │ ├── 14-ohlc(stock)-chart │ │ │ ├── basic-ohlc-chart.html │ │ │ └── ohlc-chart-with-tooltip-customization.html │ │ ├── 23-stacked-bar-100-chart │ │ │ ├── basic-stacked-bar-100-chart.html │ │ │ └── stacked-bar-100-with-index-label.html │ │ ├── 22-stacked-area-100-chart │ │ │ ├── stacked-area-100-with-index-label.html │ │ │ └── basic-stacked-area-100-chart.html │ │ ├── 12-scatter-chart │ │ │ ├── basic-scatter-chart.html │ │ │ └── multi-series-scatter-chart.html │ │ ├── 10-stacked-bar-chart │ │ │ ├── basic-stacked-bar-chart.html │ │ │ └── stacked-bar-chart-with-index-label.html │ │ └── 11-stacked-area-chart │ │ │ ├── basic -stacked-area-chart.html │ │ │ └── stacked-area-chart-with-shared-tooltip.html │ │ └── license.txt └── css │ └── styles.css ├── manage.py ├── config.py ├── models.py ├── requirements.txt ├── .gitignore └── README.md /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6.3 2 | -------------------------------------------------------------------------------- /personal_dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app:app 2 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL="postgresql://localhost/qself_dashboard" 2 | APP_SETTINGS="config.DevelopmentConfig" -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-300.woff -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-500.woff -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-700.woff -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-300.woff2 -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-500.woff2 -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-700.woff2 -------------------------------------------------------------------------------- /static/js/canvasjs/instruction.txt: -------------------------------------------------------------------------------- 1 | For standalone version include canvasjs.min.js 2 | For jQuery version include jquery.canvasjs.min.js 3 | 4 | ** DO NOT include both the files ** -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-regular.woff -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/roboto-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/fonts/roboto-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /static/js/canvasjs/canvasjs-non-commercial-1.9..textClipping: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/canvasjs-non-commercial-1.9..textClipping -------------------------------------------------------------------------------- /static/js/canvasjs/assets/economy-dashboard/images/paper-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/economy-dashboard/images/paper-texture.png -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Andreilys/personal_dashboard/HEAD/static/js/canvasjs/assets/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/HELP-US-OUT.txt: -------------------------------------------------------------------------------- 1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, 2 | Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, 3 | comprehensive icon sets or copy and paste your own. 4 | 5 | Please. Check it out. 6 | 7 | -Dave Gandy 8 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask 3 | from flask_script import Manager 4 | from flask_migrate import Migrate, MigrateCommand 5 | 6 | from app import app, db 7 | 8 | 9 | app.config.from_object(os.environ['APP_SETTINGS']) 10 | 11 | migrate = Migrate(app, db) 12 | manager = Manager(app) 13 | 14 | manager.add_command('db', MigrateCommand) 15 | 16 | 17 | if __name__ == '__main__': 18 | manager.run() 19 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: (-@fa-li-width + (4em / 14)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | basedir = os.path.abspath(os.path.dirname(__file__)) 3 | 4 | 5 | class Config(object): 6 | DEBUG = False 7 | TESTING = False 8 | CSRF_ENABLED = True 9 | SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL'] 10 | 11 | class ProductionConfig(Config): 12 | DEBUG = False 13 | 14 | 15 | class StagingConfig(Config): 16 | DEVELOPMENT = True 17 | DEBUG = True 18 | 19 | 20 | class DevelopmentConfig(Config): 21 | DEVELOPMENT = True 22 | DEBUG = True 23 | 24 | 25 | class TestingConfig(Config): 26 | TESTING = True 27 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /personal_dashboard/quotes.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | class Quote(): 4 | def __init__(self): 5 | #Quote generated from https://quotes.rest/#!/qod/get_qod_categories 6 | headers = { "Accept": "application/json"} 7 | r = requests.get('https://quotes.rest/qod', headers=headers) 8 | #Quotes has a daily limit that we exceed unforunately 9 | try: 10 | self.content = r.json()["contents"]["quotes"][0]["quote"] 11 | self.author = r.json()["contents"]["quotes"][0]["author"] 12 | except: 13 | self.content = "Everything we hear is an opinion, not a fact. Everything we see is a perspective, not the truth." 14 | self.author = "Marcus Aurelius" 15 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { float: left; } 11 | .@{fa-css-prefix}-pull-right { float: right; } 12 | 13 | .@{fa-css-prefix} { 14 | &.@{fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.@{fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .@{fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .@{fa-css-prefix}-rotate-90, 15 | :root .@{fa-css-prefix}-rotate-180, 16 | :root .@{fa-css-prefix}-rotate-270, 17 | :root .@{fa-css-prefix}-flip-horizontal, 18 | :root .@{fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /personal_dashboard/chess.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | #The chess class is used to get the number of wins/draws/losses (get_games()) as 4 | # well as the current rating for blitz chess on lichess.org 5 | class Chess(): 6 | def __init__(self): 7 | r = requests.get('https://en.lichess.org/api/user/andreilys').json() 8 | self.draw = r['count']['draw'] 9 | self.win = r['count']['win'] 10 | self.loss =r['count']['loss'] 11 | self.rating = r['perfs']['blitz']['rating'] 12 | self.url = r['url'] 13 | 14 | 15 | def get_games(self): 16 | chess_dict = {"wins" : self.win, "losses" : self.loss, "draws" : self.draw} 17 | return chess_dict 18 | 19 | 20 | def get_rating(self): 21 | return "Blitz Chess rating: {1}".format(self.url, self.rating) 22 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/04-pie-chart/basic-pie-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 29 | 30 | CanvasJS Example 31 | 32 | 33 |
34 | 35 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/06-doughnut-chart/basic-doughnut-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 29 | 30 | CanvasJS Example 31 | 32 | 33 |
34 | 35 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'), 8 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'), 10 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'), 11 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/jquery.scrollspeed/jquery.scrollspeed.min.js: -------------------------------------------------------------------------------- 1 | !function(a){jQuery.scrollSpeed=function(b,c,d){var k,l,m,e=a(document),f=a(window),g=a("html, body"),h=d||"default",i=0,j=!1;return!(window.navigator.msPointerEnabled||a("#isIE").length>0)&&void f.on("mousewheel DOMMouseScroll",function(a){var d=a.originalEvent.wheelDeltaY,n=a.originalEvent.detail;return k=e.height()>f.height(),l=e.width()>f.width(),j=!0,k&&(m=f.height(),(d<0||n>0)&&(i=i+m>=e.height()?i:i+=b),(d>0||n<0)&&(i=i<=0?0:i-=b),g.stop().animate({scrollTop:i},c,h,function(){j=!1})),l&&(m=f.width(),(d<0||n>0)&&(i=i+m>=e.width()?i:i+=b),(d>0||n<0)&&(i=i<=0?0:i-=b),g.stop().animate({scrollLeft:i},c,h,function(){j=!1})),!1}).on("scroll",function(){k&&!j&&(i=f.scrollTop()),l&&!j&&(i=f.scrollLeft())}).on("resize",function(){k&&!j&&(m=f.height()),l&&!j&&(m=f.width())})},jQuery.easing.default=function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c}}(jQuery); -------------------------------------------------------------------------------- /static/js/canvasjs/license.txt: -------------------------------------------------------------------------------- 1 | * 2 | * @preserve CanvasJS HTML5 & JavaScript Charts - v1.9.10 GA - https://canvasjs.com/ 3 | * Copyright 2017 fenopix 4 | * 5 | * --------------------- License Information -------------------- 6 | * CanvasJS is a commercial product which requires purchase of license. Without a commercial license you can use it for evaluation purposes for upto 30 days. Please refer to the following link for further details. 7 | * https://canvasjs.com/license-canvasjs/ 8 | * 9 | * 10 | *---------------------Free for Non-Commercial Use-------------------- 11 | * 12 | *For non-commercial purposes you can use the software for free under Creative Commons Attribution-NonCommercial 3.0 License. 13 | *A credit Link is added to the bottom right of the chart which should be preserved. Refer to the following link for further details on the same. 14 | * http://creativecommons.org/licenses/by-nc/3.0/deed.en_US 15 | * 16 | * -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from app import db 3 | from sqlalchemy import Column, Integer, DateTime, JSON 4 | 5 | class GoalCompletion(db.Model): 6 | __tablename__ = 'dates_completed_goals' 7 | id = db.Column(db.Integer, primary_key=True) 8 | date = db.Column(JSON) 9 | 10 | def __init__(self, date): 11 | self.date = date 12 | 13 | def __repr__(self): 14 | return ''.format(self.id) 15 | 16 | 17 | class PersonalData(db.Model): 18 | __tablename__ = 'personal_data' 19 | id = db.Column(db.Integer, primary_key=True) 20 | personal_data_dictionary = db.Column(JSON) 21 | created_date = db.Column(DateTime, default=datetime.datetime.utcnow) 22 | 23 | def __init__(self, personal_data_dictionary, created_date): 24 | self.personal_data_dictionary = personal_data_dictionary 25 | self.created_date = created_date 26 | 27 | def __repr__(self): 28 | return ''.format(self.id) 29 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/03-area-chart/area-chart-with-opacity.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | 33 | CanvasJS Example 34 | 35 | 36 |
37 | 38 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/07-spline-chart/basic-spline-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 29 | 30 | CanvasJS Example 31 | 32 | 33 | 34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/20-range-area-chart/basic-range-area-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 33 | 34 | CanvasJS Example 35 | 36 | 37 |
38 |
39 | 40 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/03-area-chart/area-chart-with-line-thickness.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 33 | 34 | CanvasJS Example 35 | 36 | 37 |
38 |
39 | 40 | -------------------------------------------------------------------------------- /personal_dashboard/personal_info_template.py: -------------------------------------------------------------------------------- 1 | lRESCUETIME_API_KEY = RESCUETIME_API_KEY 2 | TODOIST = {"email" : EMAIL, "password" : PASSWORD} 3 | SPOTIFY = {"email" : EMAIL, 'client_id' : CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': WHAT_YOU_ENTER_ON_SPOTIFY} 4 | MOVES_KEYS = {'client_id':CLIENT_ID, 'client_secret':CLIENT_SECRET, 'redirect_uri': WHAT_YOU_ENTER_ON_MOVES} 5 | WITHINGS_KEYS = {'API_KEY': API_KEY, 'API_SECRET' : API_SECRET} 6 | TOGGL_API_TOKEN = TOGGL_API_TOKEN 7 | STEPS_GOAL = STEP_GOAL 8 | FOCUS_GOAL = FOCUS_GOAL 9 | UNPRODUCTIVITY_GOAL = UNPRODUCTIVITY_GOAL 10 | #Get the JSON share link from wakatime, similar to this (https://wakatime.com/share/@0c62f2ad-9fa5-43c7-a08f-7b1562918a7d/43cd4128-5361-43db-b51b-d965e3c575a5.json): 11 | WAKATIME_CODING_TIME = JSON_LINK 12 | WAKATIME_CODING_TYPE = JSON_LINK 13 | MEDITATION_GOAL = 30 14 | STEPS_GOAL = 5000 15 | UNPRODUCTIVITY_GOAL = 1 16 | INSIGHT_MEDITATION_LOGIN = {'username' : 'user', 'password' : 'pw'} 17 | GOODREADS_INFO = {'user_id': '1', 'api_key' : '1'} 18 | MEDIUM_USER = 'a' 19 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/line-chart-with-stripline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 38 | 39 | CanvasJS Example 40 | 41 | 42 |
43 | 44 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/line-chart-with-null(empty)-data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | 33 | CanvasJS Example 34 | 35 | 36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/line-chart-with-line-customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | 33 | CanvasJS Example 34 | 35 | 36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/05-bar-chart/basic-bar-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 34 | 35 | CanvasJS Example 36 | 37 | 38 |
39 |
40 | 41 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/07-spline-chart/spline-chart-with-marker-customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | CanvasJS Example 32 | 33 | 34 | 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/basic-line-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 37 | 38 | CanvasJS Example 39 | 40 | 41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/03-area-chart/basic-area-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 37 | 38 | CanvasJS Example 39 | 40 | 41 | 42 |
43 | 44 | 45 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ajax==3.0.0 2 | alembic==0.9.6 3 | aniso8601==1.3.0 4 | arrow==0.10.0 5 | BitBucket==0.4a0 6 | blinker==1.4 7 | certifi==2017.7.27.1 8 | chardet==3.0.4 9 | click==6.7 10 | decorator==4.1.2 11 | django-appconf==1.0.2 12 | enum-compat==0.0.2 13 | eventlet==0.20.1 14 | Flask==0.12.3 15 | Flask-DebugToolbar==0.10.1 16 | Flask-Migrate==2.1.1 17 | Flask-RESTful==0.3.6 18 | Flask-Script==2.0.6 19 | Flask-SocketIO==2.9.2 20 | Flask-SQLAlchemy==2.3.1 21 | gevent==1.2.1 22 | gevent-websocket==0.9.5 23 | greenlet==0.4.12 24 | gunicorn==19.7.1 25 | idna==2.6 26 | itsdangerous==0.24 27 | Jinja2>=2.10.1 28 | Lector==0.0.3 29 | Mako==1.0.7 30 | MarkupSafe==0.23 31 | moves==0.1 32 | nokia==0.4.0 33 | numpy==1.13.1 34 | oauthlib==2.0.4 35 | pandas==0.20.3 36 | psycopg2==2.7.3.1 37 | pyperclip==1.5.27 38 | python-dateutil==2.6.1 39 | python-dotenv==0.7.1 40 | python-editor==1.0.3 41 | python-engineio==1.7.0 42 | python-socketio==1.8.0 43 | pytodoist==2.1.0 44 | pytz==2017.2 45 | rauth==0.7.3 46 | requests==2.20.0 47 | requests-oauth==0.4.1 48 | requests-oauthlib==0.8.0 49 | six==1.10.0 50 | spotipy==2.4.4 51 | SQLAlchemy>=1.3.0 52 | urllib3>=1.23 53 | Werkzeug==0.11.15 54 | xmltodict==0.11.0 55 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/21-range-spline-area-chart/basic-range-spline-area_-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 35 | 36 | CanvasJS Example 37 | 38 | 39 |
40 |
41 | 42 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/04-pie-chart/pie-chart-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 34 | 35 | CanvasJS Example 36 | 37 | 38 |
39 | 40 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/06-doughnut-chart/doughnut-chart-with-explode-on-click-disabled.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 31 | 32 | CanvasJS Example 33 | 34 | 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/17-spline-area-chart/spline-area-chart-with-stripline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 39 | 40 | CanvasJS Example 41 | 42 | 43 |
44 | 45 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/25 -jquery-plugin/basic-jquery-line-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CanvasJS Example - jQuery Line Chart 5 | 6 | 7 | 8 | 37 | 38 | 39 |
40 | 41 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/02-column-chart/column-chart-with-index-label-customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 34 | 35 | CanvasJS Example 36 | 37 | 38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/fonts/font-roboto.css: -------------------------------------------------------------------------------- 1 | /* roboto-300 - latin */ 2 | @font-face { 3 | font-family: 'Roboto'; 4 | font-style: normal; 5 | font-weight: 300; 6 | src: local('Roboto Light'), local('Roboto-Light'), url('roboto-v15-latin-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('../../fonts/roboto-v15-latin-300.woff') format('woff'); 7 | /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 8 | } 9 | /* roboto-regular - latin */ 10 | @font-face { 11 | font-family: 'Roboto'; 12 | font-style: normal; 13 | font-weight: 400; 14 | src: local('Roboto'), local('Roboto-Regular'), url('roboto-v15-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('../../fonts/roboto-v15-latin-regular.woff') format('woff'); 15 | /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 16 | } 17 | /* roboto-500 - latin */ 18 | @font-face { 19 | font-family: 'Roboto'; 20 | font-style: normal; 21 | font-weight: 500; 22 | src: local('Roboto Medium'), local('Roboto-Medium'), url('roboto-v15-latin-500.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ url('../../fonts/roboto-v15-latin-500.woff') format('woff'); 23 | /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 24 | } -------------------------------------------------------------------------------- /static/js/canvasjs/examples/24-stacked-column-100-chart/basic-stacked-column-100-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 39 | 40 | CanvasJS Example 41 | 42 | 43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/line-chart-with-zooming.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 39 | 40 | CanvasJS Example 41 | 42 | 43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/02-column-chart/basic-column-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 34 | 35 | CanvasJS Example 36 | 37 | 38 | 39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/18-range-bar-chart/basic-range-bar-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 39 | 40 | CanvasJS Example 41 | 42 | 43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/03-area-chart/area-chart-with-date-time-axis.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 34 | 35 | CanvasJS Example 36 | 37 | 38 |
39 |
40 | 41 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/05-bar-chart/bar-chart-with-stripline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 41 | 42 | CanvasJS Example 43 | 44 | 45 |
46 |
47 | 48 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/06-doughnut-chart/doughnut-chart-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 39 | 40 | CanvasJS Example 41 | 42 | 43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/line-chart-with-date-time-axis.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 37 | 38 | CanvasJS Example 39 | 40 | 41 |
42 |
43 | 44 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/18-range-bar-chart/range-bar-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 41 | 42 | CanvasJS Example 43 | 44 | 45 |
46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/line-chart-with-xy-zoom-type.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 45 | 46 | CanvasJS Example 47 | 48 | 49 |
50 |
51 | 52 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/03-area-chart/area-chart-with-xy-zoom-type.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 45 | 46 | CanvasJS Example 47 | 48 | 49 |
50 |
51 | 52 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/15-step-line-chart/basic-step-line-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 40 | 41 | CanvasJS Example 42 | 43 | 44 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/jquery.inview/jquery.inview.min.js: -------------------------------------------------------------------------------- 1 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a(require("jquery")):a(jQuery)}(function(a){function i(){var b,c,d={height:f.innerHeight,width:f.innerWidth};return d.height||(b=e.compatMode,(b||!a.support.boxModel)&&(c="CSS1Compat"===b?g:e.body,d={height:c.clientHeight,width:c.clientWidth})),d}function j(){return{top:f.pageYOffset||g.scrollTop||e.body.scrollTop,left:f.pageXOffset||g.scrollLeft||e.body.scrollLeft}}function k(){if(b.length){var e=0,f=a.map(b,function(a){var b=a.data.selector,c=a.$element;return b?c.find(b):c});for(c=c||i(),d=d||j();ed.top&&l.topd.left&&l.left 2 | 3 | 4 | 41 | 42 | CanvasJS Example 43 | 44 | 45 | 46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/26-some-common-options-across-all-chart-types/export-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 38 | 39 | 40 | CanvasJS Example 41 | 42 | 43 |
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/17-spline-area-chart/basic-spline-area-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 40 | 41 | CanvasJS Example 42 | 43 | 44 |
45 |
46 | 47 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/26-some-common-options-across-all-chart-types/culture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 43 | 44 | CanvasJS Example 45 | 46 | 47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/19-range-column-chart/basic-range-column-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 45 | 46 | CanvasJS Example 47 | 48 | 49 |
50 |
51 | 52 | -------------------------------------------------------------------------------- /personal_dashboard/todoist.py: -------------------------------------------------------------------------------- 1 | from pytodoist import todoist 2 | from .personal_info import TODOIST 3 | 4 | #Todoist class has three methods: get_past_seven_completed_task which 5 | #returns the number of tasks completed in the last 7 days, get_today_completed_task 6 | # which returns the number of tasks finished today, and get_total_tasks which 7 | # returns the number of total tasks left 8 | class Todoist(): 9 | def __init__(self): 10 | #This is meant as a safe guard against too many todoist API calls 11 | try: 12 | self.user = todoist.login(TODOIST["email"], TODOIST["password"]) 13 | self.productivity = self.user.get_productivity_stats() 14 | self.days_items = self.productivity["days_items"] 15 | except: 16 | self.days_items = [{"total_completed" : 0}] 17 | 18 | 19 | def get_past_seven_completed_tasks(self): 20 | completed_tasks = 0 21 | for i in range(len(self.days_items)): 22 | completed_tasks += self.days_items[i]["total_completed"] 23 | return completed_tasks 24 | 25 | 26 | def get_daily_completed_tasks(self): 27 | return self.days_items[0]["total_completed"] 28 | 29 | 30 | def get_total_tasks(self): 31 | number_of_tasks = 0 32 | #This is meant as a safeguard against too many Todoist API calls 33 | try: 34 | projects = self.user.get_projects() 35 | for project in projects: 36 | tasks = project.get_tasks() 37 | number_of_tasks += len(tasks) 38 | return number_of_tasks 39 | except: 40 | return 0 41 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/08-bubble-chart/basic-bubble-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 37 | 38 | CanvasJS Example 39 | 40 | 41 |
42 |
43 | 44 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/20-range-area-chart/range-area-with-null-data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 47 | 48 | CanvasJS Example 49 | 50 | 51 |
52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/26-some-common-options-across-all-chart-types/chart-with-animation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 43 | 44 | CanvasJS Example 45 | 46 | 47 |
48 | 49 | 50 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/13-candlestick-chart/candle-stick-chart-with-rising-color-and-tooltip-customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 46 | 47 | CanvasJS Example 48 | 49 | 50 |
51 |
52 | 53 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/02-column-chart/multi-series-column-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 57 | 58 | CanvasJS Example 59 | 60 | 61 | 62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/09-stacked-column-chart/basic-stacked-column-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 60 | 61 | CanvasJS Example 62 | 63 | 64 | 65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/14-ohlc(stock)-chart/basic-ohlc-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 44 | 45 | CanvasJS Example 46 | 47 | 48 |
49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/15-step-line-chart/multi-series-step-line-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 61 | 62 | CanvasJS Example 63 | 64 | 65 | 66 |
67 | 68 | 69 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/23-stacked-bar-100-chart/basic-stacked-bar-100-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 60 | 61 | CanvasJS Example 62 | 63 | 64 |
65 |
66 | 67 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/09-stacked-column-chart/stacked-column-with-total-shown-on-top.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 53 | 54 | CanvasJS Example 55 | 56 | 57 |
58 | 59 | -------------------------------------------------------------------------------- /personal_dashboard/spotify.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import spotipy 3 | import spotipy.util as util 4 | try: 5 | from .personal_info import SPOTIFY 6 | except: 7 | from personal_info import SPOTIFY 8 | 9 | 10 | 11 | #The spotify class has two methods: get_monthly_top_tracks() which returns 12 | # the top 5 tracks for that month, the get_monthly_top_artists() returns the top 13 | # three artists for that month 14 | class Spotify(): 15 | def __init__(self): 16 | #This requires a users prompting first time its run, afterwards there will 17 | # be a cached file called .cache-spotifyemail, make sure this cached file 18 | # is pushed to whatever cloud based application otherwise it will break 19 | self.token = util.prompt_for_user_token(SPOTIFY['email'],'user-top-read',client_id=SPOTIFY['client_id'],client_secret=SPOTIFY['client_secret'],redirect_uri=SPOTIFY['redirect_uri']) 20 | self.sp = spotipy.Spotify(auth=self.token) 21 | 22 | def get_monthly_top_tracks(self): 23 | top_tracks = self.sp.current_user_top_tracks(limit=5, time_range='short_term') 24 | track_list = [] 25 | for item in top_tracks['items']: 26 | track_name = item["name"] 27 | artist_name = item["artists"][0]["name"] 28 | external_url = item["external_urls"]["spotify"] 29 | track_list.append("{1} by {2}
".format(external_url, track_name, artist_name)) 30 | return ''.join(track_list) 31 | 32 | def get_monthly_top_artists(self): 33 | top_artists = self.sp.current_user_top_artists(limit=3, time_range='short_term') 34 | artist_list = [] 35 | for item in top_artists['items']: 36 | artist = item['name'] 37 | external_url = item['external_urls']['spotify'] 38 | artist_list.append("{1}
".format(external_url, artist)) 39 | return artist_list 40 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #personal_info.py 2 | __pycache__/ 3 | *.py[cod] 4 | venv 5 | __init__.py 6 | personal_info.py 7 | migrations/ 8 | .cache-12al104@queensu.ca 9 | moves_data.pkl 10 | nokia_data.pkl 11 | 12 | # Byte-compiled / optimized / DLL files 13 | __pycache__/ 14 | *.py[cod] 15 | *$py.class 16 | 17 | # C extensions 18 | *.so 19 | 20 | # Distribution / packaging 21 | .Python 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *.cover 58 | .hypothesis/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | .static_storage/ 67 | .media/ 68 | local_settings.py 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # celery beat schedule file 90 | celerybeat-schedule 91 | 92 | # SageMath parsed files 93 | *.sage.py 94 | 95 | # Environments 96 | .env 97 | .venv 98 | env/ 99 | venv/ 100 | ENV/ 101 | env.bak/ 102 | venv.bak/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/font-awesome/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon() { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration 7 | font-size: inherit; // can't have font-size inherit on line above, so need to override 8 | text-rendering: auto; // optimizelegibility throws things off #1094 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | 12 | } 13 | 14 | @mixin fa-icon-rotate($degrees, $rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 16 | -webkit-transform: rotate($degrees); 17 | -ms-transform: rotate($degrees); 18 | transform: rotate($degrees); 19 | } 20 | 21 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/26-some-common-options-across-all-chart-types/hide-and-unhide-data-series-from-legend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 70 | 71 | CanvasJS Example 72 | 73 | 74 |
75 |
76 | 77 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/22-stacked-area-100-chart/stacked-area-100-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 75 | 76 | CanvasJS Example 77 | 78 | 79 |
80 |
81 | 82 | 83 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/08-bubble-chart/bubble-chart-with-tooltip-customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 54 | 55 | CanvasJS Example 56 | 57 | 58 |
59 |
60 | 61 | -------------------------------------------------------------------------------- /personal_dashboard/withings.py: -------------------------------------------------------------------------------- 1 | from nokia import NokiaAuth, NokiaApi 2 | import pickle 3 | try: 4 | from .personal_info import WITHINGS_KEYS 5 | except: 6 | from personal_info import WITHINGS_KEYS 7 | 8 | #The withings class opens up the pickle file and saves it as the weight unless 9 | # that file doesn't exist than the user needs to authenticate and save a new pickle 10 | #file 11 | class Withings(): 12 | def __init__(self): 13 | try: 14 | with open('personal_dashboard/nokia_data.pkl', 'rb') as pickle_file: 15 | nokia = pickle.load(pickle_file) 16 | self.measures = nokia.get_measures() 17 | measures = nokia.get_measures(limit=1) 18 | self.weight = round(float(measures[0].weight)*2.20462, 2) 19 | pickle_file.close() 20 | except: 21 | auth = NokiaAuth(WITHINGS_KEYS['API_KEY'], WITHINGS_KEYS['API_SECRET']) 22 | authorize_url = auth.get_authorize_url() 23 | print("Go to %s allow the app and copy your oauth_verifier" % authorize_url) 24 | oauth_verifier = input('Please enter your oauth_verifier: ') 25 | creds = auth.get_credentials(oauth_verifier) 26 | client = NokiaApi(creds) 27 | with open('personal_dashboard/nokia_data.pkl', 'wb') as output: 28 | pickle.dump(client, output, pickle.HIGHEST_PROTOCOL) 29 | self.measures = client.get_measures() 30 | measures = client.get_measures(limit=1) 31 | #Convert Kg to Lbs 32 | self.weight = round(float(measures[0].weight)*2.20462, 2) 33 | 34 | 35 | 36 | def get_weight_line_data(self): 37 | weight_data = [] 38 | dates = [] 39 | for data in self.measures: 40 | if data.weight: 41 | year = str(data.date).split('-')[0] 42 | month = str(data.date).split('-')[1] 43 | day = str(data.date).split('-')[2].split("T")[0] 44 | date = month + "-" + day + "-" + year 45 | dates.append(date) 46 | weight_data.append(round(float(data.weight)*2.20462, 2)) 47 | weight_data.reverse() 48 | dates.reverse() 49 | return weight_data, dates 50 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/12-scatter-chart/basic-scatter-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 71 | 72 | CanvasJS Example 73 | 74 | 75 | 76 |
77 | 78 | 79 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/26-some-common-options-across-all-chart-types/combination-charts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 86 | 87 | CanvasJS Example 88 | 89 | 90 |
91 |
92 | 93 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/10-stacked-bar-chart/basic-stacked-bar-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 76 | 77 | CanvasJS Example 78 | 79 | 80 |
81 |
82 | 83 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/13-candlestick-chart/basic-candle-stick-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 55 | 56 | CanvasJS Example 57 | 58 | 59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/19-range-column-chart/range-column-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 75 | 76 | CanvasJS Example 77 | 78 | 79 |
80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/01-line-chart/multi-series-line-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 91 | 92 | CanvasJS Example 93 | 94 | 95 |
96 |
97 | 98 | 99 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/12-scatter-chart/multi-series-scatter-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 98 | 99 | CanvasJS Example 100 | 101 | 102 |
103 |
104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /static/css/styles.css: -------------------------------------------------------------------------------- 1 | .canvasjs-chart-credit { 2 | display: none; 3 | } 4 | 5 | .quote, .list { 6 | text-align: center; 7 | padding: 20px; 8 | } 9 | 10 | .data_table { 11 | text-align: center; 12 | margin: auto; 13 | } 14 | 15 | 16 | #loading { 17 | margin:auto; 18 | text-align: center; 19 | } 20 | 21 | div, h6 { 22 | visibility: hidden; 23 | } 24 | 25 | strong { 26 | color: black; 27 | } 28 | 29 | body { 30 | background-color: #000000; 31 | color: #ffffff; 32 | font-family: "arial", Helvetica, Arial, sans-serif; 33 | font-size: 16px; 34 | line-height: 1.5; 35 | padding-bottom: 3.5rem; 36 | padding-top: 3.5rem; 37 | } 38 | 39 | h1, h2, h3, h4, h5, h6 { 40 | font-weight: 300; 41 | } 42 | 43 | canvas { 44 | font-family: "Roboto", Helvetica, Arial, sans-serif; 45 | } 46 | 47 | #quote_author { 48 | color:white; 49 | } 50 | 51 | hr { 52 | border-top: 1px solid #727273; 53 | margin-bottom: 2rem; 54 | margin-top: 2rem; 55 | } 56 | 57 | .align-center { 58 | text-align: center; 59 | } 60 | 61 | #meditation_doughnut, #pomodoro_doughnut, #unproductivity_doughnut { 62 | height: 280px; 63 | margin-top: 1rem; 64 | width: 100%; 65 | } 66 | 67 | #cal-heatmap { 68 | width:50%; 69 | margin: 0 auto; 70 | } 71 | 72 | .container { 73 | margin-bottom: 4%; 74 | } 75 | 76 | 77 | .card-header { 78 | background-color: #37474F; 79 | border-radius: 0 !important; 80 | color: white; 81 | margin-bottom: 0; 82 | padding: 1rem; 83 | } 84 | 85 | .card-block { 86 | border: 1px solid #cccccc; 87 | height: 500px; 88 | } 89 | 90 | .shadow { 91 | box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 92 | 0 1px 18px 0 rgba(0, 0, 0, 0.12), 93 | 0 3px 5px -1px rgba(0, 0, 0, 0.2); 94 | } 95 | 96 | #moves_places { 97 | color:black; 98 | } 99 | 100 | 101 | 102 | #loading, 103 | #loading:after { 104 | border-radius: 50%; 105 | width: 10em; 106 | height: 10em; 107 | } 108 | #loading { 109 | margin: 60px auto; 110 | font-size: 10px; 111 | position: relative; 112 | text-indent: -9999em; 113 | border-top: 1.1em solid rgba(255, 255, 255, 0.2); 114 | border-right: 1.1em solid rgba(255, 255, 255, 0.2); 115 | border-bottom: 1.1em solid rgba(255, 255, 255, 0.2); 116 | border-left: 1.1em solid #ffffff; 117 | -webkit-transform: translateZ(0); 118 | -ms-transform: translateZ(0); 119 | transform: translateZ(0); 120 | -webkit-animation: load8 1.1s infinite linear; 121 | animation: load8 1.1s infinite linear; 122 | } 123 | @-webkit-keyframes load8 { 124 | 0% { 125 | -webkit-transform: rotate(0deg); 126 | transform: rotate(0deg); 127 | } 128 | 100% { 129 | -webkit-transform: rotate(360deg); 130 | transform: rotate(360deg); 131 | } 132 | } 133 | @keyframes load8 { 134 | 0% { 135 | -webkit-transform: rotate(0deg); 136 | transform: rotate(0deg); 137 | } 138 | 100% { 139 | -webkit-transform: rotate(360deg); 140 | transform: rotate(360deg); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/24-stacked-column-100-chart/stacked-column-100-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 99 | 100 | CanvasJS Example 101 | 102 | 103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/jquery.scrollspeed/jquery.scrollspeed.js: -------------------------------------------------------------------------------- 1 | // Plugin: jQuery.scrollSpeed 2 | // Source: github.com/nathco/jQuery.scrollSpeed 3 | // Author: Nathan Rutzky 4 | // Update: 1.0.2 5 | 6 | (function($) { 7 | 8 | jQuery.scrollSpeed = function(step, speed, easing) { 9 | 10 | var $document = $(document), 11 | $window = $(window), 12 | $body = $('html, body'), 13 | option = easing || 'default', 14 | root = 0, 15 | scroll = false, 16 | scrollY, 17 | scrollX, 18 | view; 19 | 20 | if (window.navigator.msPointerEnabled || ($('#isIE').length > 0) ) // custom modification to detect IE 21 | 22 | return false; 23 | 24 | $window.on('mousewheel DOMMouseScroll', function(e) { 25 | 26 | var deltaY = e.originalEvent.wheelDeltaY, 27 | detail = e.originalEvent.detail; 28 | scrollY = $document.height() > $window.height(); 29 | scrollX = $document.width() > $window.width(); 30 | scroll = true; 31 | 32 | if (scrollY) { 33 | 34 | view = $window.height(); 35 | 36 | if (deltaY < 0 || detail > 0) 37 | 38 | root = (root + view) >= $document.height() ? root : root += step; 39 | 40 | if (deltaY > 0 || detail < 0) 41 | 42 | root = root <= 0 ? 0 : root -= step; 43 | 44 | $body.stop().animate({ 45 | 46 | scrollTop: root 47 | 48 | }, speed, option, function() { 49 | 50 | scroll = false; 51 | 52 | }); 53 | } 54 | 55 | if (scrollX) { 56 | 57 | view = $window.width(); 58 | 59 | if (deltaY < 0 || detail > 0) 60 | 61 | root = (root + view) >= $document.width() ? root : root += step; 62 | 63 | if (deltaY > 0 || detail < 0) 64 | 65 | root = root <= 0 ? 0 : root -= step; 66 | 67 | $body.stop().animate({ 68 | 69 | scrollLeft: root 70 | 71 | }, speed, option, function() { 72 | 73 | scroll = false; 74 | 75 | }); 76 | } 77 | 78 | return false; 79 | 80 | }).on('scroll', function() { 81 | 82 | if (scrollY && !scroll) root = $window.scrollTop(); 83 | if (scrollX && !scroll) root = $window.scrollLeft(); 84 | 85 | }).on('resize', function() { 86 | 87 | if (scrollY && !scroll) view = $window.height(); 88 | if (scrollX && !scroll) view = $window.width(); 89 | 90 | }); 91 | }; 92 | 93 | jQuery.easing.default = function (x,t,b,c,d) { 94 | 95 | return -c * ((t=t/d-1)*t*t*t - 1) + b; 96 | }; 97 | 98 | })(jQuery); -------------------------------------------------------------------------------- /personal_dashboard/books.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | import datetime 4 | try: 5 | from .personal_info import GOODREADS_INFO 6 | except: 7 | from personal_info import GOODREADS_INFO 8 | 9 | class Books(): 10 | def __init__(self): 11 | self.goodreads_url = f'https://www.goodreads.com/review/list/{GOODREADS_INFO["user_id"]}.xml?key={GOODREADS_INFO["api_key"]}&v=2&shelf=read&per_page=80&page=1' 12 | self.response = requests.get(self.goodreads_url) 13 | self.soup = BeautifulSoup(self.response.text, 'html.parser') 14 | self.reviews = self.soup.find('reviews') 15 | 16 | 17 | # Returns the past n books you've read 18 | def get_past_n_books(self, number_of_books): 19 | books = [] 20 | urls = [] 21 | for review in self.reviews.find_all('review'): 22 | for title in review.find('title'): 23 | books.append(title) 24 | for url in review.find('url'): 25 | urls.append(url) 26 | books_and_url = "Recently Read Books:
    " 27 | for i in range(number_of_books): 28 | books_and_url += f'
  1. {books[i]}
  2. ' 29 | books_and_url += "
" 30 | return books_and_url 31 | 32 | # Returns a dict containing your reading history for the past 6 months 33 | def get_past_reading_history(self): 34 | dates = [] 35 | data_values = [] 36 | month_dict = {'Jan' : 1, 'Feb' : 2, 'Mar' : 3, 'Apr' : 4, 'May' : 5, 'Jun' : 6, 'Jul' : 7, 'Aug' : 8, 'Sep' : 9, 'Oct' : 10, 'Nov' : 11, 'Dec' : 12} 37 | books_dict = {} 38 | seven_months_ago = self.get_n_months_ago(6) 39 | for review in self.reviews.find_all('review'): 40 | for date_read in review.find('date_added'): 41 | month = date_read.split(" ")[1] 42 | year = date_read.split(" ")[5] 43 | date = datetime.date(int(year), month_dict[month], 1) 44 | if date >= seven_months_ago: 45 | date = str(date) 46 | if date in books_dict: 47 | books_dict[date] += 1 48 | else: 49 | books_dict[date] = 1 50 | #If there was a month that I didn't finish a book, I want to set it to 0 51 | for some_month in range(7): 52 | some_month_ago = str(self.get_n_months_ago(some_month)) 53 | if some_month_ago not in books_dict: 54 | books_dict[some_month_ago] = 0 55 | sorted_keys = sorted(books_dict.keys()) 56 | for key in sorted_keys: 57 | year = key.split("-")[0] 58 | month = key.split("-")[1] 59 | dates.append(f'{month}/{year}') 60 | data_values.append(books_dict[key]) 61 | reading_history = [{"label" : "Number of Books Read", "backgroundColor": "#33702a", "data" : data_values}] 62 | return reading_history, dates 63 | 64 | 65 | # Helper function which returns n month ago from now 66 | def get_n_months_ago(self, month): 67 | past_month = datetime.datetime.now().month - month 68 | past_year = datetime.datetime.now().year 69 | if past_month < 1: 70 | past_month = 12 + past_month 71 | past_year = past_year - 1 72 | return datetime.date(past_year, past_month, 1) -------------------------------------------------------------------------------- /static/js/canvasjs/assets/no-ui-slider/nouislider.min.css: -------------------------------------------------------------------------------- 1 | /*! nouislider - 8.5.1 - 2016-04-24 16:00:30 */ 2 | 3 | 4 | .noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative;direction:ltr}.noUi-base{width:100%;height:100%;position:relative;z-index:1}.noUi-origin{position:absolute;right:0;top:0;left:0;bottom:0}.noUi-handle{position:relative;z-index:1}.noUi-stacking .noUi-handle{z-index:10}.noUi-state-tap .noUi-origin{-webkit-transition:left .3s,top .3s;transition:left .3s,top .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-base,.noUi-handle{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;left:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;left:-6px;top:-17px}.noUi-background{background:#FAFAFA;box-shadow:inset 0 1px 1px #f0f0f0}.noUi-connect{background:#3FB8AF;box-shadow:inset 0 0 3px rgba(51,51,51,.45);-webkit-transition:background 450ms;transition:background 450ms}.noUi-origin{border-radius:2px}.noUi-target{border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-target.noUi-connect{box-shadow:inset 0 0 3px rgba(51,51,51,.45),0 3px 6px -5px #BBB}.noUi-draggable{cursor:w-resize}.noUi-vertical .noUi-draggable{cursor:n-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect,[disabled].noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-origin{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-large,.noUi-marker-sub{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate3d(-50%,50%,0);transform:translate3d(-50%,50%,0)}.noUi-marker-horizontal.noUi-marker{margin-left:-1px;width:2px;height:5px}.noUi-marker-horizontal.noUi-marker-sub{height:10px}.noUi-marker-horizontal.noUi-marker-large{height:15px}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate3d(0,-50%,0);transform:translate3d(0,-50%,0);padding-left:25px}.noUi-marker-vertical.noUi-marker{width:5px;height:2px;margin-top:-1px}.noUi-marker-vertical.noUi-marker-sub{width:10px}.noUi-marker-vertical.noUi-marker-large{width:15px}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;padding:5px;text-align:center}.noUi-horizontal .noUi-handle-lower .noUi-tooltip{top:-32px}.noUi-horizontal .noUi-handle-upper .noUi-tooltip{bottom:-32px}.noUi-vertical .noUi-handle-lower .noUi-tooltip{left:120%}.noUi-vertical .noUi-handle-upper .noUi-tooltip{right:120%} -------------------------------------------------------------------------------- /personal_dashboard/articles.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import datetime 4 | try: 5 | from .personal_info import MEDIUM_USER 6 | except: 7 | from personal_info import MEDIUM_USER 8 | 9 | 10 | class Articles(): 11 | def __init__(self): 12 | self.medium_url = f'https://medium.com/@{MEDIUM_USER}/' 13 | self.response = requests.get(self.medium_url + 'latest?format=json') 14 | self.response_dict = json.loads( 15 | self.response.text.split('])}while(1);')[1]) 16 | self.posts = self.response_dict['payload']['references']['Post'] 17 | 18 | 19 | # Returns the past n articles you've writte 20 | def get_past_n_articles(self, number_of_articles): 21 | titles = [] 22 | urls = [] 23 | for key in self.posts: 24 | titles.append(self.posts[key]['title']) 25 | urls.append(self.medium_url + self.posts[key]['id']) 26 | titles_and_url = "Recently Written Articles:
    " 28 | for i in range(number_of_articles): 29 | titles_and_url += f'
  1. {titles[i]}
  2. ' 30 | titles_and_url += "
" 31 | return titles_and_url 32 | 33 | 34 | # Returns a dict containing your reading history for the past n months 35 | def get_past_n_month_writing_history(self, months): 36 | dates = [] 37 | data_values = [] 38 | month_dict = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 39 | 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} 40 | article_count_dict = {} 41 | n_months_ago = self.get_n_months_ago(months) 42 | for key in self.posts: 43 | timestamp = int(self.posts[key]['firstPublishedAt']) / 1000 44 | month_published = datetime.datetime.fromtimestamp(timestamp).month 45 | year_published = datetime.datetime.fromtimestamp(timestamp).year 46 | dt = datetime.date(int(year_published), int(month_published), 1) 47 | if dt >= n_months_ago: 48 | dt = str(dt) 49 | if dt in article_count_dict: 50 | article_count_dict[dt] += 1 51 | else: 52 | article_count_dict[dt] = 1 53 | #If there was a month that I didn't finish an article, I want to set it to 0 54 | for some_month in range(7): 55 | some_month_ago = str(self.get_n_months_ago(some_month)) 56 | if some_month_ago not in article_count_dict: 57 | article_count_dict[some_month_ago] = 0 58 | sorted_keys = sorted(article_count_dict.keys()) 59 | for key in sorted_keys: 60 | year = key.split("-")[0] 61 | month = key.split("-")[1] 62 | dates.append(f'{month}/{year}') 63 | data_values.append(article_count_dict[key]) 64 | writing_history = [{"label": "Number of Articles Written", 65 | "backgroundColor": "#33702a", "data": data_values}] 66 | return writing_history, dates 67 | 68 | 69 | # Helper function which returns n month ago from now 70 | def get_n_months_ago(self, month): 71 | past_month = datetime.datetime.now().month - month 72 | past_year = datetime.datetime.now().year 73 | if past_month < 1: 74 | past_month = 12 + past_month 75 | past_year = past_year - 1 76 | return datetime.date(past_year, past_month, 1) 77 | 78 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/22-stacked-area-100-chart/basic-stacked-area-100-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 100 | 101 | CanvasJS Example 102 | 103 | 104 |
105 |
106 | 107 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/10-stacked-bar-chart/stacked-bar-chart-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 116 | 117 | CanvasJS Example 118 | 119 | 120 |
121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/11-stacked-area-chart/basic -stacked-area-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 100 | 101 | CanvasJS Example 102 | 103 | 104 |
105 |
106 | 107 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/web-analytics/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #ffffff; 3 | font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; 4 | font-size: 14px; 5 | padding-top: 64px; 6 | } 7 | 8 | #header { 9 | background-color: #34495e; 10 | height: 64px; 11 | margin-bottom: 0; 12 | padding: 0; 13 | position: fixed; 14 | } 15 | 16 | #header .container-fluid { 17 | padding: 8px; 18 | } 19 | 20 | #header .navbar-header { 21 | margin: 0; 22 | } 23 | 24 | #header #sidebar-toggle-button { 25 | color: #fff; 26 | cursor: pointer; 27 | display: inline-block; 28 | font-size: 17px; 29 | margin: 0 15px; 30 | padding: 12px 6px; 31 | } 32 | 33 | #header .brand { 34 | display: inline-block; 35 | padding: 12px 16px 12px 0; 36 | } 37 | 38 | #header .brand a { 39 | color: #fff; 40 | font-size: 24px; 41 | line-height: 1; 42 | text-decoration: none; 43 | } 44 | 45 | #sidebar { 46 | background-color: #eceef1; 47 | bottom: 0; 48 | display: block; 49 | left: 0; 50 | overflow-x: hidden; 51 | overflow-y: auto; 52 | padding: 0; 53 | position: fixed; 54 | top: 64px; 55 | width: 264px; 56 | z-index: 1000; 57 | } 58 | 59 | #sidebar > ul { 60 | padding-bottom: 24px; 61 | } 62 | 63 | #sidebar > ul > li { 64 | margin: 0; 65 | } 66 | 67 | #sidebar > ul > li > a { 68 | color: #20252b; 69 | display: block; 70 | padding: 12px 24px; 71 | text-decoration: none; 72 | } 73 | 74 | #sidebar > ul > li > a > span { 75 | font-size: 17px; 76 | font-weight: 500; 77 | margin-left: 17px; 78 | } 79 | 80 | #sidebar > ul > li > a:hover, 81 | #sidebar > ul > li > a.active { 82 | border-right: 4px solid #34495e; 83 | } 84 | 85 | 86 | #sidebar > ul > .divider { 87 | background-color: #ccc; 88 | height: 1px; 89 | margin: 0; 90 | overflow: hidden; 91 | } 92 | 93 | #page-content-wrapper > .container-fluid { 94 | padding-top: 0.75rem; 95 | } 96 | 97 | /*------ sidebar toggle ------*/ 98 | .sidebar-toggle { 99 | display: none !important; 100 | } 101 | 102 | .page-content-toggle { 103 | margin-left: inherit !important; 104 | } 105 | 106 | @media (min-width: 1600px) { 107 | #sidebar { 108 | display: none; 109 | } 110 | .sidebar-toggle { 111 | display: block !important; 112 | } 113 | .page-content-toggle { 114 | margin-left: 264px !important; 115 | } 116 | } 117 | /*------ /sidebar toggle ------*/ 118 | 119 | /*------ real time page charts ------*/ 120 | #users-device-doughnut-chart, 121 | #users-medium-pie-chart, 122 | #users-category-pie-chart, 123 | #page-views-per-second-column-chart, 124 | #page-views-per-minute-column-chart, 125 | #users-state-bar-chart { 126 | height: 300px; 127 | } 128 | /*------ /real time page charts ------*/ 129 | 130 | /*------ overview page charts ------*/ 131 | #revenue-spline-area-chart, 132 | #annual-revenue-by-category-pie-chart, 133 | #monthly-revenue-by-category-column-chart { 134 | height: 300px; 135 | } 136 | 137 | #visitors-chart, 138 | #users-spline-chart { 139 | height: 320px; 140 | } 141 | /*------ /overview page charts ------*/ 142 | 143 | .card { 144 | background-color: inherit; 145 | border: none; 146 | } 147 | 148 | .card-title { 149 | border-bottom: 1px solid; 150 | padding-bottom: 10px; 151 | font-size: 26px; 152 | font-weight: 300; 153 | } 154 | 155 | #revenue-tag, .custom-tag { 156 | background-color: #d9695f; 157 | border-radius: 0; 158 | font-weight: 400; 159 | vertical-align: top; 160 | } 161 | 162 | .custom-tag { 163 | font-size: 100%; 164 | } 165 | 166 | #visitors-chart-back-button { 167 | padding: 0.3rem 0.6rem; 168 | } 169 | 170 | #visitors-chart-tag { 171 | position: absolute; 172 | } -------------------------------------------------------------------------------- /personal_dashboard/toggl.py: -------------------------------------------------------------------------------- 1 | import requests 2 | try: 3 | from .personal_info import TOGGL_API_TOKEN 4 | except: 5 | from personal_info import TOGGL_API_TOKEN 6 | import base64 7 | import decimal 8 | import time 9 | import math 10 | import datetime as DT 11 | 12 | 13 | #The Toggl class is meant to get the pomodoro sessions using the get_pomodoros method 14 | class Toggl(): 15 | def __init__(self): 16 | headers = { 17 | "Authorization": "", 18 | "Content-Type": "application/json", 19 | "Accept": "*/*", 20 | "User-Agent": "python/urllib", 21 | } 22 | authHeader = TOGGL_API_TOKEN + ":" + "api_token" 23 | authHeader = "Basic " + str(base64.b64encode(authHeader.encode()))[2:] 24 | authHeader = authHeader.replace('\'', "") 25 | headers['Authorization'] = authHeader 26 | url = 'https://www.toggl.com/api/v8/time_entries?api_token='+TOGGL_API_TOKEN 27 | self.json = requests.get(url, headers=headers).json() 28 | 29 | 30 | def get_pomodoros(self, dates): 31 | pomodoroDict = {} 32 | for index, time_entry in enumerate(self.json): 33 | end_time = time_entry['start'].split('T')[0] 34 | if end_time in dates: 35 | time_in_hours = time_entry['duration']/60/60 36 | try: 37 | description = time_entry['description'] 38 | except: 39 | description = "No description" 40 | if description != 'Pomodoro Break': 41 | if time_in_hours < 0: 42 | epoch_time = int(time.time()) 43 | time_in_hours = (epoch_time + time_entry['duration'])/60/60 44 | if description in pomodoroDict: 45 | pomodoroDict[description] += round(time_in_hours, 2) 46 | else: 47 | pomodoroDict[description] = round(time_in_hours, 2) 48 | return pomodoroDict 49 | 50 | 51 | def get_daily_pomodoros(self): 52 | dates = [time.strftime("%Y-%m-%d")] 53 | return self.get_pomodoros(dates) 54 | 55 | 56 | def get_past_seven_days_pomodoros(self): 57 | dates = [] 58 | today = DT.date.today() 59 | for i in range(7): 60 | date = today - DT.timedelta(days=i) 61 | date = date.strftime("%Y-%m-%d") 62 | dates.append(date) 63 | return self.get_pomodoros(dates) 64 | 65 | # for use in the create_toggl_bar function in scripts.js 66 | def get_daily_week_view(self): 67 | toggl_data = [] 68 | pomodoros = self.get_past_seven_days_pomodoros() 69 | #This is to loop through all the different descriptions in Toggl 70 | for pomodoro in pomodoros: 71 | counter = 6 72 | weekly_data = [pomodoro] 73 | #We loop through a given week to find the times for this specific Toggl description 74 | for i in range(7): 75 | today = DT.date.today() 76 | date = today - DT.timedelta(days=counter) 77 | counter -= 1 78 | date = date.strftime("%Y-%m-%d") 79 | year = date.split('-')[0] 80 | month = date.split('-')[1] 81 | day = date.split('-')[2] 82 | #Here we need to get the pomodoros for that specific day 83 | pomodoros = self.get_pomodoros(date) 84 | for second_pomodoro in pomodoros: 85 | if second_pomodoro == pomodoro: 86 | daily_data = {'year' : year, 'month' : month, 'day' : day, 'value' : pomodoros[pomodoro]} 87 | weekly_data.append(daily_data) 88 | else: 89 | daily_data = {'year': year, 'month': month, 'day': day, 'value': 0} 90 | weekly_data.append(daily_data) 91 | toggl_data.append(weekly_data) 92 | return toggl_data -------------------------------------------------------------------------------- /static/js/canvasjs/examples/11-stacked-area-chart/stacked-area-chart-with-shared-tooltip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 127 | 128 | CanvasJS Example 129 | 130 | 131 |
132 |
133 | 134 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/14-ohlc(stock)-chart/ohlc-chart-with-tooltip-customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 76 | 77 | CanvasJS Example 78 | 79 | 80 |
81 |
82 | 83 | -------------------------------------------------------------------------------- /personal_dashboard/meditation.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import requests 3 | from datetime import timedelta 4 | try: 5 | from .personal_info import INSIGHT_MEDITATION_LOGIN 6 | except: 7 | from personal_info import INSIGHT_MEDITATION_LOGIN 8 | 9 | 10 | class Meditation(): 11 | '''The meditation class gets meditation data from 12 | insight timer and returns it in a dictionary that maps 13 | the date to the time meditated''' 14 | def __init__(self): 15 | self.insight_csv_url = "https://profile.insighttimer.com/sessions/export" 16 | self.meditation_data = self.get_insight_data() 17 | 18 | def get_insight_data(self): 19 | ''' Retrieves the meditation data in a CSV format from insight timer''' 20 | session = requests.Session() 21 | session.auth = (INSIGHT_MEDITATION_LOGIN['username'], INSIGHT_MEDITATION_LOGIN['password']) 22 | session.post(self.insight_csv_url) 23 | response = session.get(self.insight_csv_url) 24 | # This gets rid of the header and deletes the empty space 25 | return response.text.split("\n")[2:-1] 26 | 27 | 28 | def get_weekly_meditation_data(self): 29 | '''Takes in the response from insight timer CSV and returns 30 | a meditation dictionary containing the dates and times for the past week''' 31 | meditation_dict = {} 32 | dates = [] 33 | data_values = [] 34 | today = datetime.date.today() 35 | week_ago = today - datetime.timedelta(days=7) 36 | for row in self.meditation_data: 37 | meditation_date = self.get_date(row) 38 | meditation_date_key = row.split(" ")[0] 39 | if meditation_date <= week_ago: 40 | break 41 | meditation_time = row.split(",")[1].split(":")[1] 42 | if meditation_date_key in meditation_dict: 43 | meditation_dict[meditation_date_key] += int(meditation_time) 44 | else: 45 | meditation_dict[meditation_date_key] = int(meditation_time) 46 | # If there was a day with no meditation, the day should be set it to 0 47 | for some_day in range(1, 7): 48 | some_day_ago = str(self.get_n_days_ago(some_day)) 49 | if some_day_ago not in meditation_dict: 50 | meditation_dict[some_day_ago] = 0 51 | sorted_keys = sorted(meditation_dict.keys()) 52 | for key in sorted_keys: 53 | month = key.split("/")[0] 54 | day = key.split("/")[1] 55 | dates.append(f'{day}/{month}') 56 | data_values.append(meditation_dict[key]) 57 | meditation_history = [{"label" : "Minutes Meditated", "backgroundColor": "#33702a", "data" : data_values}] 58 | return meditation_history, dates 59 | 60 | 61 | def get_n_days_ago(self, day): 62 | today = datetime.datetime.now() 63 | days_ago = datetime.timedelta(days = day) 64 | day = today - days_ago 65 | day = str(day).split(" ")[0] 66 | date = day.split("-") 67 | year = date[0] 68 | month = date[1] 69 | day = date[2] 70 | date_formatted = f'{month}/{day}/{year}' 71 | return date_formatted 72 | 73 | 74 | def get_current_meditation_time(self): 75 | '''Takes in the response from insight timer CSV and returns 76 | how much you meditated today.''' 77 | meditation_dict = {} 78 | today = datetime.date.today() 79 | total_meditation = 0 80 | for row in self.meditation_data: 81 | meditation_date = self.get_date(row) 82 | if meditation_date != today: 83 | break 84 | meditation_time = row.split(",")[1].split(":")[1] 85 | total_meditation += int(meditation_time) 86 | return total_meditation 87 | 88 | 89 | def get_date(self, row): 90 | '''Takes in a insight row and returns only the meditation 91 | date in YYYY-MM-DD datetime format''' 92 | date_and_time = row.split(",")[0] 93 | month, day, year = map(int, date_and_time.split(" ")[0].split("/")) 94 | hour, mins, _ = map(int, date_and_time.split(" ")[1].split(":")) 95 | # minus 5 hours to get EST time since insight timer is in UTC 96 | converted_date = datetime.datetime( 97 | year, month, day, hour, mins) - timedelta(hours=5) 98 | # Since we only care about the date, thats the only thing we return 99 | return datetime.date(converted_date.year, converted_date.month, converted_date.day) 100 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/21-range-spline-area-chart/range-spline-area-with-line-chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 136 | 137 | CanvasJS Example 138 | 139 | 140 |
141 |
142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Personal Dashboard 2 | ![](https://i.imgur.com/6eOElpe.png) 3 | 4 | This is a personal dashboard I built to aggregate and visualize all the different tracking services that track my life. The dashboard is updated in real time and can be found at https://qself-dashboard.herokuapp.com/ 5 | 6 | Right now it visualizes: 7 | - Time spent online, along with productivtity and unproductivity (Rescuetime) 8 | - Steps and location (Moves) 9 | - Chess (lichess.com) 10 | - Weight (Withings smart scale) 11 | - Pomodoro time (Toggl.com) 12 | - Todo's (Todoist) 13 | - Time spent programming (Wakatime) 14 | - Top artists and songs for the month (Spotify) 15 | 16 | ## Table of Contents 17 | 18 | - [Installation and Setup](#installation) 19 | - [Support](#support) 20 | - [Contributing](#contributing) 21 | - [Licensing](#licensing) 22 | 23 | 24 | ## Installation and Setup 25 | 26 | First make sure you have python3 installed and are using it throughout, since some of the syntax doesn't port over to python2. 27 | 28 | ```sh 29 | git clone https://github.com/Andreilys/personal_dashboard 30 | ``` 31 | Install [Postgres](https://www.postgresql.org/download/) 32 | Now, enter your API credentials into personal\_info\_template.py and rename it to personal\_info.py. Run these commands next: 33 | 34 | ```sh 35 | psql 36 | CREATE DATABASE qself_dashboard; 37 | \q 38 | Run python manage.py db init 39 | python manage.py db migrate 40 | python manage.py db upgrade 41 | #you should now be able to run the application 42 | python3 app.py 43 | ``` 44 | 45 | You'll now need to give permission to Spotify for the app to access your data (Which will save a .emailCache file) followed by withings (make sure you are just copy+pasting oauth\_verifier and not including oauth\_token) and Moves which should then create permanent pickle files in your personal\_dashboard folder. 46 | 47 | You're done setting it up locally! 48 | 49 | **Setting up on Heroku**
50 | With the application working locally, you might be interested in hosting it on the cloud, I prefer heroku so i'll walk you through instructions on how to set up there. First things first, make sure you register an account on heroku and download their [Heroku Toolbelt](https://devcenter.heroku.com/articles/heroku-cli), afterwards login with heroku login. 51 | 52 | Once logged in run these commands 53 | ```sh 54 | heroku create (your app name) 55 | git remote add pro git@heroku.com:YOUR\_APP\_NAME.git 56 | git add personal\_dashboard/personal\_info.py -f 57 | git add migrations -f 58 | git add nokia\_data.pkl -f 59 | git add moves\_data.pkl -f 60 | git commit -m "adding personal info and migrations folder" (may need to login to github for this) 61 | git push pro master 62 | heroku config:set APP\_SETTINGS=config.ProductionConfig 63 | heroku addons:create heroku-postgresql:hobby-dev --app (YOUR\_APP\_NAME) 64 | heroku run python manage.py db upgrade --app (YOUR\_APP\_NAME) 65 | ``` 66 | 67 | Thats it! The other thing you should consider doing is setting the timezone of your heroku app so that everything is in sync, you can do this with the following command (Substituting the timezone with your own) 68 | 69 | ```sh 70 | heroku config:add TZ="America/Los\_Angeles" 71 | ``` 72 | ## Support 73 | 74 | Please [open an issue](https://github.com/Andreilys/personal_dashboard/issues/new) for support. 75 | 76 | 77 | ## Contributing 78 | 79 | Please contribute using [Github Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits, and [open a pull request](https://github.com/andreilys/personal_dashboard/compare). 80 | 81 | ## Licensing 82 | 83 | CanvasJS is a commercial product and commercial usage of CanvasJS requires you to purchase a license. Without a commercial license, you can use it for evaluation purposes only. Please refer to the following link for further details: https://canvasjs.com 84 | 85 | Copyright (c) 2017, Andrei Lyskov 86 | 87 | Permission is hereby granted, free of charge, to any person obtaining a copy 88 | of this software and associated documentation files (the "Software"), to deal 89 | in the Software without restriction, including without limitation the rights 90 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 91 | copies of the Software, and to permit persons to whom the Software is 92 | furnished to do so, subject to the following conditions: 93 | 94 | The above copyright notice and this permission notice shall be included in all 95 | copies or substantial portions of the Software. 96 | 97 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 98 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 99 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 100 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 101 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 102 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 103 | SOFTWARE. 104 | -------------------------------------------------------------------------------- /static/js/canvasjs/examples/23-stacked-bar-100-chart/stacked-bar-100-with-index-label.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 179 | 180 | CanvasJS Example 181 | 182 | 183 |
184 |
185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /static/js/canvasjs/assets/economy-dashboard/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-image: url("images/paper-texture.png"); 3 | } 4 | blockquote { 5 | border-left: 2px solid #065f66; 6 | margin-left: 5px; 7 | padding-left: 10px; 8 | } 9 | blockquote p { 10 | display: inline; 11 | } 12 | .author { 13 | color: #8793A4; 14 | font-family: 'Bookman Old Style', serif; 15 | font-size: 20px; 16 | } 17 | .author::before { 18 | content: '\2014 \00A0'; 19 | } 20 | .header { 21 | padding-top: 15px; 22 | } 23 | .heading { 24 | color: #065f66; 25 | font-family: Garamond, serif; 26 | font-size: 50px; 27 | font-weight: 900; 28 | } 29 | .quotes { 30 | font-size: 23px; 31 | margin-left: 10px; 32 | } 33 | .quotes-size { 34 | color: #065f66; 35 | font-size: 27px !important; 36 | } 37 | .chart-title { 38 | color: #065F66; 39 | font-size: 25px; 40 | font-weight: 600; 41 | height: 40px; 42 | margin-left: 15px; 43 | margin-bottom: 35px; 44 | padding: 5px; 45 | text-align: left; 46 | } 47 | .life-expec-chart-info { 48 | padding-top: 10px; 49 | top: 25px; 50 | } 51 | .play-btn { 52 | cursor: pointer; 53 | font-size: 30px !important; 54 | margin-left: 40%; 55 | } 56 | .pause-btn { 57 | cursor: pointer; 58 | font-size: 30px !important; 59 | margin-left: 40%; 60 | } 61 | .noUi-connect { 62 | background: #c0392b; 63 | } 64 | .noUi-handle { 65 | border: 0px; 66 | box-shadow: inset 0 0 0px #c0392b,inset 0 0px 7px #c0392b,0 0px 6px -3px #c0392b; 67 | } 68 | .noUi-base { 69 | cursor: pointer; 70 | } 71 | .noUi-horizontal .noUi-handle { 72 | border-radius: 50%; 73 | background-color: #c0392b; 74 | cursor: pointer; 75 | height: 24px; 76 | left: -12px; 77 | top: -9px; 78 | width: 24px; 79 | } 80 | .noUi-handle::after, .noUi-handle::before { 81 | display: none; 82 | } 83 | .noUi-value-sub { 84 | color: #686868; 85 | font-size: 11; 86 | } 87 | .noUi-value.noUi-value-horizontal.noUi-value-sub { 88 | cursor: pointer; 89 | } 90 | .noUi-pips { 91 | color: #686868; 92 | } 93 | #year-slider { 94 | height: 8px; 95 | } 96 | .slider{ 97 | margin-left: 0%; 98 | margin-top: 12px; 99 | } 100 | .population-chart-info{ 101 | padding-top: 10px; 102 | top: 80px; 103 | } 104 | .chart-info-description { 105 | color: #878787; 106 | font-size: 18px; 107 | text-align: left; 108 | } 109 | .economy-definition { 110 | color: #878787; 111 | display: block; 112 | font-size: 23px; 113 | margin-bottom: 0px; 114 | margin-top: 10px; 115 | } 116 | .remove-population-charts-padding{ 117 | padding-left: 0px; 118 | padding-right: 0px; 119 | } 120 | .population-charts-title{ 121 | color: #065F66; 122 | font-size: 25px; 123 | font-weight: 600; 124 | height: 40px; 125 | margin-bottom: 35px; 126 | padding: 5px; 127 | text-align: left; 128 | } 129 | @media (min-width: 1020px) { 130 | .author { 131 | color: #8793A4; 132 | font-family: 'Bookman Old Style', serif; 133 | font-size: 20px; 134 | margin-left: 786px; 135 | } 136 | } 137 | @media (max-width: 885px) { 138 | .economy-definition { 139 | color: #878787; 140 | display: block; 141 | font-size: 20px; 142 | margin-bottom: 0px; 143 | margin-top: 10px; 144 | } 145 | } 146 | @media (min-width: 861px) and (max-width: 1018px) { 147 | .author { 148 | color: #8793A4; 149 | font-family: 'Bookman Old Style', serif; 150 | font-size: 18px; 151 | margin-left: 80%; 152 | } 153 | } 154 | @media (min-width: 547px) and (max-width: 880px) { 155 | .author { 156 | color: #8793A4; 157 | font-family: 'Bookman Old Style', serif; 158 | font-size: 18px; 159 | margin-left: 70%; 160 | } 161 | } 162 | @media (min-width: 359px) and (max-width: 547px) { 163 | .author { 164 | color: #8793A4; 165 | font-family: 'Bookman Old Style', serif; 166 | font-size: 18px; 167 | margin-left: 50%; 168 | } 169 | } 170 | @media (max-width: 359px) { 171 | .author { 172 | color: #8793A4; 173 | font-family: 'Bookman Old Style', serif; 174 | font-size: 18px; 175 | margin-left: 30%; 176 | } 177 | .heading { 178 | color: #065f66; 179 | font-family: Garamond, serif; 180 | font-size: 45px; 181 | font-weight: 900; 182 | line-height: 52px; 183 | } 184 | } 185 | @media (max-width: 509px) { 186 | .heading { 187 | color: #065f66; 188 | font-family: Garamond, serif; 189 | font-size: 45px; 190 | font-weight: 900; 191 | line-height: 52px; 192 | } 193 | } 194 | @media (max-width: 544px) { 195 | .slider { 196 | margin-left: 5%; 197 | margin-top: 12px; 198 | } 199 | } 200 | @media (min-width:701px) { 201 | #GDP-per-capita-bubble-chart { 202 | height: 500px; 203 | } 204 | } 205 | @media (max-width:700px) and (min-width: 501px) { 206 | #GDP-per-capita-bubble-chart { 207 | height: 400px; 208 | } 209 | } 210 | @media (max-width:500px) { 211 | #GDP-per-capita-bubble-chart { 212 | height: 300px; 213 | } 214 | } 215 | @media (max-width: 600px) { 216 | #population-line-chart { 217 | height: 300px; 218 | } 219 | #working-population-line-chart { 220 | height: 300px; 221 | } 222 | #merchandise-imports-exports-column-chart { 223 | height: 300px; 224 | } 225 | } 226 | @media (min-width: 601px) { 227 | #population-line-chart { 228 | height: 400px; 229 | } 230 | #working-population-line-chart { 231 | height: 400px; 232 | } 233 | } 234 | @media (max-width: 991px) and (min-width: 601px) { 235 | #merchandise-imports-exports-column-chart { 236 | height: 400px; 237 | } 238 | } 239 | @media (min-width: 992px) { 240 | #merchandise-imports-exports-column-chart { 241 | height: 500px; 242 | } 243 | } -------------------------------------------------------------------------------- /personal_dashboard/moves.py: -------------------------------------------------------------------------------- 1 | import requests 2 | try: 3 | from .personal_info import MOVES_KEYS 4 | except: 5 | from personal_info import MOVES_KEYS 6 | import pickle 7 | from statistics import mean 8 | from datetime import datetime 9 | 10 | #The moves class has the following methods: get_current_days_steps() which 11 | # returns the current days steps, get_average_past_seven_steps() which takes 12 | # the 7 past days of steps and returns the average. The other methods are mostly 13 | #helper methods 14 | class Moves(): 15 | def __init__(self): 16 | self.client_id = MOVES_KEYS['client_id'] 17 | self.client_secret = MOVES_KEYS['client_secret'] 18 | self.redirect_uri = MOVES_KEYS['redirect_uri'] 19 | self.auth_url = 'https://api.moves-app.com/oauth/v1/' 20 | self.base_url = 'https://api.moves-app.com/api/1.1/user/' 21 | try: 22 | with open('personal_dashboard/moves_data.pkl', 'rb') as pickle_file: 23 | moves = pickle.load(pickle_file) 24 | self.refresh_token = moves['refresh_token'] 25 | self.access_token = moves['access_token'] 26 | self.get_access_token() 27 | except: 28 | request_url = "https://api.moves-app.com/oauth/v1/authorize?response_type=code&client_id=" + MOVES_KEYS['client_id'] +"&scope=activity+location" 29 | print("Please go to this URL and authorize: " + request_url) 30 | auth_code = input("What is the authorization code in the code= slug? ") 31 | post_request = requests.post(self.auth_url + "access_token?grant_type=authorization_code&code=" + auth_code + "&client_id=" + self.client_id + 32 | "&client_secret=" + self.client_secret + "&redirect_uri="+ self.redirect_uri).json() 33 | self.access_token = post_request['access_token'] 34 | self.refresh_token = post_request['refresh_token'] 35 | moves = {"access_token" : self.access_token, "refresh_token" : self.refresh_token} 36 | with open('personal_dashboard/moves_data.pkl', 'wb') as output: 37 | pickle.dump(moves, output, pickle.HIGHEST_PROTOCOL) 38 | 39 | 40 | def get_access_token(self): 41 | data = requests.get(self.auth_url + 'tokeninfo?access_token=' + self.access_token) 42 | if data.status_code != 200: 43 | refresh = requests.post(self.auth_url + "access_token?grant_type=refresh_token&refresh_token=" + self.access_token + "&client_id=" + self.client_id + "&client_secret=" + client_secret).json() 44 | self.refresh_token = refresh["refresh_token"] 45 | self.access_token = refresh["access_token"] 46 | 47 | 48 | def get_current_days_steps(self): 49 | #This is meant as a catch in case no steps have been recorded today 50 | try: 51 | current_days_steps = requests.get(self.base_url + 'summary/daily?pastDays=1&access_token=' + self.access_token).json() 52 | step_count = 0 53 | for i in range(len(current_days_steps[0]["summary"])): 54 | try: 55 | step_count += current_days_steps[0]["summary"][i]["steps"] 56 | except: 57 | pass 58 | return step_count 59 | except Exception as e: 60 | return 0 61 | 62 | 63 | def get_past_seven_days_steps(self): 64 | past_seven_days_steps = requests.get(self.base_url + 'summary/daily?pastDays=8&access_token=' + self.access_token).json() 65 | past_seven_days_arr = [] 66 | # Minus one to only look at the past 7 days instead of including the current as well 67 | for index in range(len(past_seven_days_steps) - 1): 68 | try: 69 | day_object_length = len(past_seven_days_steps[index]["summary"]) 70 | except: 71 | day_object_length = 0 72 | for i in range(day_object_length): 73 | try: 74 | past_seven_days_arr.append(past_seven_days_steps[index]["summary"][i]["steps"]) 75 | except: 76 | past_seven_days_arr.append(0) 77 | return past_seven_days_arr 78 | 79 | 80 | def get_average_past_seven_steps(self): 81 | seven_days_steps_arr = self.get_past_seven_days_steps() 82 | try: 83 | average = round(mean(seven_days_steps_arr)) 84 | except: 85 | average = 0 86 | return average 87 | 88 | #Turn this method into a visualizaton for map 89 | def get_past_seven_days_places(self): 90 | past_seven_days_places = requests.get(self.base_url + 'places/daily?pastDays=7&access_token=' + self.access_token) 91 | past_seven_days_places_json = past_seven_days_places.json() 92 | # first 0 is for the length of places, second 0 is for length of segments 93 | past_seven_days_places_set = set() 94 | for i in range(len(past_seven_days_places_json)): 95 | try: 96 | segment_length = len(past_seven_days_places_json[i]['segments']) 97 | except: 98 | segment_length = 0 99 | for j in range(segment_length): 100 | try: 101 | past_seven_days_places_set.add(past_seven_days_places_json[i]['segments'][j]['place']['name']) 102 | except: 103 | continue 104 | return ', '.join(past_seven_days_places_set) 105 | 106 | #This function is used to return dates and a formatted list containing dictionaries 107 | # for use to create the steps_bar 108 | def get_daily_week_view(self): 109 | daily_steps_array = [] 110 | average_steps_array = [] 111 | avg = self.get_average_past_seven_steps() 112 | #fill array with 7 values of average 113 | for i in range(7): 114 | average_steps_array.append(avg) 115 | past_seven_days_steps = requests.get(self.base_url + 'summary/daily?pastDays=7&access_token=' + self.access_token).json() 116 | for steps in past_seven_days_steps: 117 | try: 118 | daily_steps_array.append(steps['summary'][0]['steps']) 119 | except: 120 | daily_steps_array.append(0) 121 | steps_data = [{"label" : "Daily Steps", "backgroundColor": "#33702a", "data" : daily_steps_array}, 122 | {"label" : "Average Steps", "backgroundColor": "#b30000", "data" : average_steps_array}] 123 | return steps_data 124 | -------------------------------------------------------------------------------- /personal_dashboard/rescuetime.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from time import strftime 3 | try: 4 | from .personal_info import RESCUETIME_API_KEY 5 | except: 6 | from personal_info import RESCUETIME_API_KEY 7 | import datetime as DT 8 | 9 | #This rescuetime class interacts with the rescuetime API allowing it to pull current day and weekly data 10 | # The methods that are used are the get_current_days_data() which returns the 11 | # hours of productive, unproductive hours and the top three sources and get_past_seven_days_data() 12 | # which returns 13 | class RescueTime: 14 | def __init__(self): 15 | self.key = RESCUETIME_API_KEY 16 | 17 | #This method returns a dictionary containing the top three contributing sources to rescuetime, 18 | # productive minutes and unproductive minutes spent up until now 19 | 20 | def get_current_days_data(self): 21 | try: 22 | response = requests.get( 23 | "https://www.rescuetime.com/anapi/data?key={0}&format=json".format(RESCUETIME_API_KEY)) 24 | if response.status_code == 502: 25 | daily_data = {"productive_hours": "NULL", 26 | "unproductive_hours": "NULL", "top_three_sources": "NULL"} 27 | return daily_data 28 | json = response.json()["rows"] 29 | # In the event that theres currently no daily data, return 0 30 | except: 31 | daily_data = {"productive_hours": 0, 32 | "unproductive_hours": 0, "top_three_sources": None} 33 | return daily_data 34 | top_three_sources = [] 35 | productive_hours = 0 36 | unproductive_hours = 0 37 | for index, data in enumerate(json): 38 | if index < 3: 39 | top_three_sources.append( 40 | str(data[3]).capitalize() + " - " + str(round(data[1]/60/60, 2))) 41 | if data[5] > 0: 42 | productive_hours += data[1]/60/60 43 | elif data[5] < 0: 44 | unproductive_hours += data[1]/60/60 45 | productive_hours = round(productive_hours, 2) 46 | unproductive_hours = round(unproductive_hours, 2) 47 | daily_data = {"productive_hours": productive_hours, "unproductive_hours": 48 | unproductive_hours, "top_three_sources": ", ".join(top_three_sources)} 49 | return daily_data 50 | 51 | # This method is a helper method because the request get method occasionally throews 52 | # an error by returning a None object 53 | 54 | def get_weekly_data(self): 55 | connected = False 56 | while not connected: 57 | try: 58 | today = DT.date.today() 59 | week_ago = today - DT.timedelta(days=7) 60 | response = requests.get("https://www.rescuetime.com/anapi/data?key={0}&perspective=rank&interval=week&restrict_begin={1}&restrict_end={2}&format=json".format( 61 | RESCUETIME_API_KEY, str(week_ago), str(today))) 62 | if response.status_code == 200: 63 | connected = True 64 | except: 65 | pass 66 | return response.json()["rows"] 67 | 68 | #This method returns a dictionary containing the top five contributing sources to rescuetime, 69 | # productive hours and unproductive hours in the last 7 days 70 | 71 | def get_past_seven_days_data(self): 72 | json = self.get_weekly_data() 73 | #This is meant as a safeguard in case resucetime has no weekly data 74 | if json == 0: 75 | weekly_data = {"productive_hours": 0, 76 | "unproductive_hours": 0, "top_five_sources": "None"} 77 | top_five_sources = [] 78 | productive_hours = 0 79 | unproductive_hours = 0 80 | for index, data in enumerate(json): 81 | #Grab the first 5 sources since its sorted by ascending order in terms of hours 82 | if index < 5: 83 | top_five_sources.append( 84 | str(data[3]).capitalize() + " - " + str(round(data[1]/60/60, 2))) 85 | if data[5] > 0: 86 | productive_hours += data[1]/60/60 87 | elif data[5] < 0: 88 | unproductive_hours += data[1]/60/60 89 | productive_hours = round(productive_hours, 2) 90 | unproductive_hours = round(unproductive_hours, 2) 91 | weekly_data = {"productive_hours": productive_hours, 92 | "unproductive_hours": unproductive_hours, "top_five_sources": ", ".join(top_five_sources)} 93 | return weekly_data 94 | 95 | 96 | def get_rescuetime_data(self, date): 97 | today = DT.date.today() 98 | date = today - DT.timedelta(days=date) 99 | connected = False 100 | while not connected: 101 | try: 102 | response = requests.get( 103 | "https://www.rescuetime.com/anapi/data?key={0}&perspective=rank&interval=week&restrict_begin={1}&restrict_end={2}&format=json".format(RESCUETIME_API_KEY, str(date), str(date))) 104 | if response.status_code == 200: 105 | connected = True 106 | except: 107 | pass 108 | return date, response.json()["rows"] 109 | 110 | #This function is used to return dates and a formatted list containing dictionaries 111 | # for use in the create_rescuetime_bar function in scripts.js 112 | 113 | def get_daily_week_view(self): 114 | dates = [] 115 | productive_array_values = [] 116 | unproductive_array_values = [] 117 | dates = [] 118 | for i in range(1, 8): 119 | today = DT.date.today() 120 | date = today - DT.timedelta(days=i) 121 | dates.append(str(date)) 122 | daily_feed_json = requests.get( 123 | "https://www.rescuetime.com/anapi/daily_summary_feed?key={0}&format=json".format(RESCUETIME_API_KEY)).json() 124 | for day in daily_feed_json: 125 | if day['date'] in dates: 126 | productive_array_values.append(day['all_productive_hours']) 127 | unproductive_array_values.append(day['all_distracting_hours']) 128 | # We need to reverse because otherwise the dates will be out of order on the graph 129 | productive_array_values.reverse() 130 | unproductive_array_values.reverse() 131 | rescuetime_data = [{"label": "Productive Hours", "backgroundColor": "#33702a", "data": productive_array_values}, 132 | {"label": "Unproductive Hours", "backgroundColor": "#b30000", "data": unproductive_array_values}] 133 | dates = ["-".join(date.split("-")[1:]) for date in dates] 134 | dates.reverse() 135 | return rescuetime_data, dates 136 | --------------------------------------------------------------------------------