bissell-wrike-python/README.md
Dave Porter 255c03b65d Add real-time monitoring service (v2.0)
Major additions:
- wrike_monitor.py: Real-time folder monitoring with watchdog
- Daily email reports at 7PM with comprehensive statistics
- Failed file handling with error logs
- Periodic scanning for missed files
- systemd service support for production deployment
- Sequential processing to prevent race conditions
- Proper parent/child folder matching using childIds

Configuration:
- Easy path configuration for local/production
- Configurable Wrike space ID
- Email settings for daily reports
- Auto-cleanup of processed files (24h retention)

Documentation:
- INSTALLATION.md: Complete systemd service setup guide
- QUICKSTART.md: Quick reference for both tools
- Updated README.md with tool comparison
- requirements.txt with clear dependencies

Bug fixes:
- Fixed duplicate folder/project creation via childIds matching
- Added logging for skipped deliverables
- Improved cache management

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 14:31:19 -04:00

579 lines
16 KiB
Markdown

# Wrike Import Tools
Two Python tools for automatically importing project structures and deliverable tasks into Wrike from JSON files.
## Tools Overview
### 1. wrike_import.py - Simple Batch Processor
A standalone script for one-time or manual batch imports. Processes all JSON files in a directory and exits.
### 2. wrike_monitor.py - Real-time Monitor Service
A continuous monitoring service that watches a folder 24/7, processes files as they arrive, and sends daily email reports. Designed to run as a systemd service.
## Common Features
Both tools create the following Wrike structure:
- **Folders** (product categories like "Dry Specialty", "Air", "PDC")
- **Projects** (campaigns with timelines and metadata)
- **Tasks** (deliverables with custom fields and due dates)
Both intelligently handle duplicates by checking OMG numbers before creating new items.
## Features Comparison
### wrike_import.py (Batch Script)
- ✅ Simple command-line usage
- ✅ Process all files and exit
- ✅ Good for one-time imports or manual runs
- ✅ Moves processed files to Processed/
- ✅ Auto-cleanup of old files
### wrike_monitor.py (Service)
-**Real-time folder monitoring** with watchdog
-**Automatic processing** as files arrive
-**Daily email reports** at 7PM
-**Periodic scanning** for missed files
-**Failed file handling** with error logs
-**Auto-cleanup** of processed files (24h)
-**Statistics tracking** and performance monitoring
-**Systemd service** support for production
-**Sequential processing** to prevent race conditions
### Common Features (Both)
-**Automatic folder creation** from BusinessArea hierarchy
-**Project management** with start/end dates and campaign codes
-**Task creation** with comprehensive custom fields
-**Duplicate prevention** via OMG number checking
-**Smart caching** to minimize API calls
-**Detailed logging** with progress tracking
## Quick Start
### For Batch Processing (wrike_import.py)
```bash
# Install dependencies
pip install requests
# Run on a folder
python wrike_import.py /path/to/json/files/
```
### For Real-time Monitoring (wrike_monitor.py)
```bash
# Install dependencies
pip install -r requirements.txt
# Configure paths in wrike_monitor.py (lines 41-44)
# Run the monitor
python wrike_monitor.py
```
See [INSTALLATION.md](INSTALLATION.md) for systemd service setup.
## Requirements
### Python Dependencies
**For wrike_import.py:**
```bash
pip install requests
```
**For wrike_monitor.py:**
```bash
pip install requests watchdog schedule
# Or use requirements.txt:
pip install -r requirements.txt
```
### Python Version
- Python 3.6 or higher
### Wrike Requirements
- Valid Wrike API token with write permissions
- Access to the target Wrike space
- Custom fields configured in the space (see Configuration section)
## Installation
1. Clone or download this repository
2. Install dependencies:
```bash
pip install requests
```
3. Configure your Wrike API token in the script (see Configuration)
## Configuration
### 1. API Token
Edit `wrike_import.py` and update the `WRIKE_TOKEN` constant:
```python
WRIKE_TOKEN = "your_wrike_api_token_here"
```
### 2. Target Space
Update the `STAGING_SPACE_ID` to point to your target Wrike space:
```python
STAGING_SPACE_ID = "MQAAAABpz7l_" # Your space ID
```
To find your space ID:
```bash
curl -X GET "https://www.wrike.com/api/v4/spaces" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### 3. Custom Fields
The script expects the following custom fields to exist in your Wrike space:
| Field Name | Type | ID Variable | Usage |
|------------|------|-------------|-------|
| Budget | Currency | `budget` | Project budgets |
| Impact | Dropdown | `impact` | Priority level |
| Notes | Text | `notes` | Additional information |
| RAG | Dropdown | `rag` | Status (Red/Amber/Green) |
| Deliverable Category | Dropdown | `deliverable_category` | Type of deliverable |
| Actions | Text | `actions` | Next steps |
| Shoot date | Date | `shoot_date` | Photography date |
| OMG # | Text | `omg_number` | Job/Campaign number |
| Box Link | Text | `box_link` | Link to assets |
| Owner | Contacts | `owner` | Task owner |
To create these fields, use the Wrike API or create them manually in the Wrike interface.
**Update custom field IDs** in the script after creating them:
```python
CUSTOM_FIELDS = {
"budget": "YOUR_BUDGET_FIELD_ID",
"impact": "YOUR_IMPACT_FIELD_ID",
# ... etc
}
```
## JSON File Format
### Required Structure
```json
{
"JobSpecification": {
"ProjectDetails": {
"BusinessArea": "BISSELL > PRODUCT MARKETING > Dry Specialty",
"Title": "PowerClean FurFinder + FurGuard 2025",
"Description": "<p>PowerClean FurFinder + FurGuard 2025-related workflows</p>",
"StartDate": "2025-04-08 13:00:00+00",
"EndDate": "2025-12-31 17:06:00+00"
},
"JobDetails": {
"Number": "5791330",
"Title": "PowerClean Corded Mass User Guide",
"CampaignCode": "1647476",
"JobCategory": "UX User Guide",
"MediaType": "Creative development",
"Type": "Creative development",
"Notes": "User guide for mass market",
"BriefDate": "2025-05-23 16:00:00+00",
"DueDate": "2025-09-17 16:04:00+00",
"BusinessArea": "BISSELL > PRODUCT MARKETING > Dry Specialty"
}
}
}
```
### Field Mapping
#### Project Level
| JSON Field | Maps To | Notes |
|------------|---------|-------|
| `ProjectDetails.BusinessArea` | Folder name | Last segment (e.g., "Dry Specialty") |
| `ProjectDetails.Title` | Project title | |
| `ProjectDetails.Description` | Project description | HTML tags removed |
| `ProjectDetails.StartDate` | Project start date | Converted to YYYY-MM-DD |
| `ProjectDetails.EndDate` | Project end date | Converted to YYYY-MM-DD |
| `JobDetails.CampaignCode` | Project OMG # | Custom field |
#### Task/Deliverable Level
| JSON Field | Maps To | Notes |
|------------|---------|-------|
| `JobDetails.Number` | Task OMG # | Used for duplicate detection |
| `JobDetails.Title` | Task title | |
| `JobDetails.Notes` | Task description | |
| `JobDetails.JobCategory` | Deliverable Category | Custom field |
| `JobDetails.MediaType` | Deliverable Category | Fallback if JobCategory empty |
| `JobDetails.Type` | Notes field | Combined with Details |
| `JobDetails.BriefDate` | Task start date | |
| `JobDetails.DueDate` | Task due date | |
## Usage
### Basic Usage
Process all JSON files in a directory:
```bash
python wrike_import.py /path/to/json/files/
```
### Example
```bash
# Process files in the current directory
python wrike_import.py ./jobs/
# Process files in a specific folder
python wrike_import.py ~/Documents/wrike_imports/
```
### Output Example
```
Found 3 JSON file(s) to process
Target: Wrike Staging Space (MQAAAABpz7l_)
================================================================================
Processing: 5791330.json
================================================================================
1. Processing folder: 'Dry Specialty'
Found existing folder 'Dry Specialty': MQAAAABpz123
2. Processing project: 'PowerClean FurFinder + FurGuard 2025'
Campaign Code: 1647476
Found existing project 'PowerClean FurFinder + FurGuard 2025': MQAAAABpz456
3. Processing deliverable task
Checking for existing task with OMG #: 5791330
Creating new task 'PowerClean Corded Mass User Guide' (Job #5791330)
✓ Created task 'PowerClean Corded Mass User Guide': MAAAAABpz789
✓ Successfully processed 5791330.json
→ Moved to: Processed/5791330.json
================================================================================
Processing: 5791331.json
================================================================================
1. Processing folder: 'Air'
Found existing folder 'Air': MQAAAABpz124
2. Processing project: 'Air Purifier 2025'
Campaign Code: 1647477
Found existing project 'Air Purifier 2025': MQAAAABpz457
3. Processing deliverable task
Checking for existing task with OMG #: 5791331
Found existing task: MAAAAABpz790
⊙ Task 'Air Purifier Hero Image' already exists (Job #5791331) - skipping
⊙ Successfully processed 5791331.json (task already exists)
→ Moved to: Processed/5791331.json
================================================================================
CLEANUP
================================================================================
Deleted old file: 5791329.json
Deleted old file: 5791328.json
Deleted 2 file(s) older than 24 hours from Processed folder
================================================================================
SUMMARY
================================================================================
Total files: 3
Successful: 3
Skipped (already exists): 1
Failed: 0
Moved to Processed: 3
Folders created/found: 2
Projects created/found: 3
```
## How It Works
### 1. Folder Management
- Extracts the last segment from `BusinessArea` (e.g., "BISSELL > PRODUCT MARKETING > Dry Specialty" → "Dry Specialty")
- Checks if folder exists in the target space
- Creates folder if not found
- Caches folder IDs for performance
### 2. Project Management
- Searches for existing project by title in the folder
- Creates new project if not found
- Converts folder to project with start/end dates
- Adds campaign code as OMG # custom field
- Caches project IDs for performance
### 3. Task Management
- Checks for existing task by OMG # (job number)
- **If found**: Updates existing task with new data
- **If not found**: Creates new task
- Populates all custom fields and dates
### 4. Duplicate Handling
The script prevents duplicates using OMG numbers:
- **Projects**: Matched by title within folder
- **Tasks**: Matched by OMG # custom field (job number)
When processing the same JSON file twice:
- 1st run: Creates folder, project, and task
- 2nd run:
- Finds existing folder and project (reuses them)
- Finds existing task with same OMG # → **Skips task creation**
- Marks file as "skipped" and moves to Processed folder
**Important**: Tasks are never updated once created. If a task with the same OMG # exists, it's left unchanged.
### 5. File Management
After successful processing:
- **Moves file** to `Processed/` subfolder (auto-created)
- **Failed files** remain in the source directory for retry
- **Cleanup**: Deletes files older than 24 hours from Processed folder
Directory structure:
```
json_files/
├── 5791331.json # Pending
├── 5791332.json # Pending
└── Processed/
├── 5791330.json # Processed today (kept)
└── 5791329.json # Processed >24h ago (deleted)
```
## Troubleshooting
### Common Issues
#### 1. Authentication Error
```
Error making Wrike request: 401 Unauthorized
```
**Solution**: Verify your API token is correct and has write permissions.
#### 2. Custom Field Not Found
```
Error: Custom field ID not found
```
**Solution**: Ensure all custom fields are created in Wrike and IDs are updated in the script.
#### 3. No JSON Files Found
```
No JSON files found in '/path/to/directory'
```
**Solution**:
- Verify the directory path is correct
- Ensure files have `.json` extension
- Check file permissions
#### 4. Invalid Space ID
```
Error: Space 'MQAAAABpz7l_' not found
```
**Solution**:
- Get your space ID from the API: `GET /spaces`
- Update `STAGING_SPACE_ID` in the script
### Debug Mode
To see detailed API responses, modify the `make_wrike_request` function to print full responses:
```python
def make_wrike_request(method, endpoint, data=None):
# ... existing code ...
print(f"Response: {response.json()}") # Add this line
return response.json()
```
## API Rate Limits
Wrike API has rate limits:
- **100 requests per minute** per token
- **1000 requests per hour** per token
The script is optimized with caching to minimize API calls, but for very large batches (100+ files), you may need to:
1. Process in smaller batches
2. Add delays between requests
## Data Mapping Reference
### Date Format Conversion
Input: `"2025-05-23 16:00:00+00"`
Output: `"2025-05-23"`
### HTML Cleaning
Input: `"<p>PowerClean FurFinder + FurGuard 2025-related workflows</p>"`
Output: `"PowerClean FurFinder + FurGuard 2025-related workflows"`
### Business Area Parsing
Input: `"BISSELL > PRODUCT MARKETING > Dry Specialty"`
Output: `"Dry Specialty"` (folder name)
## Advanced Usage
### Custom Field Mapping
To add more custom fields, update the script:
1. Add field ID to `CUSTOM_FIELDS` dictionary
2. Update `create_or_update_deliverable_task()` function:
```python
# Add your custom field
custom_fields.append({
"id": CUSTOM_FIELDS["your_field_name"],
"value": job_details.get("YourJsonField", "")
})
```
### Filter by Business Area
To process only specific business areas, add a filter in `process_json_file()`:
```python
folder_name = parse_business_area(business_area)
# Only process specific folders
if folder_name not in ["Dry Specialty", "Air"]:
print(f" Skipping folder '{folder_name}'")
return False
```
### Batch Processing Script
Create a wrapper script for automated processing:
```bash
#!/bin/bash
# process_all.sh
IMPORT_DIR="/path/to/json/files"
# Run import (automatically moves to Processed/ and cleans up old files)
python wrike_import.py "$IMPORT_DIR"
# Optional: Send notification
if [ $? -eq 0 ]; then
echo "Wrike import completed successfully"
fi
```
### Scheduled Processing with Cron
Set up automatic processing every hour:
```bash
# Edit crontab
crontab -e
# Add this line to run every hour
0 * * * * /usr/bin/python3 /path/to/wrike_import.py /path/to/json/files >> /var/log/wrike_import.log 2>&1
```
### Change Cleanup Retention Period
To change the 24-hour retention to a different period, modify the cleanup call in the script:
```python
# Delete files older than 48 hours instead
deleted_count = cleanup_old_files(json_dir, hours=48)
# Delete files older than 7 days
deleted_count = cleanup_old_files(json_dir, hours=168)
```
## Security Notes
⚠️ **Important Security Considerations:**
1. **Never commit API tokens** to version control
2. Store tokens in environment variables:
```python
import os
WRIKE_TOKEN = os.environ.get('WRIKE_API_TOKEN')
```
3. Use a `.env` file for local development (add to `.gitignore`)
4. Rotate tokens regularly
5. Use read-only tokens for testing
## Support
### Getting Help
1. Check the [Wrike API Documentation](https://developers.wrike.com/api/v4/)
2. Review error messages in script output
3. Enable debug mode for detailed logging
4. Check Wrike API status at [status.wrike.com](https://status.wrike.com)
### Reporting Issues
When reporting issues, include:
- Script version
- Python version (`python --version`)
- Full error message
- Sample JSON structure (redacted)
- Wrike space configuration
## License
This script is provided as-is for internal use.
## Which Tool Should I Use?
### Use wrike_import.py if you want to:
- Import a batch of JSON files once
- Run manually when needed
- Test imports locally
- Simple command-line operation
### Use wrike_monitor.py if you want to:
- Continuous 24/7 monitoring
- Automatic processing as files arrive
- Daily email reports
- Production deployment as a service
- Detailed statistics and monitoring
## Changelog
### Version 2.0 (Current)
- **NEW**: Added wrike_monitor.py - real-time monitoring service
- **NEW**: Folder watching with watchdog
- **NEW**: Daily email reports at 7PM
- **NEW**: Failed file handling with error logs
- **NEW**: Periodic scanning for missed files
- **NEW**: Statistics tracking and performance monitoring
- **NEW**: systemd service support
- **FIXED**: Sequential processing to prevent race conditions
- **FIXED**: Proper parent/child folder matching to avoid duplicates
- **IMPROVED**: Logging for skipped deliverables
### Version 1.2
- Added automatic file movement to Processed subfolder
- Implemented auto-cleanup of files older than 24 hours
- Improved file management and organization
- Enhanced output with file movement tracking
### Version 1.1
- Added duplicate detection via OMG numbers
- Separated project and task OMG numbers
- Improved error handling
- Added caching for performance
- Enhanced logging output
### Version 1.0
- Initial release
- Basic folder/project/task creation
- Custom field mapping
---
**Last Updated**: October 2025
**Author**: Dave Porter
**Repository**: https://bitbucket.org/zlalani/bissell-wrike-python