---
title: Consolidated Morning Briefing
description: 
published: true
date: 2026-04-19T04:27:14.348Z
tags: 
editor: markdown
dateCreated: 2026-04-19T04:27:14.348Z
---

# Gremlin's Morning Briefing System

**Complete automated daily briefing integrating calendars, work taskings, and personal email**

---

## Overview

The Gremlin Morning Briefing is an automated system that generates a unified HTML dashboard displaying:
- **SIL Calendar** (Google Calendar synced to Nextcloud)
- **Work Email Taskings** (exported from Outlook as Markdown)
- **Personal Email Tasks** (parsed from MailCow IMAP via Ollama LLM)
- **Federal Holidays**
- **Future:** NetGrimoire stack status, Vikunja tasks

The briefing organizes everything into three priority zones:
- 🔥 **TODAY** - Overdue tasks, today's events, high-priority items
- 📆 **THIS WEEK** - Next 7 days planning horizon
- 📬 **LATER** - Days 8-14, backlog items

---

## Architecture

### Data Sources
1. **Google Calendar (SIL)** → Service Account API
2. **Nextcloud** → Work briefing MD file via WebDAV
3. **MailCow IMAP** → Personal email scanning
4. **Ollama (llama3.2:3b)** → Email parsing for tasks/due dates
5. **Federal Holidays API** → date.nager.at

### Processing Pipeline
```
Google Calendar ──┐
Work Email MD ────┼──> Python Script ──> HTML Briefing ──> Apache ──> Glance iframe
MailCow IMAP ─────┤         ↓
Federal Holidays ─┘    Nextcloud CalDAV
                       (SIL + Taskings calendars)
```

### Key Optimizations
- **Email Pre-screening:** Regex filters reduce Ollama calls from 285 → ~30
- **Caching:** Processed email IDs prevent re-processing
- **Zone-based bucketing:** Smart categorization by urgency and date

---

## Installation

### Prerequisites

**System Requirements:**
- Python 3.9+
- Docker Swarm (for Glance)
- Apache web server
- Nextcloud instance
- MailCow mail server
- Ollama with `llama3.2:3b` model

**Python Dependencies:**
```bash
pip3 install --break-system-packages \
  google-auth google-auth-oauthlib google-auth-httplib2 \
  google-api-python-client \
  caldav icalendar pytz requests
```

---

### 1. Google Calendar Setup

**Create Service Account:**
1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create project: `NetGrimoire`
3. Enable **Google Calendar API**
4. Create Service Account: `n8n-calendar-reader@netgrimoire.iam.gserviceaccount.com`
5. Generate JSON key → save to `/home/gremlin/.config/gcloud/netgrimoire-calendar-sa.json`
6. Set permissions: `chmod 600 /home/gremlin/.config/gcloud/netgrimoire-calendar-sa.json`

**Share Calendar:**
1. Open SIL calendar in Google Calendar
2. Settings → Share with specific people
3. Add service account email (read-only)

---

### 2. Nextcloud Setup

**Create App Password:**
1. Login to Nextcloud: `https://cloud.netgrimoire.com`
2. Settings → Security → Devices & sessions
3. Create new app password: `Gremlin Briefing Script`
4. Save password for script configuration

**Create Folders:**
```
/Briefings/
  └── work-briefing.md
```

**Upload Work Briefing:**
- Export daily work tasking email as Markdown
- Upload to `/Briefings/work-briefing.md` (overwrite daily)

---

### 3. Script Installation

**Deploy Script:**
```bash
# Create directory structure
mkdir -p /home/gremlin/scripts
mkdir -p /home/gremlin/.config/gcloud
mkdir -p /home/gremlin/logs

# Copy script
cp gremlin-briefing-final.py /home/gremlin/scripts/
chmod 600 /home/gremlin/scripts/gremlin-briefing-final.py
chmod +x /home/gremlin/scripts/gremlin-briefing-final.py

# Edit configuration
nano /home/gremlin/scripts/gremlin-briefing-final.py
```

