Skip to content

Commit c13f904

Browse files
authored
[25.06.25 / TASK-200] Feature - 주간 뉴스레터 배치 프로세스 (#33)
* feature: html2text 라이브러리 추가 * feature: 이메일 발송 관련 환경변수 추가 * feature: 주간 뉴스레터 템플릿 추가 v1.0 * feature: 주간 뉴스레터 발송 배치 v0.5 * revert: Django 어드민용 템플릿 되살리기 * featrue: newsletter logger 설정 추가 * feature: 주간 뉴스레터 발송 배치 v1.0 * feature: 주간 뉴스레터 발송 배치 v2.0 * feature: 주간 뉴스레터 발송 배치 v2.5 * modify: 코드래빗 리뷰 짜잘이들 반영 * refactor: 뉴스레터 렌더링 하는 작업 분리 * refactor: 메서드명을 명확하게 수정 및 SESClient 의존성 주입 패턴 적용 * modify: 주석 업데이트 * modify: pre-commit 적용 및 자잘한 개선 * modify: html2text 라이브러리 삭제 및 html 태그 삭제 유틸 함수 추가 메일 발송시 보기 좋은 text를 위해 추가한 라이브러리였으나, text로 받는 경우가 적음 && 전체 서비스에 메일 발송은 작은 부분을 차지 관련 의존성을 삭제하고 정규표현식으로 대체함 * modify: 리뷰 반영 및 사소한 수정 * feature: 주간 뉴스레터 템플릿 v2.0 작성자 표시, 글 바로가기, 썸네일 추가 글 아이템 박스로 표시 및 스타일 전체적으로 통일
1 parent 3ffc930 commit c13f904

File tree

12 files changed

+949
-2
lines changed

12 files changed

+949
-2
lines changed

.env.sample

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,7 @@ OPENAI_API_KEY=sk-proj-...
3535
# AWS SES
3636
AWS_ACCESS_KEY_ID=ID
3737
AWS_SECRET_ACCESS_KEY=AccEssKeY
38-
AWS_REGION=ap-northeast-2
38+
AWS_REGION=ap-northeast-2
39+
40+
# Newsletter Info
41+
DEFAULT_FROM_EMAIL='no-reply@velog-dashboard.kro.kr'

.github/workflows/test-ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ jobs:
7474
echo "AWS_ACCESS_KEY_ID=ID" >> .env
7575
echo "AWS_SECRET_ACCESS_KEY=AccEssKeY" >> .env
7676
echo "AWS_REGION=ap-northeast-2" >> .env
77+
echo "DEFAULT_FROM_EMAIL=no-reply@velog-dashboard.kro.kr" >> .env
7778
echo "AES_KEY_0=${{ secrets.AES_KEY_0 }}" >> .env
7879
echo "AES_KEY_1=${{ secrets.AES_KEY_1 }}" >> .env
7980
echo "AES_KEY_2=${{ secrets.AES_KEY_2 }}" >> .env

backoffice/settings/base.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,21 @@
217217
"encoding": "utf-8",
218218
"filename": os.path.join(BASE_DIR, "logs", "scraping.log"),
219219
},
220+
"newsletter_console": {
221+
"level": "INFO",
222+
"class": "logging.StreamHandler",
223+
"formatter": "default_formatter",
224+
},
225+
"newsletter_file": {
226+
"level": "INFO",
227+
"class": "logging.handlers.TimedRotatingFileHandler",
228+
"when": "midnight",
229+
"interval": 1,
230+
"backupCount": 7,
231+
"formatter": "default_formatter",
232+
"encoding": "utf-8",
233+
"filename": os.path.join(BASE_DIR, "logs", "newsletter.log"),
234+
},
220235
},
221236
"loggers": {
222237
"django": {
@@ -229,6 +244,11 @@
229244
"level": "INFO",
230245
"propagate": False,
231246
},
247+
"newsletter": {
248+
"handlers": ["newsletter_console", "newsletter_file"],
249+
"level": "INFO",
250+
"propagate": False,
251+
},
232252
},
233253
}
234254

insight/schemas.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from dataclasses import dataclass
2+
3+
from insight.models import WeeklyTrendInsight
4+
from modules.mail.schemas import EmailMessage
5+
6+
7+
# templates/insight/index.html 데이터 스키마
8+
@dataclass
9+
class NewsletterContext:
10+
s_date: str
11+
e_date: str
12+
is_expired_token_user: bool
13+
weekly_trend_html: str | None = None
14+
user_weekly_trend_html: str | None = None
15+
16+
17+
# templates/insight/weekly_trend.html 데이터 스키마
18+
@dataclass
19+
class WeeklyTrendContext:
20+
insight: WeeklyTrendInsight
21+
22+
23+
# templates/insight/user_weekly_trend.html 데이터 스키마
24+
@dataclass
25+
class UserWeeklyTrendContext:
26+
user: dict[str, str] # username
27+
user_weekly_stats: dict[str, int] # posts, views, likes
28+
insight: WeeklyTrendInsight
29+
30+
31+
@dataclass
32+
class Newsletter:
33+
user_id: int
34+
email_message: EmailMessage

insight/tasks/__init__.py

Whitespace-only changes.

insight/tasks/setup_django.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import os
2+
import sys
3+
4+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
5+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backoffice.settings.local")
6+
7+
import django
8+
9+
django.setup()

0 commit comments

Comments
 (0)