Skip to content

Commit

Permalink
Merge pull request #18 from Django-Wanted-Internship-3-Team/feature/i…
Browse files Browse the repository at this point in the history
…ssue-004

통계 조회 API 작성
  • Loading branch information
JaeHyuckSa authored Oct 26, 2023
2 parents 013fa88 + bec79b9 commit 79ee2b5
Show file tree
Hide file tree
Showing 26 changed files with 607 additions and 36 deletions.
41 changes: 14 additions & 27 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,28 @@ name: CI

on:
pull_request:
branches: [ main, develop ]
branches: [ "develop", "main" ]

jobs:
django-test:
runs-on: ubuntu-latest
strategy:
max-parallel: 4
matrix:
python-version: [3.11.5]

steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python -
- uses: actions/checkout@v3

- name: Install Dependencies
run: poetry install
- name: Build docker
run: docker compose build

- name: Run isort
run: poetry run isort . --check
- name: Migration test database
run: docker compose run django poetry run python manage.py migrate

- name: Run black
run: poetry run black . --check
- name: Run isort
run: docker compose run django poetry run isort . --check

- name: Run flake8
run: poetry run flake8
- name: Run black
run: docker compose run django poetry run black . --check

- name: Run migrations
run: poetry run python3 manage.py migrate
- name: Run flake8
run: docker compose run django poetry run flake8

- name: Run tests
run: poetry run python3 manage.py test
- name: Run Test
run: docker compose run django poetry run python3 manage.py test
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python:3.11-slim

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV POETRY_VERSION=1.6.1
ENV POETRY_HOME=/opt/poetry
ENV POETRY_VENV=/opt/poetry-venv

RUN python3 -m venv $POETRY_VENV \
&& $POETRY_VENV/bin/pip install -U pip setuptools \
&& $POETRY_VENV/bin/pip install poetry==${POETRY_VERSION}

ENV PATH="${PATH}:${POETRY_VENV}/bin"

RUN mkdir /app/
WORKDIR /app/

COPY pyproject.toml ./

RUN poetry install

COPY . /app
Empty file added common/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions common/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class CommonConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "common"
30 changes: 30 additions & 0 deletions common/dacorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from common.utils import mandatory_key, optional_key


def mandatories(*keys):
def decorate(func):
def wrapper(View, *args, **kwargs):
mandatory = dict()
for key in keys:
data = mandatory_key(View.request, key)
mandatory[key] = data
return func(View, m=mandatory, *args, **kwargs)

return wrapper

return decorate


def optionals(*keys):
def decorate(func):
def wrapper(View, *args, **kwargs):
optional = dict()
for arg in keys:
for key, val in arg.items():
data = optional_key(View.request, key, val)
optional[key] = data
return func(View, o=optional, *args, **kwargs)

return wrapper

return decorate
20 changes: 20 additions & 0 deletions common/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from rest_framework import status
from rest_framework.exceptions import APIException


class MissingMandatoryParameterException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = "Missing mandatory parameter"
default_code = "missing_mandatory_parameter"


class InvalidParameterException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = "Invalid parameter"
default_code = "invalid_parameter"


class UnknownServerErrorException(APIException):
status_code = status.HTTP_400_BAD_REQUEST
default_detail = "Unknown server error"
default_code = "unknown_server_error"
Empty file added common/tests/__init__.py
Empty file.
Empty file.
38 changes: 38 additions & 0 deletions common/tests/decorator/test_query_parameter_decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase


class QueryTest(APITestCase):
def test_get_query(self):
response = self.client.get(
path=reverse("query"),
data={
"name": "John",
"age": "30",
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["name"], "John")
self.assertEqual(response.data["age"], "30")

def test_post_query(self):
response = self.client.post(
path=reverse("query"),
data={
"city": "New York",
"occupation": "Engineer",
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["city"], "New York")
self.assertEqual(response.data["occupation"], "Engineer")

def test_query_fail(self):
response = self.client.get(
path=reverse("query"),
data={
"name": "John",
},
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
Empty file added common/tests/utils/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions common/tests/utils/test_date_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.test import TestCase
from django.utils import timezone

from common.utils import get_before_week, get_now


class DateUtilityTest(TestCase):
def test_get_now(self):
now = get_now()
current_date = timezone.now().strftime("%Y-%m-%d")
self.assertEqual(now, current_date)

def test_get_before_week(self):
before_week = get_before_week()
expected_date = (timezone.now() - timezone.timedelta(days=7)).strftime("%Y-%m-%d")
self.assertEqual(before_week, expected_date)
7 changes: 7 additions & 0 deletions common/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.urls import path

from common.views import QueryTestView

urlpatterns = [
path("query/", QueryTestView.as_view(), name="query"),
]
57 changes: 57 additions & 0 deletions common/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from django.utils import timezone
from rest_framework.request import Request

from common.exceptions import MissingMandatoryParameterException


####################
# Request Decorator
####################
def mandatory_key(request: Request, name: str) -> any:
try:
if request.method == "GET":
data = request.GET[name]
else:
data = request.POST[name]
if data in ["", None]:
raise MissingMandatoryParameterException()
except Exception:
try:
json_body = request.data
data = json_body[name]
if data in ["", None]:
raise MissingMandatoryParameterException()
except Exception:
raise MissingMandatoryParameterException()

return data


def optional_key(request: Request, name: str, default_value="") -> any:
try:
if request.method == "GET":
data = request.GET[name]
else:
data = request.POST[name]
if data in ["", None]:
data = default_value
except Exception:
try:
json_body = request.data
data = json_body[name]
if data in ["", None]:
data = default_value
except Exception:
data = default_value
return data


####################
# Date
####################
def get_now() -> timezone:
return timezone.now().strftime("%Y-%m-%d")


def get_before_week() -> timezone:
return (timezone.now() - timezone.timedelta(days=7)).strftime("%Y-%m-%d")
16 changes: 16 additions & 0 deletions common/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from rest_framework.response import Response
from rest_framework.views import APIView

from common.dacorator import mandatories, optionals


class QueryTestView(APIView):
@mandatories("name", "age")
def get(self, request, m):
response_data = {"name": m["name"], "age": m["age"]}
return Response(response_data)

@optionals({"city": "New York", "occupation": "Engineer"})
def post(self, request, o):
response_data = {"city": o["city"], "occupation": o["occupation"]}
return Response(response_data)
12 changes: 7 additions & 5 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

LOCAL_APPS = [
"users",
"common",
"posts",
"likes",
"shares",
Expand Down Expand Up @@ -74,8 +75,12 @@
# Database
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
"ENGINE": "django.db.backends.postgresql",
"NAME": env("POSTGRESQL_DATABASE", default="repo_1"),
"USER": env("POSTGRESQL_USER", default="postgres"),
"PASSWORD": env("POSTGRESQL_PASSWORD", default="password"),
"HOST": env("POSTGRESQL_HOST", default="localhost"),
"PORT": env("POSTGRESQL_PORT", default="5432"),
}
}

Expand Down Expand Up @@ -106,9 +111,6 @@

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATIC_URL = "/static/"
Expand Down
5 changes: 4 additions & 1 deletion config/urls.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework import permissions

from django.contrib import admin
from django.urls import path
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

Expand All @@ -22,6 +22,9 @@
urlpatterns = [
# Admin
path("admin/", admin.site.urls),
# API
path("api/posts/", include("posts.urls")),
path("api/common/", include("common.urls")),
# Swagger
path("swagger/docs/", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
]
Expand Down
33 changes: 33 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
version: '3.9'

volumes:
postgres: {}

services:
postgres:
container_name: postgres
image: postgres:16.0-alpine
volumes:
- postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=repo_1
- TZ=Asia/Seoul
restart: on-failure

django:
container_name: django
build:
context: .
dockerfile: Dockerfile
command: poetry run python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
environment:
- POSTGRESQL_HOST=postgres
ports:
- "8000:8000"
depends_on:
- postgres
restart: on-failure
Loading

0 comments on commit 79ee2b5

Please sign in to comment.