**Configuration (lines 24-48):**
```python
# Nextcloud
NEXTCLOUD_PASSWORD = 'YOUR_APP_PASSWORD_HERE'

# MailCow IMAP
MAILCOW_PASSWORD = 'YOUR_MAILCOW_PASSWORD_HERE'

# Email Keywords (flagged for attention)
EMAIL_FLAG_KEYWORDS = ['mxroute', 'travel', 'dts', 'cititravel', '.mil', 'cindy']

# VIP Senders (always surface in TODAY)
EMAIL_VIP_SENDERS = ['cindy']
```

**Test Run:**
```bash
/home/gremlin/scripts/gremlin-briefing-final.py
```

Expected output:
```
============================================================
Gremlin Briefing Generator - Optimized
============================================================
Fetching Google Calendar events...
  Found 15 SIL events
Fetching work briefing from Nextcloud...
  Found work briefing (12453 chars)
  Parsed 18 work events, 24 action items
Connecting to MailCow IMAP...
  Found 285 emails in last 14 days
  Fetched 8 new emails
Pre-screening 8 emails...
  ✓ 3 emails need processing (filtered 5 junk)
Processing 3 emails with Ollama...
  Parsing: Need to get Harvey a bath on april 19th...
  ✓ Successfully parsed 3 emails
Syncing SIL calendar to Nextcloud...
  ✓ SIL sync complete
Syncing email tasks to Nextcloud...
  ✓ Added 2 email tasks to calendar
Fetching federal holidays...
  Found 11 federal holidays
Organizing items into zones...
Generating HTML briefing...
Writing to /data/nfs/znas/Docker/web/pages/netgrimoire/public_html/briefing.html...
============================================================
✓ SUCCESS!
  TODAY: 3 events, 2 tasks, 1 flagged, 0 VIP
  THIS WEEK: 5 days, 1 tasks
  LATER: 2 days, 0 backlog
============================================================
```

---

### 4. Cron Job Setup

**Daily Execution (6:00 AM):**
```bash
crontab -e
```

Add:
```cron
0 6 * * * /usr/bin/python3 /home/gremlin/scripts/gremlin-briefing-final.py >> /home/gremlin/logs/briefing.log 2>&1
```

**Manual Trigger:**
```bash
# Run now
/home/gremlin/scripts/gremlin-briefing-final.py

# Clear email cache (force re-process all emails)
rm /home/gremlin/.gremlin-email-cache.json
```

---

### 5. Glance Integration

**Update Glance Configuration:**
```bash
nano /data/nfs/znas/Docker/glance/glance.yml
```

**Add iframe widget:**
```yaml
- type: iframe
  title: "☠️ Gremlin's Morning Briefing"
  source: "https://www.netgrimoire.com/briefing.html"
  height: 1200
```

**Restart Glance:**
```bash
docker service update --force glance_glance
```

**Access:**
- Direct: `https://www.netgrimoire.com/briefing.html`
- Via Glance: `https://home.netgrimoire.com`

---

## Configuration Guide

### Adding Email Keywords

Email keywords flag specific topics for your attention. Emails matching these keywords appear in the **🚨 Flagged Emails** section of TODAY.

**Edit Script:**
```bash
nano /home/gremlin/scripts/gremlin-briefing-final.py
```

**Modify Line 45:**
```python
EMAIL_FLAG_KEYWORDS = ['mxroute', 'travel', 'dts', 'cititravel', '.mil', 'cindy']
```

**Add keywords** (lowercase, no spaces):
```python
EMAIL_FLAG_KEYWORDS = [
    'mxroute',      # Hosting provider mentions
    'travel',       # Travel-related emails
    'dts',          # Defense Travel System
    'cititravel',   # Travel booking system
    '.mil',         # Military emails
    'cindy',        # From/mentions Cindy
    'vikunja',      # Task management mentions
    'backup',       # Backup-related alerts
    'downtime',     # Service outage mentions
]
```

**Keywords are case-insensitive** and match anywhere in subject or first 500 chars of body.

---

### Adding VIP Senders

VIP senders always appear in TODAY's **💕 VIP** section, regardless of content.

**Edit Line 46:**
```python
EMAIL_VIP_SENDERS = ['cindy']
```

