# ShiftOps Tool Architecture Documentation


**Last Updated:** 2025-11-20

## Overview

ShiftOps is a lead generation grader tool that analyzes businesses (restaurants, retail, healthcare) using Google Places API data. It generates scores across 5 operational pillars, calculates potential cost savings, analyzes competitive positioning, and provides actionable recommendations.

## Architecture Components

### Frontend Pages

1. **`v2/pages/shiftops.php`** (4416 lines)

   - Main entry point with Google Places autocomplete search
   - Handles user input and business selection
   - Initiates analysis requests (essential and enhanced modes)
   - Displays loading states and initial results
   - Stores data in localStorage for report page
   - Two-phase loading: essential mode first, enhanced mode in background

2. **`v2/pages/shiftops-report.php`** (18310 lines)
   - Report display page with comprehensive analysis
   - Gated content form (email collection)
   - HubSpot lead submission
   - PDF export functionality
   - Dynamic data refresh from localStorage
   - Progressive rendering (priority sections first)
   - Responsive design (mobile and desktop)

### API Endpoints

1. **`v2/api/shiftops.php`** (6084 lines)

   - Main API endpoint and orchestration
   - Contains `ShiftOpsAnalyzer` class (lines 708-6084)
   - Handles caching (1-hour TTL)
   - Supports essential and enhanced modes
   - Customer matching integration
   - Comprehensive error handling with fallbacks
   - Request ID tracking for logging

2. **`v2/api/shiftops-cost-calculator.php`** (922 lines)

   - Cost savings calculations
   - Team size estimation
   - ROI analysis
   - Break-even calculations

3. **`v2/api/shiftops-competitive-analyzer.php`** (910 lines)

   - Market positioning analysis
   - Competitor analysis using Google Places Nearby Search
   - Growth potential assessment

4. **`v2/api/shiftops-recommendations-engine.php`** (947 lines)

   - Priority-ranked recommendations
   - Implementation roadmap generation
   - Pillar-specific recommendations

5. **`v2/api/shiftops-user-explanations.php`** (126 lines)

   - User-friendly score explanations
   - Pillar-specific explanations
   - Grade interpretation
   - Score range explanations

6. **`v2/api/shiftops-customer-matcher.php`** (918 lines)

   - Ordio customer identification
   - Multi-strategy matching (domain, exact, prefix, fuzzy, address)
   - Lazy loading of customer list (3018+ customers)
   - Path resolution with multiple fallbacks
   - Performance optimization (domain indexing, early exit)
   - mbstring fallback for production compatibility
   - Comprehensive logging with request IDs

7. **`v2/api/shiftops-hubspot.php`** (325 lines)

   - HubSpot lead submission
   - Rich data logging to `logs/shiftops-leads-full.log`
   - UTM parameter tracking
   - Form ID: `41d07332-6697-4daa-b27e-dd60515f9c0f`
   - Portal ID: `145133546`

8. **`v2/api/shiftops-hubspot-customers.php`** (547 lines)

   - Customer list sync from HubSpot
   - Pagination handling
   - JSON file storage (`v2/data/ordio-customers.json`)
   - Conditional config loading (works without config file)

9. **`v2/api/shiftops-nps.php`** (238 lines)

   - NPS survey submission endpoint
   - Validates NPS score (0-10 integer)
   - Calculates NPS category (Promoter, Passive, Detractor)
   - Submits to HubSpot form (Form ID: `804459f7-c18d-4da6-8a0b-a81f44bb8275`)
   - Logs rich NPS data to `logs/shiftops-nps.log`
   - Includes ShiftOps context (business name, score, grade, place_id)

10. **`v2/api/shiftops-health-check.php`** (123 lines)

    - Diagnostic endpoint for customer matcher status
    - File existence and permissions check
    - Customer count verification
    - PHP configuration display

