From 072817d469e7da3942d9ea1cc4f04022919d821e Mon Sep 17 00:00:00 2001 From: YoungKwangJoo <164307740+youngkwangjoo@users.noreply.github.com> Date: Sat, 3 Aug 2024 01:55:14 +0900 Subject: [PATCH 01/25] =?UTF-8?q?READ=20ME=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사진까지 교체 --- readme.md | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 192 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3e03eb5..6bca9a4 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,192 @@ -우린 감자쓰~~~~!!!! \ No newline at end of file +
+

GitPotato

+ +

이 프로젝트는 사용자의 GitHub 및 Baekjoon 활동을 기반으로 감자 캐릭터를 성장시키는 게임형 웹 애플리케이션입니다.
사용자는 자신의 활동을 통해 감자 캐릭터를 키우고, 게임의 재미를 통해 동기 부여를 받으며 일상적인 업무와 목표를 관리할 수 있습니다.

+


+
+ +## 프로젝트 개요 +- 프로젝트 이름: GitPotato +- 프로젝트 기간: 2024년 7월 10일~2024년 8월 4일 +- 배포URL: https://www.gitpotatoes.com/ +
+ +### 팀원 구성 +| **김다연** | **주영광** | **노성우** | +|:-------------------------------:|:-----------------------------:|:-----------------------------:| +| | | | +| [dayeonkimm](https://github.com/dayeonkimm) | [youngkwangjoo](https://github.com/youngkwangjoo) | [NohSungwoo](https://github.com/NohSungwoo) | + +
+ +*** + +## 1. 개발 환경 +### 개발언어 +
+ +
+ +### 라이브러리 +
+ + +
+ +### 상태관리 +
+ +
+ +### 버전관리 +
+ +
+ +### 배포 +
+ + +
+ +### 협업툴 +
+ + + +
+
+ +## 2. 채택한 개발기술과 브랜치 전략 +### 기술 +- React의 컴포넌트 기반 구조와 JavaScript의 최신 기능을 활용하여 애플리케이션의 모듈화와 재사용성을 높였습니다 +- Tailwind CSS의 유틸리티 클래스들을 사용하여 빠르고 일관된 스타일링을 구현했습니다 +- Zustand를 활용하여 웹어플리케이션의 상태를 간단하고 직관적으로 관리했습니다, 또한 불필요한 리렌더링을 방지하고 성능을 최적화했습니다 +- ESLint와 Prettier를 사용하여 코드 스타일을 자동으로 정리하고 일관성을 유지했습니다 + +### 브랜치 전략 +- Main 프로젝트를 Fork하여 각자의 레포지토리에서 개발을 진행합니다. +- 개발을 진행할 때에는 개발 유형에 맞게 개발유형/개발구역이름 형식으로 브랜치를 생성하여 작업합니다. 예를 들어, 새로운 기능을 추가할 때는 feat/login, 버그를 수정할 때는 fix/bug123과 같은 형식을 사용합니다. +- 현재 작업하고 있는 부분의 기능 구현이 완료되면 팀원들에게 코드 리뷰를 요청합니다. Pull Request를 생성하여 코드 검토를 진행하며, 리뷰어의 피드백을 반영하여 코드를 개선합니다. +- 코드 리뷰가 완료되고 승인이 나면, Pull Request를 통해 dev 브랜치로 변경 사항을 병합합니다. 병합 후에는 dev 브랜치에서 전체적인 기능 테스트를 진행합니다. dev 브랜치의 안정성이 확보되면 main 브랜치로 병합하여 배포를 준비합니다. +- 이 전략을 통해 각 개발자는 독립적으로 작업하면서도 팀과의 협업을 원활하게 진행할 수 있습니다. 코드의 품질을 유지하고 버그를 최소화할 수 있도록 지속적으로 코드 리뷰와 테스트를 강화합니다. +
+ +## 3. Commit Convention +| 커밋 유형 | 의미 | 깃모지 | +|--------------|------------------------------------------|-----------------| +| **Feat** | 새로운 기능 추가 | :sparkles: | +| **Fix** | 버그를 고친 경우 | :bug: | +| **Docs** | 문서 수정 | :memo: | +| **Refactor** | 코드 리팩토링 | :recycle: | +| **Chore** | 패키지 매니저 수정, 그 외 기타 수정 | :package: | +| **Design** | CSS 등 사용자 UI 디자인 변경 | :art: | +| **Change** | 파일명 변경, 파일 삭제 등 기타 | :wrench: | +| **Test** | 테스트 코드, 리팩토링 테스트 코드 추가 | :clown_face: | + +
+ +## 4. 프로젝트 구조 +``` +. +├── README.md +├── package-lock.json +├── package.json +├── src +│ ├── App.jsx +│ ├── apis +│ │ ├── api +│ │ │ ├── attendance.js +│ │ │ ├── auth.js +│ │ │ ├── baekjoon.js +│ │ │ ├── github.js +│ │ │ ├── potato.js +│ │ │ ├── stack.js +│ │ │ └── todo.js +│ │ ├── services +│ │ │ ├── authService.js +│ │ │ ├── calendarService.js +│ │ │ ├── levelService.js +│ │ │ ├── settingPageService.js +│ │ │ ├── stackService.js +│ │ │ ├── todoService.js +│ │ │ └── userUpdateService.js +│ │ └── utils +│ │ └── instance.js +│ ├── assets +│ │ ├── description.json +│ │ └── images +│ ├── components +│ │ ├── common +│ │ │ ├── alert +│ │ │ ├── input +│ │ │ └── loading +│ │ ├── layout +│ │ │ ├── footer +│ │ │ └── header +│ │ ├── modal +│ │ │ ├── BaekjoonConnectModal.jsx +│ │ │ ├── ChangePotatoModal.jsx +│ │ │ ├── PotatoInfoModal.jsx +│ │ │ ├── UserUpdateModal.jsx +│ │ │ └── index.js +│ │ └── shared +│ │ ├── calendar +│ │ ├── collection +│ │ ├── info +│ │ ├── level +│ │ ├── potato +│ │ ├── stack +│ │ └── todo +│ ├── hooks +│ │ └── useCalendar +│ ├── lib +│ ├── main.jsx +│ ├── pages +│ │ ├── home +│ │ ├── landing +│ │ ├── oauthCallback +│ │ ├── setting +│ │ └── signin +│ ├── store +│ │ ├── dateStore.js +│ │ ├── todosCompleteStore.js +│ │ └── userStore.js +│ ├── styles +│ │ └── global.css +│ └── utils +│ └── storage.js +├── tailwind.config.js +└── vite.config.js +``` + +## 5. 프로젝트 +
+
+

Main Page

+ +
+
+
+

Login Page

+ +
+
+
+

Home Page

+ +
+
+
+

Update Modal

