Skip to content

Commit 5cc9018

Browse files
authored
Merge pull request #40 from Check-Data-Out/refactor/improve_newsletter_design
2 parents a157982 + 9b5cc25 commit 5cc9018

File tree

8 files changed

+459
-315
lines changed

8 files changed

+459
-315
lines changed

insight/tasks/weekly_newsletter_batch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ def _get_weekly_trend_html(self) -> str:
145145

146146
# 템플릿 렌더링이 제대로 되지 않은 경우 배치 종료
147147
if (
148-
" 주의 트렌딩 글" not in weekly_trend_html
149-
or "트렌드 분석" not in weekly_trend_html
148+
"이번 주의 트렌딩 글" not in weekly_trend_html
149+
or "주간 트렌드 분석" not in weekly_trend_html
150150
):
151151
logger.error(
152152
f"Failed to build weekly trend HTML for newsletter #{weekly_trend['id']}"

insight/tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,9 @@ def sample_newsletter(user):
161161
email_message=EmailMessage(
162162
to=[user.email],
163163
from_email=settings.DEFAULT_FROM_EMAIL,
164-
subject="Test Newsletter",
165-
text_body="Test content",
166-
html_body="<div>Test content</div>",
164+
subject="벨로그 대시보드 주간 뉴스레터 #1",
165+
text_body="Weekly Report Test content",
166+
html_body="<div>Weekly Report<br/>Velog Dashboard<br/>활동 리포트<br/>대시보드 보러가기</div>",
167167
),
168168
)
169169