11. **`v2/api/shiftops-test-matching.php`** (172 lines)
    - Test endpoint for customer matching debugging
    - Detailed matching output with character-by-character comparison
    - Useful for troubleshooting matching issues

### Frontend Assets

1. **`v2/js/shiftops-pdf-generator.js`** (1468 lines)
   - PDF export functionality
   - HTML to PDF conversion
   - Multi-page support

### Data Files

1. **`v2/data/industry_benchmarks.php`** (303 lines)

   - Industry-specific benchmarks
   - Seasonal factors
   - Peak hours data
   - Compliance requirements

2. **`v2/data/shiftops-examples.json`** (314 lines)

   - Example API requests and responses
   - Essential and enhanced mode examples
   - Reference for API integration

3. **`v2/config/shiftops-customers.php`** (55 lines)
   - Configuration constants
   - Customer matching thresholds
   - Score boost configurations
   - HubSpot API settings

## Data Flow

### User Input → Analysis → Report → HubSpot

1. **User Input** (`shiftops.php`)

   - User searches for business using Google Places autocomplete
   - Business selection triggers analysis
   - Google Places API provides business data (place_id, name, address, rating, etc.)

2. **API Request** (Essential Mode)

   - POST to `/v2/api/shiftops.php` with `mode='essential'`
   - Includes place_id, name, and all Google Places data
   - Returns minimal data for loading screen (< 2 seconds target)
   - Request ID generated for logging traceability

3. **Analysis Processing** (`shiftops.php` → `ShiftOpsAnalyzer`)

   - **Cache Check:** Generate cache key from business data, check if cached (< 1 hour old)
   - **Customer Matching:** Check if business is Ordio customer (via `ShiftOpsCustomerMatcher`)
     - Loads customer list from `v2/data/ordio-customers.json` (lazy loading)
     - Multi-strategy matching: domain → exact → prefix → fuzzy → address
     - Returns match with confidence score (0-100)
   - **Calculate Essential Scores:**
     - Location analysis (competitor density, urban/suburban)
     - Online presence (rating, photos, reviews, team size estimate)
     - ShiftOps score (5 pillars: scheduling, absence, time tracking, compliance, payroll)
     - Cost savings (team size estimation, potential savings, ROI)
     - Context data (holidays, current weather, seasonal trends)
   - **Error Handling:** Each step wrapped in try-catch with fallbacks

4. **API Request** (Enhanced Mode)

   - POST to `/v2/api/shiftops.php` with `mode='enhanced'` (background request)
   - Returns full analysis (3-5 seconds target):
     - Website analysis (performance, accessibility, SEO)
     - Competitor analysis (Google Places Nearby Search)
     - Competitive positioning (market position, growth potential)
     - Enhanced recommendations (priority-ranked, with ROI estimates)
     - Full weather forecast (16 days from Open-Meteo)
     - Business hours analysis (complexity, peak hours)

5. **Data Storage** (localStorage)

   - **Essential data** → `shiftops_report_data` (primary storage):
     - Business info, scores, cost savings, location, online presence
     - Context data (holidays, weather, seasonal trends)
     - Competitive data (may be null in essential mode)
     - Recommendations (structured: quick_wins, high_value, strategic)
     - Customer match info
   - **Enhanced data** → `shiftops_enhanced_data`:
     - Complete analysis from enhanced mode
   - **Backup data** → `shiftops_analysis_data` (legacy/fallback):
     - Fallback data structure
     - Used when primary data unavailable
   - **Report unlock status** → `shiftops_report_unlocked`:
     - Set to `'true'` when user submits email form
     - Used to unlock gated content on report page
     - Persists across page reloads
   - **NPS survey status** → `shiftops_nps_submitted`:
     - Set to `'true'` when user submits NPS survey
     - Prevents showing survey again to same user
     - Persists across page reloads
   - **NPS survey dismissed** → `shiftops_nps_dismissed`:
     - Set to `'true'` when user dismisses NPS survey
     - Prevents showing survey again to same user
     - Can be cleared to show survey again

