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

추가기능 API (최근 많이 사용한 태그 추천, 내 게시물 단기간 조회수 급상승) #21

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,23 @@ repos:
hooks:
- id: black
args: ['-l', '140', '-t', 'py311']

- repo: https://github.com/myint/autoflake
rev: v1.4
hooks:
- id: autoflake
args:
- --in-place
- --remove-unused-variables
- --remove-all-unused-imports
- --remove-unused-variables
- --ignore-init-module-imports
- --in-place
- --recursive

- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: ['--line-length', '140']
args: ['--profile', 'black', '--filter-files', 'true']

default_language_version:
python: python3.11
File renamed without changes.
2 changes: 1 addition & 1 deletion common/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from common.dacorator import mandatories, optionals
from common.decorator import mandatories, optionals


class QueryTestView(APIView):
Expand Down
35 changes: 29 additions & 6 deletions posts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# Generated by Django 4.2.6 on 2023-10-25 07:12
# Generated by Django 4.2.6 on 2023-10-28 12:59

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

initial = True

dependencies = [
('users', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
Expand All @@ -18,17 +20,17 @@ class Migration(migrations.Migration):
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=32)),
('hashtag_count', models.IntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'db_table': 'hashtag',
'db_table': 'hashtags',
},
),
migrations.CreateModel(
name='Post',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('content_id', models.UUIDField(default=uuid.uuid4, editable=False)),
('post_type', models.CharField(choices=[('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('threads', 'Threads')], max_length=16)),
('title', models.CharField(max_length=32)),
('content', models.TextField()),
Expand All @@ -37,11 +39,32 @@ class Migration(migrations.Migration):
('share_count', models.IntegerField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('hashtag', models.ManyToManyField(related_name='posts', to='posts.hashtag')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.user')),
],
options={
'db_table': 'posts',
},
),
migrations.CreateModel(
name='PostHashtag',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('hashtag', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='posts.hashtag')),
('post', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='posts.post')),
],
options={
'db_table': 'posts_hashtags',
'unique_together': {('post', 'hashtag')},
},
),
migrations.AddField(
model_name='post',
name='hashtag_set',
field=models.ManyToManyField(through='posts.PostHashtag', to='posts.hashtag'),
),
migrations.AddField(
model_name='post',
name='user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]
19 changes: 0 additions & 19 deletions posts/migrations/0002_post_content_id.py

This file was deleted.

24 changes: 17 additions & 7 deletions posts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

from django.db import models

from users.models import User


class Post(models.Model):
class PostType(models.TextChoices):
Expand All @@ -21,23 +19,35 @@ class PostType(models.TextChoices):
share_count = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
hashtag = models.ManyToManyField("HashTag", related_name="posts")
user = models.ForeignKey("users.User", on_delete=models.SET_NULL, null=True)
hashtag_set = models.ManyToManyField("HashTag", through="PostHashtag", through_fields=("post", "hashtag"))
JaeHyuckSa marked this conversation as resolved.
Show resolved Hide resolved

class Meta:
db_table = "posts"

def __str__(self):
return f"{self.title} by {self.user.email}"
return f"[{self.user.email}]: {self.title}"


class HashTag(models.Model):
name = models.CharField(max_length=32)
JaeHyuckSa marked this conversation as resolved.
Show resolved Hide resolved
hashtag_count = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
db_table = "hashtag"
db_table = "hashtags"

def __str__(self):
return self.name


class PostHashtag(models.Model):
post = models.ForeignKey("Post", on_delete=models.SET_NULL, null=True)
hashtag = models.ForeignKey("HashTag", on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
db_table = "posts_hashtags"
unique_together = ("post", "hashtag")
JaeHyuckSa marked this conversation as resolved.
Show resolved Hide resolved

def __str__(self):
return f"{self.post.title} - {self.hashtag.name}"
11 changes: 11 additions & 0 deletions posts/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from rest_framework import serializers

from posts.models import HashTag


class StatisticsQuerySerializer(serializers.Serializer):
type = serializers.ChoiceField(choices=["date", "hour"])
Expand All @@ -12,3 +14,12 @@ class StatisticsQuerySerializer(serializers.Serializer):
class StatisticsListSerializer(serializers.Serializer):
datetime = serializers.DateTimeField()
count = serializers.IntegerField()


class HashTagRecommendListSerializer(serializers.ModelSerializer):
class Meta:
model = HashTag
fields = (
"id",
"name",
)
55 changes: 55 additions & 0 deletions posts/tests/views/test_hashtag_recommend_list_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

from posts.models import HashTag, Post
from users.models import User


class HashTagRecommendListViewTest(APITestCase):
@classmethod
def setUpTestData(cls):
cls.hasgtag1 = HashTag.objects.create(
name="hashtag1",
)
cls.hasgtag2 = HashTag.objects.create(
name="hashtag2",
)
for i in range(1, 10):
cls.users = User.objects.create_user(
email=f"user{i}@example.com",
password="testpassword",
)
cls.posts = Post.objects.create(
post_type="facebook",
title=f"title {i}",
content=f"content {i}",
view_count=i,
like_count=i,
share_count=i,
user=cls.users,
)
if i % 2 == 0:
cls.posts.hashtag_set.set([cls.hasgtag2])
else:
cls.posts.hashtag_set.set([cls.hasgtag1, cls.hasgtag2])

def setUp(self):
pass
# self.access_token = self.client.post(reverse("token_obtain_pair"), self.user_data).data["access"]

def test_get_hash_tag_recommend_list_success(self):
response = self.client.get(
path=reverse("hashtag_recommend_list"),
# HTTP_AUTHORIZATION=f"Bearer {self.access_token}",
)
self.assertEqual(response.data[0]["id"], 2)
self.assertEqual(response.data[0]["name"], "hashtag2")
self.assertEqual(response.status_code, status.HTTP_200_OK)

# @TODO: 로그인 로직 구현 후 주석 풀기 @SaJH
# def test_get_hash_tag_recommend_list_fail_unauthenticated(self):
# response = self.client.get(
# path=reverse("hashtag_recommend_list"),
# )
# self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
18 changes: 9 additions & 9 deletions posts/tests/views/test_statics_list_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ def setUpTestData(cls):
share_count=i,
user=cls.users,
)
cls.posts.hashtag.set([cls.hasgtag])
cls.posts.hashtag_set.set([cls.hasgtag])

def setUp(self):
pass
# self.access_token = self.client.post(reverse("token_obtain_pair"), self.user_data).data["access"]

def test_get_statistics_list_date_success(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"type": "date",
"hashtag": "hashtag",
Expand All @@ -46,7 +46,7 @@ def test_get_statistics_list_date_success(self):

def test_get_statistics_list_hour_success(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"type": "hour",
"hashtag": "hashtag",
Expand All @@ -58,7 +58,7 @@ def test_get_statistics_list_hour_success(self):

def test_get_statistics_list_not_found(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"type": "date",
"hashtag": "hashtag1",
Expand All @@ -72,7 +72,7 @@ def test_get_statistics_list_not_found(self):
# @TODO: 로그인 로직 구현 후 주석 풀기 @SaJH
# def test_get_statistics_list_fail_unauthenticated(self):
# response = self.client.get(
# path=reverse("statistics"),
# path=reverse("statistics_list"),
# data={
# "type": "date",
# "hashtag": "hashtag",
Expand All @@ -83,7 +83,7 @@ def test_get_statistics_list_not_found(self):

def test_get_statistics_list_fail_missing_parameter(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"hashtag": "hashtag",
},
Expand All @@ -93,7 +93,7 @@ def test_get_statistics_list_fail_missing_parameter(self):

def test_get_statistics_list_fail_invalid_parameter_type(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"type": "test",
"hashtag": "hashtag",
Expand All @@ -105,7 +105,7 @@ def test_get_statistics_list_fail_invalid_parameter_type(self):

def test_get_statistics_list_fail_invalid_parameter_max_days(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"type": "date",
"start": "2023-10-1",
Expand All @@ -119,7 +119,7 @@ def test_get_statistics_list_fail_invalid_parameter_max_days(self):

def test_get_statistics_list_fail_invalid_parameter_value(self):
response = self.client.get(
path=reverse("statistics"),
path=reverse("statistics_list"),
data={
"type": "date",
"hashtag": "hashtag",
Expand Down
5 changes: 3 additions & 2 deletions posts/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from django.urls import path

from posts.views import StatisticsListView
from posts.views import HashTagRecommendListView, StatisticsListView

urlpatterns = [
path("statistics/", StatisticsListView.as_view(), name="statistics"),
path("statistics/", StatisticsListView.as_view(), name="statistics_list"),
path("hashtag/recommend/", HashTagRecommendListView.as_view(), name="hashtag_recommend_list"),
]
Loading