2
0
Files
logikonline 3a8bdd936c feat(pages): add A/B testing framework for landing pages
Implement comprehensive A/B testing system for landing page optimization:
- Database models for experiments, variants, and events
- AI-powered variant generation and analysis
- Visitor tracking with conversion metrics
- Experiment lifecycle management (draft/active/paused/completed)
- Email notifications for experiment results
- Cron job for automated experiment monitoring
- UI for viewing experiment results and statistics
2026-03-07 12:39:42 -05:00

71 lines
2.7 KiB
Go

// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT
package mailer
import (
"bytes"
"fmt"
pages_model "code.gitcaddy.com/server/v3/models/pages"
repo_model "code.gitcaddy.com/server/v3/models/repo"
user_model "code.gitcaddy.com/server/v3/models/user"
"code.gitcaddy.com/server/v3/modules/log"
"code.gitcaddy.com/server/v3/modules/setting"
sender_service "code.gitcaddy.com/server/v3/services/mailer/sender"
)
// SendExperimentApprovalEmail sends an email to the repo owner with approve/decline links
// for an A/B test experiment that has found a winning variant.
func SendExperimentApprovalEmail(owner *user_model.User, repo *repo_model.Repository,
exp *pages_model.PageExperiment, winnerVariant *pages_model.PageVariant,
approveToken, declineToken, summary string,
) {
if setting.MailService == nil {
log.Warn("Mail service not configured, cannot send experiment approval email")
return
}
if owner.Email == "" {
log.Warn("Repo owner %s has no email, cannot send experiment approval", owner.Name)
return
}
subject := fmt.Sprintf("[%s] A/B Test Results: %s", setting.AppName, exp.Name)
approveURL := fmt.Sprintf("%s-/pages/experiment/approve/%s", setting.AppURL, approveToken)
declineURL := fmt.Sprintf("%s-/pages/experiment/decline/%s", setting.AppURL, declineToken)
var body bytes.Buffer
body.WriteString("A/B Test Experiment Results\n")
body.WriteString("===========================\n\n")
body.WriteString(fmt.Sprintf("Repository: %s/%s\n", repo.OwnerName, repo.Name))
body.WriteString(fmt.Sprintf("Experiment: %s\n\n", exp.Name))
body.WriteString("Results Summary\n")
body.WriteString("---------------\n")
body.WriteString(summary + "\n\n")
if winnerVariant != nil {
body.WriteString(fmt.Sprintf("Winner: %s\n", winnerVariant.Name))
body.WriteString(fmt.Sprintf("Impressions: %d\n", winnerVariant.Impressions))
body.WriteString(fmt.Sprintf("Conversions: %d\n", winnerVariant.Conversions))
body.WriteString(fmt.Sprintf("Conversion Rate: %.2f%%\n\n", winnerVariant.ConversionRate()*100))
}
body.WriteString("Actions\n")
body.WriteString("-------\n")
body.WriteString(fmt.Sprintf("Approve (apply winning variant): %s\n\n", approveURL))
body.WriteString(fmt.Sprintf("Decline (keep current config): %s\n\n", declineURL))
body.WriteString("This link will expire in 7 days.\n")
body.WriteString(fmt.Sprintf("\n--\n%s\n%s\n", setting.AppName, setting.AppURL))
msg := sender_service.NewMessage(owner.Email, subject, body.String())
msg.Info = fmt.Sprintf("Experiment approval for %s/%s: %s", repo.OwnerName, repo.Name, exp.Name)
if err := sender_service.Send(sender, msg); err != nil {
log.Error("Failed to send experiment approval email: %v", err)
}
}