6. **Report Display** (`shiftops-report.php`)

   - Loads data from localStorage (`shiftops_report_data`)
   - Displays gated content (blurred until email submitted)
   - Progressive rendering (priority sections first):
     1. Hero section with score
     2. Pillar scores
     3. Cost savings
     4. Recommendations
     5. Competitive analysis (if available)
   - Responsive design (mobile and desktop layouts)

7. **Email Collection**

   - User submits email form (validates email format)
   - Unlocks full report (removes blur, enables PDF export)
   - Stores `shiftops_report_unlocked` in localStorage
   - Triggers HubSpot submission

8. **HubSpot Submission** (`shiftops-hubspot.php`)
   - POST to `/v2/api/shiftops-hubspot.php`
   - Includes all ShiftOps data + UTM parameters
   - Logs rich data to `logs/shiftops-leads-full.log` (JSON format, one entry per line)
   - Submits to HubSpot form (Portal ID: 145133546, Form ID: 41d07332-6697-4daa-b27e-dd60515f9c0f)
   - Rate limiting: 0.6s delay between requests

## Scoring Algorithm

### 5 Pillars (0-20 points each)

1. **Scheduling Efficiency** (0-20)

   - Digital maturity (0-5 points)
   - Operational complexity (0-7 points)
   - Team scale indicators (0-3 points)
   - Customer boost: +3 baseline + 35% boost (max 18)

2. **Absence Stability** (0-20)

   - Review sentiment analysis
   - Seasonal factors
   - Weather risk assessment
   - Customer boost: +3 baseline + 25% boost (max 20)

3. **Time Tracking Hygiene** (0-20)

   - Hours complexity analysis
   - Standard vs irregular hours
   - Customer boost: +3 baseline + 35% boost (max 18)

4. **Compliance Docs** (0-20)

   - Industry-specific requirements
   - Documentation completeness
   - Customer boost: +3 baseline + 30% boost (max 19)

5. **Payroll Readiness** (0-20)
   - DATEV integration indicators
   - Data structure quality
   - Customer boost: +3 baseline + 30% boost (max 18)

### Total Score Calculation

1. Calculate raw pillar scores (floats)
2. Sum raw scores
3. Apply data completeness multiplier (0.50-1.0)
4. Apply customer cap (max 95 for customers)
5. Round total score
6. Adjust pillar scores proportionally to match total
7. Assign grade (A+, A, A-, B+, B, B-, C+, C, C-, D+, D, D-, F)

**For complete scoring documentation, see [Scoring System Documentation](SHIFTOPS_SCORING_SYSTEM.md)**

### Data Completeness Impact

**Multiplier Calculation:**

- 4 fields complete (100%): 1.0 multiplier (no penalty)
- 3 fields complete (75%): 0.85 multiplier
- 2 fields complete (50%): 0.70 multiplier
- 1 field complete (25%): 0.50 multiplier
- 0 fields complete (0%): 0.50 multiplier

**Fields Checked:**

- Website (exists)
- Opening hours (exists)
- Reviews (exists and count > 0)
- Photos (exists and count > 0)

**Customer Adjustment:**

- Ordio customers get more lenient multiplier (0.98 minimum if website exists)
- Missing website only: 0.94 multiplier (instead of 0.95)
- Reflects that customers use digital tools, so data completeness is inherently higher

### Scoring Algorithm Details

#### Step-by-Step Calculation Process

1. **Calculate Raw Pillar Scores** (floats, 0.00-20.00)

   - Each pillar calculated independently
   - Uses business data (rating, reviews, hours, website, etc.)
   - Industry-specific baselines applied
   - Customer boost applied if applicable (baseline + percentage)

2. **Sum Raw Scores**

   - Simple addition: `$rawTotalScore = array_sum($rawScores)`

