Enhancement: Treat empty A1 folders as expected workflow
Campaign managers often create the campaign in DAM before assets are uploaded, so an empty Master Assets folder is the normal pre-asset state rather than a failure. Stop marking these as permanently failed and stop emailing on every poll. - increment_a1_retry() gains mark_failed_at_max param; empty-folder path passes False so the campaign keeps polling indefinitely until assets appear (or the DAM status changes). - Empty-folder branch now skips silently on every poll and sends a single warning email at poll 20 (~1 hour at the 3-min cadence) so genuinely stuck campaigns still surface. - New a1_to_a2_no_assets_warning email template — one-time soft warning, no permanent-failure language. - Existing reset_a1_retry() on successful A1→A2 still clears the counter when assets eventually appear. - Other folder-error paths (folder not found, etc.) keep the original 3-retry-then-fail behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ab557b78de
commit
90f326aecb
3 changed files with 65 additions and 30 deletions
|
|
@ -50,6 +50,11 @@ logging.basicConfig(
|
|||
|
||||
logger = logging.getLogger('A1toA2Box')
|
||||
|
||||
# Empty A1 folders are an expected client workflow (folder created before assets uploaded).
|
||||
# Skip silently and send a single warning email at this poll count to flag genuinely-stuck
|
||||
# campaigns without spamming. At ~3-min poll cadence, 20 polls ≈ 1 hour.
|
||||
EMPTY_FOLDER_WARNING_THRESHOLD = 20
|
||||
|
||||
def extract_creativex_from_dam_metadata(asset_metadata):
|
||||
"""
|
||||
Extract CreativeX score and URL from DAM asset metadata if present
|
||||
|
|
@ -189,45 +194,37 @@ def process_campaign(campaign, dam, box, db, notifier, config):
|
|||
logger.info("Found {} master assets".format(total_assets))
|
||||
|
||||
if total_assets == 0:
|
||||
logger.warning("No master assets found in Master Assets folder")
|
||||
|
||||
# Increment retry counter
|
||||
# Empty folders are expected when a campaign manager creates the campaign
|
||||
# before uploading assets. Track the count for visibility but never auto-fail
|
||||
# — keep retrying every poll until assets appear (or status changes in DAM).
|
||||
retry_result = db.increment_a1_retry(
|
||||
campaign_id=campaign_id,
|
||||
campaign_number=campaign_number,
|
||||
campaign_name=campaign_name,
|
||||
reason="No master assets found in Master Assets folder"
|
||||
reason="No master assets found in Master Assets folder",
|
||||
mark_failed_at_max=False
|
||||
)
|
||||
|
||||
if not retry_result['success']:
|
||||
logger.error("Failed to update retry counter")
|
||||
|
||||
is_permanently_failed = retry_result.get('permanently_failed', False)
|
||||
retry_count = retry_result.get('retry_count', 0)
|
||||
logger.info("No master assets yet (poll {}) - skipping until assets appear".format(retry_count))
|
||||
|
||||
# Determine which email template to use
|
||||
if is_permanently_failed:
|
||||
# Send FINAL failure email (after 3 attempts)
|
||||
template_name = 'a1_to_a2_permanently_failed'
|
||||
recipients = config['notifications']['recipients']['errors']
|
||||
else:
|
||||
# Send standard retry notification
|
||||
template_name = 'a1_to_a2_no_assets_retry'
|
||||
recipients = config['notifications']['recipients']['errors']
|
||||
|
||||
# Send email notification
|
||||
notifier.send_email(
|
||||
template_name=template_name,
|
||||
recipients=recipients,
|
||||
data={
|
||||
'campaign_name': campaign_name,
|
||||
'campaign_id': campaign_id,
|
||||
'campaign_number': campaign_number,
|
||||
'retry_count': retry_count,
|
||||
'max_retries': 3,
|
||||
'is_permanently_failed': is_permanently_failed
|
||||
}
|
||||
)
|
||||
# Send a single warning email when the campaign has been empty for ~1 hour
|
||||
# so genuinely-stuck campaigns still surface, without spamming on every poll.
|
||||
if retry_count == EMPTY_FOLDER_WARNING_THRESHOLD:
|
||||
logger.warning("Campaign has been empty for {} polls - sending one-time warning".format(retry_count))
|
||||
notifier.send_email(
|
||||
template_name='a1_to_a2_no_assets_warning',
|
||||
recipients=config['notifications']['recipients']['errors'],
|
||||
data={
|
||||
'campaign_name': campaign_name,
|
||||
'campaign_id': campaign_id,
|
||||
'campaign_number': campaign_number,
|
||||
'poll_count': retry_count
|
||||
}
|
||||
)
|
||||
|
||||
return {'success': False, 'processed': 0, 'failed': 0}
|
||||
|
||||
|
|
|
|||
|
|
@ -630,7 +630,7 @@ class Database:
|
|||
cursor.close()
|
||||
self.put_connection(conn)
|
||||
|
||||
def increment_a1_retry(self, campaign_id, campaign_number, campaign_name, reason):
|
||||
def increment_a1_retry(self, campaign_id, campaign_number, campaign_name, reason, mark_failed_at_max=True):
|
||||
"""
|
||||
Increment A1 retry counter and mark as permanently failed if max attempts reached
|
||||
|
||||
|
|
@ -639,6 +639,9 @@ class Database:
|
|||
campaign_number: Campaign number (e.g., C000000078)
|
||||
campaign_name: Campaign name
|
||||
reason: Description of failure (e.g., "No master assets found")
|
||||
mark_failed_at_max: If True (default), set a1_permanently_failed=True at MAX_RETRIES.
|
||||
Set False for empty-folder polling where the campaign is expected
|
||||
to eventually receive assets and should keep retrying silently.
|
||||
|
||||
Returns:
|
||||
dict with success, retry_count, permanently_failed
|
||||
|
|
@ -659,7 +662,7 @@ class Database:
|
|||
row = cursor.fetchone()
|
||||
current_count = (row[0] or 0) if row else 0
|
||||
new_count = current_count + 1
|
||||
is_permanently_failed = new_count >= MAX_RETRIES
|
||||
is_permanently_failed = mark_failed_at_max and new_count >= MAX_RETRIES
|
||||
|
||||
# Insert or update campaign status with retry tracking
|
||||
cursor.execute("""
|
||||
|
|
|
|||
|
|
@ -705,6 +705,41 @@ class Notifier:
|
|||
</div>
|
||||
"""
|
||||
},
|
||||
'a1_to_a2_no_assets_warning': {
|
||||
'subject': "⚠️ Campaign in A1 with no assets yet - {campaign_name}",
|
||||
'html': """
|
||||
<div style="font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto;">
|
||||
<div style="background-color: #ff9800; color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0;">
|
||||
<h1 style="margin: 0;">⚠️ Campaign in A1 with No Assets Yet</h1>
|
||||
</div>
|
||||
|
||||
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0;">
|
||||
<p style="margin: 0;"><strong>Campaign:</strong> {{ campaign_name }} ({{ campaign_number }})</p>
|
||||
<p style="margin: 5px 0 0 0;"><strong>Campaign ID:</strong> {{ campaign_id }}</p>
|
||||
<p style="margin: 5px 0 0 0;"><strong>Status:</strong> A1</p>
|
||||
<p style="margin: 5px 0 0 0;"><strong>Polls with empty folder:</strong> {{ poll_count }}</p>
|
||||
</div>
|
||||
|
||||
<div style="padding: 20px; background-color: #f8f9fa; border-radius: 4px; margin: 20px 0;">
|
||||
<h3 style="color: #ff9800; margin-top: 0;">Master Assets Folder Has Been Empty for ~1 Hour</h3>
|
||||
<p>This campaign has been at status A1 for roughly an hour with no master assets in the folder.</p>
|
||||
<p>This is often expected — the folder may have been created before assets were uploaded — and the system will keep checking automatically.</p>
|
||||
<p>This is a <strong>one-time warning</strong>; no further emails will be sent for this campaign.</p>
|
||||
</div>
|
||||
|
||||
<div style="background-color: #e3f2fd; border-left: 4px solid #1976d2; padding: 15px; margin: 20px 0;">
|
||||
<p style="margin: 0;"><strong>📌 Action only needed if:</strong></p>
|
||||
<ul style="margin: 10px 0;">
|
||||
<li>You expected assets to be uploaded already</li>
|
||||
<li>The campaign was set to A1 by mistake (change the status in DAM)</li>
|
||||
</ul>
|
||||
<p style="margin: 10px 0 0 0;">Otherwise no action needed — processing will start automatically as soon as assets appear in the Master Assets folder.</p>
|
||||
</div>
|
||||
|
||||
<p style="color: #666; font-size: 12px; margin-top: 20px;">A1→A2 script will continue to check silently every 3 minutes.</p>
|
||||
</div>
|
||||
"""
|
||||
},
|
||||
'a1_to_a2_permanently_failed': {
|
||||
'subject': "❌ PERMANENTLY FAILED - Campaign {campaign_name} (No Assets After 3 Attempts)",
|
||||
'html': """
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue