Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add top article feature #385

Merged
merged 15 commits into from
Jul 2, 2023
2 changes: 1 addition & 1 deletion apps/core/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class ArticleAdmin(MetaDataModelAdmin):
"content_updated_at",
"commented_at",
"url",
"hidden_at",
("hidden_at", "topped_at"),
)
readonly_fields = (
"hit_count",
Expand Down
20 changes: 20 additions & 0 deletions apps/core/migrations/0047_article_topped_at.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 3.2.16 on 2023-05-11 11:29

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0046_update_hidden_at_min_time_to_null"),
]

operations = [
migrations.AddField(
model_name="article",
name="topped_at",
field=models.DateTimeField(
blank=True, default=None, null=True, verbose_name="인기글 달성 시각"
),
),
]
18 changes: 18 additions & 0 deletions apps/core/migrations/0048_board_top_threshold.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-05-11 13:23

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0047_article_topped_at"),
]

operations = [
migrations.AddField(
model_name="board",
name="top_threshold",
field=models.SmallIntegerField(default=10, verbose_name="인기글 달성 기준 좋아요 개수"),
),
]
91 changes: 47 additions & 44 deletions apps/core/models/article.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,128 +37,125 @@ class ArticleHiddenReason(str, Enum):


class Article(MetaDataModel):
class Meta(MetaDataModel.Meta):
verbose_name = "게시물"
verbose_name_plural = "게시물 목록"

title = models.CharField(
max_length=256,
verbose_name="제목",
max_length=256,
)
content = models.TextField(
verbose_name="본문",
)
content_text = models.TextField(
editable=False,
verbose_name="text 형식 본문",
editable=False,
)

name_type = models.SmallIntegerField(
default=BoardNameType.REGULAR,
verbose_name="익명 혹은 실명 여부",
default=BoardNameType.REGULAR,
)
is_content_sexual = models.BooleanField(
default=False,
verbose_name="성인/음란성 내용",
default=False,
)
is_content_social = models.BooleanField(
default=False,
verbose_name="정치/사회성 내용",
default=False,
)

hit_count = models.IntegerField(
default=0,
verbose_name="조회수",
default=0,
)
comment_count = models.IntegerField(
default=0,
verbose_name="댓글 수",
default=0,
)
report_count = models.IntegerField(
default=0,
verbose_name="신고 수",
default=0,
)
positive_vote_count = models.IntegerField(
default=0,
verbose_name="좋아요 수",
default=0,
)
negative_vote_count = models.IntegerField(
default=0,
verbose_name="싫어요 수",
default=0,
)

migrated_hit_count = models.IntegerField(
default=0,
verbose_name="이전된 조회수",
default=0,
)
migrated_positive_vote_count = models.IntegerField(
default=0,
verbose_name="이전된 좋아요 수",
default=0,
)
migrated_negative_vote_count = models.IntegerField(
default=0,
verbose_name="이전된 싫어요 수",
default=0,
)

created_by = models.ForeignKey(
on_delete=models.CASCADE,
verbose_name="작성자",
to=settings.AUTH_USER_MODEL,
db_index=True,
on_delete=models.CASCADE,
related_name="article_set",
verbose_name="작성자",
db_index=True,
)
parent_topic = models.ForeignKey(
on_delete=models.CASCADE,
verbose_name="말머리",
to="core.Topic",
null=True,
on_delete=models.CASCADE,
related_name="article_set",
blank=True,
default=None,
null=True,
db_index=True,
related_name="article_set",
verbose_name="말머리",
default=None,
)
parent_board = models.ForeignKey(
on_delete=models.CASCADE,
verbose_name="게시판",
to="core.Board",
db_index=True,
on_delete=models.CASCADE,
related_name="article_set",
verbose_name="게시판",
db_index=True,
)

attachments = models.ManyToManyField(
verbose_name="첨부 파일(들)",
to="core.Attachment",
blank=True,
db_index=True,
verbose_name="첨부 파일(들)",
)

commented_at = models.DateTimeField(
verbose_name="마지막 댓글 시간",
null=True,
default=None,
verbose_name="마지막 댓글 시간",
)

url = models.URLField(
null=True,
verbose_name="포탈 링크",
max_length=200,
blank=True,
null=True,
default=None,
verbose_name="포탈 링크",
)

content_updated_at = models.DateTimeField(
verbose_name="제목/본문/첨부파일 수정 시간",
null=True,
default=None,
verbose_name="제목/본문/첨부파일 수정 시간",
)

hidden_at = models.DateTimeField(
verbose_name="숨김 시간",
blank=True,
null=True,
default=None,
)
topped_at = models.DateTimeField(
verbose_name="인기글 달성 시각",
blank=True,
null=True,
default=None,
verbose_name="숨김 시간",
)

class Meta(MetaDataModel.Meta):
verbose_name = "게시물"
verbose_name_plural = "게시물 목록"

def __str__(self):
return self.title

Expand Down Expand Up @@ -223,7 +220,7 @@ def update_report_count(self):

self.save()

def update_vote_status(self):
def update_vote_status(self) -> None:
self.positive_vote_count = (
self.vote_set.filter(is_positive=True).count()
+ self.migrated_positive_vote_count
Expand All @@ -233,6 +230,12 @@ def update_vote_status(self):
+ self.migrated_negative_vote_count
)

if (
self.topped_at is None
and self.positive_vote_count >= self.parent_board.top_threshold
):
self.topped_at = timezone.now()

if (
self.parent_board.is_school_communication
and self.positive_vote_count >= SCHOOL_RESPONSE_VOTE_THRESHOLD
Expand Down
68 changes: 36 additions & 32 deletions apps/core/models/board.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,18 @@ class BoardAccessPermissionType(IntEnum):


class Board(MetaDataModel):
class Meta(MetaDataModel.Meta):
verbose_name = "게시판"
verbose_name_plural = "게시판 목록"
unique_together = (
("ko_name", "deleted_at"),
("en_name", "deleted_at"),
)

slug = AutoSlugField(
populate_from=[
"en_name",
],
)
ko_name = models.CharField(
max_length=32,
verbose_name="게시판 국문 이름",
max_length=32,
)
en_name = models.CharField(
max_length=32,
verbose_name="게시판 영문 이름",
max_length=32,
)
ko_description = models.TextField(
verbose_name="게시판 국문 소개",
Expand All @@ -51,69 +43,81 @@ class Meta(MetaDataModel.Meta):
# 사용자 그룹의 값들은 `UserGroup`을 참고하세요.
read_access_mask = models.SmallIntegerField(
# UNAUTHORIZED, EXTERNAL_ORG 제외 모든 사용자 읽기 권한 부여
default=0b011011110,
null=False,
verbose_name="읽기 권한",
default=0b011011110,
)
write_access_mask = models.SmallIntegerField(
# UNAUTHORIZED, STORE_EMPLOYEE, EXTERNAL_ORG 제외 모든 사용자 쓰기 권한 부여
default=0b011011010,
null=False,
verbose_name="쓰기 권한",
default=0b011011010,
)
comment_access_mask = models.SmallIntegerField(
# UNAUTHORIZED 제외 모든 사용자 댓글 권한 부여
default=0b011111110,
null=False,
verbose_name="댓글 권한",
default=0b011111110,
)
is_readonly = models.BooleanField(
verbose_name="읽기 전용 게시판",
help_text="활성화했을 때 관리자만 글을 쓸 수 있습니다. (ex. 포탈공지)",
default=False,
help_text="활성화했을 때 관리자만 글을 쓸 수 있습니다. (ex. 포탈공지)",
)
is_hidden = models.BooleanField(
verbose_name="리스트 숨김 게시판",
help_text="활성화했을 때 메인페이지 상단바 리스트에 나타나지 않습니다. (ex. 뉴아라공지)",
default=False,
db_index=True,
default=False,
help_text="활성화했을 때 메인페이지 상단바 리스트에 나타나지 않습니다. (ex. 뉴아라공지)",
)
name_type = models.SmallIntegerField(
verbose_name="익명/실명 게시판",
help_text="게시판의 글과 댓글들이 익명 혹은 실명이도록 합니다.",
default=BoardNameType.REGULAR,
db_index=True,
default=BoardNameType.REGULAR,
help_text="게시판의 글과 댓글들이 익명 혹은 실명이도록 합니다.",
)
is_school_communication = models.BooleanField(
verbose_name="학교와의 소통 게시판",
help_text="학교 소통 게시판 글임을 표시",
default=False,
db_index=True,
default=False,
help_text="학교 소통 게시판 글임을 표시",
)
group_id = models.IntegerField(
verbose_name="그룹 ID",
default=1,
)
group_id = models.IntegerField(verbose_name="그룹 ID", default=1)
banner_image = models.ImageField(
default="default_banner.png",
upload_to="board_banner_images",
verbose_name="게시판 배너 이미지",
upload_to="board_banner_images",
default="default_banner.png",
)
ko_banner_description = models.TextField(
null=True,
verbose_name="게시판 배너에 삽입되는 국문 소개",
blank=True,
null=True,
default=None,
verbose_name="게시판 배너에 삽입되는 국문 소개",
)
en_banner_description = models.TextField(
null=True,
verbose_name="게시판 배너에 삽입되는 영문 소개",
blank=True,
null=True,
default=None,
verbose_name="게시판 배너에 삽입되는 영문 소개",
)
banner_url = models.TextField(
null=True,
verbose_name="게시판 배너를 클릭 시에 이동하는 링크",
blank=True,
null=True,
default=None,
verbose_name="게시판 배너를 클릭 시에 이동하는 링크",
)
top_threshold = models.SmallIntegerField(
verbose_name="인기글 달성 기준 좋아요 개수",
default=10,
)

class Meta(MetaDataModel.Meta):
verbose_name = "게시판"
verbose_name_plural = "게시판 목록"
unique_together = (
("ko_name", "deleted_at"),
("en_name", "deleted_at"),
)

def __str__(self) -> str:
return self.ko_name
Expand Down
Loading