Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions analytics/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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'])
Expand Down Expand Up @@ -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,
Expand All @@ -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")
Expand Down Expand Up @@ -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,
Expand All @@ -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")
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand Down
14 changes: 2 additions & 12 deletions campaigns/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand All @@ -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
Expand All @@ -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:
Expand Down
21 changes: 17 additions & 4 deletions campaigns/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Loading