**Add multiple VIPs:**
```python
EMAIL_VIP_SENDERS = [
    'cindy',           # Wife
    'boss@company',    # Boss's email
    'urgent@',         # Any address containing "urgent"
]
```

**Matching is case-insensitive substring match** on the `From:` field.

---

### Adjusting Email Search Window

**Default:** 14 days (last 2 weeks)

**Edit Line 44:**
```python
EMAIL_SEARCH_DAYS = 14
```

**Change to 7 days:**
```python
EMAIL_SEARCH_DAYS = 7
```

**Warning:** Larger windows = more emails to process. Pre-screening mitigates this, but 30+ days may be slow.

---

### Customizing Ollama Model

**Default:** `llama3.2:3b` (fast, good quality for email parsing)

**Edit Line 49:**
```python
OLLAMA_MODEL = 'llama3.2:3b'
```

**Alternative models:**
```python
OLLAMA_MODEL = 'qwen2.5-coder:7b'  # Better date extraction, slower
OLLAMA_MODEL = 'llama3.1:8b'       # More accurate, much slower
```

**Test model availability:**
```bash
curl http://docker4:11434/api/tags
```

---

## Email Cache Management

The script tracks processed emails in `/home/gremlin/.gremlin-email-cache.json` to avoid re-processing.

**View cache:**
```bash
cat /home/gremlin/.gremlin-email-cache.json
```

**Clear cache (force re-process all emails):**
```bash
rm /home/gremlin/.gremlin-email-cache.json
```

**Cache grows indefinitely.** Periodically clear to prevent bloat:
```bash
# Monthly cache reset (add to crontab)
0 0 1 * * rm /home/gremlin/.gremlin-email-cache.json
```

---

## Work Email Export Process

**Daily Workflow:**

1. **Export from Outlook:**
   - Select "Daily Executive Briefing" email
   - Save as `.md` file

2. **Upload to Nextcloud:**
   - Navigate to `/Briefings/`
   - Upload/replace `work-briefing.md`

3. **Script automatically fetches** on next run

**Expected MD Format:**
```markdown
## Daily Executive Briefing: April 18 - April 24, 2026

### Next Week's Schedule

| Date | Time | Subject | Location |
|:---|:---|:---|:---|
| Mon, Apr 20 | 08:30 - 09:30 | Meeting Name | Conference Room |

### Prioritized Action Items

| Priority | From | Subject | Action / Deadline |
|:---|:---|:---|:---|
| **High** | Boss Name | Task Subject | Action required: Details here |
| Normal | Colleague | Task Subject | Action required: Details here |
```

**Script parses:**
- Schedule table → merges into calendar view
- Action items table → displays in **📧 Work Taskings**
- Filters out "Canceled:" meetings

---

## Troubleshooting

### Script Fails: Google Calendar Auth Error

**Error:**
```
Error fetching Google Calendar: invalid_grant
```

**Fix:**
1. Verify service account JSON file exists:
   ```bash
   ls -l /home/gremlin/.config/gcloud/netgrimoire-calendar-sa.json
   ```
2. Check calendar sharing in Google Calendar settings
3. Regenerate service account key if needed

---

### Script Fails: Nextcloud Connection Error

**Error:**
```
Error fetching work briefing: HTTP 401
```

**Fix:**
1. Verify Nextcloud app password
2. Test WebDAV manually:
   ```bash
   curl -u graymutt:YOUR_PASSWORD \
     https://cloud.netgrimoire.com/remote.php/dav/files/graymutt/Briefings/work-briefing.md
   ```
3. Regenerate app password if needed

---

### Script Fails: MailCow IMAP Error

**Error:**
```
Error fetching emails: Login failed
```

**Fix:**
1. Verify MailCow credentials
2. Test IMAP manually:
   ```bash
   telnet 192.168.5.16 143
   # Type: a001 LOGIN phil@pncharris.com YOUR_PASSWORD
   ```
3. Check MailCow IMAP is enabled for user

---

### Ollama Parsing Returns Null

**Symptom:** Emails found, but `Successfully parsed 0 emails`

**Causes:**
1. Ollama not running:
   ```bash
   curl http://docker4:11434/api/tags
   ```
