Merge pull request 'enhancement/csv-output-and-refactor' (#2) from enhancement/csv-output-and-refactor into master

Reviewed-on: https://git.nickhepler.cloud/nick/wallos-fetcher/pulls/2
This commit is contained in:
Nick Hepler 2025-04-23 08:19:17 -04:00
commit 888e6aeef8
2 changed files with 110 additions and 119 deletions

View File

@ -2,16 +2,18 @@
Wallos Fetcher is a command-line utility designed to retrieve and export subscription data from a self-hosted [Wallos](https://github.com/WallosApp/Wallos) instance—an open-source personal subscription tracker. Wallos Fetcher is a command-line utility designed to retrieve and export subscription data from a self-hosted [Wallos](https://github.com/WallosApp/Wallos) instance—an open-source personal subscription tracker.
This script helps you keep track of all your recurring expenses and share that information in human-readable or machine-readable formats, making it especially useful for budgeting, personal finance planning, and digital legacy preparation for family members. This script helps you keep track of all your recurring expenses and share that information in human-readable or machine-readable formats. It's especially useful for budgeting, personal finance planning, and digital legacy preparation for family members.
--- ---
## ✨ Features ## ✨ Features
- 📄 **Markdown output by default** easy to read, easy to share - 📄 **Markdown output by default** clean, readable reports
- 📊 **CSV output** spreadsheet-friendly for Excel, Numbers, etc.
- 💾 Export as **Markdown**, **CSV**, or **JSON** - 💾 Export as **Markdown**, **CSV**, or **JSON**
- 🔍 Optionally include **full subscription details** (e.g., cycle, auto-renew, currency) - 🔍 Optionally include **full subscription details** (e.g., cycle, auto-renew, currency)
- 🗃 Output to a custom file - 🗃 Output to a custom file
- 🧼 Modular, readable Bash script using secure temp file handling
- 🧰 Lightweight, portable, and dependency-minimal (requires `curl` and `jq`) - 🧰 Lightweight, portable, and dependency-minimal (requires `curl` and `jq`)
--- ---
@ -44,6 +46,8 @@ chmod +x wallos-fetch.sh
./wallos-fetch.sh [options] ./wallos-fetch.sh [options]
``` ```
> This script requires `curl` and `jq`. Run `./wallos-fetch.sh --help` to see all options.
### ✅ Example Commands ### ✅ Example Commands
| Purpose | Command | | Purpose | Command |
@ -90,11 +94,34 @@ Structured export for use in spreadsheets or data processing tools.
- Default fields: `id,name,price,next_payment,category_name,payment_method_name` - Default fields: `id,name,price,next_payment,category_name,payment_method_name`
- `--full` option adds: `currency_id,cycle,auto_renew`, etc. - `--full` option adds: `currency_id,cycle,auto_renew`, etc.
```csv
"id","name","price","next_payment","category_name","payment_method_name"
"1","Netflix","17.99","2025-04-21","Entertainment","Credit Card"
"2","Spotify","9.99","2025-05-01","Music","PayPal"
```
### 📦 JSON ### 📦 JSON
Raw response data from the Wallos API—useful for backups or integrations. Raw response data from the Wallos API—useful for backups, custom reporting, or integrations.
```json
{
"subscriptions": [
{
"id": 1,
"name": "Netflix",
"price": 17.99,
"currency_id": "USD",
"next_payment": "2025-04-21",
"category_name": "Entertainment",
"payment_method_name": "Credit Card"
}
]
}
```
--- ---
## 🔐 Authentication ## 🔐 Authentication
You can set your API key in one of two ways: You can set your API key in one of two ways:
@ -125,6 +152,8 @@ API_KEY="your_api_key_here"
> ⚠️ Be cautious: hardcoding secrets in scripts can pose a security risk if the file is shared or version-controlled. > ⚠️ Be cautious: hardcoding secrets in scripts can pose a security risk if the file is shared or version-controlled.
---
## 📜 License ## 📜 License
This project is licensed under the [GNU General Public License v3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html). This project is licensed under the [GNU General Public License v3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html).
@ -134,7 +163,7 @@ This project is licensed under the [GNU General Public License v3.0 or later](ht
## 🤝 Contributions ## 🤝 Contributions
Feedback, bug reports, and pull requests are welcome! Feedback, bug reports, and pull requests are welcome!
Submit issues or improvements via your Gitea repository. Submit issues or improvements via the Gitea repository.
--- ---

View File

@ -1,161 +1,123 @@
#!/bin/bash #!/bin/bash
VERSION="2025.04" VERSION="2025.05"
# Configuration
HOST="https://your-api-host.com" HOST="https://your-api-host.com"
ENDPOINT="/api/subscriptions/get_subscriptions.php" ENDPOINT="/api/subscriptions/get_subscriptions.php"
API_KEY="${API_KEY:-$WALLOS_API_KEY}" API_KEY="${API_KEY:-$WALLOS_API_KEY}"
# Defaults
MODE="md" MODE="md"
FULL=false FULL=false
OUTPUT="" # Will set default per mode later OUTPUT=""
TMP_FILE=$(mktemp)
# 🚨 Dependencies check
for dep in curl jq; do
command -v "$dep" >/dev/null 2>&1 || {
echo "❌ Missing required dependency: $dep"
exit 1
}
done
# Show help
show_help() { show_help() {
cat << EOF cat << EOF
Wallos Fetcher Script - v$VERSION Wallos Fetcher Script - v$VERSION
Usage: ./wallos-fetch.sh [OPTIONS] Usage: ./wallos-fetch.sh [OPTIONS]
Fetch and export subscription data from a Wallos instance.
Options: Options:
--json Output raw JSON --json Output raw JSON
--md Output Markdown table --md Output Markdown table
--full Include more fields in CSV/Markdown --csv Output CSV table
--full Include more fields
--output <file> Custom output filename --output <file> Custom output filename
--help Show this help message and exit --help Show this help message
--version Show version and exit --version Show version
Default output is Markdown.
EOF EOF
} }
# Argument parser parse_args() {
while [[ "$#" -gt 0 ]]; do while [[ "$#" -gt 0 ]]; do
case "$1" in case "$1" in
--json) MODE="json" ;; --json) MODE="json" ;;
--md) MODE="md" ;; --md) MODE="md" ;;
--csv) MODE="csv" ;;
--full) FULL=true ;; --full) FULL=true ;;
--output) --output) shift; OUTPUT="$1" ;;
shift
OUTPUT="$1"
;;
--help) show_help; exit 0 ;; --help) show_help; exit 0 ;;
--version) echo " wallos-fetch v$VERSION"; exit 0 ;; --version) echo "🧾 wallos-fetch v$VERSION"; exit 0 ;;
*) echo "Unknown option: $1" && show_help && exit 1 ;; *) echo "Unknown option: $1"; show_help; exit 1 ;;
esac esac
shift shift
done done
# Set default filenames if none specified # Set default output filename
if [[ -z "$OUTPUT" ]]; then [[ -z "$OUTPUT" ]] && OUTPUT="subscriptions.${MODE}"
case "$MODE" in }
json) OUTPUT="subscriptions.json" ;;
md) OUTPUT="subscriptions.md" ;;
csv) OUTPUT="subscriptions.csv" ;;
esac
fi
# Construct the API URL fetch_data() {
URL="${HOST}${ENDPOINT}?api_key=${API_KEY}" URL="${HOST}${ENDPOINT}?api_key=${API_KEY}"
RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" "$URL")
STATUS=$(echo "$RESPONSE" | sed -n 's/^HTTP_STATUS://p')
BODY=$(echo "$RESPONSE" | sed '/^HTTP_STATUS:/d')
# Perform the request echo "$BODY" > "$TMP_FILE"
RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" "$URL")
BODY=$(echo "$RESPONSE" | sed -n '1,/^HTTP_STATUS:/p' | sed '$d')
STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTP_STATUS://')
echo "HTTP Status: $STATUS" if [[ "$STATUS" != "200" ]]; then
echo "❌ API request failed with status: $STATUS"
rm -f "$TMP_FILE"
exit 1
fi
}
if [ "$STATUS" -eq 200 ]; then output_json() {
echo "$BODY" > temp_response.json mv "$TMP_FILE" "$OUTPUT"
if [ "$MODE" == "json" ]; then
mv temp_response.json "$OUTPUT"
echo "💾 Saved JSON to $OUTPUT" echo "💾 Saved JSON to $OUTPUT"
}
elif [ "$MODE" == "md" ]; then output_markdown() {
# Write markdown header and description
{ {
echo "# 🧾 Active Subscriptions Overview" echo "# 🧾 Active Subscriptions Overview"
echo ""
echo "This document lists all recurring subscriptions tied to this household or individual. It includes costs, renewal dates, categories, and payment methods. This record is provided as a reference for financial planning, digital legacy management, or to assist family members in case of emergencies or estate matters."
echo ""
echo "_Last updated: $(date +'%Y-%m-%d')_" echo "_Last updated: $(date +'%Y-%m-%d')_"
echo "" echo ""
} > "$OUTPUT" } > "$OUTPUT"
if [ "$FULL" = true ]; then if $FULL; then
echo "| ID | Name | Price | Currency | Next Payment | Cycle | Auto Renew | Category | Payment Method |" >> "$OUTPUT" echo "| ID | Name | Price | Currency | Next Payment | Cycle | Auto Renew | Category | Payment Method |" >> "$OUTPUT"
echo "|----|------|-------|----------|---------------|-------|-------------|----------|----------------|" >> "$OUTPUT" echo "|----|------|-------|----------|---------------|-------|-------------|----------|----------------|" >> "$OUTPUT"
jq -r ' jq -r '.subscriptions[] | [.id, .name, .price, .currency_id, .next_payment, .cycle, .auto_renew, .category_name, .payment_method_name] | @tsv' "$TMP_FILE" |
.subscriptions[] | [ while IFS=$'\t' read -r id name price curr next cycle renew cat pay; do
.id,
.name,
(.price | tostring),
.currency_id,
.next_payment,
.cycle,
.auto_renew,
(.category_name | gsub("&gt;"; ">") | gsub("&amp;"; "&")),
.payment_method_name
] | @tsv
' temp_response.json | while IFS=$'\t' read -r id name price curr next cycle renew cat pay; do
echo "| $id | $name | $price | $curr | $next | $cycle | $renew | $cat | $pay |" >> "$OUTPUT" echo "| $id | $name | $price | $curr | $next | $cycle | $renew | $cat | $pay |" >> "$OUTPUT"
done done
else else
echo "| ID | Name | Price | Next Payment | Category | Payment Method |" >> "$OUTPUT" echo "| ID | Name | Price | Next Payment | Category | Payment Method |" >> "$OUTPUT"
echo "|----|------|-------|---------------|----------|----------------|" >> "$OUTPUT" echo "|----|------|-------|---------------|----------|----------------|" >> "$OUTPUT"
jq -r ' jq -r '.subscriptions[] | [.id, .name, .price, .next_payment, .category_name, .payment_method_name] | @tsv' "$TMP_FILE" |
.subscriptions[] | [ while IFS=$'\t' read -r id name price next cat pay; do
.id,
.name,
(.price | tostring),
.next_payment,
(.category_name | gsub("&gt;"; ">") | gsub("&amp;"; "&")),
.payment_method_name
] | @tsv
' temp_response.json | while IFS=$'\t' read -r id name price next cat pay; do
echo "| $id | $name | $price | $next | $cat | $pay |" >> "$OUTPUT" echo "| $id | $name | $price | $next | $cat | $pay |" >> "$OUTPUT"
done done
fi fi
rm -f temp_response.json echo "💾 Saved Markdown to $OUTPUT"
echo "💾 Saved Markdown table to $OUTPUT" rm -f "$TMP_FILE"
}
else output_csv() {
if [ "$FULL" = true ]; then if $FULL; then
echo '"id","name","price","currency_id","next_payment","cycle","auto_renew","category_name","payment_method_name"' > "$OUTPUT" echo '"id","name","price","currency_id","next_payment","cycle","auto_renew","category_name","payment_method_name"' > "$OUTPUT"
jq -r ' jq -r '.subscriptions[] | [.id, .name, .price, .currency_id, .next_payment, .cycle, .auto_renew, .category_name, .payment_method_name] | @csv' "$TMP_FILE" >> "$OUTPUT"
.subscriptions[] | [
.id,
.name,
.price,
.currency_id,
.next_payment,
.cycle,
.auto_renew,
.category_name,
.payment_method_name
] | @csv
' temp_response.json >> "$OUTPUT"
else else
echo '"id","name","price","next_payment","category_name","payment_method_name"' > "$OUTPUT" echo '"id","name","price","next_payment","category_name","payment_method_name"' > "$OUTPUT"
jq -r ' jq -r '.subscriptions[] | [.id, .name, .price, .next_payment, .category_name, .payment_method_name] | @csv' "$TMP_FILE" >> "$OUTPUT"
.subscriptions[] | [
.id,
.name,
.price,
.next_payment,
.category_name,
.payment_method_name
] | @csv
' temp_response.json >> "$OUTPUT"
fi fi
rm -f temp_response.json
echo "💾 Saved CSV to $OUTPUT" echo "💾 Saved CSV to $OUTPUT"
fi rm -f "$TMP_FILE"
else }
echo "❌ Request failed. Response not saved."
fi # 🏁 Main
parse_args "$@"
fetch_data
case "$MODE" in
json) output_json ;;
md) output_markdown ;;
csv) output_csv ;;
esac