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
71 lines
2.7 KiB
Go
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)
|
|
}
|
|
}
|