+ +
+
+
+ + +## 6. Architecture 및 ERD +### Architecture +![아키텍쳐](https://github.com/user-attachments/assets/57e04dea-a784-47a2-af1d-990ecc9c3ca1) + + From 8be7a24641f4a0d34e96bacb40e7694fa7f70047 Mon Sep 17 00:00:00 2001 From: YoungKwangJoo <164307740+youngkwangjoo@users.noreply.github.com> Date: Sat, 3 Aug 2024 02:14:25 +0900 Subject: [PATCH 02/25] =?UTF-8?q?READ=20ME=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기술스택 변경 중.. --- readme.md | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/readme.md b/readme.md index 6bca9a4..d66e6fd 100644 --- a/readme.md +++ b/readme.md @@ -21,32 +21,16 @@ *** -## 1. 개발 환경 -### 개발언어 +## 1. 기술 스택
- + + + +
- -### 라이브러리 -
- - -
- -### 상태관리 -
- -
- -### 버전관리
-
- -### 배포 -
-
### 협업툴 From e20069195dae42a6a9478b4c747b42209225cd97 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:09:16 +0900 Subject: [PATCH 03/25] docker-compose.yml --- docker-compose.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 372dcda..0b3e88d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,8 @@ services: - DB_USER=${RDS_USERNAME} - DB_PASSWORD=${RDS_PASSWORD} env_file: - - .env + - .env + # 개발용 @@ -53,7 +54,7 @@ services: # - .env # depends_on: # - db - + # db: # PostgreSQL Database # image: postgres:16-alpine @@ -66,4 +67,5 @@ services: # env_file: # - .env + From 95db2c6762edfe0841119ae675041b00583372a9 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:11:07 +0900 Subject: [PATCH 04/25] Dockerfile --- Dockerfile | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8fb6ca7..7a14c02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,29 +19,26 @@ EXPOSE 8000 # ARG DEV=false -RUN python -m venv /py && \ - /py/bin/pip install --upgrade pip && \ - /py/bin/pip install -r /tmp/requirements.txt && \ - apk add --update --no-cache jpeg-dev && \ - apk add --update --no-cache --virtual .tmp-build-deps \ - build-base musl-dev zlib zlib-dev linux-headers && \ - # if [ $DEV = "true" ]; \ - # then /py/bin/pip install -r /tmp/requirements.dev.txt ; \ - # fi && \ - rm -rf /tmp && \ - apk del .tmp-build-deps && \ - adduser \ - --disabled-password \ - --no-create-home \ - django-user - -ENV PATH="/py/bin:$PATH" - +# 가상 환경 설정 및 패키지 설치 +RUN python -m venv /py +RUN /py/bin/pip install --upgrade pip +RUN /py/bin/pip install --no-cache-dir -r /tmp/requirements.txt + +# 시스템 패키지 설치 +RUN apk add --update --no-cache jpeg-dev +RUN apk add --update --no-cache --virtual .tmp-build-deps \ + build-base musl-dev zlib zlib-dev linux-headers \ + && apk del .tmp-build-deps + +# django-user 생성 및 권한 설정 +RUN if ! getent passwd django-user; then adduser -D django-user; fi +USER root +RUN chown -R django-user:django-user /py/lib/python3.11/site-packages USER django-user -# 이 명령어를 추가하여 pytest를 설치합니다. -RUN /py/bin/pip install pytest pytest-django - +# 추가 패키지 설치 +ENV PATH="/py/bin:$PATH" +RUN /py/bin/pip install --no-cache-dir pytest pytest-django django-cors-headers # 개발용 @@ -87,4 +84,5 @@ RUN /py/bin/pip install pytest pytest-django # USER django-user # # 이 명령어를 추가하여 pytest를 설치합니다. -# RUN /py/bin/pip install pytest pytest-django \ No newline at end of file +# RUN /py/bin/pip install pytest pytest-django + From d207fdbcd95bbdb3a90ec11e0e45e32e1e62b212 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:12:52 +0900 Subject: [PATCH 05/25] cors setting --- potato_project/app/settings.py | 61 ++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/potato_project/app/settings.py b/potato_project/app/settings.py index f0f2833..7eaac4d 100644 --- a/potato_project/app/settings.py +++ b/potato_project/app/settings.py @@ -53,8 +53,10 @@ "allauth.account", "allauth.socialaccount", "allauth.socialaccount.providers.github", + "corsheaders", ] + INSTALLED_APPS = DJANGO_SYSTEM_APPS + CUSTOM_USER_APPS # Custom user model @@ -71,6 +73,7 @@ # 미들웨어 설정 MIDDLEWARE = [ + "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", @@ -115,6 +118,9 @@ "USER": os.environ.get("RDS_USERNAME"), "PASSWORD": os.environ.get("RDS_PASSWORD"), "PORT": os.environ.get("RDS_PORT", 5432), + "OPTIONS": { + "client_encoding": "UTF8", # UTF-8 문자셋 설정 + }, } } @@ -205,6 +211,55 @@ } } -SOCIALACCOUNT_LOGIN_ON_GET = True -LOGIN_REDIRECT_URL = "/oauth-callback/" -ACCOUNT_LOGOUT_REDIRECT_URL = "/landing/" +SOCIALACCOUNT_LOGIN_ON_GET = False +# LOGIN_REDIRECT_URL = "/oauth-callback/" +# ACCOUNT_LOGOUT_REDIRECT_URL = "/landing/" + +DEFAULT_CHARSET = "utf-8" + +DEFAULT_CHARSET = "utf-8" + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "console": { + "class": "logging.StreamHandler", + }, + }, + "root": { + "handlers": ["console"], + "level": "DEBUG", + }, +} + +# CORS_ORIGIN_WHITELIST = ['http://localhost:5173', 'http://127.0.0.1:5173', 'https://www.gitpotatoes.com',] # 특정 Origin만 허용 +CORS_ALLOWED_ORIGINS = [ + "https://www.gitpotatoes.com", # 실제 배포 프론트엔드 URL + "http://localhost:5173", # 프론트엔드 로컬 서버 URL + "http://127.0.0.1:5173", # 프론트엔드 로컬 서버 URL +] +CORS_ALLOW_CREDENTIALS = True # 쿠키 등 credential 정보 허용 +CORS_ALLOW_METHODS = [ + "DELETE", + "GET", + "OPTIONS", + "PATCH", + "POST", + "PUT", +] +CORS_ALLOW_HEADERS = [ + "accept", + "accept-encoding", + "authorization", + "content-type", + "dnt", + "origin", + "user-agent", + "x-csrftoken", + "x-requested-with", +] +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + +CSRF_COOKIE_SECURE = True +SESSION_COOKIE_SECURE = True From 605a1e1666246bbfe081016f51c9adc42d9cabfd Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:13:59 +0900 Subject: [PATCH 06/25] isort, black --- potato_project/attendances/views.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/potato_project/attendances/views.py b/potato_project/attendances/views.py index 3014709..a85f113 100644 --- a/potato_project/attendances/views.py +++ b/potato_project/attendances/views.py @@ -40,7 +40,9 @@ def increment(self, request): # 출석날짜가 오늘이면 이미 출석함을 반환 attendance = self.get_user_attendance(user) if attendance and attendance.date == today: - return Response({"오늘은 출석을 이미 하셨어요!"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"오늘은 출석을 이미 하셨어요!"}, status=status.HTTP_400_BAD_REQUEST + ) # 새로운 출석 기록 생성 new_attendance = Attendance.objects.create( @@ -71,4 +73,6 @@ def decrement(self, request): user.save() # 성공 응답 반환 - return Response({"message": "물건을 구매했습니다.", "total_coins": user.total_coins}) + return Response( + {"message": "물건을 구매했습니다.", "total_coins": user.total_coins} + ) From 6776c32db50023274039e5ec62fb35cd4e61bcaf Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:15:50 +0900 Subject: [PATCH 07/25] =?UTF-8?q?=20=EA=B9=83=ED=97=88=EB=B8=8C=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EA=B0=90?= =?UTF-8?q?=EC=9E=90=20=ED=9A=8D=EB=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/githubs/signals.py | 122 ++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 potato_project/githubs/signals.py diff --git a/potato_project/githubs/signals.py b/potato_project/githubs/signals.py new file mode 100644 index 0000000..78467e8 --- /dev/null +++ b/potato_project/githubs/signals.py @@ -0,0 +1,122 @@ +from datetime import date, timedelta + +from django.db.models.signals import post_save +from django.dispatch import receiver +from githubs.models import Github +from potatoes.models import Potato + + +@receiver(post_save, sender=Github) +def get_winter_potato(sender, instance, created, **kwargs): + # 새 Github 데이터가 생성 or 업데이트, 날짜가 크리스마스이며, commit_num이 1 이상인 경우에만 실행 + if ( + created + and instance.commit_num >= 1 + and instance.date.month == 12 + and instance.date.day == 25 + ): # 월과 일만 비교 + try: + # potato_type_id=6인 감자 조회 + potato = Potato.objects.get(user=instance.user, potato_type_id=6) + if not potato.is_acquired: # 이미 획득한 경우에는 변경하지 않음 + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + # 해당 감자가 없는 경우 에러 처리 (필요에 따라 추가) + pass + + +@receiver(post_save, sender=Github) +def get_ghost_potato(sender, instance, created, **kwargs): + if ( + created + and instance.commit_num >= 1 + and instance.date.month == 10 + and instance.date.day == 31 + ): + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=7) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=Github) +def get_crystal_potato(sender, instance, created, **kwargs): + if created and instance.commit_num >= 1: + # 30일 전 날짜 계산 + thirty_days_ago = instance.date.date() - timedelta(days=30) + + # 30일 연속 커밋 여부 확인 + commits_in_30_days = ( + Github.objects.filter( + user=instance.user, + date__gte=thirty_days_ago, + date__lte=instance.date.date(), + commit_num__gte=1, + ) + .values("date") + .distinct() + .count() + ) + + if commits_in_30_days == 30: + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=8) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=Github) +def get_dirty_potato(sender, instance, created, **kwargs): + if created: + # 30일 전 날짜 계산 + thirty_days_ago = instance.date.date() - timedelta(days=30) + + # 30일 동안 커밋이 있었는지 확인 + any_commits_in_30_days = Github.objects.filter( + user=instance.user, + date__gte=thirty_days_ago, + date__lte=instance.date.date(), + commit_num__gte=1, + ).exists() + + if not any_commits_in_30_days: + # 30일 연속 커밋이 없는 경우 감자 아이디 9 획득 로직 실행 + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=9) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass # 필요에 따라 에러 처리 추가 + + +@receiver(post_save, sender=Github) +def get_green_potato(sender, instance, created, **kwargs): + if created: + # 90일 전 날짜 계산 + ninety_days_ago = instance.date.date() - timedelta(days=90) + + # 90일 동안 커밋이 있었는지 확인 + any_commits_in_90_days = Github.objects.filter( + user=instance.user, + date__gte=ninety_days_ago, + date__lte=instance.date.date(), + commit_num__gte=1, + ).exists() + + if not any_commits_in_90_days: + # 90일 연속 커밋이 없는 경우 감자 아이디 10 획득 로직 실행 + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=10) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass # 필요에 따라 에러 처리 추가 From b571aac7673d87bc0f92856de0bfc450320b200a Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:16:08 +0900 Subject: [PATCH 08/25] =?UTF-8?q?=20=EB=A0=88=EB=B2=A8=EC=97=85?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EA=B0=90=EC=9E=90=20=ED=9A=8D?= =?UTF-8?q?=EB=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/users/signals.py | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 potato_project/users/signals.py diff --git a/potato_project/users/signals.py b/potato_project/users/signals.py new file mode 100644 index 0000000..3fc6bb5 --- /dev/null +++ b/potato_project/users/signals.py @@ -0,0 +1,71 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from potatoes.models import Potato +from users.models import User + + +@receiver(post_save, sender=User) +def get_level_two_potato(sender, instance, **kwargs): + # 레벨이 2로 변경되었고, 이전 레벨이 2가 아닐 때만 실행 + if ( + instance.potato_level == 2 + and instance.tracker.previous("potato_level") != 2 + and not kwargs.get("created", False) + ): + try: + # potato_type_id=2인 감자 조회 + potato = Potato.objects.get(user=instance.user, potato_type_id=2) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + # 해당 감자가 없는 경우 에러 처리 (필요에 따라 추가) + pass + + +@receiver(post_save, sender=User) +def get_level_three_potato(sender, instance, **kwargs): + if ( + instance.potato_level == 3 + and instance.tracker.previous("potato_level") != 3 + and not kwargs.get("created", False) + ): + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=3) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=User) +def get_level_four_potato(sender, instance, **kwargs): + if ( + instance.potato_level == 4 + and instance.tracker.previous("potato_level") != 4 + and not kwargs.get("created", False) + ): + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=4) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + + +@receiver(post_save, sender=User) +def get_level_five_potato(sender, instance, **kwargs): + if ( + instance.potato_level == 5 + and instance.tracker.previous("potato_level") != 5 + and not kwargs.get("created", False) + ): + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=5) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass From a704b244cd395d67311d089e69c454e524aee305 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:17:16 +0900 Subject: [PATCH 09/25] =?UTF-8?q?=20=EA=B9=83=ED=97=88=EB=B8=8C?= =?UTF-8?q?=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 반환값 개수 매칭 에러 수정 - 필터에 author 추가해서 사용자가 커밋한 내용만 필터 --- potato_project/githubs/views.py | 97 ++++++++++++++++----------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/potato_project/githubs/views.py b/potato_project/githubs/views.py index 60c6c4d..53a6589 100644 --- a/potato_project/githubs/views.py +++ b/potato_project/githubs/views.py @@ -1,11 +1,10 @@ from datetime import datetime, timedelta -# githubs api를 불러오는 함수 -# 깃허브 API 호출 서비스 import requests from django.db.models import Avg, Sum from django.http import JsonResponse from django.utils import timezone +from rest_framework import status from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView @@ -13,22 +12,25 @@ from .models import Github as GithubModel -# 깃허브 API 호출 서비스 class GitHubAPIService: def __init__(self, access_token): self.access_token = access_token - def get_commits(self, repo): + def get_commits(self, repo, author): github_api_url = f"https://api.github.com/repos/{repo}/commits" + params = { + "author": author, # author 파라미터 설정 + "per_page": 100, + } headers = { "Authorization": f"token {self.access_token}", "Accept": "application/vnd.github.v3+json", } - response = requests.get(github_api_url, headers=headers) + response = requests.get(github_api_url, headers=headers, params=params) if response.status_code == 200: - return response.json() + return response.json(), 200 else: return None, response.status_code @@ -42,22 +44,7 @@ def get_repos(self, username): response = requests.get(github_api_url, headers=headers) if response.status_code == 200: - return response.json() - else: - return None, response.status_code - - def get_total_commits(self, username): - github_api_url = f"https://api.github.com/search/commits?q=author:{username}" - headers = { - "Authorization": f"token {self.access_token}", - "Accept": "application/vnd.github.v3+json", - } - - response = requests.get(github_api_url, headers=headers) - - if response.status_code == 200: - data = response.json() - return data["total_count"] + return response.json(), 200 else: return None, response.status_code @@ -83,9 +70,12 @@ def get(self, request): return Response({"error": "깃허브 액세스 토큰이 없습니다."}, status=401) github_service = GitHubAPIService(user.github_access_token) + db_service = GitHubDatabaseService() # 오늘 날짜 - today = timezone.now().date() + today = timezone.localtime(timezone.now()).date() + + print("today: ", today) # 7일 전 날짜 week_ago = today - timedelta(days=7) @@ -94,57 +84,64 @@ def get(self, request): if repos is not None: # 오늘, 7일간 커밋 수 계산 - today_commit_count = 0 - week_commit_count = 0 + today_commits = 0 for repo in repos: - commits, _ = github_service.get_commits(repo["full_name"]) + commits, _ = github_service.get_commits( + repo["full_name"], user.username + ) if commits is not None: for commit in commits: commit_date = datetime.strptime( commit["commit"]["author"]["date"], "%Y-%m-%dT%H:%M:%SZ" ).date() if commit_date == today: - today_commit_count += 1 - if commit_date >= week_ago: - week_commit_count += 1 - - # 7일 평균 커밋 수 계산 - week_average_commit_count = round(week_commit_count / 7, 2) - - # 총 커밋 수 가져오기 (get_total_commits 사용) - total_commit_count = github_service.get_total_commits(user.username) + today_commits += 1 # 오늘 커밋 수 데이터베이스에 저장 db_service = GitHubDatabaseService() - db_service.update_or_create_commit_record(user, today_commit_count, today) + db_service.update_or_create_commit_record(user, today_commits, today) - # 경험치 및 레벨 업데이트 - user.exp = ( + # 최근 7일간 커밋 수 및 전체 커밋 수 계산 + week_commits = ( + GithubModel.objects.filter(user=user, date__gte=week_ago).aggregate( + Sum("commit_num") + )["commit_num__sum"] + or 0 + ) + total_commits = ( GithubModel.objects.filter(user=user).aggregate(Sum("commit_num"))[ "commit_num__sum" ] or 0 ) - level_up_threshold = 50 * 1.5**user.level - while user.exp >= level_up_threshold: - user.level += 1 - user.exp -= level_up_threshold - level_up_threshold = 50 * 1.5**user.level + + # 7일 평균 커밋 수 계산 + week_average_commit_count = round(week_commits / 7, 2) + + # 경험치 및 레벨 업데이트 + user.potato_exp = total_commits + level_up_threshold = int(50 * 1.5 ** (user.potato_level - 1)) + while user.potato_exp >= level_up_threshold: + user.potato_level += 1 + user.potato_exp -= level_up_threshold + level_up_threshold = int(50 * 1.5 ** (user.potato_level - 1)) user.save() # 다음 레벨까지 필요한 경험치 계산 - next_level_exp = 50 * 1.5**user.level - user.exp + next_level_exp = int(50 * 1.5 ** (user.potato_level - 1)) - user.potato_exp # 응답 데이터 생성 commit_statistics = { - "today_commit_count": today_commit_count, - "week_commit_count": week_commit_count, - "total_commit_count": total_commit_count, + "today_commit_count": today_commits, + "week_commit_count": week_commits, + "total_commit_count": total_commits, "week_average_commit_count": week_average_commit_count, - "level": user.level, - "exp": user.exp, - "next_level_exp": next_level_exp, # 다음 레벨까지 필요한 경험치 추가 + "level": user.potato_level, + "exp": user.potato_exp, + "next_level_exp": int( + next_level_exp + ), # 다음 레벨까지 필요한 경험치 추가 } return JsonResponse(commit_statistics, safe=False) From 25c8f4949bd1ef4b45a1845baeb2380c07f67406 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:18:20 +0900 Subject: [PATCH 10/25] =?UTF-8?q?=20permission=5Fclasses=20=3D?= =?UTF-8?q?=20[AllowAny]=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/potato_types/views.py | 3 +++ potato_project/stacks/views.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/potato_project/potato_types/views.py b/potato_project/potato_types/views.py index 7836229..0cb5a08 100644 --- a/potato_project/potato_types/views.py +++ b/potato_project/potato_types/views.py @@ -2,6 +2,7 @@ from django.db import DatabaseError from rest_framework import status from rest_framework.exceptions import ValidationError +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView @@ -11,6 +12,8 @@ # 전체 감자 목록 조회 class PotatoesList(APIView): + permission_classes = [AllowAny] + def get(self, request): try: potatoes = PotatoType.objects.all() diff --git a/potato_project/stacks/views.py b/potato_project/stacks/views.py index 2295b5b..0f8d617 100644 --- a/potato_project/stacks/views.py +++ b/potato_project/stacks/views.py @@ -2,6 +2,7 @@ from django.db import DatabaseError from rest_framework import status from rest_framework.exceptions import ValidationError +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView @@ -11,6 +12,8 @@ # 전체 스택 조회 class StackList(APIView): + permission_classes = [AllowAny] + def get(self, request): try: stacks = Stack.objects.all() From 7ff0a43fe0815fa5536d84627d939a6de2a1d559 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:19:08 +0900 Subject: [PATCH 11/25] potato_id -> potato_type_id --- potato_project/potatoes/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/potato_project/potatoes/views.py b/potato_project/potatoes/views.py index 1c13239..4d33f58 100644 --- a/potato_project/potatoes/views.py +++ b/potato_project/potatoes/views.py @@ -57,8 +57,8 @@ class PotatoSelectPatch(APIView): def patch(self, request): try: - potato_id = request.data.get("id") - potato = Potato.objects.get(id=potato_id, user=request.user) + potato_type_id = request.data.get("id") + potato = Potato.objects.get(potato_type=potato_type_id, user=request.user) if not potato: return Response( From 21b5058fb7718aae449a3e6ffa62df6551c83afd Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:20:38 +0900 Subject: [PATCH 12/25] =?UTF-8?q?=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/todos/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/potato_project/todos/models.py b/potato_project/todos/models.py index d2d4326..e4ba831 100644 --- a/potato_project/todos/models.py +++ b/potato_project/todos/models.py @@ -4,7 +4,6 @@ class Todo(TimeStampedModel): - # field이름은 _id를 붙이지 않는게 좋다고하네? user = models.ForeignKey(User, on_delete=models.CASCADE) task = models.CharField(max_length=50) is_done = models.BooleanField(default=False) From 71ec3644c1aa1a942d7665bd0c8f268c8edf84dd Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:20:59 +0900 Subject: [PATCH 13/25] =?UTF-8?q?=20=EB=B3=80=EC=88=98=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/todos/views.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/potato_project/todos/views.py b/potato_project/todos/views.py index 1048ae8..a6a3598 100644 --- a/potato_project/todos/views.py +++ b/potato_project/todos/views.py @@ -20,14 +20,14 @@ def perform_create(self, serializer): date_str = self.request.data.get("date") # 프론트엔드에서 전달된 날짜 문자열 try: # datetime 객체 생성 - date_obj = datetime.strptime(date_str, "%Y-%m-%d").date() + date = datetime.strptime(date_str, "%Y-%m-%d").date() except (ValueError, TypeError): return Response( {"error": "Invalid date format or missing date."}, status=status.HTTP_400_BAD_REQUEST, ) - serializer.save(user=self.request.user, date=date_obj) + serializer.save(user=self.request.user, date=date) # 2. 투두리스트 항목 수정 (UI에서 입력 받은 데이터 + 선택된 날짜로 수정) @@ -46,14 +46,14 @@ def perform_update(self, serializer): date_str = self.request.data.get("date") try: # datetime 객체 생성 - date_obj = datetime.strptime(date_str, "%Y-%m-%d").date() + date = datetime.strptime(date_str, "%Y-%m-%d").date() except (ValueError, TypeError): return Response( {"error": "Invalid date format or missing date."}, status=status.HTTP_400_BAD_REQUEST, ) - serializer.save(date=date_obj) + serializer.save(date=date) # 3. 투두리스트 항목 삭제 @@ -93,6 +93,7 @@ class TodoMarkUndoneView(generics.UpdateAPIView): def get_object(self): todo_id = self.kwargs.get("id") + return get_object_or_404(Todo, id=todo_id, user=self.request.user) def get_queryset(self): From 796d1aa247e5c3d9fafaa435bdc5d0fe8ebd8fe7 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:21:25 +0900 Subject: [PATCH 14/25] =?UTF-8?q?=20=EA=B0=90=EC=9E=90=EB=A0=88?= =?UTF-8?q?=EB=B2=A8=20=EB=94=94=ED=8F=B4=ED=8A=B8=200=20->=201?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/users/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/potato_project/users/models.py b/potato_project/users/models.py index bb7bccb..50e773f 100644 --- a/potato_project/users/models.py +++ b/potato_project/users/models.py @@ -59,7 +59,7 @@ class User(AbstractBaseUser, PermissionsMixin, TimeStampedModel): nickname = models.CharField(max_length=255, null=False) # 감자 관련 필드 - potato_level = models.PositiveIntegerField(null=False, default=0) + potato_level = models.PositiveIntegerField(null=False, default=1) potato_exp = models.PositiveIntegerField(null=False, default=0) total_coins = models.PositiveIntegerField(default=0) From 652b837453c0bbe3674453671759c5e3a923c8b5 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:21:49 +0900 Subject: [PATCH 15/25] =?UTF-8?q?=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/users/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/potato_project/users/urls.py b/potato_project/users/urls.py index eaea092..d4ce7d3 100644 --- a/potato_project/users/urls.py +++ b/potato_project/users/urls.py @@ -14,7 +14,7 @@ views.CustomTokenRefreshView.as_view(), name="token_refresh", ), - path("api/accounts/logout/", views.logout_view, name="logout"), + path("api/logout/", views.logout_view, name="logout"), path("api/accounts/profile/", views.UserDetail.as_view(), name="user_detail"), path( "api/accounts/baekjoon_id/", From 4b59ebc0d3a219f8f560ec46690923650b526d40 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Sun, 4 Aug 2024 22:22:53 +0900 Subject: [PATCH 16/25] =?UTF-8?q?=20=EB=A6=AC=EB=94=94=EB=A0=89?= =?UTF-8?q?=EC=85=98=20url=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/users/views.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/potato_project/users/views.py b/potato_project/users/views.py index 27785c6..4deb7f9 100644 --- a/potato_project/users/views.py +++ b/potato_project/users/views.py @@ -30,10 +30,9 @@ load_dotenv() # .env 파일 로드 - state = os.environ.get("STATE") -# BASE_URL = "http://43.201.150.178:8000/" -BASE_URL = "http://localhost:8000/" # 프론트엔드 URL로 변경해야 함 +BASE_URL = "https://api.gitpotatoes.com/" +# BASE_URL = "http://localhost:8000" GITHUB_CALLBACK_URI = BASE_URL + "accounts/github/callback/" @@ -98,6 +97,7 @@ def github_callback(request): "https://api.github.com/user", headers={"Authorization": f"Bearer {access_token}"}, ) + user_response.encoding = "utf-8" user_json = user_response.json() # 에러 처리 @@ -181,7 +181,20 @@ def github_callback(request): secure=True, samesite="Lax", ) - return response + + jwt_access_token = login_data.get("access_token") + jwt_refresh_token = login_data.get("refresh_token") + user_data = ( + { + "pk": user.pk, + "username": user.username, + "profile_url": user.profile_url, + "nickname": user.nickname, + }, + ) + redirect_url = f"https://www.gitpotatoes.com/oauth-callback?access_token={jwt_access_token}&refresh_token={jwt_refresh_token}&user={json.dumps(user_data)}" + return redirect(redirect_url) + # return response class GithubLogin(SocialLoginView): @@ -211,7 +224,6 @@ def get_response(self): "nickname": user.nickname, }, } - # return redirect(settings.LOGIN_REDIRECT_URL) return JsonResponse(response_data) return response From 24b6cd3cc0da8a249c78782e42450020b9cb7673 Mon Sep 17 00:00:00 2001 From: DAYEON Date: Sun, 4 Aug 2024 22:27:20 +0900 Subject: [PATCH 17/25] Update docker-compose.yml --- docker-compose.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index c3e41ee..207bb77 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -72,14 +72,3 @@ services: - - db: # PostgreSQL Database - image: postgres:16-alpine - volumes: - - ./data/db:/var/lib/postgresql/data - environment: - - POSTGRES_DB=${POSTGRES_DB} - - POSTGRES_USER=${POSTGRES_USER} - - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - env_file: - - .env \ No newline at end of file From f01d0577935a143529836a7926ff830f46e2f64f Mon Sep 17 00:00:00 2001 From: DAYEON Date: Sun, 4 Aug 2024 22:31:51 +0900 Subject: [PATCH 18/25] =?UTF-8?q?=20csrf=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/app/settings.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/potato_project/app/settings.py b/potato_project/app/settings.py index 7eaac4d..70035c6 100644 --- a/potato_project/app/settings.py +++ b/potato_project/app/settings.py @@ -261,5 +261,3 @@ ] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") -CSRF_COOKIE_SECURE = True -SESSION_COOKIE_SECURE = True From c42874b6b5abaec8550e2684735ea38e670ac32c Mon Sep 17 00:00:00 2001 From: YoungKwangJoo <164307740+youngkwangjoo@users.noreply.github.com> Date: Mon, 5 Aug 2024 23:57:43 +0900 Subject: [PATCH 19/25] Update readme.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tree 추가 --- readme.md | 245 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 175 insertions(+), 70 deletions(-) diff --git a/readme.md b/readme.md index d66e6fd..d7a44a0 100644 --- a/readme.md +++ b/readme.md @@ -72,76 +72,181 @@ ## 4. 프로젝트 구조 ``` -. -├── README.md -├── package-lock.json -├── package.json -├── src -│ ├── App.jsx -│ ├── apis -│ │ ├── api -│ │ │ ├── attendance.js -│ │ │ ├── auth.js -│ │ │ ├── baekjoon.js -│ │ │ ├── github.js -│ │ │ ├── potato.js -│ │ │ ├── stack.js -│ │ │ └── todo.js -│ │ ├── services -│ │ │ ├── authService.js -│ │ │ ├── calendarService.js -│ │ │ ├── levelService.js -│ │ │ ├── settingPageService.js -│ │ │ ├── stackService.js -│ │ │ ├── todoService.js -│ │ │ └── userUpdateService.js -│ │ └── utils -│ │ └── instance.js -│ ├── assets -│ │ ├── description.json -│ │ └── images -│ ├── components -│ │ ├── common -│ │ │ ├── alert -│ │ │ ├── input -│ │ │ └── loading -│ │ ├── layout -│ │ │ ├── footer -│ │ │ └── header -│ │ ├── modal -│ │ │ ├── BaekjoonConnectModal.jsx -│ │ │ ├── ChangePotatoModal.jsx -│ │ │ ├── PotatoInfoModal.jsx -│ │ │ ├── UserUpdateModal.jsx -│ │ │ └── index.js -│ │ └── shared -│ │ ├── calendar -│ │ ├── collection -│ │ ├── info -│ │ ├── level -│ │ ├── potato -│ │ ├── stack -│ │ └── todo -│ ├── hooks -│ │ └── useCalendar -│ ├── lib -│ ├── main.jsx -│ ├── pages -│ │ ├── home -│ │ ├── landing -│ │ ├── oauthCallback -│ │ ├── setting -│ │ └── signin -│ ├── store -│ │ ├── dateStore.js -│ │ ├── todosCompleteStore.js -│ │ └── userStore.js -│ ├── styles -│ │ └── global.css -│ └── utils -│ └── storage.js -├── tailwind.config.js -└── vite.config.js +potato_project +├── app +│   ├── __init__.py +│   ├── __pycache__ +│   ├── asgi.py +│   ├── settings.py +│   ├── urls.py +│   └── wsgi.py +├── attendances +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_initial.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── serializers.py +│   ├── tests.py +│   ├── urls.py +│   └── views.py +├── baekjoons +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_initial.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── tests.py +│   ├── urls.py +│   └── views.py +├── common +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── tests.py +│   └── views.py +├── core +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   ├── management +│   │   ├── __init__.py +│   │   ├── __pycache__ +│   │   └── commands +│   │   ├── __init__.py +│   │   ├── __pycache__ +│   │   └── wait_for_db.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── tests.py +│   └── views.py +├── githubs +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_initial.py +│   │   ├── 0003_alter_github_date.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── signals.py +│   ├── test +│   │   ├── __init__.py +│   │   └── tests.py +│   ├── urls.py +│   └── views.py +├── manage.py +├── potato_types +│   ├── __init__.py +│   ├── __pycache__ +│   ├── actions.py +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_add_initial_data.py +│   │   ├── 0003_alter_potatotype_options.py +│   │   ├── 0003_remove_potatotype_potato_image.py +│   │   ├── 0004_merge_20240730_0318.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── serializers.py +│   ├── tests.py +│   ├── urls.py +│   └── views.py +├── potatoes +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_initial.py +│   │   ├── 0003_rename_potato_type_id_potato_potato_type.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── serializers.py +│   ├── tests.py +│   ├── urls.py +│   └── views.py +├── stacks +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0001_initial.py.save +│   │   ├── 0002_add_initial_stacks.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── serializers.py +│   ├── tests.py +│   ├── urls.py +│   ├── views.py +│   └── views.py.save +├── todos +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_initial.py +│   │   ├── 0003_alter_todo_date.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── serializers.py +│   ├── tests.py +│   ├── urls.py +│   └── views.py +├── user_stacks +│   ├── __init__.py +│   ├── __pycache__ +│   ├── admin.py +│   ├── apps.py +│   │   ├── 0001_initial.py +│   │   ├── 0002_initial.py +│   │   ├── __init__.py +│   │   └── __pycache__ +│   ├── models.py +│   ├── serializers.py +│   ├── tests.py +│   ├── urls.py +│   └── views.py +└── users + ├── __init__.py + ├── __pycache__ + ├── admin.py + ├── apps.py + │   ├── 0001_initial.py + │   ├── 0002_alter_user_potato_level.py + │   ├── __init__.py + │   └── __pycache__ + ├── models.py + ├── serializers.py + ├── signals.py + ├── tests.py + ├── urls.py + ├── views.py + ├── views.py.save + └── views.py.save.1 ``` ## 5. 프로젝트 From ae196e11c68e14a2feb7d56b91501b7c985277c1 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Tue, 13 Aug 2024 00:22:28 +0900 Subject: [PATCH 20/25] =?UTF-8?q?=20=EC=B5=9C=EC=A2=85=20main=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/app/settings.py | 9 +- potato_project/githubs/models.py | 2 +- potato_project/githubs/signals.py | 170 +++++++++++++++++------------- potato_project/githubs/views.py | 16 ++- potato_project/potatoes/views.py | 1 - potato_project/users/signals.py | 13 ++- 6 files changed, 125 insertions(+), 86 deletions(-) diff --git a/potato_project/app/settings.py b/potato_project/app/settings.py index 7eaac4d..579c0fc 100644 --- a/potato_project/app/settings.py +++ b/potato_project/app/settings.py @@ -217,8 +217,6 @@ DEFAULT_CHARSET = "utf-8" -DEFAULT_CHARSET = "utf-8" - LOGGING = { "version": 1, "disable_existing_loggers": False, @@ -236,8 +234,8 @@ # CORS_ORIGIN_WHITELIST = ['http://localhost:5173', 'http://127.0.0.1:5173', 'https://www.gitpotatoes.com',] # 특정 Origin만 허용 CORS_ALLOWED_ORIGINS = [ "https://www.gitpotatoes.com", # 실제 배포 프론트엔드 URL - "http://localhost:5173", # 프론트엔드 로컬 서버 URL - "http://127.0.0.1:5173", # 프론트엔드 로컬 서버 URL + # 'http://localhost:5173', # 프론트엔드 로컬 서버 URL + # 'http://127.0.0.1:5173', # 프론트엔드 로컬 서버 URL ] CORS_ALLOW_CREDENTIALS = True # 쿠키 등 credential 정보 허용 CORS_ALLOW_METHODS = [ @@ -260,6 +258,3 @@ "x-requested-with", ] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") - -CSRF_COOKIE_SECURE = True -SESSION_COOKIE_SECURE = True diff --git a/potato_project/githubs/models.py b/potato_project/githubs/models.py index ba83439..894e14e 100644 --- a/potato_project/githubs/models.py +++ b/potato_project/githubs/models.py @@ -8,7 +8,7 @@ class Github(TimeStampedModel): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="User_id") commit_num = models.BigIntegerField(verbose_name="Commit Number") - date = models.DateField(default=timezone.now) + date = models.DateField() def __str__(self): return f"{self.date}-{self.commit_num}" diff --git a/potato_project/githubs/signals.py b/potato_project/githubs/signals.py index 78467e8..63c04a7 100644 --- a/potato_project/githubs/signals.py +++ b/potato_project/githubs/signals.py @@ -7,11 +7,10 @@ @receiver(post_save, sender=Github) -def get_winter_potato(sender, instance, created, **kwargs): - # 새 Github 데이터가 생성 or 업데이트, 날짜가 크리스마스이며, commit_num이 1 이상인 경우에만 실행 +def get_winter_potato(sender, instance, **kwargs): + # 새 Github 데이터가 생성 or 업데이트, 날짜가 크리스마스이며, commit_num이 > if ( - created - and instance.commit_num >= 1 + instance.commit_num >= 1 and instance.date.month == 12 and instance.date.day == 25 ): # 월과 일만 비교 @@ -27,10 +26,9 @@ def get_winter_potato(sender, instance, created, **kwargs): @receiver(post_save, sender=Github) -def get_ghost_potato(sender, instance, created, **kwargs): +def get_ghost_potato(sender, instance, **kwargs): if ( - created - and instance.commit_num >= 1 + instance.commit_num >= 1 and instance.date.month == 10 and instance.date.day == 31 ): @@ -44,79 +42,109 @@ def get_ghost_potato(sender, instance, created, **kwargs): @receiver(post_save, sender=Github) -def get_crystal_potato(sender, instance, created, **kwargs): - if created and instance.commit_num >= 1: +def get_crystal_potato(sender, instance, **kwargs): + if instance.commit_num >= 1: # 30일 전 날짜 계산 - thirty_days_ago = instance.date.date() - timedelta(days=30) + thirty_days_ago = instance.date - timedelta(days=30) - # 30일 연속 커밋 여부 확인 - commits_in_30_days = ( - Github.objects.filter( - user=instance.user, - date__gte=thirty_days_ago, - date__lte=instance.date.date(), - commit_num__gte=1, - ) - .values("date") - .distinct() - .count() + # 30일치 데이터가 있는지 확인 + oldest_record = ( + Github.objects.filter(user=instance.user).order_by("date").first() ) + if oldest_record and (instance.date - oldest_record.date).days >= 30: + # 30일 연속 커밋 여부 확인 + commits_in_30_days = ( + Github.objects.filter( + user=instance.user, + date__gte=thirty_days_ago, + date__lte=instance.date, + commit_num__gte=1, + ) + .values("date") + .distinct() + .count() + ) - if commits_in_30_days == 30: - try: - potato = Potato.objects.get(user=instance.user, potato_type_id=8) - if not potato.is_acquired: - potato.is_acquired = True - potato.save() - except Potato.DoesNotExist: - pass + if commits_in_30_days == 30: + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=8) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass + else: + # 30일치 데이터가 없는 경우 로그 남기기 또는 다른 처리 + print( + f"Not enough data for user {instance.user.id}. Oldest record date: {oldest_record.date if oldest_record else 'No records'}" + ) @receiver(post_save, sender=Github) -def get_dirty_potato(sender, instance, created, **kwargs): - if created: +def get_dirty_potato(sender, instance, **kwargs): + if instance.commit_num == 0: # 30일 전 날짜 계산 - thirty_days_ago = instance.date.date() - timedelta(days=30) - - # 30일 동안 커밋이 있었는지 확인 - any_commits_in_30_days = Github.objects.filter( - user=instance.user, - date__gte=thirty_days_ago, - date__lte=instance.date.date(), - commit_num__gte=1, - ).exists() - - if not any_commits_in_30_days: - # 30일 연속 커밋이 없는 경우 감자 아이디 9 획득 로직 실행 - try: - potato = Potato.objects.get(user=instance.user, potato_type_id=9) - if not potato.is_acquired: - potato.is_acquired = True - potato.save() - except Potato.DoesNotExist: - pass # 필요에 따라 에러 처리 추가 + thirty_days_ago = instance.date - timedelta(days=30) + + # 30일치 데이터가 있는지 확인 + oldest_record = ( + Github.objects.filter(user=instance.user).order_by("date").first() + ) + if oldest_record and (instance.date - oldest_record.date).days >= 30: + # 30일 동안 커밋이 있었는지 확인 + any_commits_in_30_days = Github.objects.filter( + user=instance.user, + date__gte=thirty_days_ago, + date__lte=instance.date, + commit_num__gte=1, + ).exists() + + if not any_commits_in_30_days: + # 30일 연속 커밋이 없는 경우 감자 아이디 9 획득 로직 실행 + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=9) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass # 필요에 따라 에러 처리 추가 + else: + # 30일치 데이터가 없는 경우 로그 남기기 또는 다른 처리 + print( + f"Not enough data for user {instance.user.id}. Oldest record date: {oldest_record.date if oldest_record else 'No records'}" + ) @receiver(post_save, sender=Github) -def get_green_potato(sender, instance, created, **kwargs): - if created: +def get_green_potato(sender, instance, **kwargs): + if instance.commit_num == 0: # 90일 전 날짜 계산 - ninety_days_ago = instance.date.date() - timedelta(days=90) - - # 90일 동안 커밋이 있었는지 확인 - any_commits_in_90_days = Github.objects.filter( - user=instance.user, - date__gte=ninety_days_ago, - date__lte=instance.date.date(), - commit_num__gte=1, - ).exists() - - if not any_commits_in_90_days: - # 90일 연속 커밋이 없는 경우 감자 아이디 10 획득 로직 실행 - try: - potato = Potato.objects.get(user=instance.user, potato_type_id=10) - if not potato.is_acquired: - potato.is_acquired = True - potato.save() - except Potato.DoesNotExist: - pass # 필요에 따라 에러 처리 추가 + ninety_days_ago = instance.date - timedelta(days=90) + + # 90일치 데이터가 있는지 확인 + oldest_record = ( + Github.objects.filter(user=instance.user).order_by("date").first() + ) + if oldest_record and (instance.date - oldest_record.date).days >= 90: + # 90일 동안 커밋이 있었는지 확인 + any_commits_in_90_days = Github.objects.filter( + user=instance.user, + date__gte=ninety_days_ago, + date__lte=instance.date, + commit_num__gte=1, + ).exists() + + if not any_commits_in_90_days: + # 90일 연속 커밋이 없는 경우 감자 아이디 10 획득 로직 실행 + try: + potato = Potato.objects.get(user=instance.user, potato_type_id=10) + if not potato.is_acquired: + potato.is_acquired = True + potato.save() + except Potato.DoesNotExist: + pass # 필요에 따라 에러 처리 추가 + else: + # 90일치 데이터가 없는 경우 로그 남기기 또는 다른 처리 + print( + f"Not enough data for user {instance.user.id}. Oldest record date: {oldest_record.date if oldest_record else 'No records'}" + ) diff --git a/potato_project/githubs/views.py b/potato_project/githubs/views.py index 53a6589..71becdb 100644 --- a/potato_project/githubs/views.py +++ b/potato_project/githubs/views.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta +import pytz import requests from django.db.models import Avg, Sum from django.http import JsonResponse @@ -92,9 +93,17 @@ def get(self, request): ) if commits is not None: for commit in commits: - commit_date = datetime.strptime( - commit["commit"]["author"]["date"], "%Y-%m-%dT%H:%M:%SZ" - ).date() + commit_date = ( + timezone.make_aware( + datetime.strptime( + commit["commit"]["author"]["date"], + "%Y-%m-%dT%H:%M:%SZ", + ), + timezone=pytz.UTC, + ) + .astimezone(timezone.get_current_timezone()) + .date() + ) if commit_date == today: today_commits += 1 @@ -121,6 +130,7 @@ def get(self, request): # 경험치 및 레벨 업데이트 user.potato_exp = total_commits + user.potato_level = 1 level_up_threshold = int(50 * 1.5 ** (user.potato_level - 1)) while user.potato_exp >= level_up_threshold: user.potato_level += 1 diff --git a/potato_project/potatoes/views.py b/potato_project/potatoes/views.py index 4d33f58..96d0078 100644 --- a/potato_project/potatoes/views.py +++ b/potato_project/potatoes/views.py @@ -10,7 +10,6 @@ from .serializers import PotatoSerializer -# 유저의 포테이토가 다가지고있지, 획득을 못햇던 햇던, 유저가감자를 획득했는지, 대표이미지 설정했는지. # 유저의 감자 조회 class MyPotatoDetail(APIView): permission_classes = [IsAuthenticated] diff --git a/potato_project/users/signals.py b/potato_project/users/signals.py index 3fc6bb5..57f9f84 100644 --- a/potato_project/users/signals.py +++ b/potato_project/users/signals.py @@ -9,10 +9,11 @@ def get_level_two_potato(sender, instance, **kwargs): # 레벨이 2로 변경되었고, 이전 레벨이 2가 아닐 때만 실행 if ( instance.potato_level == 2 + and instance.tracker.has_changed('potato_level') and instance.tracker.previous("potato_level") != 2 and not kwargs.get("created", False) ): - try: + try: # potato_type_id=2인 감자 조회 potato = Potato.objects.get(user=instance.user, potato_type_id=2) if not potato.is_acquired: @@ -23,10 +24,12 @@ def get_level_two_potato(sender, instance, **kwargs): pass + @receiver(post_save, sender=User) def get_level_three_potato(sender, instance, **kwargs): if ( instance.potato_level == 3 + and instance.tracker.has_changed('potato_level') and instance.tracker.previous("potato_level") != 3 and not kwargs.get("created", False) ): @@ -44,9 +47,10 @@ def get_level_four_potato(sender, instance, **kwargs): if ( instance.potato_level == 4 and instance.tracker.previous("potato_level") != 4 + and instance.tracker.has_changed('potato_level') and not kwargs.get("created", False) ): - try: + try: potato = Potato.objects.get(user=instance.user, potato_type_id=4) if not potato.is_acquired: potato.is_acquired = True @@ -55,17 +59,20 @@ def get_level_four_potato(sender, instance, **kwargs): pass + @receiver(post_save, sender=User) def get_level_five_potato(sender, instance, **kwargs): if ( instance.potato_level == 5 + and instance.tracker.has_changed('potato_level') and instance.tracker.previous("potato_level") != 5 and not kwargs.get("created", False) ): - try: + try: potato = Potato.objects.get(user=instance.user, potato_type_id=5) if not potato.is_acquired: potato.is_acquired = True potato.save() except Potato.DoesNotExist: pass + From 6faa74963e697d2fdd0df4abb26038c5a42cefcd Mon Sep 17 00:00:00 2001 From: DAYEON Date: Tue, 13 Aug 2024 00:37:19 +0900 Subject: [PATCH 21/25] Update readme.md --- readme.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index f6195ef..b33d976 100644 --- a/readme.md +++ b/readme.md @@ -245,9 +245,7 @@ potato_project ├── signals.py ├── tests.py ├── urls.py - ├── views.py - ├── views.py.save - └── views.py.save.1 + └── views.py ``` ## 5. 프로젝트 @@ -295,8 +293,9 @@ GitHub와 백준 활동으로 성장하는 감자 캐릭터를 키우며 생산 - 사용자 스택 조회, 수정, 업데이트 - 캘린더 조회 - Todo 조회, 추가, 수정, 삭제 -- 회원가입$ +- 회원가입 - 소셜 로그인, 깃허브, 백준 연동 +- 로그아웃, 회원 탈퇴 (추가 예정) - 테스트 코드 (추가 예정) - 감자 코인 (추가 예정) - 감자 상점 (추가 예정) From 5bf4e77f0fe1cdce9d0f2f7e7962ada43b1073ac Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Fri, 20 Sep 2024 11:26:21 +0900 Subject: [PATCH 22/25] =?UTF-8?q?=EC=8B=9C=EA=B7=B8=EB=84=90=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/githubs/apps.py | 3 +++ potato_project/todos/views.py | 7 +++---- potato_project/users/apps.py | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/potato_project/githubs/apps.py b/potato_project/githubs/apps.py index b3180a3..8f32ed0 100644 --- a/potato_project/githubs/apps.py +++ b/potato_project/githubs/apps.py @@ -4,3 +4,6 @@ class GithubsConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "githubs" + + def ready(self): + import githubs.signals diff --git a/potato_project/todos/views.py b/potato_project/todos/views.py index 6a1b581..b614e8a 100644 --- a/potato_project/todos/views.py +++ b/potato_project/todos/views.py @@ -27,7 +27,7 @@ def perform_create(self, serializer): status=status.HTTP_400_BAD_REQUEST, ) - serializer.save(user=self.request.user, date=date_obj) + serializer.save(user=self.request.user, date=date) # 2. 투두리스트 항목 수정 (UI에서 입력 받은 데이터 + 선택된 날짜로 수정) @@ -50,10 +50,9 @@ def perform_update(self, serializer): except (ValueError, TypeError): return Response( {"error": "Invalid date format or missing date."}, - status=status.HTTP_400_BAD_REQUEST, - ) + status=status.HTTP_400_BAD_REQUEST,) - serializer.save(date=date_obj) + serializer.save(date=date) # 3. 투두리스트 항목 삭제 diff --git a/potato_project/users/apps.py b/potato_project/users/apps.py index 88f7b17..7e511c4 100644 --- a/potato_project/users/apps.py +++ b/potato_project/users/apps.py @@ -4,3 +4,6 @@ class UsersConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "users" + + def ready(self): + import users.signals From f409e28d01a97f1731ff3718c87ffcac5175f353 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Fri, 20 Sep 2024 11:40:48 +0900 Subject: [PATCH 23/25] isort, black --- potato_project/githubs/apps.py | 2 +- potato_project/todos/views.py | 3 ++- potato_project/users/apps.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/potato_project/githubs/apps.py b/potato_project/githubs/apps.py index 8f32ed0..e012887 100644 --- a/potato_project/githubs/apps.py +++ b/potato_project/githubs/apps.py @@ -4,6 +4,6 @@ class GithubsConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "githubs" - + def ready(self): import githubs.signals diff --git a/potato_project/todos/views.py b/potato_project/todos/views.py index b614e8a..a6a3598 100644 --- a/potato_project/todos/views.py +++ b/potato_project/todos/views.py @@ -50,7 +50,8 @@ def perform_update(self, serializer): except (ValueError, TypeError): return Response( {"error": "Invalid date format or missing date."}, - status=status.HTTP_400_BAD_REQUEST,) + status=status.HTTP_400_BAD_REQUEST, + ) serializer.save(date=date) diff --git a/potato_project/users/apps.py b/potato_project/users/apps.py index 7e511c4..4697735 100644 --- a/potato_project/users/apps.py +++ b/potato_project/users/apps.py @@ -4,6 +4,6 @@ class UsersConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "users" - + def ready(self): import users.signals From e6bc18971d74bc81b21f8c058d285274bba83d8f Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Fri, 20 Sep 2024 11:58:37 +0900 Subject: [PATCH 24/25] =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- potato_project/todos/views.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/potato_project/todos/views.py b/potato_project/todos/views.py index a6a3598..ea0454d 100644 --- a/potato_project/todos/views.py +++ b/potato_project/todos/views.py @@ -4,6 +4,7 @@ from django.shortcuts import get_object_or_404 from django.utils import timezone from rest_framework import generics, permissions, status +from rest_framework.exceptions import ValidationError from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response @@ -47,13 +48,9 @@ def perform_update(self, serializer): try: # datetime 객체 생성 date = datetime.strptime(date_str, "%Y-%m-%d").date() + serializer.save(date=date) # serializer에 날짜 저장 except (ValueError, TypeError): - return Response( - {"error": "Invalid date format or missing date."}, - status=status.HTTP_400_BAD_REQUEST, - ) - - serializer.save(date=date) + raise ValidationError({"error": "Invalid date format or missing date."}) # 3. 투두리스트 항목 삭제 From 1576df972349736aa81abae2cbb067a43c9a9bf1 Mon Sep 17 00:00:00 2001 From: dayeonkimm Date: Fri, 20 Sep 2024 18:03:42 +0900 Subject: [PATCH 25/25] =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 13 ++---- potato_project/users/signals.py | 79 ++++++++++++++++++++------------- requirements.txt | 2 + 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 207bb77..380cbf8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,16 +18,14 @@ services: python manage.py migrate && python manage.py runserver --noreload 0.0.0.0:8000" environment: - - DB_HOST=${DB_HOST} - - DB_NAME=${DB_NAME} - - DB_USER=${DB_USER} - - DB_PASSWORD=${DB_PASSWORD} + - DB_HOST=${RDS_HOSTNAME} + - DB_NAME=${RDS_DB_NAME} + - DB_USER=${RDS_USERNAME} + - DB_PASSWORD=${RDS_PASSWORD} - PYDEVD_DISABLE_FILE_VALIDATION=1 env_file: - .env - - # 개발용 # # version: "3.11" @@ -69,6 +67,3 @@ services: # - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # env_file: # - .env - - - diff --git a/potato_project/users/signals.py b/potato_project/users/signals.py index 57f9f84..d4fc90c 100644 --- a/potato_project/users/signals.py +++ b/potato_project/users/signals.py @@ -5,17 +5,21 @@ @receiver(post_save, sender=User) -def get_level_two_potato(sender, instance, **kwargs): +def get_level_two_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + # 레벨이 2로 변경되었고, 이전 레벨이 2가 아닐 때만 실행 if ( - instance.potato_level == 2 - and instance.tracker.has_changed('potato_level') - and instance.tracker.previous("potato_level") != 2 - and not kwargs.get("created", False) + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 2 ): - try: + try: # potato_type_id=2인 감자 조회 - potato = Potato.objects.get(user=instance.user, potato_type_id=2) + potato = Potato.objects.get(user=instance, potato_type_id=2) if not potato.is_acquired: potato.is_acquired = True potato.save() @@ -24,17 +28,22 @@ def get_level_two_potato(sender, instance, **kwargs): pass - @receiver(post_save, sender=User) -def get_level_three_potato(sender, instance, **kwargs): +def get_level_three_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 3로 변경되었고, 이전 레벨이 3가 아닐 때만 실행 if ( - instance.potato_level == 3 - and instance.tracker.has_changed('potato_level') - and instance.tracker.previous("potato_level") != 3 - and not kwargs.get("created", False) + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 3 ): try: - potato = Potato.objects.get(user=instance.user, potato_type_id=3) + # potato_type_id=3인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=3) if not potato.is_acquired: potato.is_acquired = True potato.save() @@ -43,15 +52,21 @@ def get_level_three_potato(sender, instance, **kwargs): @receiver(post_save, sender=User) -def get_level_four_potato(sender, instance, **kwargs): +def get_level_four_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 4로 변경되었고, 이전 레벨이 4가 아닐 때만 실행 if ( - instance.potato_level == 4 - and instance.tracker.previous("potato_level") != 4 - and instance.tracker.has_changed('potato_level') - and not kwargs.get("created", False) + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 4 ): - try: - potato = Potato.objects.get(user=instance.user, potato_type_id=4) + try: + # potato_type_id=4인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=4) if not potato.is_acquired: potato.is_acquired = True potato.save() @@ -59,20 +74,24 @@ def get_level_four_potato(sender, instance, **kwargs): pass - @receiver(post_save, sender=User) -def get_level_five_potato(sender, instance, **kwargs): +def get_level_five_potato(sender, instance, created, **kwargs): + if created: + return + + # 이전 인스턴스 재조회 + previous_instance = User.objects.get(pk=instance.pk) + + # 레벨이 5로 변경되었고, 이전 레벨이 5가 아닐 때만 실행 if ( - instance.potato_level == 5 - and instance.tracker.has_changed('potato_level') - and instance.tracker.previous("potato_level") != 5 - and not kwargs.get("created", False) + previous_instance.potato_level != instance.potato_level + and instance.potato_level == 5 ): - try: - potato = Potato.objects.get(user=instance.user, potato_type_id=5) + try: + # potato_type_id=5인 감자 조회 + potato = Potato.objects.get(user=instance, potato_type_id=5) if not potato.is_acquired: potato.is_acquired = True potato.save() except Potato.DoesNotExist: pass - diff --git a/requirements.txt b/requirements.txt index 5dfcc9a..afeb782 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,3 +18,5 @@ pytest pytest-django PyGithub>=1.55 +pytz +