2. Model not pulled:
   ```bash
   docker exec -it ollama ollama pull llama3.2:3b
   ```
3. Timeout issues (increase Line 368):
   ```python
   timeout=30  # Increase to 60 for slower models
   ```

---

### Email Pre-screening Too Aggressive

**Symptom:** Important emails not appearing in briefing

**Fix:** Check pre-screening patterns in `should_process_email()` (Lines 297-334)

**Add more date patterns:**
```python
date_patterns = [
    r'\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b',
    r'\b\d{4}-\d{2}-\d{2}\b',
    # Add custom patterns
    r'next week',
    r'by end of',
]
```

**Add more action keywords:**
```python
action_keywords = [
    'due', 'deadline', 'task', 'todo',
    # Add custom keywords
    'please review',
    'need your input',
    'waiting on',
]
```

---

### HTML Not Rendering in Glance

**Symptom:** Blank iframe or "403 Forbidden"

**Fix:**
1. Verify Apache serving file:
   ```bash
   curl https://www.netgrimoire.com/briefing.html
   ```
2. Check file permissions:
   ```bash
   ls -l /data/nfs/znas/Docker/web/pages/netgrimoire/public_html/briefing.html
   # Should be: -rw-r--r-- 1 1964 1964
   ```
3. Verify Apache VirtualHost config:
   ```bash
   cat /data/nfs/znas/Docker/web/apache/netgrimoire.conf
   ```

---

## Future Enhancements

### Planned Integrations

**NetGrimoire Stack Status:**
```python
def fetch_netgrimoire_status():
    """Fetch Uptime Kuma API for service health"""
    # Query Uptime Kuma heartbeat API
    # Return: {services_up: N, services_down: N, warnings: [...]}
```

**Vikunja Tasks:**
```python
def fetch_vikunja_tasks():
    """Fetch tasks from Vikunja API"""
    # Query Vikunja API for open tasks
    # Merge into TODAY/THIS WEEK/LATER zones
```

**Additional Google Calendars:**
- PNC Fish and More
- Personal calendar
- Shared family calendar

**Gremlin Mood States:**
- Time-based commentary variation
- Workload-based sass level
- Stack health integration

---

## File Locations

### Script Files
```
/home/gremlin/
├── scripts/
│   └── gremlin-briefing-final.py
├── logs/
│   └── briefing.log
└── .config/
    └── gcloud/
        └── netgrimoire-calendar-sa.json
```

### Data Files
```
/home/gremlin/
└── .gremlin-email-cache.json

/data/nfs/znas/Docker/web/pages/netgrimoire/public_html/
└── briefing.html
```

### Nextcloud Files
```
/Briefings/
└── work-briefing.md
```

---

## Related Documentation

- [Google Calendar API](https://developers.google.com/calendar/api/guides/overview)
- [Nextcloud WebDAV](https://docs.nextcloud.com/server/latest/user_manual/en/files/access_webdav.html)
- [Ollama API](https://github.com/ollama/ollama/blob/main/docs/api.md)
- [Glance Configuration](https://github.com/glanceapp/glance)

---

## Maintenance

### Weekly Tasks
- ✅ Review flagged keywords effectiveness
- ✅ Check email cache size
- ✅ Verify Nextcloud work briefing uploads

### Monthly Tasks
- ✅ Clear email cache
- ✅ Review Ollama parsing accuracy
- ✅ Update work email export process if needed

### Quarterly Tasks
- ✅ Review Google Calendar sharing permissions
- ✅ Rotate Nextcloud app password
- ✅ Update federal holidays API if needed

---

## Support

**Logs:**
```bash
# Script execution log
tail -f /home/gremlin/logs/briefing.log

# Glance container logs
docker service logs glance_glance --tail 50
```

**Manual debugging:**
```bash
# Run script with verbose output
python3 -u /home/gremlin/scripts/gremlin-briefing-final.py

# Test individual components
python3 -c "from google.oauth2 import service_account; print('Google auth OK')"
```

---

*Last updated: April 18, 2026*
*Maintained by: Phil Harris (graymutt)*