3. **Apply Data Completeness Multiplier**

   - Multiplier based on available data fields (see above)
   - `$adjustedScore = $rawTotalScore * $dataCompleteness['multiplier']`

4. **Apply Customer Cap**

   - If Ordio customer: `min(MAX_TOTAL_SCORE, $adjustedScore)` where MAX_TOTAL_SCORE = 95
   - Rationale: Customers shouldn't score 100 (implies no improvement needed)
   - Maintains credibility while recognizing good practices

5. **Round Total Score**

   - Customers: `round($adjustedScore)` (rounds up)
   - Non-customers: `floor($adjustedScore)` (rounds down)

6. **Adjust Pillar Scores Proportionally**

   - Uses `adjustPillarScoresToMatchTotal()` function
   - Calculates ratio: `$ratio = $targetTotal / $rawSum`
   - Applies ratio to each pillar score
   - Rounds up pillars with highest fractional parts until sum matches
   - Ensures: `sum($pillarScores) == $totalScore`

7. **Assign Grade**
   - 90-100: A+
   - 80-89: A
   - 70-79: B
   - 60-69: C
   - 40-59: D
   - 0-39: F

#### Example Calculation

**Non-Customer Example:**

- Raw scores: [12.5, 11.3, 10.8, 13.2, 12.1] = 59.9
- Data completeness: 75% (3 fields) → multiplier 0.95
- Adjusted: 59.9 \* 0.95 = 56.905
- Rounded: floor(56.905) = 56
- Pillar scores adjusted: [12, 11, 10, 13, 10] = 56 ✓
- Grade: D

**Customer Example:**

- Raw scores: [15.2, 14.8, 15.5, 16.1, 15.3] = 76.9
- Data completeness: 100% (4 fields) → multiplier 1.0
- Adjusted: 76.9 \* 1.0 = 76.9
- Customer cap: min(95, 76.9) = 76.9
- Rounded: round(76.9) = 77
- Pillar scores adjusted: [15, 15, 16, 16, 15] = 77 ✓
- Grade: B

**Customer with Boost Example:**

- Raw scores: [18.5, 19.2, 18.8, 19.5, 18.9] = 94.9
- Data completeness: 100% → multiplier 1.0
- Adjusted: 94.9 \* 1.0 = 94.9
- Customer cap: min(95, 94.9) = 94.9
- Rounded: round(94.9) = 95
- Pillar scores adjusted: [19, 19, 19, 19, 19] = 95 ✓
- Grade: A+

## Caching Strategy

### Cache Key Generation

- Based on: place_id, name, rating, user_ratings_total, types, mode
- Format: `shiftops_analysis_{md5_hash}.json`
- Location: `v2/cache/systems/shiftops/`

### Cache TTL

- 1 hour (3600 seconds)
- Automatic expiration on read
- Manual cleanup on cache miss

### Cache Flow

1. Generate cache key from business data
2. Check if cache file exists
3. Validate cache age (< 1 hour)
4. Return cached data or generate new analysis
5. Store new analysis in cache

## Customer Matching

### Matching Strategies (in order of confidence)

1. **Domain Match** (95% confidence)

   - Exact domain match from website

2. **Exact Name Match** (100% confidence)

   - Normalized name exact match

3. **Prefix Match** (75-90% confidence)

   - One name starts with another (≥5 chars)

4. **Fuzzy Match** (75-95% confidence)

   - Levenshtein distance similarity (≥85%)

5. **Address Match** (+5% boost)
   - Additional validation if name matches

### Customer Boost

- Baseline bonus: +3 points per pillar
- Percentage boost: 25-35% per pillar
- Maximum scores: 18-20 per pillar
- Total score cap: 95

## External Integrations

### Google Places API

- Autocomplete for search
- Place Details for business data
- Nearby Search for competitor analysis
- Photo API for business images

### Weather API (Open-Meteo)

