{"id":19601545,"url":"https://github.com/berttejeda/bert.dashboard","last_synced_at":"2026-01-04T15:03:38.014Z","repository":{"id":182448042,"uuid":"668515003","full_name":"berttejeda/bert.dashboard","owner":"berttejeda","description":null,"archived":false,"fork":false,"pushed_at":"2024-09-03T18:56:33.000Z","size":17420,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-07T13:43:23.049Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/berttejeda.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-20T02:07:45.000Z","updated_at":"2024-09-03T18:56:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"9a9f0e29-d141-4c23-994f-558abcaaa645","html_url":"https://github.com/berttejeda/bert.dashboard","commit_stats":null,"previous_names":["berttejeda/bert.dashboard"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/berttejeda/bert.dashboard","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berttejeda%2Fbert.dashboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berttejeda%2Fbert.dashboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berttejeda%2Fbert.dashboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berttejeda%2Fbert.dashboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/berttejeda","download_url":"https://codeload.github.com/berttejeda/bert.dashboard/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/berttejeda%2Fbert.dashboard/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28139841,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-31T02:00:06.200Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T09:18:52.785Z","updated_at":"2026-01-04T15:03:37.999Z","avatar_url":"https://github.com/berttejeda.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\r\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\r\n**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*\r\n\r\n- [Overview](#overview)\r\n    - [What is this and why did you make it?](#what-is-this-and-why-did-you-make-it)\r\n    - [How did you make this?](#how-did-you-make-this)\r\n    - [What's it look like?](#whats-it-look-like)\r\n- [Features](#features)\r\n- [Quick Start](#quick-start)\r\n    - [Step 1 - Install](#step-1---install)\r\n    - [Step 2 - Create your configuration file](#step-2---create-your-configuration-file)\r\n    - [Step 3 - Launch!](#step-3---launch)\r\n        - [Usage](#usage)\r\n- [Building the app](#building-the-app)\r\n- [Building the installer](#building-the-installer)\r\n- [Developing the app](#developing-the-app)\r\n- [Configuration File](#configuration-file)\r\n    - [Configuration File - Defaults](#app-defaults)\r\n    - [Storing credentials - OS Keyring](#storing-credentials---os-keyring)\r\n        - [Windows](#windows)\r\n- [Lessons](#lessons)\r\n    - [Jinja Templating](#jinja-templating)\r\n- [WebTerminal](#webterminal)\r\n- [Appendix](#appendix)\r\n    - [Github Pages](#github-pages)\r\n\r\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\r\n\r\n\u003ca name=\"top\"\u003e\u003c/a\u003e\r\n===================\r\nBert's Dashboard\r\n===================\r\n\r\n\u003ca name=\"top\"\u003e\u003c/a\u003e\r\n\u003ca name=\"overview\"\u003e\u003c/a\u003e\r\n# Overview\r\n\r\n\u003ca name=\"what-is-this-and-why-did-you-make-it\"\u003e\u003c/a\u003e    \r\n## What is this and why did you make it? \r\n\r\nThis is my attempt at creating a sort of life dashboard for myself.\r\n\r\nThe aim is to make it as customizable as possible in as easy a way as possible.\r\n\r\nThis project is the successor to [bert.bill](https://github.com/berttejeda/bert.bill).\r\n\r\nAs such, the interactive lesson features from that project have been ported over.\r\n\r\nAs with the previous project, the interactive lesson feature was inspired by [Katacoda](https://www.katacoda.com/).\r\nAnd, as with the previous iteration of my work, instead of a website with learning examples, you have a web app that creates hands-on lessons from markdown-formatted, jina-templated documents, \r\ncomplete with a web terminal for interactive practice.\r\n\r\n\u003ca name=\"how-did-you-make-this\"\u003e\u003c/a\u003e    \r\n## How did you make this?\r\n\r\n- The UI\r\n    - [ReactJS](https://reactjs.org/) for the UI framework\r\n    - [TailwindCSS](https://tailwindcss.com/) for easier CSS\r\n    - [Mosaic Lite Admin Dashboard](https://github.com/cruip/tailwind-dashboard-template) for the UI layout\r\n    - [React Redux](https://react-redux.js.org/) for session state management\r\n- The API \r\n    - [Python Flask](https://flask.palletsprojects.com/en/2.1.x/)\r\n\r\n\u003ca name=\"whats-it-look-like\"\u003e\u003c/a\u003e    \r\n## What's it look like?\r\n\r\nHere's a screenshot of the dashboard:\r\n\r\n![dashboard](res/dashboard.png)\r\n\r\nHere's a screenshot of the Knowledgebase layout:\r\n\r\n![knowledgebase](res/knowledgebase.png)\r\n\r\nHere's a screenshot of an interactive lesson:\r\n\r\n![lesson](res/lesson.gif)\r\n\r\n\u003ca name=\"features\"\u003e\u003c/a\u003e\r\n# Features\r\n\r\n* The dashboard cards can be configured via the dashboard configration file: [etc/dashboard.yaml](etc/dashboard.yaml).\r\n* The knowledgebase catalog is configured via the lessons configuration file: [etc/lessons.yaml](etc/lessons.yaml)\r\n    * [Lessons](#Lessons) are Markdown-formatted files\r\n        1. First rendered as [jinja](https://jinja.palletsprojects.com/en/3.0.x/) templates\r\n        1. Then rendered as HTML\u003cbr /\u003e\r\n* Web-based terminals via [xtermjs](https://github.com/xtermjs/xterm.js/) component\u003cbr /\u003e\r\n  See section on [WebTerminal](#webterminal)\r\n* Local Webterminal websocket is also available\r\n    * Utilizes [spyder-terminal](https://github.com/spyder-ide/spyder-terminal) component\r\n    * You can practice the lesson material with your own OS/system\r\n        * Simply **click** on a command, and it will be executed in the underlying shell via web terminal!\r\n        * Default shell is bash (for now)\r\n\r\n\u003ca name=\"quick-start\"\u003e\u003c/a\u003e\r\n# Quick Start\r\n  \r\n\u003ca name=\"step-1---install\"\u003e\u003c/a\u003e\r\n## Step 1 - Install\r\n\r\n**Note** You'll need a minimum python version of 3.7 for the app to work\r\n\r\nYou can install the app in any of the following ways:\r\n\r\n* Install pip package from pypi.org: `pip install btdashboard`\r\n* Install pip package from locally cloned repo: \u003cbr /\u003e\r\n```\r\ngit clone -b ${REMOTE_BRANCH-main} https://github.com/berttejeda/bert.dashboard.git\r\ncd bert.dashboard\r\nnvm install\r\nnpm config set timeout 100000\r\nnpm install -g parcel yarn\r\nyarn install --frozen-lockfile --network-timeout 100000\r\nyarn compile:ui:dev\r\npip install .\r\n```\r\n  * From the above, [nvm](https://github.com/nvm-sh/nvm) is used to install \r\n  [nodejs](https://nodejs.org/en/), and we use [yarn](https://yarnpkg.com/) for installing package dependencies,\r\n  and [parcel](https://parceljs.org/) for the web build.\r\n  * [.nvmrc](.nvmrc) is set to use node v16.5.0\r\n  * To install from the locally cloned repo in development mode, do the same as above, but with `pip install -e .` instead\r\n* You can also install the pip package directly from git repo `pip install git+http://www.github.com/berttejeda/bert.dashboard.git`,\u003cbr /\u003e\r\n  but you'll need to obtain the HTML assets and point the app to them, see the [Appendix](#appendix)\r\n\r\n\u003ca name=\"step-2---create-your-configuration-file\"\u003e\u003c/a\u003e\r\n## Step 2 - Adjust your configuration files as needed\r\n\r\nUnder the [etc](etc) directory, you'll find the following configuration files:\r\n\r\n- [app.yaml](etc/app.yaml)\r\n- [dashboard.yaml](etc/dashboard.yaml)\r\n- [lessons.yaml](etc/lessons.yaml)\r\n- [sidebar.yaml](etc/sidebar.yaml)\r\n\r\nIf not specified, the app will use the configuration files above.\r\n\r\nTODO: Documentation on configuration file options.\r\n\r\n\u003ca name=\"step-3---launch\"\u003e\u003c/a\u003e\r\n## Step 3 - Launch!\r\n\r\n* If installed via pip, launch the app from your terminal: `btdashboard`\r\n* If installed via pip as a [development instance](#step-1---install), launch via python with `python ./btdashboard/app.py`\u003cbr /\u003e\r\n\r\n\u003ca name=\"usage\"\u003e\u003c/a\u003e\r\n### Usage\r\n\r\nUsage information can be obtained by invoking the executable with the `--help` flag, as with: `btdashboard --help` or `python ./btdashboard/app.py --help`.\r\n\r\nThe help output should be similar to:\r\n\r\n```\r\nusage: btdashboard [-h] [--username USERNAME] [--password PASSWORD]\r\n              [--lesson-url LESSON_URL]\r\n              [--static-assets-folder STATIC_ASSETS_FOLDER] [--app-id APP_ID]\r\n              [--app-user APP_USER] [--app-config-file APP_CONFIG_FILE]\r\n              [--lessons-config-file LESSONS_CONFIG_FILE]\r\n              [--dashboard-config-file DASHBOARD_CONFIG_FILE]\r\n              [--sidebar-config-file SIDEBAR_CONFIG_FILE]\r\n              [--cors-origin CORS_ORIGIN] [--logfile-path LOGFILE_PATH]\r\n              [--host-address HOST_ADDRESS] [--port PORT]\r\n              [--webterminal-listen-host WEBTERMINAL_LISTEN_HOST]\r\n              [--webterminal-listen-port WEBTERMINAL_LISTEN_PORT]\r\n              [--webterminal-host WEBTERMINAL_HOST]\r\n              [--webterminal-shell WEBTERMINAL_SHELL]\r\n              [--webterminal-shell-command WEBTERMINAL_SHELL_COMMAND]\r\n              [--open-browser-delay OPEN_BROWSER_DELAY]\r\n              [--logfile-write-mode {a,w}] [--config-file-templatized]\r\n              [--api-only] [--webterminal-only] [--all-in-one] [--no-browser]\r\n              [--debug] [--no-verify-tls] [--no-render-markdown]\r\n              [run]\r\n\r\nBert's Dashboard\r\n\r\npositional arguments:\r\n  run\r\n\r\noptional arguments:\r\n  -h, --help            show this help message and exit\r\n  --username USERNAME, -U USERNAME\r\n                        Username, if the URL requires authentication\r\n  --password PASSWORD, -P PASSWORD\r\n                        Password, if the URL requires authentication\r\n  --lesson-url LESSON_URL, -url LESSON_URL\r\n                        The URL for the lesson definition\r\n  --static-assets-folder STATIC_ASSETS_FOLDER, -S STATIC_ASSETS_FOLDER\r\n                        Explicity specify the folder for static HTML assets\r\n  --app-id APP_ID, -id APP_ID\r\n                        Specify the App's Identifier\r\n  --app-user APP_USER, -iu APP_USER\r\n                        Specify the App's User\r\n  --app-config-file APP_CONFIG_FILE, -cfa APP_CONFIG_FILE\r\n                        Path to app configuration file\r\n  --lessons-config-file LESSONS_CONFIG_FILE, -cfl LESSONS_CONFIG_FILE\r\n                        Path to lessons configuration file\r\n  --dashboard-config-file DASHBOARD_CONFIG_FILE, -cfd DASHBOARD_CONFIG_FILE\r\n                        Path to dashboard configuration file\r\n  --sidebar-config-file SIDEBAR_CONFIG_FILE, -cfs SIDEBAR_CONFIG_FILE\r\n                        Path to sidebar configuration file\r\n  --cors-origin CORS_ORIGIN, -o CORS_ORIGIN\r\n                        Override CORS origin pattern\r\n  --logfile-path LOGFILE_PATH, -L LOGFILE_PATH\r\n                        Path to logfile\r\n  --host-address HOST_ADDRESS, -l HOST_ADDRESS\r\n                        Override default host address\r\n  --port PORT, -p PORT  Override default listening port\r\n  --webterminal-listen-host WEBTERMINAL_LISTEN_HOST, -wlh WEBTERMINAL_LISTEN_HOST\r\n                        Override default listening host address for the\r\n                        webterminal socket\r\n  --webterminal-listen-port WEBTERMINAL_LISTEN_PORT, -wlp WEBTERMINAL_LISTEN_PORT\r\n                        Override default listening port for the webterminal\r\n                        socket\r\n  --webterminal-host WEBTERMINAL_HOST, -wh WEBTERMINAL_HOST\r\n                        Override the webterminal socket address to which the\r\n                        UI initially connects\r\n  --webterminal-shell WEBTERMINAL_SHELL, -wS WEBTERMINAL_SHELL\r\n                        Override default shell for the webterminal session\r\n  --webterminal-shell-command WEBTERMINAL_SHELL_COMMAND, -wSc WEBTERMINAL_SHELL_COMMAND\r\n                        Override default shell command for the webterminal\r\n                        session\r\n  --open-browser-delay OPEN_BROWSER_DELAY, -bd OPEN_BROWSER_DELAY\r\n                        Override default time in seconds to delay when opening\r\n                        the system's web browser\r\n  --logfile-write-mode {a,w}, -w {a,w}\r\n                        File mode when writing to log file, 'a' to append, 'w'\r\n                        to overwrite\r\n  --config-file-templatized, -fT\r\n                        Render configuration via jinja2 templating\r\n  --api-only            Don't serve static assets, only start API\r\n  --webterminal-only    Don't serve static assets or start API, only invoke\r\n                        Webterminal Websocket\r\n  --all-in-one, -aio    Run the shell websocket process alongside app\r\n  --no-browser, -nobrowser\r\n                        If applicable, don't open the web browser\r\n  --debug\r\n  --no-verify-tls, -notls\r\n                        Verify SSL cert when downloading web content\r\n  --no-render-markdown, -nomarkdown\r\n```\r\n\r\n\u003ca name=\"building-the-app\"\u003e\u003c/a\u003e\r\n# Starting the development instance of the app\r\n\r\n* Install Python 3.7+\r\n* Install this project's required version of [nodejs](https://nodejs.org/en/) (v18.11.0): `nvm install`\u003cbr /\u003e\r\n  Note that this command implicitly reads the local [.nvmrc](.nvmrc)\r\n* Install yarn \u0026 parcel: `npm install -g yarn parcel`\r\n* Install node modules: `yarn install --frozen-lockfile`\r\n* Install python prerequisites: `pip3 install -e .`\r\n* Launch the development instance of the UI: `yarn start:dev:ui`\u003cbr /\u003e\r\n* Launch the api and local webterminal process: `yarn start:api`\u003cbr /\u003e\r\n  Under the hood, this launches `python btdashboard/app.py --debug --api-only -aio`, \r\n  see [package.json](package.json)\r\n\r\n[Back to Top](#top)\r\n\u003ca name=\"developing-the-app\"\u003e\u003c/a\u003e\r\n# Developing the app\r\n\r\nStarting the development instance of the UI will cause it to reload\r\nwhenever you make changes to any of the UI \r\nfiles under the [src](src) folder.\r\n\r\nThis functionality helps increase UI development velocity.\r\n\r\nI have not yet implemented similar functionality for the API files under [btdashboard](btdashboard),\r\nso you'll have to kill and reload the API whenever you make changes.\r\n\r\n\u003ca name=\"app-defaults\"\u003e\u003c/a\u003e\r\n# App Defaults\r\n\r\nIf no settings can be found, the app will resort to its defaults, \r\nsee [defaults.py](btdashboard/defaults.py) \r\n\r\nAs such, the defaults settings call for the import of an external config, hosted in my [bert.lessons](https://github.com/berttejeda/bert.lessons) repo: \u003cbr /\u003e\r\nsee [bert.lessons/lessons.yaml](https://raw.githubusercontent.com/berttejeda/bert.lessons/main/lessons.yaml)\r\n\r\nThis external config is where I am listing all of my (mostly) hand-crafted tutorials and learning materials.\r\n\r\n\u003ca name=\"lessons\"\u003e\u003c/a\u003e\r\n# Lessons\r\n\r\nAs already mentioned, lessons are Markdown-formatted \r\nfiles interpreted as [jinja](https://jinja.palletsprojects.com/en/3.0.x/) \r\ntemplates.\r\n\r\nThese are defined in the [lessons.yaml](etc/lessons.yaml) configuration file.\r\n\r\nYou can override the location of this file via the appropriate cli parameter.\r\n\r\nReview the `--help` output for details.\r\n\r\n## TODO \r\n\r\nImplement per-lesson credentials\r\n\r\n\u003ca name=\"jinja-templating\"\u003e\u003c/a\u003e\r\n## Jinja Templating\r\n\r\nTo add to the templating goodies provided by the Jinja library,\r\nI've exposed the OS Environment via the _environment_ key of a\r\nspecial variable named _session_.\r\n\r\nThis means you should be able to dynamically load any OS-level environment \r\nvariable into your lesson material, e.g. \r\n\r\n```markdown\r\n# Overview\r\n\r\n{% set USERNAME = session['environment'].get('USER') or session['environment'].get('USERNAME')  %}\r\nHello {{ USERNAME }}, welcome to Lesson 1\r\n```\r\n\r\n\u003ca name=\"webterminal\"\u003e\u003c/a\u003e\r\n# WebTerminal\r\n\r\nEvery lesson rendered through the app includes a web-based terminal \r\nemulator component that allows for practicing the lesson material.\r\n\r\nThese web terminals are embedded in the user interface, \r\navailable at its footer.\r\n\r\nAs mentioned before, the underlying technology for these web \r\nterminals is [xterm.js](https://github.com/xtermjs/xterm.js/).\r\n\r\nAs such, the xterm.js component requires a websocket to a bash process.\r\n\r\nBy [default](btdashboard/defaults.py), the bert.dashboard web app \r\nwill attempt to connect to a local instance of the websocket via _http://127.0.0.1:10001/_.\r\n\r\nYou can get this websocket running either by:\r\n\r\n- Installing btdashboard with `pip install btdashboard` and running `btdashboard -aio` or by installing all requirements and running `python btdashboard/app.py -aio`\u003cbr /\u003e\r\n  Doing so will launch a local websocket that forwards keystrokes to a bash process on your system\r\n\r\n\u003ca name=\"appendix\"\u003e\u003c/a\u003e\r\n# Appendix\r\n\r\n\u003ca name=\"configuration-files\"\u003e\u003c/a\u003e\r\n## Configuration files\r\n\r\nImportant note about the configuration files:\r\n\r\n- Environment variable interpolation is supported, e.g.\u003cbr /\u003e\r\n  `user: ${USER}`, \u003cbr /\u003e\r\n  You can see this in use in the [app](etc/app.yaml) config file\r\n\r\n\u003ca name=\"github-pages\"\u003e\u003c/a\u003e\r\n## Github Pages\r\n\r\nIf you want to publish the assets from your own fork of this repo:\r\n\r\n1. Fork this repo\r\n1. Configure [Github Pages](https://docs.github.com/en/pages/quickstart) \r\n1. Run `yarn install` to initialize the project requirements\r\n1. Run `yarn deploy` to publish the static assets to the `gh-pages` branch","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberttejeda%2Fbert.dashboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fberttejeda%2Fbert.dashboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberttejeda%2Fbert.dashboard/lists"}