moving to microservice client/server model

This commit is contained in:
Charles N Wyble 2024-12-14 20:02:10 -06:00
parent 40e1cbc498
commit c9f70602ba
13 changed files with 2 additions and 671 deletions

View File

@ -1,19 +0,0 @@
# Data Gathering for the @ReachableCEO daily stakeholder report
## Introduction
I'm trying to life a fully instrumented CTO/founder life and #buildInPublic. And unlike most of the founders doing so, I'm actually publishing daily reports and live streaming my workstation all dya.
I want to automate the instrumentation/reporting process as much as possible to get the highest fidelity information for stakeholders consumption.
## Data sources
- Apple Health (exporting via AutoHealthExport)
- Workout (evaluating a cou
- Nutrition
- Daily habit tracking
- ActivityWatch
- WakaAPI
## Scripts

View File

@ -1,83 +1 @@
#!/bin/bash
# Script to query Gitea API for a user's activity on a specific date
########################################################################################################
#Obtain gitea api key from bitwarden
########################################################################################################
####################################
## Step 0: Set to use tsys server
####################################
bw logout
echo "Setting cli to use tsys bitwarden server..."
bw config server https://pwvault.turnsys.com
####################################
## Step 1: login to bitwarden
####################################
# From: https://bitwarden.com/help/cli/#using-an-api-key
### Set apikey environment varaible
echo "Sourcing clientid/apikey data..."
source D:/tsys/secrets/bitwarden/data/apikey-bitwarden-reachableceo
### Login to vault using apikey...
echo "Logging in..."
bw login --apikey $BW_CLIENTID $BW_CLIENTSECRET
### Step 1.1: unlock / save session id
echo "Unlocking..."
export BW_SESSION="$(bw unlock --passwordenv TSYS_BW_PASSWORD_REACHABLECEO --raw)"
### Step 2: retrive a value into an environment variable
export GITEA_APIKEY="$(bw get password APIKEY-Gitea)"
########################################################################################################
# Accrss gitea data
########################################################################################################
# Script to query Gitea API for a user's activity on a specific date
# Usage: ./get_gitea_user_activity.sh <username> <date> [GITEA_URL] [TOKEN]
# Set username, date, and default Gitea URL
USERNAME="${1:-reachableceo}" # Default to "reachableceo" if not provided
DATE="${2:-$(date +%Y-%m-%d)}" # Default to today's date if not provided
GITEA_URL="${3:-https://git.knownelement.com}" # Default Gitea URL if not provided
TOKEN="${GITEA_APIKEY}" # Use APIKEY-GItea or passed argument
# API Endpoint for user activities
API_ENDPOINT="$GITEA_URL/api/v1/users/$USERNAME/timeline"
# Make the API call
if [ -n "$TOKEN" ]; then
# If token is provided, use it in the Authorization header
RESPONSE=$(curl -s -H "Authorization: token $TOKEN" "$API_ENDPOINT")
else
# If no token is provided, make an unauthenticated request
RESPONSE=$(curl -s "$API_ENDPOINT")
fi
# Check for API errors
if [[ "$RESPONSE" == "Not found" || -z "$RESPONSE" ]]; then
echo "Error: User '$USERNAME' not found, or endpoint is incorrect."
exit 1
fi
# Validate JSON response
if ! echo "$RESPONSE" | jq empty >/dev/null 2>&1; then
echo "Error: Invalid JSON response from Gitea API."
echo "Response: $RESPONSE"
exit 1
fi
# Filter the activity by date using jq
echo "$RESPONSE" | jq --arg date "$DATE" '[.[] | select(.created_at | startswith($date))]'
hi

View File

@ -1,59 +1 @@
#!/bin/bash
# Functions
# Search for a note by title and return its ID
search_note() {
local title="$1"
echo "Searching for note with title: $title"
note_id=$(joplin search "$title" --fields id --limit 1 --json | jq -r '.[0].id')
if [[ -z "$note_id" || "$note_id" == "null" ]]; then
echo "Error: Note with title '$title' not found."
exit 1
fi
echo "Found note with ID: $note_id"
echo "$note_id"
}
# Export the note by ID to the specified directory
export_note() {
local note_id="$1"
local output_dir="$2"
echo "Exporting note ID: $note_id to directory: $output_dir"
mkdir -p "$output_dir" || {
echo "Error: Unable to create directory $output_dir"
exit 1
}
# Export the note with attachments to the specified directory
joplin export "$note_id" --format md --output "$output_dir" || {
echo "Error: Failed to export note ID $note_id"
exit 1
}
echo "Note exported successfully to $output_dir"
}
# Main function
main() {
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <note-title> <output-directory>"
exit 1
fi
local note_title="$1"
local output_dir="$2"
# Search for the note and get its ID
local note_id
note_id=$(search_note "$note_title")
# Export the note
export_note "$note_id" "$output_dir"
}
# Run the main function with all script arguments
main "$@"
hi

View File

@ -1,97 +0,0 @@
#!/usr/bin/env bash
# Use the Joplin CLI and create a new note for today. Let me pass in the notebook folder path. Use bash functions.
# Created by chatgpt
# https://chatgpt.com/share/6750c7df-6b2c-8005-a91c-1bc5b3875170
# shellcheck disable=SC1090
# Bash3 Boilerplate Setup
set -o errexit
set -o nounset
set -o pipefail
IFS=$'\n\t'
# Constants
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_VERSION="1.0"
readonly SCRIPT_AUTHOR="Charles N Wyble"
readonly SCRIPT_DESC="Create a new Daily Stakeholder Report note in Joplin"
# Logging and Debugging (from bash3boilerplate)
readonly LOG_FILE="/tmp/${SCRIPT_NAME}.log"
readonly TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
info() { echo "[INFO] [$TIMESTAMP] $*"; }
error() { echo "[ERROR] [$TIMESTAMP] $*" >&2; }
# Default Exit Codes
readonly ERR_JOPLIN_NOT_INSTALLED=10
readonly ERR_NOTEBOOK_NOT_FOUND=20
# Function: Usage Instructions
usage() {
cat <<EOF
$SCRIPT_DESC
Usage:
$SCRIPT_NAME <notebook_folder_path>
Example:
$SCRIPT_NAME "Stakeholder Reports"
Options:
-h, --help Display this help message.
EOF
}
# Check dependencies
require_joplin() {
if ! command -v joplin &>/dev/null; then
error "Joplin CLI is not installed or not in PATH. Please install it and try again."
exit $ERR_JOPLIN_NOT_INSTALLED
fi
}
# Check if notebook exists in Joplin
notebook_exists() {
local notebook="$1"
if ! joplin use "$notebook" >/dev/null 2>&1; then
error "Notebook '$notebook' does not exist. Please create it first."
exit $ERR_NOTEBOOK_NOT_FOUND
fi
}
# Create a new note in the specified notebook
create_note() {
local notebook="$1"
local today_date=$(date '+%d-%m-%Y') # Format: YYYY-MM-DD
local title="Daily Stakeholder Report - $date"
local content="## Daily Stakeholder Report\n\n**Date:** $date\n\n---\n\n### Key Updates\n\n- \n\n### Issues\n\n- \n\n### Next Steps\n\n- \n"
joplin create note --title "$title" --body "$content" >/dev/null
info "Note created successfully in notebook '$notebook'."
}
# Main Function
main() {
local notebook_path="$1"
# Ensure Joplin CLI is present
require_joplin
# Verify notebook existence
notebook_exists "$notebook_path"
# Create a new note
create_note "$notebook_path"
}
# Argument Parsing
if [[ $# -lt 1 ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
usage
exit 0
fi
main "$1"

View File

@ -1,114 +0,0 @@
#!/usr/bin/env bash
# Written by ChatGPT
# https://chatgpt.com/share/6750c7df-6b2c-8005-a91c-1bc5b3875170
# Prompt:
# Pull any issues from Redmine that have a due date of today via API
# shellcheck disable=SC1090
# Bash3 Boilerplate Setup
set -o errexit
set -o nounset
set -o pipefail
IFS=$'\n\t'
# Constants
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_VERSION="1.0"
readonly SCRIPT_AUTHOR="Charles N Wyble"
readonly SCRIPT_DESC="Pull Redmine issues with a due date of today and output in Markdown"
# Logging and Debugging
readonly LOG_FILE="/tmp/${SCRIPT_NAME}.log"
readonly TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
info() { echo "[INFO] [$TIMESTAMP] $*" | tee -a "$LOG_FILE"; }
error() { echo "[ERROR] [$TIMESTAMP] $*" >&2 | tee -a "$LOG_FILE"; }
# Default Exit Codes
readonly ERR_CURL_NOT_INSTALLED=10
readonly ERR_API_FAILURE=20
# Configuration
readonly REDMINE_INSTANCE="projects.knownelement.com"
readonly REDMINE_API_URL="https://${REDMINE_INSTANCE}"
readonly REDMINE_API_KEY="your_api_key_here"
readonly DATE=$(date '+%Y-%m-%d')
# Function: Usage Instructions
usage() {
cat <<EOF
$SCRIPT_DESC
Usage:
$SCRIPT_NAME <project_id>
Options:
-h, --help Display this help message.
Example:
$SCRIPT_NAME 123
EOF
}
# Check dependencies
require_curl() {
if ! command -v curl &>/dev/null; then
error "curl is not installed or not in PATH. Please install it and try again."
exit $ERR_CURL_NOT_INSTALLED
fi
}
# Fetch issues from Redmine API
fetch_issues() {
local project_id="$1"
local url="${REDMINE_API_URL}/issues.json?key=${REDMINE_API_KEY}&project_id=${project_id}&due_date=${DATE}"
info "Fetching issues with due date ${DATE} for project ID ${project_id}..."
response=$(curl -s -w "%{http_code}" -o /tmp/redmine_response.json "$url")
http_code=$(tail -n1 <<<"$response")
if [[ "$http_code" -ne 200 ]]; then
error "Failed to fetch issues from Redmine API. HTTP Code: $http_code"
exit $ERR_API_FAILURE
fi
info "Issues fetched successfully. Parsing and converting to Markdown..."
generate_markdown /tmp/redmine_response.json
}
# Generate Markdown output from JSON response
generate_markdown() {
local json_file="$1"
local markdown_output="/tmp/redmine_issues.md"
echo "# Redmine Issues for ${DATE}" > "$markdown_output"
echo "" >> "$markdown_output"
jq -r --arg base_url "$REDMINE_API_URL" '.issues[] | "## [\(.subject)](\($base_url)/issues/\(.id))\n- **ID**: \(.id)\n- **Status**: \(.status.name)\n- **Due Date**: \(.due_date)\n\n---"' \
"$json_file" >> "$markdown_output"
info "Markdown output written to $markdown_output"
cat "$markdown_output"
}
# Main Function
main() {
local project_id="$1"
# Ensure curl is installed
require_curl
# Fetch issues for the project
fetch_issues "$project_id"
}
# Argument Parsing
if [[ $# -lt 1 ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
usage
exit 0
fi
main "$1"

View File

@ -1,12 +0,0 @@
title: ReachableCEO 2025 Plan
titlepage: true
toc: true
toc-own-page: true
header-left: "\\hspace{1cm}"
header-center: "\\leftmark"
header-right: "Page \\thepage"
footer-left: "Charles N Wyble"
footer-center: "Tenacity. Velocity. Focus."
page-background: "./background5.pdf"
titlepage-logo: "D:/tsys/@ReachableCEO/ReachableCEO.png"
urlcolor: blue

View File

@ -1,70 +0,0 @@
%PDF-1.5
%µí®û
3 0 obj
<< /Length 4 0 R
/Filter /FlateDecode
>>
stream
xœmŽ;Â@ Dû=…/€±½þí18 „ûKì¢D
ráÑÈóƯÂ0f™á|%˜ß… •1ÓÁš¡DÀég,7˜
!iÂjÒ”»P µ/c<>h¶pxr‰<C382>ˆá±1Wã^Ž™Qd£%qó!º9ZDj¿¥ UÅѪíÙûËAµp<C2B5>ñr$Õ&]x_ìƒÊȽŸI±WÿÃ?f¦r)Ì6EU
endstream
endobj
4 0 obj
168
endobj
2 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
5 0 obj
<< /Type /Page
/Parent 1 0 R
/MediaBox [ 0 0 595.275574 841.889771 ]
/Contents 3 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 2 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 5 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Creator (cairo 1.14.8 (http://cairographics.org))
/Producer (cairo 1.14.8 (http://cairographics.org))
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000582 00000 n
0000000282 00000 n
0000000015 00000 n
0000000260 00000 n
0000000354 00000 n
0000000647 00000 n
0000000774 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
826
%%EOF

View File

@ -1,14 +0,0 @@
#!/bin/bash
INPUT_FILE="../dsr-build-temp/@DailyStakeholderReports/StrategicPlans/2025-ReachableCEO-Plan.md"
OUTPUT_FILE="../dsr-build-output/ReachableCEO2025Plan.pdf"
METADATA_FILE="2025Plan.yml"
TEMPLATE="eisvogel"
pandoc \
$INPUT_FILE \
--template $TEMPLATE \
--metadata-file=$METADATA_FILE \
--from markdown \
--to=pdf \
--output $OUTPUT_FILE

View File

@ -1,17 +0,0 @@
#!/bin/bash
TODAY_DATE=$(date +%m-%d-%Y)
INPUT_FILE="DSR-$TODAY_DATE.md"
OUTPUT_FILE="/d/tsys/@ReachableCEO/DailyStakeholderReport-Pipeline/dsr-build-output/DSR-$TODAY_DATE.pdf"
METADATA_FILE="/d/tsys/@ReachableCEO/DailyStakeholderReport-Pipeline/dsr-publish/daily-stakeholder-report.yml"
TEMPLATE="eisvogel"
cd /d/tsys/@ReachableCEO/DailyStakeholderReport-Pipeline/dsr-build-temp/@DailyStakeholderReports/Today || exit
pandoc \
$INPUT_FILE \
--template $TEMPLATE \
--metadata-file=$METADATA_FILE \
--from markdown \
--to=pdf \
--output $OUTPUT_FILE

View File

@ -1,11 +0,0 @@
title: Daily Stakeholder Report - \today
urlcolor: blue
titlepage: true
header-left: "\\hspace{1cm}"
header-center: "\\leftmark"
header-right: "Page \\thepage"
footer-left: "Charles N Wyble"
footer-center: "Tenacity. Velocity. Focus."
footer-right: "[Source code](https://git.knownelement.com/reachableceo/DailyStakeholderReport-Pipeline/src/branch/main/dsr-publish/create-dsr-pdf.sh)"
page-background: "D:/tsys/@ReachableCEO/DailyStakeholderReport-Pipeline/dsr-publish/background5.pdf"
titlepage-logo: "D:/tsys/@ReachableCEO/ReachableCEO.png"

View File

@ -1,131 +0,0 @@
#!/bin/bash
secrets_manager()
{
echo "Obtaining discourse api key..."
bw logout
####################################
## Step 0: Set to use tsys server
####################################
bw config server https://pwvault.turnsys.com
####################################
## Step 1: login to bitwarden
####################################
# From: https://bitwarden.com/help/cli/#using-an-api-key
### Set apikey environment varaible
source D:/tsys/secrets/bitwarden/data/apikey-bitwarden-reachableceo
### Login to vault using apikey...
bw login --apikey $BW_CLIENTID $BW_CLIENTSECRET
### Step 1.1: unlock / save session id
export BW_SESSION="$(bw unlock --passwordenv TSYS_BW_PASSWORD_REACHABLECEO --raw)"
### Step 2: retrive a value into an environment variable
export DISCOURSE_APIKEY="$(bw get password APIKEY-discourse)"
}
post_dsr()
{
TODAY_DATE=$(date +%m-%d-%Y)
echo "Posting DSR..."
DISCOURSE_URL="https://community.turnsys.com" # e.g., https://forum.example.com
API_KEY="$DISCOURSE_APIKEY" # Your API key
API_USERNAME="reachableceo" # API username or admin account
# The category ID to post to (update to match your forum's categories)
CATEGORY_ID=61
# The title for the post (generated here; customize as needed)
TITLE="Daily Stakeholder Report - $TODAY_DATE"
# The content of the post
CONTENT="Please use the link below to download today's stakeholder report."
# The file to upload (from the second argument or auto-generated based on date)
FILE_PATH="../dsr-build-output/DSR-$TODAY_DATE.pdf"
# Check if the file exists
if [ ! -f "$FILE_PATH" ]; then
echo "File not found: $FILE_PATH"
exit 1
fi
# Upload the file
echo "Uploading file..."
upload_response=$(curl -s -X POST "$DISCOURSE_URL/uploads.json" \
-H "Content-Type: multipart/form-data" \
-H "Api-Key: $API_KEY" \
-H "Api-Username: $API_USERNAME" \
-F "file=@$FILE_PATH;type=application/pdf" \
-F "type=composer")
echo "Upload Response: $upload_response"
# Extract the short_url from the response
short_url=$(echo "$upload_response" | /mingw64/bin/jq -r '.short_url')
# Check if the short_url was returned
if [ "$short_url" == "null" ]; then
echo "Failed to extract short_url. Response:"
echo "$upload_response"
exit 1
fi
echo "File uploaded successfully. Short URL: $short_url"
# Append the file link to the post content (Markdown format)
CONTENT="$CONTENT\n\n[Download todays report in PDF format]($short_url)"
# Create the new topic
echo "Creating new topic..."
post_response=$(curl -s -X POST "$DISCOURSE_URL/posts.json" \
-H "Content-Type: application/json" \
-H "Api-Key: $API_KEY" \
-H "Api-Username: $API_USERNAME" \
-d @- <<EOF
{
"title": "$TITLE",
"raw": "$CONTENT",
"category": $CATEGORY_ID
}
EOF
)
echo "Post Response: $post_response"
# Check if the post creation was successful
if echo "$post_response" | grep -q '"id":'; then
echo "Post created successfully!"
else
echo "Failed to create post. Response:"
echo "$post_response"
exit 1
fi
}
#Get discourse api key
secrets_manager
# - Create a new topic
# - upload PDF to discourse
# - attach uploaded PDF to the topic
post_dsr

View File

@ -1,35 +0,0 @@
#!/bin/bash
# Wrap up my instrumented day into a (mostly) automated report and publish to discourse
# Gather DSR assets
# My manually entered notations
./dsr-input/dsr-gather-joplin-log.sh
# My gitea data
./dsr-input/dsr-gather-gitea.sh
# My redmine data
./dsr-input/dsr-gather-redmine.sh
# My wakapi data
./dsr-input/dsr-gather-waka-api.sh
# My activity watch data
./dsr-input/dsr-gather-activitywatch.sh
# My habit tracker data
./dsr-input/dsr-gather-habits.sh
# My health/fitnes data
./dsr-input/dsr-gather-fitness.sh
# My diet data
./dsr-input/dsr-gather-diet.sh
# Produce DSR PDF asset
./dsr-publish/create-dsr-pdf.sh
# Publish DSR to the world
./dsr-publish/publish-dsr.sh

View File

@ -1,9 +0,0 @@
#!/bin/bash
# Start my instrumented day
# Create a new blank DSR for the day
./dsr-joplin-create/dsr-new.sh
# Populate my Joplin note "Todays objectives" section based on Redmine due dates
./dsr-joplin-create/dsr-populate-objectives.sh