- 16-day forecast
- Current weather conditions
- Free tier, no API key required

### Holiday API (Nager.Date)

- Public holidays by country
- Regional holiday support
- Free tier, no API key required

### HubSpot API

- Form submission endpoint
- Customer list sync (companies API)
- Rate limiting: 0.6s between requests

## File Dependencies

```
shiftops.php
├── industry_benchmarks.php
├── shiftops-cost-calculator.php
│   └── industry_benchmarks.php
├── shiftops-competitive-analyzer.php
├── shiftops-recommendations-engine.php
├── shiftops-user-explanations.php (NEW)
├── shiftops-customer-matcher.php
│   └── shiftops-customers.php (config)
└── shiftops-hubspot-customers.php (for sync)
```

## Data Structures

### Business Data (Input)

```php
[
    'place_id' => string,
    'name' => string,
    'formatted_address' => string,
    'website' => string,
    'types' => array,
    'rating' => float,
    'photos' => array,
    'geometry' => ['location' => ['lat', 'lng']],
    'user_ratings_total' => int,
    'opening_hours' => array,
    'service_options' => array,
    'reviews' => array,
    'address_components' => array
]
```

### Analysis Response

```php
[
    'business_info' => array,
    'location_analysis' => array,
    'online_presence' => array,
    'shiftops_score' => [
        'total_score' => int (0-100),
        'pillar_scores' => [
            'scheduling_efficiency' => int (0-20),
            'absence_stability' => int (0-20),
            'time_tracking_hygiene' => int (0-20),
            'compliance_docs' => int (0-20),
            'payroll_readiness' => int (0-20)
        ],
        'grade' => string (A+, A, A-, B+, B, B-, C+, C, C-, D+, D, D-, F),
        'benchmark_comparison' => array,
        'data_completeness' => array
    ],
    'cost_savings' => array,
    'context_data' => array,
    'competitive_positioning' => array (enhanced mode),
    'recommendations' => array (enhanced mode)
]
```

## Performance Considerations

1. **Two-Phase Loading**

   - Essential mode: Fast initial load (< 2s)
   - Enhanced mode: Background loading (additional 3-5s)

2. **Caching**

   - 1-hour cache reduces API calls
   - File-based caching (no database)

3. **LocalStorage Optimization**

   - Essential data only: ~50-100KB
   - Enhanced data: ~200-300KB
   - Weather forecast truncated to 7 days
   - Holidays filtered to next 30 days

4. **API Rate Limiting**
   - Google Places: 1000 requests/day (free tier)
   - Weather: Unlimited (Open-Meteo)
   - Holiday: Unlimited (Nager.Date)
   - HubSpot: 100 requests per 10 seconds

## Error Handling

1. **API Errors**

   - Graceful fallbacks for external APIs
   - Estimated data when APIs fail
   - Error logging to `logs/shiftops-debug.log`

2. **Data Validation**

   - Required fields validation
   - Type checking
   - Range validation for scores

3. **Cache Errors**
   - Silent failure (proceed without cache)
   - Cache cleanup on corruption

## Security Considerations

1. **API Keys**

   - Google Places API key in code (should be env variable)
   - HubSpot token in config (env variable support)

2. **Input Validation**

   - Sanitize user input
   - Validate place_id format
   - Rate limiting (cache helps)

3. **XSS Prevention**
   - JSON encoding for localStorage
   - HTML escaping in templates

## Future Improvements

1. **Code Organization**

   - Split large files (shiftops.php is 3700+ lines)
   - Extract common utilities
   - Better separation of concerns

2. **Testing**

   - Unit tests for scoring algorithms
   - Integration tests for API endpoints
   - E2E tests for user flow

3. **Performance**

   - Database caching instead of files
   - Redis for distributed caching
   - CDN for static assets

4. **Documentation**
   - API documentation (OpenAPI/Swagger)
   - Code comments
   - Developer guide
