diff --git a/analytics/views.py b/analytics/views.py index f428684..1641648 100644 --- a/analytics/views.py +++ b/analytics/views.py @@ -88,6 +88,9 @@ def weekly_analytics(request): campaigns = Campaign.objects.filter(user=request.user, id=campaign_id) else: campaigns = Campaign.objects.filter(user=request.user) + + selected_campaign = campaigns.first() if campaign_id else None + subscriber_list = selected_campaign.subscriber_list if selected_campaign else None # Calculate date range for the past 7 days end_date = timezone.now() @@ -107,6 +110,7 @@ def weekly_analytics(request): 'delivery_rate': 0, 'open_rate': 0, 'click_rate': 0, + 'subscriber_count': 0, } # Get all email events for user's campaigns in the past 7 days @@ -151,6 +155,13 @@ def weekly_analytics(request): if delivered > 0: data['open_rate'] = round((opened / delivered) * 100, 2) data['click_rate'] = round((clicked / delivered) * 100, 2) + + if subscriber_list: + date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() + day_end = timezone.make_aware(datetime.combine(date_obj, datetime.max.time())) + data['subscriber_count'] = subscriber_list.subscribers.filter( + created_at__lte=day_end + ).count() # Convert to sorted list result = sorted(daily_data.values(), key=lambda x: x['date']) @@ -293,7 +304,7 @@ def track_open(request, tracking_id, encoded_email): if subscriber_email: # Always process synchronously (no Celery) try: - from campaigns.models import EmailEvent, Campaign + from campaigns.models import EmailEvent # Find the sent event with the same tracking ID sent_event = EmailEvent.objects.get( id=tracking_id, @@ -316,11 +327,6 @@ def track_open(request, tracking_id, encoded_email): event_type='opened' ) - # Update campaign metrics - campaign = sent_event.email.campaign - campaign.open_count += 1 - campaign.save(update_fields=['open_count']) - logger.info(f"Recorded open event for {subscriber_email}") else: logger.debug(f"Open event already exists for {subscriber_email}, skipping duplicate") @@ -349,7 +355,7 @@ def message_split_gif(request): if subscriber_email: # Always process synchronously (no Celery) try: - from campaigns.models import EmailEvent, Campaign + from campaigns.models import EmailEvent # Find the sent event with the same tracking ID sent_event = EmailEvent.objects.get( id=tracking_id, @@ -372,11 +378,6 @@ def message_split_gif(request): event_type='opened' ) - # Update campaign metrics - campaign = sent_event.email.campaign - campaign.open_count += 1 - campaign.save(update_fields=['open_count']) - logger.info(f"Recorded open event for {subscriber_email}") else: logger.debug(f"Open event already exists for {subscriber_email}, skipping duplicate") @@ -415,7 +416,7 @@ def track_click(request, tracking_id): from django.conf import settings if sys.platform == 'win32' and settings.DEBUG: # Process synchronously - from campaigns.models import EmailEvent, Campaign + from campaigns.models import EmailEvent try: sent_event = EmailEvent.objects.get( id=tracking_id, @@ -430,11 +431,6 @@ def track_click(request, tracking_id): event_type='clicked', link_clicked=destination_url ) - - # Update campaign metrics - campaign = sent_event.email.campaign - campaign.click_count += 1 - campaign.save(update_fields=['click_count']) except EmailEvent.DoesNotExist: pass except Exception: diff --git a/campaigns/tasks.py b/campaigns/tasks.py index 791cdd8..7ad3712 100644 --- a/campaigns/tasks.py +++ b/campaigns/tasks.py @@ -1218,7 +1218,7 @@ def _safe_replacement(val): def process_email_open(tracking_id, subscriber_email): """Process email open event.""" - from .models import EmailEvent, Campaign + from .models import EmailEvent try: # Find the sent event with the same tracking ID @@ -1243,11 +1243,6 @@ def process_email_open(tracking_id, subscriber_email): event_type='opened' ) - # Update campaign metrics - campaign = sent_event.email.campaign - campaign.open_count += 1 - campaign.save(update_fields=['open_count']) - logger.info(f"Recorded open event for {subscriber_email}") else: logger.debug(f"Open event already exists for {subscriber_email}, skipping duplicate") @@ -1260,7 +1255,7 @@ def process_email_open(tracking_id, subscriber_email): def process_email_click(tracking_id, subscriber_email, link_url): """Process email click event.""" - from .models import EmailEvent, Campaign + from .models import EmailEvent try: # Find the sent event with the same tracking ID @@ -1278,11 +1273,6 @@ def process_email_click(tracking_id, subscriber_email, link_url): link_clicked=link_url ) - # Update campaign metrics (count each click) - campaign = sent_event.email.campaign - campaign.click_count += 1 - campaign.save(update_fields=['click_count']) - logger.info(f"Recorded click event for {subscriber_email} on {link_url}") except EmailEvent.DoesNotExist: diff --git a/campaigns/views.py b/campaigns/views.py index f88ecc3..528a809 100644 --- a/campaigns/views.py +++ b/campaigns/views.py @@ -876,6 +876,14 @@ def campaign_analysis_view(request): elif ev.event_type == 'complained': by_month[month]['complaints'] += 1 + # Keep top metric cards in sync with real-time event data + campaign.sent_count = events.filter(event_type='sent').count() + campaign.open_count = events.filter(event_type='opened').count() + campaign.click_count = events.filter(event_type='clicked').count() + campaign.bounce_count = events.filter(event_type='bounced').count() + campaign.unsubscribe_count = events.filter(event_type='unsubscribed').count() + campaign.complaint_count = events.filter(event_type='complained').count() + # Per-email breakdown with unique opens for e in campaign.emails.all(): sent = EmailEvent.objects.filter(email=e, event_type='sent').count() @@ -941,10 +949,15 @@ def campaign_stats_api(request, campaign_id): event_type='complained' ).count() - # Get opens and clicks from campaign model (these are incremented via signals) - campaign.refresh_from_db() - open_count = campaign.open_count - click_count = campaign.click_count + # Get opens and clicks from events for consistency across all event sources + open_count = EmailEvent.objects.filter( + email__campaign=campaign, + event_type='opened' + ).count() + click_count = EmailEvent.objects.filter( + email__campaign=campaign, + event_type='clicked' + ).count() # Calculate rates delivered_count = sent_count - bounce_count diff --git a/docs/press/15. Subject - {{first_name}} {{last_name}} story idea on DripEmails.org.txt b/docs/press/15. Subject - {{first_name}} {{last_name}} story idea on DripEmails.org.txt index 00d6184..34ad50c 100644 --- a/docs/press/15. Subject - {{first_name}} {{last_name}} story idea on DripEmails.org.txt +++ b/docs/press/15. Subject - {{first_name}} {{last_name}} story idea on DripEmails.org.txt @@ -17,7 +17,7 @@ What DripEmails.org offers: - Gmail + IMAP auto-engagement workflows - Multi-step drip campaigns with flexible timing - AI-assisted drafting and revision for campaign emails -- Personalized templates with merge fields like {{first_name}}, {{last_name}}, and {{email}} +- Personalized templates with merge fields like {{first_name}} and {{email}} - Campaign analytics (opens, clicks, and engagement tracking) If useful, I can share: diff --git a/docs/press/16. Subject - {{first_name}} {{last_name}} covering smarter email follow-up at DripEmails.org.txt b/docs/press/16. Subject - {{first_name}} {{last_name}} covering smarter email follow-up at DripEmails.org.txt index fec8bca..7259627 100644 --- a/docs/press/16. Subject - {{first_name}} {{last_name}} covering smarter email follow-up at DripEmails.org.txt +++ b/docs/press/16. Subject - {{first_name}} {{last_name}} covering smarter email follow-up at DripEmails.org.txt @@ -17,7 +17,7 @@ Product highlights: - Gmail + IMAP integration for inbox-aware automation - Configurable multi-step drip sequences - AI-assisted writing and message revision -- Merge-field personalization including {{first_name}}, {{last_name}}, and {{email}} +- Merge-field personalization including {{first_name}}, and {{email}} - Engagement analytics for optimization over time Happy to provide anything useful for coverage: diff --git a/docs/press/28. Subject - {{first_name}} feature idea on reply-driven lifecycle email automation.txt b/docs/press/28. Subject - {{first_name}} feature idea on reply-driven lifecycle email automation.txt new file mode 100644 index 0000000..b64ede4 --- /dev/null +++ b/docs/press/28. Subject - {{first_name}} feature idea on reply-driven lifecycle email automation.txt @@ -0,0 +1,29 @@ +Subject: {{first_name}}, feature idea: reply-driven lifecycle email automation at DripEmails.org + +Hi {{first_name}}, + +I wanted to share a potential feature angle your publication may find timely. + +DripEmails.org helps teams automate follow-up after inbox engagement, so high-intent conversations do not stall after the first response. + +By connecting Gmail or IMAP, teams can continue conversations through sequenced workflows while preserving a personal tone. + +Why this could resonate: +- Email teams are shifting from campaign blasts to lifecycle continuity +- Inbound conversations are often where revenue intent is strongest +- Automation quality now depends on timing plus relevance + +Platform highlights: +- Gmail + IMAP connection for workflow triggers +- Multi-step drip sequences with configurable delays +- AI-assisted writing and content revision +- Merge fields such as {{first_name}} and {{email}} +- Engagement analytics for optimization decisions + +If useful, I can send a concise demo, screenshots, and founder availability. + +Website: https://dripemails.org +Blog: https://dripemails.org/resources/blog/ + +Best, +DripEmails.org diff --git a/docs/press/29. Subject - {{first_name}} editorial angle on reducing inbound response decay.txt b/docs/press/29. Subject - {{first_name}} editorial angle on reducing inbound response decay.txt new file mode 100644 index 0000000..f4764bb --- /dev/null +++ b/docs/press/29. Subject - {{first_name}} editorial angle on reducing inbound response decay.txt @@ -0,0 +1,29 @@ +Subject: {{first_name}}, editorial angle: reducing inbound response decay with DripEmails.org + +Hi {{first_name}}, + +Sharing a concise editorial idea in case it fits your upcoming coverage plans. + +DripEmails.org is built for a practical gap in email operations: keeping inbound conversations moving after an initial reply. + +With Gmail or IMAP connected, teams can launch personalized follow-up steps automatically and reduce manual delays. + +Potential angle points: +- Follow-up latency is a major cause of lost conversation momentum +- Teams need automation that still reads like one-to-one communication +- AI-assisted drafting is becoming operational, not experimental + +Product snapshot: +- Gmail + IMAP inbox integrations +- Time-based, multi-step drip workflows +- AI-supported message drafting and iteration +- Personalization using {{first_name}} and {{email}} +- Tracking data for opens, clicks, and behavior trends + +Happy to share a short walkthrough and supporting materials if helpful. + +Website: https://dripemails.org +Blog: https://dripemails.org/resources/blog/ + +Thanks, +DripEmails.org diff --git a/docs/press/30. Subject - {{first_name}} story pitch on human-like automated follow-up.txt b/docs/press/30. Subject - {{first_name}} story pitch on human-like automated follow-up.txt new file mode 100644 index 0000000..717a74f --- /dev/null +++ b/docs/press/30. Subject - {{first_name}} story pitch on human-like automated follow-up.txt @@ -0,0 +1,29 @@ +Subject: {{first_name}}, story pitch: human-like automated follow-up from DripEmails.org + +Hi {{first_name}}, + +I wanted to send over a story pitch that may align with your audience. + +DripEmails.org helps teams automate follow-up from real inbox engagement while keeping messaging personalized and context-aware. + +Instead of stopping at outbound sends, teams can continue conversations with scheduled workflows triggered through Gmail or IMAP. + +Why this is relevant now: +- Teams are measured on continuity, not just send volume +- Inbound reply management is still highly manual in many stacks +- Practical AI tools are reducing the writing burden for operators + +What DripEmails.org provides: +- Gmail + IMAP powered automation +- Flexible multi-step follow-up sequencing +- AI-assisted writing and message refinement +- Merge-field personalization with {{first_name}} and {{email}} +- Analytics for engagement and campaign performance + +If this is useful, I can share demo access, screenshots, and interview options. + +Website: https://dripemails.org +Blog: https://dripemails.org/resources/blog/ + +Best regards, +DripEmails.org diff --git a/docs/press/31. Subject - {{first_name}} media note on Gmail IMAP continuity workflows.txt b/docs/press/31. Subject - {{first_name}} media note on Gmail IMAP continuity workflows.txt new file mode 100644 index 0000000..ea5d339 --- /dev/null +++ b/docs/press/31. Subject - {{first_name}} media note on Gmail IMAP continuity workflows.txt @@ -0,0 +1,29 @@ +Subject: {{first_name}}, media note: Gmail + IMAP continuity workflows at DripEmails.org + +Hi {{first_name}}, + +Quick media note in case this supports a broader email infrastructure story. + +DripEmails.org enables teams to run automated follow-up after inbox engagement, using Gmail and IMAP integrations to keep conversations active. + +The result is fewer dropped threads and more consistent next-step communication without heavy manual effort. + +Coverage themes you could explore: +- The rise of continuity workflows in modern email programs +- How teams balance automation speed with personalization quality +- Why inbound engagement automation is becoming a core metric lever + +Highlights: +- Native Gmail + IMAP integration paths +- Sequenced follow-up campaigns with timed delivery +- AI-assisted copy generation and revision tools +- Personalization merge fields including {{first_name}} and {{email}} +- Analytics for engagement and workflow tuning + +If helpful, I can provide visuals, a product walkthrough, and background notes. + +Website: https://dripemails.org +Blog: https://dripemails.org/resources/blog/ + +Thanks, +DripEmails.org diff --git a/docs/press/32. Subject - {{first_name}} coverage concept on operationalizing inbound engagement.txt b/docs/press/32. Subject - {{first_name}} coverage concept on operationalizing inbound engagement.txt new file mode 100644 index 0000000..a0590d2 --- /dev/null +++ b/docs/press/32. Subject - {{first_name}} coverage concept on operationalizing inbound engagement.txt @@ -0,0 +1,29 @@ +Subject: {{first_name}}, coverage concept: operationalizing inbound engagement with DripEmails.org + +Hi {{first_name}}, + +I wanted to share a coverage concept focused on execution in day-to-day email operations. + +DripEmails.org helps teams operationalize inbound engagement by automating personalized follow-up after replies and interactions. + +With Gmail or IMAP connected, follow-up workflows can run consistently while teams maintain message quality. + +Why the angle may land: +- Most automation tools emphasize outbound sends over conversation continuity +- Teams are prioritizing dependable response workflows across lifecycle stages +- AI-assisted writing is improving turnaround without sacrificing personalization + +Product capabilities: +- Gmail + IMAP workflow triggers +- Multi-step drip sequences with custom timing +- AI-assisted draft generation and editing +- Merge-field support for {{first_name}} and {{email}} +- Engagement tracking for iterative optimization + +If useful, I can send a short brief, screenshots, and use-case examples. + +Website: https://dripemails.org +Blog: https://dripemails.org/resources/blog/ + +Best, +DripEmails.org diff --git a/docs/press/33. Subject - {{first_name}} newsroom brief on follow-up timing and engagement.txt b/docs/press/33. Subject - {{first_name}} newsroom brief on follow-up timing and engagement.txt new file mode 100644 index 0000000..b2ced7c --- /dev/null +++ b/docs/press/33. Subject - {{first_name}} newsroom brief on follow-up timing and engagement.txt @@ -0,0 +1,29 @@ +Subject: {{first_name}}, newsroom brief: follow-up timing and engagement at DripEmails.org + +Hi {{first_name}}, + +Sending over a brief newsroom idea that may fit stories on practical automation outcomes. + +DripEmails.org helps teams automate post-engagement follow-up so inbound conversations keep progressing instead of slowing down. + +Through Gmail and IMAP integrations, teams can schedule personalized sequences that improve timing discipline across the funnel. + +Possible editorial takeaways: +- Follow-up timing is often the hidden variable in email performance +- Inbound conversation automation can reduce operational bottlenecks +- Teams increasingly need measurable workflow consistency, not just campaign volume + +DripEmails.org at a glance: +- Gmail + IMAP integration for trigger-based automation +- Configurable multi-step drip campaign logic +- AI-assisted drafting and revision support +- Personalization fields like {{first_name}} and {{email}} +- Analytics for engagement and workflow impact + +If helpful, I can share a quick demo, screenshots, and founder interview windows. + +Website: https://dripemails.org +Blog: https://dripemails.org/resources/blog/ + +Best regards, +DripEmails.org diff --git a/templates/campaigns/campaign_analysis.html b/templates/campaigns/campaign_analysis.html index 4eee98a..15cb8aa 100644 --- a/templates/campaigns/campaign_analysis.html +++ b/templates/campaigns/campaign_analysis.html @@ -90,7 +90,7 @@
{% trans "No subscribers found for this campaign" %}
+