insight/tests/tasks/test_weekly_newsletter_batch.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ def test_build_newsletters_success(
196196
assert len(newsletters) == 1
197197
assert newsletters[0].user_id == user.id
198198
assert newsletters[0].email_message.to[0] == user.email
199+
# 제목 포맷 검증
200+
assert "벨로그 대시보드 주간 뉴스레터" in newsletters[0].email_message.subject
199201

200202
@patch("insight.tasks.weekly_newsletter_batch.logger")
201203
def test_send_newsletters_success(

insight/tests/tasks/test_weekly_newsletter_template.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ def test_get_weekly_trend_html_success(
6161

6262
assert trending_summary[0]["title"] in weekly_trend_html
6363
assert trend_analysis["insights"] in weekly_trend_html
64-
assert "이 주의 트렌딩 글" in weekly_trend_html
65-
assert "트렌드 분석" in weekly_trend_html
64+
assert "벨로그 주간 트렌드" in weekly_trend_html
65+
assert "이번 주의 트렌딩 글" in weekly_trend_html
66+
assert "주간 트렌드 분석" in weekly_trend_html
6667

6768
@patch("insight.tasks.weekly_newsletter_batch.logger")
6869
@pytest.mark.django_db
@@ -124,12 +125,11 @@ def test_get_user_weekly_trend_html_success(
124125

125126
assert trending_summary[0]["title"] in user_weekly_trend_html
126127
assert trend_analysis["insights"] in user_weekly_trend_html
127-
assert (
128-
f'<b>{user_weekly_stats["new_posts"]}개</b>의 글을 작성'
129-
in user_weekly_trend_html
130-
)
128+
assert f'{user_weekly_stats["new_posts"]}개의 글' in user_weekly_trend_html
131129
assert "마지막으로 글을 작성하신지" not in user_weekly_trend_html
132130
assert user.username in user_weekly_trend_html
131+
assert "이번주에 작성한 글" in user_weekly_trend_html
132+
assert "주간 내 활동 분석" in user_weekly_trend_html
133133

134134
@patch("insight.tasks.weekly_newsletter_batch.logger")
135135
def test_get_user_weekly_trend_html_inactive_user(
@@ -147,13 +147,16 @@ def test_get_user_weekly_trend_html_inactive_user(
147147
insight_data = inactive_user_weekly_trend.insight
148148
user_weekly_reminder = insight_data.get("user_weekly_reminder")
149149

150-
assert "이번주에 쓴 글" not in user_weekly_trend_html
151-
assert "내 글을 분석해보면?" not in user_weekly_trend_html
152-
assert "글을 작성하지 않으셨네요" in user_weekly_trend_html
153-
assert (
154-
f'마지막으로 글을 작성하신지 {user_weekly_reminder["days_ago"]}일이 지났어요'
155-
in user_weekly_trend_html
156-
)
150+
assert "이번주에 작성한 글" not in user_weekly_trend_html
151+
assert "주간 내 활동 분석" not in user_weekly_trend_html
152+
# days_ago가 있는 경우와 없는 경우 모두 처리
153+
if user_weekly_reminder.get("days_ago"):
154+
assert (
155+
f'😭 마지막으로 글을 작성하신지 {user_weekly_reminder["days_ago"]}일이 지났어요!'
156+
in user_weekly_trend_html
157+
)
158+
else:
159+
assert "😭 글을 작성하지 않으셨네요!" in user_weekly_trend_html
157160

158161
@patch("insight.tasks.weekly_newsletter_batch.logger")
159162
def test_get_user_weekly_trend_html_exception(
@@ -193,6 +196,8 @@ def test_get_newsletter_html_success(self, mock_logger, newsletter_batch):
193196
assert weekly_trend_html in newsletter_html
194197
assert user_weekly_trend_html in newsletter_html
195198
assert "대시보드 보러가기" in newsletter_html
199+
assert "Weekly Report" in newsletter_html
200+
assert "Velog Dashboard" in newsletter_html
196201

197202
@patch("insight.tasks.weekly_newsletter_batch.logger")
198203
def test_get_newsletter_html_expired_token_user(
@@ -210,10 +215,12 @@ def test_get_newsletter_html_expired_token_user(
210215
)
211216

212217
# 템플릿 렌더링 검증
213-
assert "토큰이 만료" in newsletter_html
218+
assert "🚨 잠시만요, 토큰이 만료된 것 같아요!" in newsletter_html
219+
assert "토큰이 만료되어 정상적으로 통계를 수집할 수 없었어요" in newsletter_html
214220
assert weekly_trend_html in newsletter_html
215221
assert user_weekly_trend_html not in newsletter_html
216222
assert "대시보드 보러가기" in newsletter_html
223+
assert "활동 리포트" in newsletter_html
217224

218225
@patch("insight.tasks.weekly_newsletter_batch.logger")
219226
def test_get_newsletter_html_exception(

templates/insights/index.html

Lines changed: 152 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,159 @@
11
<!DOCTYPE html>
2-
<html lang="en">
3-
<head>
4-
<meta charset="UTF-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>Velog Dashboard Weekly</title>
7-
</head>
8-
<body style="margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, sans-serif;">
9-
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f5f5f5;">
10-
<tr>
11-
<td align="center" style="padding: 20px;">
12-
<table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; background-color: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);">
13-
<tr>
14-
<td style="padding: 30px;">
15-
<div class="header" style="text-align: center; margin-bottom: 20px;">
16-
<h1 style="text-decoration: underline; margin-bottom: 0; font-size: 32px; color: #333333;">
17-
<span style="color: #63e6be;">Velog Dashboard</span> Weekly
18-
</h1>
19-
<p style="font-size: 14px; color: #666;">
20-
{{s_date}} ~ {{e_date}} 사이의 트렌드를 전달해드려요.
21-
</p>
22-
</div>
2+
<html lang="ko">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Velog Dashboard Weekly Report</title>
7+
</head>
8+
<body style="margin: 0; padding: 0; background-color: #ebebeb; font-family: 'Apple SD Gothic Neo', 'Malgun Gothic', '맑은 고딕', -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;">
9+
<table
10+
width="100%"
11+
cellpadding="0"
12+
cellspacing="0"
13+
style="background-color: #ebebeb; box-sizing: border-box;"
14+
>
15+
<tr>
16+
<td
17+
align="center"
18+
style="padding: 30px 20px; box-sizing: border-box;"
19+
>
20+
<table
21+
width="100%"
22+
cellpadding="0"
23+
cellspacing="0"
24+
style="max-width: 600px; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);"
25+
>
26+
<tr>
27+
<td style="padding: 50px 25px; box-sizing: border-box;">
28+
<!-- Header -->
29+
<div
30+
class="header"
31+
style="text-align: center; margin-bottom: 30px; box-sizing: border-box;"
32+
>
33+
<div style="margin-bottom: 8px; box-sizing: border-box;">
34+
<h2 style="font-size: 24px; font-weight: 900; color: #63E6BE; margin: 0; letter-spacing: 0; box-sizing: border-box;">
35+
Velog Dashboard
36+
</h2>
37+
<h1 style="font-size: 32px; font-weight: 900; color: #000000; margin: 0; letter-spacing: 0; box-sizing: border-box;">
38+
Weekly Report
39+
</h1>
40+
</div>
41+
<p style="font-size: 12px; font-weight: 500; color: #acacac; margin: 0; letter-spacing: 0; box-sizing: border-box;">
42+
<span style="font-weight: 700; text-decoration: underline">{{s_date}} ~ {{e_date}}</span> 사이의 트렌드를 전달해드려요
43+
</p>
44+
</div>
2345

24-
<div style="text-align: center; margin: 40px 0;">
25-
<a
26-
href="https://velog-dashboard.kro.kr/?utm_source=email&utm_medium=weekly_analysis&utm_campaign=dashboard_cta"
27-
style="display: inline-block; background: linear-gradient(135deg, #20c997 0%, #17a2b8 100%); color: white; text-decoration: none; padding: 16px 32px; border-radius: 50px; font-weight: 600; font-size: 16px; box-shadow: 0 4px 15px rgba(32, 201, 151, 0.3); transition: all 0.3s ease; border: none;">
28-
📊 velog dashboard에서 전체 통계 체크하기!
29-
</a>
30-
</div>
46+
<!-- CTA Button -->
47+
<div style="text-align: center; margin: 30px 0; box-sizing: border-box;">
48+
<a
49+
href="https://velog-dashboard.kro.kr/?utm_source=email&utm_medium=weekly_analysis&utm_campaign=dashboard_cta"
50+
style="display: inline-block; background: linear-gradient(90deg, #63e6be 0%, #7abbec 100%); color: #ffffff; text-decoration: none; padding: 16px 32px; border-radius: 100px; font-weight: 900; font-size: 12px; letter-spacing: 0; line-height: 14.4px; box-sizing: border-box;"
51+
>
52+
Velog Dashboard에서 전체 통계 체크하기!
53+
</a>
54+
</div>
3155

32-
<div class="insights" style="color: #333333; font-size: 14px; line-height: 1.6;">
33-
{% if is_expired_token_user %}
34-
<div style="background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 6px; margin: 30px 0;">
35-
<p style="font-size: 16px; font-weight: bold; color: #574202; margin: 0 0 10px 0;">
36-
🚨 잠깐, 토큰이 만료 되신 것 같아요!
37-
</p>
38-
<p style="color: #51441b; margin: 0;">
39-
토큰이 만료되면 더 이상 통계를 수집할 수 없어요. 토큰을 재발급 받으시려면
40-
<a href="https://velog-dashboard.kro.kr/?utm_source=email&utm_medium=weekly_analysis&utm_campaign=token_expire" style="color: #63e6be; font-weight: bold;">여기</a>에서 재로그인 해주세요.
41-
</p>
42-
</div>
43-
{% endif %}
56+
<div
57+
class="insights"
58+
style="color: #333333; font-size: 14px; line-height: 1.6; box-sizing: border-box;"
59+
>
60+
{% if weekly_trend_html %}
61+
{{weekly_trend_html}}
62+
{% endif %}
63+
64+
{% if not is_expired_token_user and user_weekly_trend_html %}
65+
{{user_weekly_trend_html}}
66+
{% endif %}
4467

45-
{% if weekly_trend_html %}
46-
{{weekly_trend_html}}
47-
{% endif %}
48-
49-
{% if not is_expired_token_user and user_weekly_trend_html %}
50-
{{user_weekly_trend_html}}
51-
{% endif %}
52-
</div>
68+
{% if is_expired_token_user %}
69+
<!-- Token Expired Warning -->
70+
<div style="margin-bottom: 40px; box-sizing: border-box;">
71+
<h2 style="font-size: 24px; font-weight: 900; color: #000000; margin-bottom: 20px; letter-spacing: 0; box-sizing: border-box;">
72+
{% if user.username %}
73+
{{user.username}}님의 활동 리포트
74+
{% else %}
75+
활동 리포트
76+
{% endif %}
77+
</h2>
78+
<div style="background-color: #fffbd7; border-radius: 8px; padding: 20px; margin: 20px 0; box-sizing: border-box;">
79+
<p style="font-size: 16px; font-weight: 800; color: #000000; margin: 0 0 10px 0; line-height: 20px; box-sizing: border-box;">
80+
🚨 잠시만요, 토큰이 만료된 것 같아요!
81+
</p>
82+
<p style="color: #999999; margin: 0; font-size: 13px; font-weight: 500; line-height: 20px; letter-spacing: 0; box-sizing: border-box;">
83+
토큰이 만료되어 정상적으로 통계를 수집할 수 없었어요.<br />
84+
토큰을 재발급받으시려면
85+
<a
86+
href="https://velog-dashboard.kro.kr/?utm_source=email&utm_medium=weekly_analysis&utm_campaign=token_expire"
87+
style="color: #999999; font-weight: bold; text-decoration: underline; box-sizing: border-box;"
88+
>
89+
여기
90+
</a>
91+
에서 다시 로그인해주세요.
92+
</p>
93+
</div>
94+
</div>
95+
{% endif %}
5396

54-
<hr style="border: none; border-top: 1px solid #eee; margin: 30px 0;">
97+
</div>
98+
</td>
99+
</tr>
100+
</table>
55101

56-
<div class="footer" style="margin-top: 20px; text-align: center;">
57-
<a href="https://velog-dashboard.kro.kr/main?utm_source=email&utm_medium=weekly_analysis&utm_campaign=dashboard_cta" style="color: #63e6be; font-size: 12px; margin: 0 5px; text-decoration: none;">
58-
대시보드 보러가기
59-
</a>
60-
<a href="https://nuung.notion.site/terms-of-service" style="color: #666; font-size: 12px; margin: 0 5px; text-decoration: none;">
61-
서비스 이용약관
62-
</a>
63-
<a href="https://nuung.notion.site/privacy-policy" style="color: #666; font-size: 12px; margin: 0 5px; text-decoration: none;">
64-
개인정보처리방침
65-
</a>
66-
</div>
67-
</td>
68-
</tr>
69-
</table>
70-
</td>
71-
</tr>
102+
<!-- Footer - Outside content box -->
103+
<div
104+
class="footer"
105+
style="margin-top: 20px; text-align: center; width: 100%; max-width: 600px;"
106+
>
107+
<div style="display: flex; align-items: center; justify-content: center; margin-bottom: 10px; gap: 10px;">
108+
<svg
109+
width="20"
110+
height="20"
111+
style="border-radius: 2px"
112+
viewBox="0 0 20 20"
113+
fill="none"
114+
xmlns="http://www.w3.org/2000/svg"
115+
>
116+
<rect
117+
width="20"
118+
height="20"
119+
rx="0.46875"
120+
fill="#1EC996"
121+
/>
122+
<path
123+
d="M4.52499 5.81977L5.63155 12.4487H5.70632L9.36355 5.81977H10.1646L5.89451 13.4137H5.07206L3.7239 5.81977H4.52499ZM12.2818 13.4137H9.55815L11.0191 5.81977H13.7749C16.1034 5.81977 17.2495 7.24625 16.7955 9.60623C16.3374 11.9872 14.6424 13.4137 12.2818 13.4137ZM11.4206 12.0606H12.4781C13.9521 12.0606 14.8664 11.3054 15.1933 9.60623C15.5182 7.91753 14.8924 7.17283 13.4291 7.17283H12.361L11.4206 12.0606Z"
124+
fill="white"
125+
/>
126+
</svg>
127+
128+
<span style="font-size: 15px; font-weight: 700; color: #4d4d4d; letter-spacing: 0; line-height: 18px;">
129+
Velog Dashboard
130+
</span>
131+
</div>
132+
<p style="font-size: 12px; font-weight: 500; color: #4d4d4d; margin: 0; letter-spacing: 0; line-height: 14.4px;">
133+
<a
134+
href="https://velog-dashboard.kro.kr/main?utm_source=email&utm_medium=weekly_analysis&utm_campaign=dashboard_cta"
135+
style="color: #4d4d4d; text-decoration: underline; box-sizing: border-box;"
136+
>
137+
대시보드 보러가기
138+
</a>
139+
&nbsp;&nbsp;|&nbsp;&nbsp;
140+
<a
141+
href="https://nuung.notion.site/terms-of-service"
142+
style="color: #4d4d4d; text-decoration: underline; box-sizing: border-box;"
143+
>
144+
서비스 이용약관
145+
</a>
146+
&nbsp;&nbsp;|&nbsp;&nbsp;
147+
<a
148+
href="https://nuung.notion.site/privacy-policy"
149+
style="color: #4d4d4d; text-decoration: underline; box-sizing: border-box;"
150+
>
151+
개인정보처리방침
152+
</a>
153+
</p>
154+
</div>
155+
</td>
156+
</tr>
72157
</table>
73-
</body>
74-
</html>
158+
</body>
159+
</html>

0 commit comments

Comments
 (0)