# Yearly Forecast Topic Include/Exclude Functionality

## Overview

The `generate_yearly_html()` function in `/pdf-files-app/generatehtml.php` now supports topic filtering, allowing users to selectively include or exclude specific report sections from yearly forecast PDFs. This implementation follows the same pattern used in the personality profile function.

---

## Implementation Details

### Topics Initialization

**Location**: [Line 217-220](generatehtml.php#L217)

```php
// Initialize topics array for yearly forecast filtering
$topics = array();
if(isset($init_data_json->topics)) {
    $topics = (array)$init_data_json->topics;
}
```

The topics array is populated from the JSON-decoded `$init_data_json->topics` object, which is passed from the API request.

### Filter Pattern

**Pattern**: `isset($topics['topic_key']) && $topics['topic_key'] === 0` to **EXCLUDE** a topic

**Default Behavior**: Topics are **INCLUDED** by default unless explicitly set to `0`

---

## Supported Topics for Yearly Forecast

### 1. Period Cycles
- **Topic Key**: `period_cycles`
- **PageID**: `2`
- **Code Location**: [Lines 621-647](generatehtml.php#L621)
- **Filter Location**: [Line 621](generatehtml.php#L621)
- **Default**: Included
- **Description**: Displays the 3 major life periods (childhood, productivity, harvest/wisdom)
- **Content**: Period headers, introductions, and interpretive text

**Filter Usage**:
```php
if(!(isset($topics['period_cycles']) && $topics['period_cycles'] === 0)) {
    // Display Period Cycles section
}
```

---

### 2. Pinnacle Cycles
- **Topic Key**: `pinnacle_cycles`
- **PageID**: `1`
- **Code Location**: [Lines 649-755](generatehtml.php#L649)
- **Filter Location**: [Line 650](generatehtml.php#L650)
- **Default**: Included
- **Description**: Displays the 4 pinnacle cycles with ages of occurrence
- **Content**: Pinnacle numbers, age ranges, and interpretations

**Filter Usage**:
```php
if(!(isset($topics['pinnacle_cycles']) && $topics['pinnacle_cycles'] === 0)) {
    // Display Pinnacle Cycles section
}
```

---

### 3. Transits: Physical
- **Topic Key**: `transits_physical`
- **PageID**: `3`
- **Code Location**: [Lines 770-815](generatehtml.php#L770)
- **Filter Location**: [Line 770](generatehtml.php#L770)
- **Type**: `1` (in header_val loop)
- **Default**: Included (if data exists)
- **Description**: Physical transits based on first name letter transitions
- **Content**: Current year transit descriptions and interpretations

**Filter Usage**:
```php
if($type == 1 && !empty($PHY_OP) && !(isset($topics['transits_physical']) && $topics['transits_physical'] === 0)) {
    // Display Physical Transits section
}
```

---

### 4. Transits: Mental
- **Topic Key**: `transits_mental`
- **PageID**: `3`
- **Code Location**: [Lines 817-839](generatehtml.php#L817)
- **Filter Location**: [Line 817](generatehtml.php#L817)
- **Type**: `2` (in header_val loop)
- **Default**: Included (if data exists)
- **Description**: Mental transits based on middle name letter transitions
- **Content**: Current year mental transit descriptions and interpretations

**Filter Usage**:
```php
if($type == 2 && !empty($MEN_OP) && !(isset($topics['transits_mental']) && $topics['transits_mental'] === 0)) {
    // Display Mental Transits section
}
```

---

### 5. Transits: Spiritual
- **Topic Key**: `transits_spiritual`
- **PageID**: `3`
- **Code Location**: [Lines 841-913](generatehtml.php#L841)
- **Filter Location**: [Line 841](generatehtml.php#L841)
- **Type**: `3` (in header_val loop)
- **Default**: Included (if data exists)
- **Description**: Spiritual transits based on last name letter transitions
- **Content**: Current year spiritual transit descriptions and interpretations

**Filter Usage**:
```php
if($type == 3 && !empty($SPR_OP) && !(isset($topics['transits_spiritual']) && $topics['transits_spiritual'] === 0)) {
    // Display Spiritual Transits section
}
```

---

### 6. Personal Years
- **Topic Key**: `personal_years`
- **PageID**: `6`
- **Code Location**: [Lines 944-962](generatehtml.php#L944)
- **Filter Location**: [Line 947](generatehtml.php#L947)
- **Default**: Included
- **Description**: Personal Year calculations for current and next year
- **Content**: Personal Year numbers and their interpretations

**Filter Usage**:
```php
if(!(isset($topics['personal_years']) && $topics['personal_years'] === 0)) {
    // Display Personal Years section
}
```

---

### 7. Personal Months
- **Topic Key**: `personal_months`
- **PageID**: `7`
- **Code Location**: [Lines 1156-1186](generatehtml.php#L1156)
- **Filter Location**: [Line 1159](generatehtml.php#L1159)
- **Default**: Included
- **Description**: Monthly personal number calculations showing current year months
- **Content**: Personal Month numbers for each month in the report year

**Filter Usage**:
```php
if(!(isset($topics['personal_months']) && $topics['personal_months'] === 0)) {
    // Display Personal Months section
}
```

---

### 8. Personal Days
- **Topic Key**: `personal_days`
- **Status**: **NOT IMPLEMENTED** - Not present in yearly forecast function
- **Reason**: Yearly forecast focuses on year/month level insights; daily forecasts are in separate reports
- **Note**: If needed in future, would require adding daily calculation logic to `generate_yearly_html()`

---

## Database Tables Referenced

### `header_details`
- **PageID 1**: Pinnacle headers
- **PageID 2**: Period headers  
- **PageID 3**: Transit headers (Physical=type1, Mental=type2, Spiritual=type3)
- **PageID 6**: Personal Year headers
- **PageID 7**: Personal Month headers

### `outputtext_details`
- **PageID 1**: Pinnacle interpretations
- **PageID 2**: Period interpretations
- **PageID 3**: Transit interpretations
- **PageID 6**: Personal Year interpretations
- **PageID 7**: Personal Month interpretations

### `transitsoutputtext_details`
- **PageID 3**: Transit letter interpretations (indexed by transit text/number)

---

## API Request Examples

### Include All Topics (Default)
```json
{
  "init_firstname": "John",
  "init_report_first_name": "John",
  "init_data_json": {
    "topics": {}
  }
}
```

### Exclude Specific Topics
```json
{
  "init_firstname": "John",
  "init_report_first_name": "John",
  "init_data_json": {
    "topics": {
      "period_cycles": 0,
      "transits_mental": 0,
      "personal_months": 0
    }
  }
}
```

### Include Only Period and Pinnacle Cycles
```json
{
  "init_firstname": "John",
  "init_report_first_name": "John",
  "init_data_json": {
    "topics": {
      "transits_physical": 0,
      "transits_mental": 0,
      "transits_spiritual": 0,
      "personal_years": 0,
      "personal_months": 0
    }
  }
}
```

---

## Code Changes Summary

| Topic | Line # | Change Type | Details |
|-------|--------|------------|---------|
| **Initialization** | 217-220 | Added | Topics array setup from JSON |
| **Period Cycles** | 621, 647 | Added | Filter condition + closing brace |
| **Pinnacle Cycles** | 650, 755 | Added | Filter condition + closing brace |
| **Transits Physical** | 770 | Modified | Added topic filter to condition |
| **Transits Mental** | 817 | Modified | Added topic filter to condition |
| **Transits Spiritual** | 841 | Modified | Added topic filter to condition |
| **Personal Years** | 947, 962 | Added | Filter condition + closing brace |
| **Personal Months** | 1159, 1187 | Added | Filter condition + closing brace |

---

## Testing Checklist

- [ ] Test with all topics included (default behavior)
- [ ] Test excluding one topic at a time
- [ ] Test excluding multiple topics simultaneously
- [ ] Test excluding transits but including cycles
- [ ] Test excluding personal year/month but including cycles
- [ ] Verify PDF page breaks align correctly when sections are excluded
- [ ] Verify no empty page breaks when consecutive sections are excluded
- [ ] Test with different PDF types (`full_yearly`, `nineyr`, `ultimate`)
- [ ] Verify database queries execute correctly for included sections
- [ ] Confirm font styling applies to all included topics

---

## Filter Logic Reference

All topic filters follow this pattern:

```php
// Check before displaying major section
if(!(isset($topics['topic_key']) && $topics['topic_key'] === 0)) {
    // Display section code here
    // ...closing brace at end of section
}
```

**Key Points**:
- Double negative: `!(isset() && === 0)` means "if NOT (exclude)"
- Returns true if topic should be displayed
- Returns false if topic is explicitly excluded (set to 0)
- Missing topics default to displayed (secure default)

---

## Integration with Personality Profile

The yearly forecast topic filtering now uses the **same architecture** as the personality profile:
- Same topic key naming convention
- Same filter pattern (`isset()` check against 0)
- Same default behavior (included unless excluded)
- Same JSON input structure from API

This ensures consistency across all report types.

---

## Future Enhancements

1. **Personal Days**: Could be added if daily-level forecasts needed
2. **Essence Cycles**: Could add filtering for essence section
3. **Duality Section**: Could add filtering for duality interpretations
4. **Custom Report Profiles**: Could save topic preferences per user
5. **Report Templates**: Could create preset topic combinations

---

## Related Documentation

- [Cycles Analysis](CYCLES_ANALYSIS.md) - Period and Pinnacle cycle implementation
- [PDF Functions Reference](pdf-files-app/README.md) - API documentation
- [Personality Profile Topics](PERSONALITY_TOPICS.md) - Similar implementation reference

