From 913e226bc259861a1348cf14116a4e773dc7990f Mon Sep 17 00:00:00 2001 From: RowonChung Date: Mon, 9 Jan 2023 16:13:23 +0900 Subject: [PATCH 01/44] feat(be): create LC schedule --- backend/lc/admin.py | 3 --- backend/lc/models.py | 13 +++++++++++++ backend/lc/serializers.py | 5 +++++ backend/lc/views/admin.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) delete mode 100644 backend/lc/admin.py create mode 100644 backend/lc/serializers.py diff --git a/backend/lc/admin.py b/backend/lc/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/backend/lc/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/backend/lc/models.py b/backend/lc/models.py index 71a8362..72b6cdd 100644 --- a/backend/lc/models.py +++ b/backend/lc/models.py @@ -1,3 +1,16 @@ from django.db import models +from django.db.models.fields.related import ForeignKey +from FG.models import FG # Create your models here. +class LC(models.Model): + id = models.AutoField(primary_key= True) + fg_n_id = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='fg_n' ) + fg_s_id = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='fg_s' ) + schedule = IntegerField() + name = CharField(max_length=10) + total = IntegerField() + +class Schedule(models.Model): + date = DataField(default=datatime.now) + day = IntegerField() \ No newline at end of file diff --git a/backend/lc/serializers.py b/backend/lc/serializers.py new file mode 100644 index 0000000..f8099a5 --- /dev/null +++ b/backend/lc/serializers.py @@ -0,0 +1,5 @@ +from django.db import models +from restt_framework import serializers + +class CreateScheduleSerializer(serializers.Serializer): + date = serializers.dateField() \ No newline at end of file diff --git a/backend/lc/views/admin.py b/backend/lc/views/admin.py index e69de29..ed6b5bc 100644 --- a/backend/lc/views/admin.py +++ b/backend/lc/views/admin.py @@ -0,0 +1,30 @@ +from ..models import LC, Schedule +from ..serializers import (FGSerializer, CreateScheduleSerializer) +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.decorators import api_view + +class ScheduleAPI(APIView): + def get(self, request): + if request.user.role != ADMIN: + return Response({"error":True, "data":"not Admin"}) + schedule = Schedule.objects.get() + return Response({"error":False, "data":schedule}) + + def put(self, request): + data = Schedule.objects.get() + data.delete() + if request.user.role != ADMIN: + return Response({"error":True, "data":"not Admin"}) + data = request.data + for d in data: + serializer = CreateScheduleSerializer(d) + serializer.is_valid(raise_exception=True) + data.sort() + n = 1 + for d in data: + schedule = Schedule.objects.create(date=d, day = n) + n+= 1 + return Response({"error":False, "data":None}) + + From 2455b6ed9abd35fa388745c7946f1ae4981435f1 Mon Sep 17 00:00:00 2001 From: pinecone28 Date: Mon, 9 Jan 2023 19:12:24 +0900 Subject: [PATCH 02/44] fix: fix error in lc app --- backend/fnet/urls.py | 4 +-- backend/lc/migrations/0001_initial.py | 37 +++++++++++++++++++++++++++ backend/lc/models.py | 7 +++-- backend/lc/serializers.py | 4 +-- backend/lc/urls/admin.py | 7 ++++- backend/lc/urls/user.py | 7 ++++- backend/lc/views/admin.py | 21 +++++++++++---- 7 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 backend/lc/migrations/0001_initial.py diff --git a/backend/fnet/urls.py b/backend/fnet/urls.py index 3581273..e0f7e51 100644 --- a/backend/fnet/urls.py +++ b/backend/fnet/urls.py @@ -22,8 +22,8 @@ path('api/admin/', include('fg.urls.admin')), # path('api/', include('freshman.urls.user')), # path('api/admin/', include('freshman.urls.admin')), - # path('api/', include('lc.urls.user')), - # path('api/admin/', include('lc.urls.admin')), + path('api/', include('lc.urls.user')), + path('api/admin/', include('lc.urls.admin')), # path('api/', include('notice.urls.user')), # path('api/admin/', include('notice.urls.admin')), # path('api/', include('todo.urls.user')), diff --git a/backend/lc/migrations/0001_initial.py b/backend/lc/migrations/0001_initial.py new file mode 100644 index 0000000..f545e3f --- /dev/null +++ b/backend/lc/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.10 on 2023-01-09 07:27 + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Schedule', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField(default=datetime.datetime.now)), + ('day', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='LC', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('schedule', models.IntegerField()), + ('name', models.CharField(max_length=10)), + ('total', models.IntegerField()), + ('fg_n_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fg_n', to=settings.AUTH_USER_MODEL)), + ('fg_s_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fg_s', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/backend/lc/models.py b/backend/lc/models.py index 72b6cdd..547cbf0 100644 --- a/backend/lc/models.py +++ b/backend/lc/models.py @@ -1,6 +1,9 @@ from django.db import models from django.db.models.fields.related import ForeignKey -from FG.models import FG +from fg.models import FG +from django.db.models.deletion import CASCADE +from django.db.models.fields import CharField, AutoField, IntegerField, DateField +from datetime import datetime # Create your models here. class LC(models.Model): @@ -12,5 +15,5 @@ class LC(models.Model): total = IntegerField() class Schedule(models.Model): - date = DataField(default=datatime.now) + date = DateField(default=datetime.now) day = IntegerField() \ No newline at end of file diff --git a/backend/lc/serializers.py b/backend/lc/serializers.py index f8099a5..a9bcabe 100644 --- a/backend/lc/serializers.py +++ b/backend/lc/serializers.py @@ -1,5 +1,5 @@ from django.db import models -from restt_framework import serializers +from rest_framework import serializers class CreateScheduleSerializer(serializers.Serializer): - date = serializers.dateField() \ No newline at end of file + date = serializers.DateField() \ No newline at end of file diff --git a/backend/lc/urls/admin.py b/backend/lc/urls/admin.py index 1500611..d143924 100644 --- a/backend/lc/urls/admin.py +++ b/backend/lc/urls/admin.py @@ -1 +1,6 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.admin import ScheduleAPI + +urlpatterns = [ + path('schedule/', ScheduleAPI.as_view(), name="schedule_api"), +] \ No newline at end of file diff --git a/backend/lc/urls/user.py b/backend/lc/urls/user.py index 1500611..d143924 100644 --- a/backend/lc/urls/user.py +++ b/backend/lc/urls/user.py @@ -1 +1,6 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.admin import ScheduleAPI + +urlpatterns = [ + path('schedule/', ScheduleAPI.as_view(), name="schedule_api"), +] \ No newline at end of file diff --git a/backend/lc/views/admin.py b/backend/lc/views/admin.py index ed6b5bc..b567951 100644 --- a/backend/lc/views/admin.py +++ b/backend/lc/views/admin.py @@ -1,22 +1,33 @@ from ..models import LC, Schedule -from ..serializers import (FGSerializer, CreateScheduleSerializer) +from ..serializers import (CreateScheduleSerializer) from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.decorators import api_view +import datetime +import dateutil.parser class ScheduleAPI(APIView): def get(self, request): - if request.user.role != ADMIN: - return Response({"error":True, "data":"not Admin"}) + # if request.user.role != ADMIN: + # return Response({"error":True, "data":"not Admin"}) schedule = Schedule.objects.get() return Response({"error":False, "data":schedule}) + def post(self, request): + data = request.data + serializer = CreateScheduleSerializer(data) + Schedule.objects.create(date=dateutil.parser.parse(data["date"]).date(), day = 1) + return Response({"error":False, "data":None}) + + def put(self, request): data = Schedule.objects.get() - data.delete() + + if len(data) > 0: + data.delete() if request.user.role != ADMIN: return Response({"error":True, "data":"not Admin"}) - data = request.data + data = request.data.data for d in data: serializer = CreateScheduleSerializer(d) serializer.is_valid(raise_exception=True) From eba12b45497b4207f41f36571c0cae75ff6bbce2 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Fri, 27 Jan 2023 20:55:34 +0900 Subject: [PATCH 03/44] fix: fix dev db container --- backend/fnet/dev_settings.py | 2 +- backend/fnet/settings.py | 12 ++++++------ backend/init_db.sh | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/fnet/dev_settings.py b/backend/fnet/dev_settings.py index 672fdfb..c449e9e 100644 --- a/backend/fnet/dev_settings.py +++ b/backend/fnet/dev_settings.py @@ -9,7 +9,7 @@ 'USER': 'fnet', 'PASSWORD': 'fnet', 'HOST': '127.0.0.1', - 'PORT': 3306, + 'PORT': 1398, } } diff --git a/backend/fnet/settings.py b/backend/fnet/settings.py index 7e99a4e..c569e81 100644 --- a/backend/fnet/settings.py +++ b/backend/fnet/settings.py @@ -109,12 +109,12 @@ # Database # https://docs.djangoproject.com/en/4.1/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } -} +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': BASE_DIR / 'db.sqlite3', +# } +# } # Password validation diff --git a/backend/init_db.sh b/backend/init_db.sh index 6a1c7a0..20fc586 100755 --- a/backend/init_db.sh +++ b/backend/init_db.sh @@ -8,7 +8,7 @@ fi sleep 2 docker rm -f fnet-mysql-dev -docker run -it -d -e MYSQL_DATABASE=fnet -e MYSQL_USER=fnet -e MYSQL_PASSWORD=fnet -e MYSQL_ROOT_PASSWORD=root -p 127.0.0.1:1398:1398 --name fnet-mysql-dev mysql:8.0.27 +docker run -it -d -e MYSQL_DATABASE=fnet -e MYSQL_USER=fnet -e MYSQL_PASSWORD=fnet -e MYSQL_ROOT_PASSWORD=root -p 127.0.0.1:1398:3306 --name fnet-mysql-dev mysql:8.0.27 if [ "$1" = "--migrate" ]; then sleep 3 From cce14bc59b4317318933b164ae6dfc64b67482dc Mon Sep 17 00:00:00 2001 From: k1g99 Date: Fri, 27 Jan 2023 23:17:32 +0900 Subject: [PATCH 04/44] chore(be): add lc and freshman models and migrate --- backend/freshman/migrations/0001_initial.py | 27 ++++++++++++++ backend/freshman/models.py | 24 +++++++++++++ backend/lc/migrations/0001_initial.py | 40 +++++++++++++++++++++ backend/lc/models.py | 25 +++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 backend/freshman/migrations/0001_initial.py create mode 100644 backend/lc/migrations/0001_initial.py diff --git a/backend/freshman/migrations/0001_initial.py b/backend/freshman/migrations/0001_initial.py new file mode 100644 index 0000000..4f72d43 --- /dev/null +++ b/backend/freshman/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.10 on 2023-01-27 12:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('lc', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Freshman', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('phone_number', models.CharField(max_length=13, null=True)), + ('register', models.BooleanField(default=False)), + ('department', models.CharField(choices=[('NC', 'nat'), ('EN', 'eng'), ('SS', 'soc'), ('HS', 'hum')], max_length=10)), + ('lc', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='lc.lc')), + ], + ), + ] diff --git a/backend/freshman/models.py b/backend/freshman/models.py index 71a8362..d532ab3 100644 --- a/backend/freshman/models.py +++ b/backend/freshman/models.py @@ -1,3 +1,27 @@ from django.db import models +from django.db.models.deletion import CASCADE +from django.db.models.fields import CharField, BooleanField +from django.db.models.fields.related import ForeignKey +from lc.models import LC # Create your models here. +class Freshman(models.Model): + NATURAL_SCIENCE = 'NC' + ENGINEERING = 'EN' + SOCIAL_SCIENCE = 'SS' + HUMANITIES_SCIENCE = 'HS' + DEPARTMENT_CHOICES = [ + (NATURAL_SCIENCE, 'nat'), + (ENGINEERING, 'eng'), + (SOCIAL_SCIENCE, 'soc'), + (HUMANITIES_SCIENCE, 'hum'), + ] + + lc = ForeignKey(LC, on_delete=CASCADE, null=True) + name = CharField(max_length=30) + phone_number = CharField(max_length=13, null=True) + register = BooleanField(default=False) + department = CharField(max_length=10, choices=DEPARTMENT_CHOICES) # n(자연과학),e(공학),s(사회과학),h(인문사회) + + def __str__(self): + return self.name \ No newline at end of file diff --git a/backend/lc/migrations/0001_initial.py b/backend/lc/migrations/0001_initial.py new file mode 100644 index 0000000..0a471ca --- /dev/null +++ b/backend/lc/migrations/0001_initial.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.10 on 2023-01-27 12:28 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Schedule', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField()), + ('day', models.IntegerField()), + ], + ), + migrations.CreateModel( + name='LC', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('schedule', models.IntegerField()), + ('name', models.CharField(max_length=10)), + ('total', models.IntegerField()), + ('fg_n_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fg_n', to=settings.AUTH_USER_MODEL)), + ('fg_s_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fg_s', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'db_table': 'lc', + 'ordering': ['schedule', 'name'], + }, + ), + ] diff --git a/backend/lc/models.py b/backend/lc/models.py index 71a8362..8b0770d 100644 --- a/backend/lc/models.py +++ b/backend/lc/models.py @@ -1,3 +1,28 @@ from django.db import models +from django.db.models.deletion import CASCADE +from django.db.models.fields import CharField, DateField, IntegerField +from django.db.models.fields.related import ForeignKey +from fg.models import FG # Create your models here. +class Schedule(models.Model): + date = DateField() + day = IntegerField() + + def __str__(self): + return f'{self.date}' + +class LC(models.Model): + id = models.AutoField(primary_key=True) + fg_n_id = ForeignKey(FG, on_delete=CASCADE, null=True, related_name='fg_n') + fg_s_id = ForeignKey(FG, on_delete=CASCADE, null=True, related_name='fg_s') + schedule = IntegerField() + name = CharField(max_length=10) + total = IntegerField() + + class Meta: + db_table = 'lc' + ordering = ['schedule', 'name'] + + def __str__(self): + return self.name \ No newline at end of file From fb2d7c933745b89ab5f58a03be0d85f46900fd84 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Sat, 28 Jan 2023 00:23:05 +0900 Subject: [PATCH 05/44] feat(be): create api for freshman --- backend/fnet/urls.py | 4 +- backend/freshman/admin.py | 3 +- backend/freshman/serializers.py | 66 +++++++++++++++++++++++++++++++++ backend/freshman/urls/admin.py | 7 +++- backend/freshman/urls/user.py | 7 +++- backend/freshman/views/admin.py | 64 ++++++++++++++++++++++++++++++++ backend/freshman/views/user.py | 24 ++++++++++++ backend/lc/admin.py | 3 ++ 8 files changed, 173 insertions(+), 5 deletions(-) create mode 100644 backend/freshman/serializers.py diff --git a/backend/fnet/urls.py b/backend/fnet/urls.py index 3581273..8ced4d1 100644 --- a/backend/fnet/urls.py +++ b/backend/fnet/urls.py @@ -20,8 +20,8 @@ path('admin/', admin.site.urls), path('api/', include('fg.urls.user')), path('api/admin/', include('fg.urls.admin')), - # path('api/', include('freshman.urls.user')), - # path('api/admin/', include('freshman.urls.admin')), + path('api/', include('freshman.urls.user')), + path('api/admin/', include('freshman.urls.admin')), # path('api/', include('lc.urls.user')), # path('api/admin/', include('lc.urls.admin')), # path('api/', include('notice.urls.user')), diff --git a/backend/freshman/admin.py b/backend/freshman/admin.py index 8c38f3f..801196f 100644 --- a/backend/freshman/admin.py +++ b/backend/freshman/admin.py @@ -1,3 +1,4 @@ from django.contrib import admin - +from .models import Freshman # Register your models here. +admin.site.register(Freshman) \ No newline at end of file diff --git a/backend/freshman/serializers.py b/backend/freshman/serializers.py new file mode 100644 index 0000000..1c53b34 --- /dev/null +++ b/backend/freshman/serializers.py @@ -0,0 +1,66 @@ +from django.db import models +from rest_framework import serializers +from .models import Freshman + +class FreshmanSerializer(serializers.ModelSerializer): + name = serializers.SerializerMethodField() + phone_number = serializers.SerializerMethodField() + lc = serializers.SerializerMethodField() + register = serializers.SerializerMethodField() + class Meta: + model = Freshman + fields = ['name','phone_number', 'lc', 'register'] + + def get_name(self, obj): + return obj.name + + def get_phone_number(self, obj): + return obj.phone_number[4:8] + + def get_lc(self, obj): + return obj.lc.name + + def get_register(self, obj): + if obj.register: + return 'O' + else: + return 'X' + + +class FreshmanLCSerializer(serializers.ModelSerializer): + name = serializers.SerializerMethodField() + lc = serializers.SerializerMethodField() + department = serializers.SerializerMethodField() + register = serializers.SerializerMethodField() + + class Meta: + model = Freshman + fields = ['name', 'department', 'lc', 'register'] + + def get_name(self, obj): + return obj.name + + def get_lc(self, obj): + return obj.lc.name + + def get_department(self, obj): + if obj.department == "EN": + return "공학" + elif obj.department == "NC": + return "자연과학" + elif obj.department == "HS": + return "인문사회" + elif obj.department == "SS": + return "사회과학" + + def get_register(self, obj): + if obj.register: + return 'O' + else: + return 'X' + +class registerFreshmanSerializer(serializers.Serializer): + freshman_id = serializers.IntegerField() + +class FreshmanFileUploadSerializer(serializers.Serializer): + file = serializers.FileField() \ No newline at end of file diff --git a/backend/freshman/urls/admin.py b/backend/freshman/urls/admin.py index 1500611..73293b6 100644 --- a/backend/freshman/urls/admin.py +++ b/backend/freshman/urls/admin.py @@ -1 +1,6 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.admin import setFreshmanAPI + +urlpatterns = [ + path("freshman/", setFreshmanAPI.as_view()) +] \ No newline at end of file diff --git a/backend/freshman/urls/user.py b/backend/freshman/urls/user.py index 1500611..9507c51 100644 --- a/backend/freshman/urls/user.py +++ b/backend/freshman/urls/user.py @@ -1 +1,6 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.user import getLcMemberList + +urlpatterns = [ + path("freshman/", getLcMemberList) +] \ No newline at end of file diff --git a/backend/freshman/views/admin.py b/backend/freshman/views/admin.py index e69de29..7f0b2d1 100644 --- a/backend/freshman/views/admin.py +++ b/backend/freshman/views/admin.py @@ -0,0 +1,64 @@ +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.decorators import api_view +from openpyxl import load_workbook +from django.db import transaction, IntegrityError + +from lc.models import LC +from freshman.models import Freshman +from freshman.serializers import FreshmanFileUploadSerializer, FreshmanSerializer, registerFreshmanSerializer + +class setFreshmanAPI(APIView): + def get(self, request): + try: + queryset = Freshman.objects.all() + except Freshman.DoesNotExist: + return Response({"error": True, "data": "Freshman does not exist"}) + + data = FreshmanSerializer(queryset, many=True).data + + return Response({"error": False, "data": data}) + + def post(self, request): + serializer = FreshmanFileUploadSerializer(data = request.data) + serializer.is_valid(raise_exception=True) + file = serializer.validated_data['file'] + print(file) + rows = load_workbook(file).active.rows + data_list = [[cell.value for cell in row] for row in rows] + # remove header + data_list.pop(0) + # 성명/휴대폰번호/LC/지망모집단위 + + freshman_list = [] + for data in data_list: + lc_name = data[2] + try: + lc = LC.objects.get(name=lc_name) + except LC.DoesNotExist: + return Response({"error": True, "data": "LC does not exist. Check your file again"}) + + if Freshman.objects.filter(name=data[0], phone_number=data[1]).exists(): + continue + freshman_list.append(Freshman(lc=lc, name=data[1], department=data[0], phone_number=data[2])) + + try: + with transaction.atomic(): + Freshman.objects.bulk_create(freshman_list) + except IntegrityError as e: + return Response({"data": str(e).split("\n")[0]}) + return Response({"error":False}) + + def put(self, request): + data = request.data + serializer = registerFreshmanSerializer(data=data) + serializer.is_valid(raise_exception=True) + + try: + freshman = Freshman.objects.get(id=data["freshman_id"]) + except Freshman.DoesNotExist: + return Response({"error": True, "data": "Freshman does not exist"}) + + freshman.register = not freshman.register + freshman.save() + return Response({"error": False, "data": {}}) \ No newline at end of file diff --git a/backend/freshman/views/user.py b/backend/freshman/views/user.py index e69de29..17598e7 100644 --- a/backend/freshman/views/user.py +++ b/backend/freshman/views/user.py @@ -0,0 +1,24 @@ +from rest_framework.response import Response +from rest_framework.decorators import api_view + +from lc.models import LC +from freshman.models import Freshman +from freshman.serializers import FreshmanLCSerializer + +@api_view(['GET']) +def getLcMemberList(request): + lc_name = request.GET.get("lc") + + if not lc_name: + return Response({"error": True, "data": "LC name is required"}) + print(lc_name) + + try: + lc_id = LC.objects.get(name=lc_name) + except LC.DoesNotExist: + return Response({"error": True, "data": "LC does not exist"}) + + queryset = Freshman.objects.filter(lc_id=lc_id) + data = FreshmanLCSerializer(queryset, many=True).data + + return Response({"error": False, "data": data}) \ No newline at end of file diff --git a/backend/lc/admin.py b/backend/lc/admin.py index 8c38f3f..f8c415e 100644 --- a/backend/lc/admin.py +++ b/backend/lc/admin.py @@ -1,3 +1,6 @@ from django.contrib import admin +from .models import LC, Schedule # Register your models here. +admin.site.register(Schedule) +admin.site.register(LC) \ No newline at end of file From a5bc04d0fabc8c448f889345e56d5f8a189489c2 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Sun, 29 Jan 2023 10:44:33 +0900 Subject: [PATCH 06/44] chore(be): add display object in django admin site about Schedule model day --- backend/fnet/settings.py | 8 +++++--- backend/lc/models.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/fnet/settings.py b/backend/fnet/settings.py index c569e81..726aac4 100644 --- a/backend/fnet/settings.py +++ b/backend/fnet/settings.py @@ -32,8 +32,8 @@ CORS_ALLOW_CREDENTIALS = True CSRF_TRUSTED_ORIGINS = ["127.0.0.1:8000", "localhost:8000"] CORS_ORIGIN_WHITELIST = ( - 'localhost:8000', - '127.0.0.1:8000', + 'http://localhost:3000', + 'http://127.0.0.1:8000', ) CORS_ALLOW_HEADERS = ( 'access-control-allow-credentials', @@ -72,10 +72,12 @@ 'freshman', 'lc', 'notice', - 'todo' + 'todo', + 'corsheaders', # React와 연결 하기 위한 CORS 추가 ] MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', # React와 연결 하기 위한 CORS 추가 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', diff --git a/backend/lc/models.py b/backend/lc/models.py index 8b0770d..7120ace 100644 --- a/backend/lc/models.py +++ b/backend/lc/models.py @@ -10,7 +10,7 @@ class Schedule(models.Model): day = IntegerField() def __str__(self): - return f'{self.date}' + return f'[{self.day}] {self.date}' class LC(models.Model): id = models.AutoField(primary_key=True) From 433f927e630de9eb3ca05b68bc1e18f9e3e455c2 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Sun, 29 Jan 2023 11:35:49 +0900 Subject: [PATCH 07/44] feat(fe): add getFreshman api connection --- .../src/screen/setting/freshmanSetting.tsx | 102 +++--------------- frontend/src/service/user.service.ts | 14 ++- 2 files changed, 25 insertions(+), 91 deletions(-) diff --git a/frontend/src/screen/setting/freshmanSetting.tsx b/frontend/src/screen/setting/freshmanSetting.tsx index d11189d..28cc8db 100644 --- a/frontend/src/screen/setting/freshmanSetting.tsx +++ b/frontend/src/screen/setting/freshmanSetting.tsx @@ -1,102 +1,32 @@ import React, { useState } from 'react' import { Container, Grid, Box, Button, Divider } from '@mui/material' import { Header, Title, MenuButton, AdminTable, Loading } from '../../component' - -const data = [ - { - name: '김일건', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건1', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건2', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건3', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건4', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건5', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건6', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건7', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건8', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건9', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, - { - name: '김일건10', - phone_number: '010-1234-5678', - lc: 'LC08', - register: true, - }, -] +import { useQuery } from 'react-query' +import { getFreshman } from '../../service' export default function FgSettingScreen() { - const [tableData, updateTableData] = useState([]) - const [loading, setLoading] = useState(false) + const [freshmanData, setFreshmanData] = useState([]) + const [loading, setLoading] = useState(true) const tableColumn = [ { id: 'index', label: '#' }, { id: 'name', label: '이름' }, - { id: 'phone_number', label: '전화번호' }, + { id: 'phone_number', label: '전화번호 (뒷자리)' }, { id: 'lc', label: 'LC' }, { id: 'register', label: '등록' }, ] - // api 작동 확인 필요 - /* - const fetchUsers = async () => { - try { - setLoading(true) - const res = await api.getFreshmanList() - updateTableData(res.data.data) - } catch (err) { - alert(err) + useQuery('freshmans', getFreshman, { + refetchOnWindowFocus: false, + onSuccess: data => { + setFreshmanData(data.data) setLoading(false) - } - } - */ - + }, + onError: error => { + console.log(error) + }, + }) + const uploadFile = async (event: React.ChangeEvent) => { if (event.target.files != null) { setLoading(true) @@ -140,7 +70,7 @@ export default function FgSettingScreen() { - {loading ? : } + {loading ? : } diff --git a/frontend/src/service/user.service.ts b/frontend/src/service/user.service.ts index be394f6..2a95530 100644 --- a/frontend/src/service/user.service.ts +++ b/frontend/src/service/user.service.ts @@ -1,13 +1,17 @@ import { api } from '.' type LoginUser = { - name: string, - password: string, + name: string + password: string } export async function login(data: LoginUser) { - await api.post('login/', data); + await api.post('login/', data) } export async function logout() { - await api.get('logout/'); -} \ No newline at end of file + await api.get('logout/') +} + +export async function getFreshman() { + return await api.get('admin/freshman/') +} From c0b1d9967b4989764b33d8df0c6cbd40fa7c1203 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Fri, 3 Feb 2023 00:07:56 +0900 Subject: [PATCH 08/44] feat: add uploadFreshman api connection --- backend/freshman/models.py | 9 +++-- backend/freshman/views/admin.py | 28 +++++++++----- .../src/screen/setting/freshmanSetting.tsx | 38 +++++++++---------- frontend/src/service/user.service.ts | 6 +++ 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/backend/freshman/models.py b/backend/freshman/models.py index d532ab3..7e78e98 100644 --- a/backend/freshman/models.py +++ b/backend/freshman/models.py @@ -10,11 +10,12 @@ class Freshman(models.Model): ENGINEERING = 'EN' SOCIAL_SCIENCE = 'SS' HUMANITIES_SCIENCE = 'HS' + DEPARTMENT_CHOICES = [ - (NATURAL_SCIENCE, 'nat'), - (ENGINEERING, 'eng'), - (SOCIAL_SCIENCE, 'soc'), - (HUMANITIES_SCIENCE, 'hum'), + (NATURAL_SCIENCE, '자연과학계열'), + (ENGINEERING, '공학계열'), + (SOCIAL_SCIENCE, '사회과학계열'), + (HUMANITIES_SCIENCE, '인문과학계열'), ] lc = ForeignKey(LC, on_delete=CASCADE, null=True) diff --git a/backend/freshman/views/admin.py b/backend/freshman/views/admin.py index 7f0b2d1..8ffef6e 100644 --- a/backend/freshman/views/admin.py +++ b/backend/freshman/views/admin.py @@ -1,6 +1,7 @@ +from rest_framework.exceptions import ParseError +# from rest_framework.views import status from rest_framework.views import APIView from rest_framework.response import Response -from rest_framework.decorators import api_view from openpyxl import load_workbook from django.db import transaction, IntegrityError @@ -20,34 +21,43 @@ def get(self, request): return Response({"error": False, "data": data}) def post(self, request): + # print(request.data) serializer = FreshmanFileUploadSerializer(data = request.data) serializer.is_valid(raise_exception=True) file = serializer.validated_data['file'] - print(file) + # print(file) rows = load_workbook(file).active.rows data_list = [[cell.value for cell in row] for row in rows] # remove header data_list.pop(0) - # 성명/휴대폰번호/LC/지망모집단위 + # 성명/휴대폰번호/LC/지망모집단위 freshman_list = [] + department_dict = {'자연과학계열':'NC', '공학계열':'EN', '사회과학계열':'SS', '인문과학계열':'HS'} + for data in data_list: lc_name = data[2] try: lc = LC.objects.get(name=lc_name) except LC.DoesNotExist: - return Response({"error": True, "data": "LC does not exist. Check your file again"}) + raise ParseError("LC does not exist. Check your file again") + # return Response({"error": True, "message": "LC does not exist. Check your file again"}, status=status.HTTP_400_BAD_REQUEST) - if Freshman.objects.filter(name=data[0], phone_number=data[1]).exists(): - continue - freshman_list.append(Freshman(lc=lc, name=data[1], department=data[0], phone_number=data[2])) + # if Freshman.objects.filter(name=data[0], phone_number=data[1]).exists(): + # continue + freshman_list.append(Freshman(lc=lc, name=data[0], department=department_dict[data[3]], phone_number=data[1])) + # 기존 data 모두 삭제 + Freshman.objects.all().delete() + try: with transaction.atomic(): Freshman.objects.bulk_create(freshman_list) except IntegrityError as e: - return Response({"data": str(e).split("\n")[0]}) - return Response({"error":False}) + raise ParseError({"data": str(e).split("\n")[0]}) + # return Response({"data": str(e).split("\n")[0]}) + post_success_data = FreshmanSerializer(freshman_list, many=True).data + return Response({"error":False, "data": post_success_data}) def put(self, request): data = request.data diff --git a/frontend/src/screen/setting/freshmanSetting.tsx b/frontend/src/screen/setting/freshmanSetting.tsx index 28cc8db..507335e 100644 --- a/frontend/src/screen/setting/freshmanSetting.tsx +++ b/frontend/src/screen/setting/freshmanSetting.tsx @@ -1,8 +1,8 @@ import React, { useState } from 'react' import { Container, Grid, Box, Button, Divider } from '@mui/material' import { Header, Title, MenuButton, AdminTable, Loading } from '../../component' -import { useQuery } from 'react-query' -import { getFreshman } from '../../service' +import { useMutation, useQuery } from 'react-query' +import { getFreshman, upLoadFreshman } from '../../service' export default function FgSettingScreen() { const [freshmanData, setFreshmanData] = useState([]) @@ -26,30 +26,28 @@ export default function FgSettingScreen() { console.log(error) }, }) - + + + const uploadFileMutate = useMutation(upLoadFreshman) const uploadFile = async (event: React.ChangeEvent) => { + event.preventDefault() if (event.target.files != null) { setLoading(true) - // api 작동 확인 필요 - /* const formData = new FormData() formData.append('file', event.target.files[0]) - try { - const res = await api.uploadFreshman(formData); - if (!res.data.error) { - alert('Upload Successful'); - setSingleFile(null) - await fetchUsers() - } - } catch (err) { - alert(err) - } finally{ + event.target.value = '' - } - */ - setTimeout(() => { - setLoading(false) - }, 500) + uploadFileMutate.mutate(formData, { + onSuccess: data => { + setLoading(false) + setFreshmanData(data.data) + // console.log(freshmanData) + }, + onError: error => { + alert(error) + setLoading(false) + }, + }) } else { alert('파일이 선택되지 않았습니다.') } diff --git a/frontend/src/service/user.service.ts b/frontend/src/service/user.service.ts index 2a95530..b5190c3 100644 --- a/frontend/src/service/user.service.ts +++ b/frontend/src/service/user.service.ts @@ -15,3 +15,9 @@ export async function logout() { export async function getFreshman() { return await api.get('admin/freshman/') } + +export async function upLoadFreshman(file: any) { + return await api.post('admin/freshman/', file, { + headers: { 'Content-Type': 'text/xml' }, + }) +} From 8feca82ccd3a614755701943b3d6ad564a327c4f Mon Sep 17 00:00:00 2001 From: k1g99 Date: Fri, 3 Feb 2023 02:06:20 +0900 Subject: [PATCH 09/44] feat: add registerFreshman api connection --- backend/freshman/serializers.py | 16 +-- frontend/src/screen/register/register.tsx | 139 +++++++++++++--------- frontend/src/service/user.service.ts | 4 + 3 files changed, 97 insertions(+), 62 deletions(-) diff --git a/backend/freshman/serializers.py b/backend/freshman/serializers.py index 1c53b34..b2585c5 100644 --- a/backend/freshman/serializers.py +++ b/backend/freshman/serializers.py @@ -3,13 +3,15 @@ from .models import Freshman class FreshmanSerializer(serializers.ModelSerializer): + id = serializers.IntegerField() name = serializers.SerializerMethodField() phone_number = serializers.SerializerMethodField() lc = serializers.SerializerMethodField() - register = serializers.SerializerMethodField() + register = serializers.BooleanField() + # register = serializers.SerializerMethodField() class Meta: model = Freshman - fields = ['name','phone_number', 'lc', 'register'] + fields = ['id', 'name','phone_number', 'lc', 'register'] def get_name(self, obj): return obj.name @@ -20,11 +22,11 @@ def get_phone_number(self, obj): def get_lc(self, obj): return obj.lc.name - def get_register(self, obj): - if obj.register: - return 'O' - else: - return 'X' + # def get_register(self, obj): + # if obj.register: + # return 'O' + # else: + # return 'X' class FreshmanLCSerializer(serializers.ModelSerializer): diff --git a/frontend/src/screen/register/register.tsx b/frontend/src/screen/register/register.tsx index 705fa01..1ccd3ba 100644 --- a/frontend/src/screen/register/register.tsx +++ b/frontend/src/screen/register/register.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { Header } from '../../component' +import { Header, Loading } from '../../component' import { TextField, Checkbox, @@ -14,31 +14,47 @@ import { } from '@mui/material' import { BsSuitHeart, BsSuitHeartFill } from 'react-icons/bs' import { Container } from '@mui/system' +import { useMutation, useQuery } from 'react-query' +import { getFreshman, registerFreshman } from '../../service' interface registerData { id: number name: string - phoneNumber: number - LC: string - submit: boolean + phone_number: number + lc: string + register: boolean } -const originalRows: registerData[] = [ - { id: 1, name: '박민서', phoneNumber: 1243, LC: 'LC23', submit: true }, - { id: 2, name: '이승준', phoneNumber: 5213, LC: 'LC21', submit: true }, - { id: 3, name: '정노원', phoneNumber: 2567, LC: 'LC96', submit: false }, - { id: 4, name: '심지연', phoneNumber: 3426, LC: 'LC43', submit: true }, - { id: 5, name: '장선영', phoneNumber: 8245, LC: 'LC63', submit: false }, - { id: 6, name: '이창준', phoneNumber: 1238, LC: 'LC78', submit: true }, - { id: 7, name: '배성빈', phoneNumber: 1263, LC: 'LC12', submit: false }, -] +// const originalRows: registerData[] = [ +// { id: 1, name: '박민서', phone_number: 1243, lc: 'LC23', register: true }, +// { id: 2, name: '이승준', phone_number: 5213, lc: 'LC21', register: true }, +// { id: 3, name: '정노원', phone_number: 2567, lc: 'LC96', register: false }, +// { id: 4, name: '심지연', phone_number: 3426, lc: 'LC43', register: true }, +// { id: 5, name: '장선영', phone_number: 8245, lc: 'LC63', register: false }, +// { id: 6, name: '이창준', phone_number: 1238, lc: 'LC78', register: true }, +// { id: 7, name: '배성빈', phone_number: 1263, lc: 'LC12', register: false }, +// ] export default function RegisterScreen() { - const [rows, setRows] = useState(originalRows) + const [loading, setLoading] = useState(true) + const [originalRows, setOriginalRows] = useState([]) + const [rows, setRows] = useState([]) const [searched, setSearched] = useState('') const [page, setPage] = React.useState(0) const [rowsPerPage, setRowsPerPage] = React.useState(10) + useQuery('registerFreshmans', getFreshman, { + refetchOnWindowFocus: false, + onSuccess: data => { + setOriginalRows(data.data) + setRows(data.data) + setLoading(false) + }, + onError: error => { + console.log(error) + }, + }) + const handleChangePage = (event: unknown, newPage: number) => { setPage(newPage) } @@ -56,10 +72,20 @@ export default function RegisterScreen() { setRows(filteredRows) } + const registerMutate = useMutation(registerFreshman) const handleCheckBox = (event: React.ChangeEvent, id: number) => { const updatedRows = rows.map((data) => { if (data.id === id) { - return { ...data, submit: event.target.checked } + registerMutate.mutate(id, { + onSuccess: data =>{ + // alert('반영되었습니다.') + }, + onError: error => { + alert('잠시 후 다시 시도해주세요') + setLoading(true) + }, + }) + return { ...data, register: event.target.checked } } return data }) @@ -78,47 +104,50 @@ export default function RegisterScreen() { onChange={(event) => requestSearch(event.target.value)} > - - - - - - 이름 - 전화번호 - LC - 접수 - - - - {rows.map((row) => ( - - {row.name} - {row.phoneNumber} - {row.LC} - - { - handleCheckBox(event, row.id)} - icon={} - checkedIcon={} - /> - } - + {loading ? : + + +
+ + + 이름 + 전화번호 + LC + 접수 - ))} - -
-
- + + + {rows.map((row) => ( + + {row.name} + {row.phone_number} + {row.lc} + + { + handleCheckBox(event, row.id)} + icon={} + checkedIcon={} + /> + } + + + ))} + + + + + + } ) } diff --git a/frontend/src/service/user.service.ts b/frontend/src/service/user.service.ts index b5190c3..9038222 100644 --- a/frontend/src/service/user.service.ts +++ b/frontend/src/service/user.service.ts @@ -21,3 +21,7 @@ export async function upLoadFreshman(file: any) { headers: { 'Content-Type': 'text/xml' }, }) } + +export async function registerFreshman(freshman_id: number) { + return await api.put('admin/freshman/', { freshman_id }) +} From b28adabcfb5ef0c7386344793f3515bc14a2ebea Mon Sep 17 00:00:00 2001 From: k1g99 Date: Fri, 3 Feb 2023 10:54:04 +0900 Subject: [PATCH 10/44] feat: add getLcMemberList api connection --- backend/freshman/views/user.py | 19 ++++--- frontend/src/Router.tsx | 2 +- frontend/src/component/lc/lcStatus.tsx | 2 +- frontend/src/screen/LC/lcMember.tsx | 78 ++++++++++++-------------- frontend/src/service/user.service.ts | 4 ++ 5 files changed, 52 insertions(+), 53 deletions(-) diff --git a/backend/freshman/views/user.py b/backend/freshman/views/user.py index 17598e7..0d7614c 100644 --- a/backend/freshman/views/user.py +++ b/backend/freshman/views/user.py @@ -7,16 +7,17 @@ @api_view(['GET']) def getLcMemberList(request): - lc_name = request.GET.get("lc") + lc_id = request.GET.get("lc") + # lc_nam1e = request.GET.get("lc") + # print(lc_name) + # if not lc_name: + # return Response({"error": True, "data": "LC name is required"}) + # print(lc_name) - if not lc_name: - return Response({"error": True, "data": "LC name is required"}) - print(lc_name) - - try: - lc_id = LC.objects.get(name=lc_name) - except LC.DoesNotExist: - return Response({"error": True, "data": "LC does not exist"}) + # try: + # lc_id = LC.objects.get(name=lc_name) + # except LC.DoesNotExist: + # return Response({"error": True, "data": "LC does not exist"}) queryset = Freshman.objects.filter(lc_id=lc_id) data = FreshmanLCSerializer(queryset, many=True).data diff --git a/frontend/src/Router.tsx b/frontend/src/Router.tsx index c828c99..e579aab 100644 --- a/frontend/src/Router.tsx +++ b/frontend/src/Router.tsx @@ -21,7 +21,7 @@ export default class Router extends Component { }> } /> - } /> + } /> } /> } /> } /> diff --git a/frontend/src/component/lc/lcStatus.tsx b/frontend/src/component/lc/lcStatus.tsx index c06cfc6..b1efb24 100644 --- a/frontend/src/component/lc/lcStatus.tsx +++ b/frontend/src/component/lc/lcStatus.tsx @@ -35,7 +35,7 @@ function LCStatus({ sReg, nReg, eReg, hReg }: LCStatusType) { 전체 접수 인원 - {sReg + sReg + eReg + nReg} + {sReg + hReg + eReg + nReg} diff --git a/frontend/src/screen/LC/lcMember.tsx b/frontend/src/screen/LC/lcMember.tsx index 84df66c..1dd8437 100644 --- a/frontend/src/screen/LC/lcMember.tsx +++ b/frontend/src/screen/LC/lcMember.tsx @@ -10,9 +10,12 @@ import { TableHead, TableRow, } from '@mui/material' -import React from 'react' +import React, { useState } from 'react' import { createTheme, ThemeProvider } from '@mui/material/styles' -import {Header, LCStatus} from '../../component' +import {Header, LCStatus, Loading} from '../../component' +import { getLcMemberList } from '../../service' +import { useQuery } from 'react-query' +import { useParams } from 'react-router-dom' const TableCellTheme = createTheme({ components: { @@ -29,52 +32,43 @@ const TableCellTheme = createTheme({ }, }) +interface lcMemberDataInterface { + name: string + department: string + register: string +} + export default function LcMemberScreen() { - const data = [ - { name: 'kim ilgun', department: 'n', register: true }, - { name: '박민서', department: 'n', register: true }, - { name: '박민서', department: 'n', register: true }, - { name: '박민서', department: 'n', register: true }, - { name: '박민서', department: 'n', register: true }, - { name: '정노원', department: 'n', register: false }, - { name: '정노원', department: 'n', register: false }, - { name: '정노원', department: 'n', register: false }, - { name: '정노원', department: 'n', register: false }, - { name: '정노원', department: 'n', register: false }, - { name: '정노원', department: 'n', register: false }, - { name: '김일건', department: 'e', register: false }, - { name: '한새로오름', department: 's', register: true }, - { name: '김일건', department: 'h', register: true }, - { name: '김일건', department: 'n', register: true }, - { name: '정노원', department: 'n', register: true }, - { name: '김일건', department: 'e', register: true }, - { name: '한새로오름', department: 's', register: false }, - { name: '김일건', department: 'h', register: false }, - { name: '김일건', department: 'n', register: false }, - ] + let { id } = useParams() + const [lcData, setlcData] = useState([]) + const [loading, setLoading] = useState(true) + + useQuery(['lcMember', id], () => getLcMemberList(id as string), { + refetchOnWindowFocus: false, + onSuccess: data => { + setlcData(data.data) + setLoading(false) + console.log(data.data) + }, + onError: error => { + alert(error) + }, + }) let sRegister = 0 let nRegister = 0 let eRegister = 0 let hRegister = 0 - data.forEach((member) => { - if (member.register) { - if (member.department === 'n') nRegister++ - else if (member.department === 'e') eRegister++ - else if (member.department === 's') sRegister++ - else if (member.department === 'h') hRegister++ + lcData.forEach((member) => { + if (member.register === 'O') { + if (member.department === '자연과학') nRegister++ + else if (member.department === '공학') eRegister++ + else if (member.department === '인문사회') sRegister++ + else if (member.department === '사회과학') hRegister++ } }) - const DepartmentName = (department: string) => { - if (department === 'n') return '자연과학' - else if (department === 'e') return '공학' - else if (department === 'h') return '인문사회' - else if (department === 's') return '사회과학' - return '-' - } - const LCMemberTable = () => { return ( @@ -97,12 +91,12 @@ export default function LcMemberScreen() { - {data.map((r, i) => ( + {lcData.map((r, i) => ( {i + 1} {r.name} - {DepartmentName(r.department)} - {r.register ? 'O' : 'X'} + {r.department} + {r.register} ))} @@ -122,7 +116,7 @@ export default function LcMemberScreen() { - + {loading ? : } diff --git a/frontend/src/service/user.service.ts b/frontend/src/service/user.service.ts index 9038222..269997d 100644 --- a/frontend/src/service/user.service.ts +++ b/frontend/src/service/user.service.ts @@ -16,6 +16,10 @@ export async function getFreshman() { return await api.get('admin/freshman/') } +export async function getLcMemberList(lc_id: string) { + return await api.get(`freshman?lc=${lc_id}`) +} + export async function upLoadFreshman(file: any) { return await api.post('admin/freshman/', file, { headers: { 'Content-Type': 'text/xml' }, From be1a3a1edef66b584ac41915d50718a2756f9a1e Mon Sep 17 00:00:00 2001 From: RowonChung Date: Thu, 9 Feb 2023 20:10:31 +0900 Subject: [PATCH 11/44] todo + schedule api --- backend/lc/serializers.py | 9 +++++- backend/lc/urls/user.py | 3 ++ backend/lc/views/admin.py | 24 +++++--------- backend/lc/views/user.py | 51 ++++++++++++++++++++++++++++++ backend/notice/serializers.py | 37 ++++++++++++++++++++++ backend/todo/admin.py | 2 ++ backend/todo/models.py | 19 ++++++++++- backend/todo/serializers.py | 7 +++++ backend/todo/urls/user.py | 8 ++++- backend/todo/views/user.py | 59 +++++++++++++++++++++++++++++++++++ 10 files changed, 200 insertions(+), 19 deletions(-) create mode 100644 backend/notice/serializers.py create mode 100644 backend/todo/serializers.py diff --git a/backend/lc/serializers.py b/backend/lc/serializers.py index a9bcabe..ac24e43 100644 --- a/backend/lc/serializers.py +++ b/backend/lc/serializers.py @@ -1,5 +1,12 @@ from django.db import models +from .models import Schedule, LC from rest_framework import serializers class CreateScheduleSerializer(serializers.Serializer): - date = serializers.DateField() \ No newline at end of file + date = serializers.DateField() + +class ScheduleSerializer(serializers.ModelSerializer): + class Meta: + model = Schedule + fields = "__all__" + \ No newline at end of file diff --git a/backend/lc/urls/user.py b/backend/lc/urls/user.py index d143924..1eede35 100644 --- a/backend/lc/urls/user.py +++ b/backend/lc/urls/user.py @@ -1,6 +1,9 @@ from django.urls import path from ..views.admin import ScheduleAPI +from ..views.user import TodayLCAPI, LCAPI urlpatterns = [ path('schedule/', ScheduleAPI.as_view(), name="schedule_api"), + path('LC/', LCAPI.as_view(), name="LC_api"), + path('ToayLC/', TodayLCAPI.as_view(), name="TodayLC_api") ] \ No newline at end of file diff --git a/backend/lc/views/admin.py b/backend/lc/views/admin.py index b567951..47db210 100644 --- a/backend/lc/views/admin.py +++ b/backend/lc/views/admin.py @@ -1,5 +1,5 @@ from ..models import LC, Schedule -from ..serializers import (CreateScheduleSerializer) +from ..serializers import (CreateScheduleSerializer, ScheduleSerializer) from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.decorators import api_view @@ -7,34 +7,26 @@ import dateutil.parser class ScheduleAPI(APIView): - def get(self, request): - # if request.user.role != ADMIN: - # return Response({"error":True, "data":"not Admin"}) - schedule = Schedule.objects.get() - return Response({"error":False, "data":schedule}) - def post(self, request): data = request.data serializer = CreateScheduleSerializer(data) - Schedule.objects.create(date=dateutil.parser.parse(data["date"]).date(), day = 1) - return Response({"error":False, "data":None}) + schedule = Schedule.objects.create(date=dateutil.parser.parse(data["date"]).date(), day = 1) + return Response({"error":False, "data": ScheduleSerializer(schedule).data}) def put(self, request): - data = Schedule.objects.get() + data = Schedule.objects.filter() if len(data) > 0: data.delete() - if request.user.role != ADMIN: - return Response({"error":True, "data":"not Admin"}) - data = request.data.data + # if request.user.role != ADMIN:1 + # return Response({"error":True, "data":"not Admin"}) + data = request.data["datelist"] for d in data: serializer = CreateScheduleSerializer(d) - serializer.is_valid(raise_exception=True) - data.sort() n = 1 for d in data: - schedule = Schedule.objects.create(date=d, day = n) + Schedule.objects.create(date=dateutil.parser.parse(d["date"]).date(), day = n) n+= 1 return Response({"error":False, "data":None}) diff --git a/backend/lc/views/user.py b/backend/lc/views/user.py index e69de29..2ee3446 100644 --- a/backend/lc/views/user.py +++ b/backend/lc/views/user.py @@ -0,0 +1,51 @@ +from ..models import LC, Schedule +from ..serializers import (CreateScheduleSerializer, ScheduleSerializer) +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.decorators import api_view +import datetime +import dateutil.parser + +class ScheduleAPI(APIView): + def get(self, request): + try: + queryset = Schedule.objects.filter() + data = ScheduleSerializer(queryset, many = True).data + except Schedule.DoesNotExist: + return Response({"error": True, "data": "NO data"}) + return Response({"error":False, "data":data}) + +class TodayLCAPI(APIView): + def get(self, request): + user_campus = request.user.campus + current_datetime = datetime.date.today() + try: + schedule = Schedule.objects.get(date = current_datetime) + except Schedule.DoesNotExist(date = current_datetime): + return Response({"error":False, "data": None}) + if user_campus == "n": + try: + todayLC = LC.objects.get(day = schedule["day"], fg_n_id = request.user.id) + except LC.DoesNotExist(day = schedule["day"], fg_n_id = request.user.id): + return Response({"error":False, "data": None}) + else: + try: + todayLC = LC.objects.get(day = schedule["day"], fg_s_id = request.user.id) + except LC.DoesNotExist(day = schedule["day"], fg_s_id = request.user.id): + return Response({"error":False, "data": None}) + return Response({"error": False, "data": todayLC}) + +class LCAPI(APIView): + def get(self, request): + user_campus = request.user.campus + if user_campus == "n": + try: + todayLC = LC.objects.get(fg_n_id = request.user.id) + except LC.DoesNotExist(fg_n_id = request.user.id): + return Response({"error":False, "data": None}) + else: + try: + todayLC = LC.objects.get(fg_s_id = request.user.id) + except LC.DoesNotExist(fg_s_id = request.user.id): + return Response({"error":False, "data": None}) + return Response({"error": False, "data": todayLC}) diff --git a/backend/notice/serializers.py b/backend/notice/serializers.py new file mode 100644 index 0000000..171f9a0 --- /dev/null +++ b/backend/notice/serializers.py @@ -0,0 +1,37 @@ +from django.db import models +from rest_framework import serializers +from .models import Notice, Comment +from fg.serializers import FGSerializer + +class NoticeSerializer(serializers.ModelSerializer): + class Meta: + model = Notice + fields = "__all__" + +class CreateNoticeSerializer(serializers.Serializer): + title = serializers.CharField() + content = serializers.CharField() + +class EditNoticeSerializer(serializers.Serializer): + id = serializers.IntegerField() + title = serializers.CharField() + content = serializers.CharField() + + +class CommentSerializer(serializers.ModelSerializer): + created_by = FGSerializer() + class Meta: + model = Comment + fields = "__all__" + +class CreateCommentSerializer(serializers.Serializer): + notice_id = serializers.IntegerField() + content = serializers.CharField() + +class EditCommentSerializer(serializers.Serializer): + id = serializers.IntegerField() + content = serializers.CharField() + +class CheckCommentSerializer(serializers.Serializer): + id = serializers.IntegerField() + check = serializers.BooleanField() \ No newline at end of file diff --git a/backend/todo/admin.py b/backend/todo/admin.py index 8c38f3f..28c98ee 100644 --- a/backend/todo/admin.py +++ b/backend/todo/admin.py @@ -1,3 +1,5 @@ from django.contrib import admin + + # Register your models here. diff --git a/backend/todo/models.py b/backend/todo/models.py index 71a8362..b7beb79 100644 --- a/backend/todo/models.py +++ b/backend/todo/models.py @@ -1,3 +1,20 @@ from django.db import models +from django.db.models.fields.related import ForeignKey +from fg.models import FG +from django.db.models.deletion import CASCADE +from django.db.models.fields import CharField, AutoField, IntegerField, BooleanField -# Create your models here. + + +class Todo(models.Model): + id = models.AutoField(db_column='todo_id' ,primary_key= True) + created_by = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='created_by' ) + content = CharField(max_length=100) + common = models.BooleanField() + + +class Todo_check(models.Model): + id = models.AutoField(primary_key= True) + todo_id = ForeignKey(Todo,db_column='todo_id' ,on_delete=CASCADE, null= True, related_name='todo_id' ) + fg_id = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='fg_id' ) + check = models.BooleanField() diff --git a/backend/todo/serializers.py b/backend/todo/serializers.py new file mode 100644 index 0000000..9723060 --- /dev/null +++ b/backend/todo/serializers.py @@ -0,0 +1,7 @@ +from django.db import models +from .models import Todo +from rest_framework import serializers + +class CreateTodoSerializer(serializers.Serializer): + content = serializers.CharField() + common = serializers.BooleanField() diff --git a/backend/todo/urls/user.py b/backend/todo/urls/user.py index 1500611..7e0d641 100644 --- a/backend/todo/urls/user.py +++ b/backend/todo/urls/user.py @@ -1 +1,7 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.user import Todo_checkAPI, TodoAPI + +urlpatterns = [ + path('todo/', Todo_checkAPI.as_view(), name="Todocheck_api"), + path('todocheck/', TodoAPI.as_view(), name="Todo_api"), +] \ No newline at end of file diff --git a/backend/todo/views/user.py b/backend/todo/views/user.py index e69de29..ec4af22 100644 --- a/backend/todo/views/user.py +++ b/backend/todo/views/user.py @@ -0,0 +1,59 @@ +from ..models import Todo, Todo_check +from ..serializers import CreateTodoSerializer +from fg.models import FG +from rest_framework.response import Response +from rest_framework.views import APIView + + +class TodoAPI(APIView): + def get(self, request): + get_common = request.GET.get("common") + try: + todos = Todo.objects.prefetch_related('todo_id').filter(common = get_common, fg_id = request.user) + except Todo.DoesNotExist: + msg = "Todo does not exist" + return Response({"error": True, "data": msg}) + return Response({"error": False, "data" : todos}) + + def post(self, request): + data = request.data + serializer = CreateTodoSerializer(data) + if data.objects.get(common = False): + todo = Todo.objects.create(create_by = request.user, content = data["content"],common = False) + todo_check= Todo_check.objects.create(todo_id = todo["id"], fg_id = request.user, check = False) + return Response({"error": False, "data": todo}) + else: + try: + fgs = FG.objects.get() + except FG.DoesNotExist: + msg = "FG does not exist" + return Response({"error": True, "data": msg}) + for fg in fgs: + todo = Todo.objects.create(create_by = request.user, content = data["content"],common = False) + todo_check= Todo_check.objects.create(todo_id = todo["id"], fg_id = fg["id"], check = False) + return Response({"error": False, "data": todo}) + + def put(self, request): + data = request.data + try: + todo = Todo.objects.get(id = data.pop("id")) + except Todo.DoesNotExist: + msg = "Todo does not exist" + return Response({"error":True, "data": msg}) + + todo.centent = data["content"] + todo.save() + return Response({"error": False, "data": todo}) + +class Todo_checkAPI(APIView): + def put(self, request): + data = request.data + try: + todo_check = Todo_check.objects.get(id = data["id"]) + except Todo_check.DoesNotExist: + msg = "Todo Check does not exist" + return Response({"error":True, "data": msg}) + + todo_check.check = data["check"] + todo_check.save() + return Response({"error": False, "data": todo_check}) \ No newline at end of file From b542f0b785b4e8dacd8ba31914a7817b0b654221 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Sun, 12 Feb 2023 18:34:21 +0900 Subject: [PATCH 12/44] fix: fix merge conflict and errors --- backend/fnet/settings.py | 26 ++----------------- frontend/src/screen/LC/lcMember.tsx | 4 +-- frontend/src/screen/register/register.tsx | 6 ++--- .../src/screen/setting/freshmanSetting.tsx | 6 ++--- frontend/src/service/user.service.ts | 15 ++++++----- 5 files changed, 18 insertions(+), 39 deletions(-) diff --git a/backend/fnet/settings.py b/backend/fnet/settings.py index 2262c0e..28305ba 100644 --- a/backend/fnet/settings.py +++ b/backend/fnet/settings.py @@ -38,8 +38,8 @@ CORS_ALLOW_CREDENTIALS = True CSRF_TRUSTED_ORIGINS = ["127.0.0.1:8000", "localhost:8000"] CORS_ORIGIN_WHITELIST = ( - 'localhost:8000', - '127.0.0.1:8000', + 'https://localhost:8000', + 'https://127.0.0.1:8000', ) CORS_ALLOW_HEADERS = ( 'access-control-allow-credentials', @@ -80,19 +80,11 @@ 'lc', 'notice', 'todo', -<<<<<<< HEAD - 'corsheaders', # React와 연결 하기 위한 CORS 추가 -] - -MIDDLEWARE = [ - 'corsheaders.middleware.CorsMiddleware', # React와 연결 하기 위한 CORS 추가 -======= 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ->>>>>>> main 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -121,19 +113,6 @@ ] WSGI_APPLICATION = 'fnet.wsgi.application' -<<<<<<< HEAD - - -# Database -# https://docs.djangoproject.com/en/4.1/ref/settings/#databases - -# DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.sqlite3', -# 'NAME': BASE_DIR / 'db.sqlite3', -# } -# } -======= REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', @@ -145,7 +124,6 @@ 'rest_framework.authentication.TokenAuthentication', ], } ->>>>>>> main # Password validation diff --git a/frontend/src/screen/LC/lcMember.tsx b/frontend/src/screen/LC/lcMember.tsx index 1dd8437..4964971 100644 --- a/frontend/src/screen/LC/lcMember.tsx +++ b/frontend/src/screen/LC/lcMember.tsx @@ -13,7 +13,7 @@ import { import React, { useState } from 'react' import { createTheme, ThemeProvider } from '@mui/material/styles' import {Header, LCStatus, Loading} from '../../component' -import { getLcMemberList } from '../../service' +import { UserService } from '../../service' import { useQuery } from 'react-query' import { useParams } from 'react-router-dom' @@ -43,7 +43,7 @@ export default function LcMemberScreen() { const [lcData, setlcData] = useState([]) const [loading, setLoading] = useState(true) - useQuery(['lcMember', id], () => getLcMemberList(id as string), { + useQuery(['lcMember', id], () => UserService.getLcMemberList(id as string), { refetchOnWindowFocus: false, onSuccess: data => { setlcData(data.data) diff --git a/frontend/src/screen/register/register.tsx b/frontend/src/screen/register/register.tsx index 1ccd3ba..51c6b03 100644 --- a/frontend/src/screen/register/register.tsx +++ b/frontend/src/screen/register/register.tsx @@ -15,7 +15,7 @@ import { import { BsSuitHeart, BsSuitHeartFill } from 'react-icons/bs' import { Container } from '@mui/system' import { useMutation, useQuery } from 'react-query' -import { getFreshman, registerFreshman } from '../../service' +import { UserService } from '../../service' interface registerData { id: number @@ -43,7 +43,7 @@ export default function RegisterScreen() { const [page, setPage] = React.useState(0) const [rowsPerPage, setRowsPerPage] = React.useState(10) - useQuery('registerFreshmans', getFreshman, { + useQuery('registerFreshmans', UserService.getFreshman, { refetchOnWindowFocus: false, onSuccess: data => { setOriginalRows(data.data) @@ -72,7 +72,7 @@ export default function RegisterScreen() { setRows(filteredRows) } - const registerMutate = useMutation(registerFreshman) + const registerMutate = useMutation(UserService.registerFreshman) const handleCheckBox = (event: React.ChangeEvent, id: number) => { const updatedRows = rows.map((data) => { if (data.id === id) { diff --git a/frontend/src/screen/setting/freshmanSetting.tsx b/frontend/src/screen/setting/freshmanSetting.tsx index 507335e..6f04064 100644 --- a/frontend/src/screen/setting/freshmanSetting.tsx +++ b/frontend/src/screen/setting/freshmanSetting.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react' import { Container, Grid, Box, Button, Divider } from '@mui/material' import { Header, Title, MenuButton, AdminTable, Loading } from '../../component' import { useMutation, useQuery } from 'react-query' -import { getFreshman, upLoadFreshman } from '../../service' +import { UserService } from '../../service' export default function FgSettingScreen() { const [freshmanData, setFreshmanData] = useState([]) @@ -16,7 +16,7 @@ export default function FgSettingScreen() { { id: 'register', label: '등록' }, ] - useQuery('freshmans', getFreshman, { + useQuery('freshmans', UserService.getFreshman, { refetchOnWindowFocus: false, onSuccess: data => { setFreshmanData(data.data) @@ -28,7 +28,7 @@ export default function FgSettingScreen() { }) - const uploadFileMutate = useMutation(upLoadFreshman) + const uploadFileMutate = useMutation(UserService.upLoadFreshman) const uploadFile = async (event: React.ChangeEvent) => { event.preventDefault() if (event.target.files != null) { diff --git a/frontend/src/service/user.service.ts b/frontend/src/service/user.service.ts index 9b6eaf0..e84eed1 100644 --- a/frontend/src/service/user.service.ts +++ b/frontend/src/service/user.service.ts @@ -6,21 +6,21 @@ type LoginUser = { } class UserService { - async login(data:LoginUser) { - return await api.post('login/', data); + async login(data: LoginUser) { + return await api.post('login/', data) } async logout() { - await api.get('logout/'); + await api.get('logout/') } async get(id: number, token: string) { - const data = await api.get(`fg?id=${id}`, token); + const data = await api.get(`fg?id=${id}`, token) return data.data } async getFreshman() { return await api.get('admin/freshman/') } - async function getLcMemberList(lc_id: string) { + async getLcMemberList(lc_id: string) { return await api.get(`freshman?lc=${lc_id}`) } @@ -29,9 +29,10 @@ class UserService { headers: { 'Content-Type': 'text/xml' }, }) } - + async registerFreshman(freshman_id: number) { return await api.put('admin/freshman/', { freshman_id }) + } } -export default new UserService() \ No newline at end of file +export default new UserService() From 4717561865f477209c02f6f467c6a27b099b0a0e Mon Sep 17 00:00:00 2001 From: RowonChung Date: Sun, 12 Feb 2023 22:37:09 +0900 Subject: [PATCH 13/44] feat(fe): create Todoservice --- frontend/src/model/index.ts | 4 +++- frontend/src/model/todo.ts | 16 ++++++++++++++++ frontend/src/service/todo.service.ts | 25 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 frontend/src/model/todo.ts create mode 100644 frontend/src/service/todo.service.ts diff --git a/frontend/src/model/index.ts b/frontend/src/model/index.ts index f8f0fb0..f2ca00a 100644 --- a/frontend/src/model/index.ts +++ b/frontend/src/model/index.ts @@ -1,2 +1,4 @@ export * from './user' -export * from './notice' \ No newline at end of file +export * from './notice' +export * from './todo' +export * from './LC' \ No newline at end of file diff --git a/frontend/src/model/todo.ts b/frontend/src/model/todo.ts new file mode 100644 index 0000000..d1a775e --- /dev/null +++ b/frontend/src/model/todo.ts @@ -0,0 +1,16 @@ +import { FG } from "./user" + +export type Todo = { + id: number, + created_by: FG, + content: string, + common : boolean +} + +export type TodoCheck = { + id : number, + todo_id : number, + fg_id : number, + check : boolean +} + diff --git a/frontend/src/service/todo.service.ts b/frontend/src/service/todo.service.ts new file mode 100644 index 0000000..a0cc0a7 --- /dev/null +++ b/frontend/src/service/todo.service.ts @@ -0,0 +1,25 @@ +import {api} from '.' +import { Todo,TodoCheck } from '../model'; + +class TodoServiceClass { + async create(input: Pick){ + return await api.post('todo/', input); + } + async get(common:boolean){ + return await api.get(`todo?common=${common}`); + } + async put(input: Pick){ + return await api.post('todo/', input); + } +} + +class TodoCheckServiceClass{ + async put(input: Pick){ + return await api.put('todocheck/',input) + } +} + +const TodoService = new TodoServiceClass() +const TodoCheckService = new TodoCheckServiceClass() + +export {TodoService,TodoCheckService} \ No newline at end of file From 55e9032c41e2d93c7da9f57d1a1b8cb2cf3bc3ad Mon Sep 17 00:00:00 2001 From: RowonChung Date: Sun, 12 Feb 2023 22:48:50 +0900 Subject: [PATCH 14/44] feat(fe): add lc model --- frontend/src/model/LC.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 frontend/src/model/LC.ts diff --git a/frontend/src/model/LC.ts b/frontend/src/model/LC.ts new file mode 100644 index 0000000..3d825c5 --- /dev/null +++ b/frontend/src/model/LC.ts @@ -0,0 +1,15 @@ +import { FG } from "./user" + +export type LC = { + id : number , + fg_n_id : number , + fg_s_id : number , + schedule : number , + name : string, + total : number +} + +export type Schedule = { + date : Date, + day : number +} \ No newline at end of file From f24f4ef02d0564cf51bb5e389241e856bd947bc9 Mon Sep 17 00:00:00 2001 From: pinecone28 Date: Mon, 13 Feb 2023 01:22:14 +0900 Subject: [PATCH 15/44] feat: implement todo page --- backend/fnet/dev_settings.py | 2 +- backend/fnet/urls.py | 3 +- backend/todo/migrations/0001_initial.py | 35 +++++++++++++ backend/todo/models.py | 1 - backend/todo/serializers.py | 5 ++ backend/todo/urls/admin.py | 1 - backend/todo/urls/user.py | 4 +- frontend/src/component/todo/TodoEdit.tsx | 37 ++++++++++---- frontend/src/component/todo/TodoSection.tsx | 56 +++++++++++++-------- frontend/src/screen/todo/todo.tsx | 2 +- frontend/src/service/index.ts | 4 +- frontend/src/service/todo.service.ts | 8 +-- 12 files changed, 114 insertions(+), 44 deletions(-) create mode 100644 backend/todo/migrations/0001_initial.py delete mode 100644 backend/todo/urls/admin.py diff --git a/backend/fnet/dev_settings.py b/backend/fnet/dev_settings.py index 672fdfb..c449e9e 100644 --- a/backend/fnet/dev_settings.py +++ b/backend/fnet/dev_settings.py @@ -9,7 +9,7 @@ 'USER': 'fnet', 'PASSWORD': 'fnet', 'HOST': '127.0.0.1', - 'PORT': 3306, + 'PORT': 1398, } } diff --git a/backend/fnet/urls.py b/backend/fnet/urls.py index 582c4dc..af35a8c 100644 --- a/backend/fnet/urls.py +++ b/backend/fnet/urls.py @@ -26,6 +26,5 @@ path('api/admin/', include('lc.urls.admin')), path('api/', include('notice.urls.user')), path('api/admin/', include('notice.urls.admin')), - # path('api/', include('todo.urls.user')), - # path('api/admin/', include('todo.urls.admin')), + path('api/', include('todo.urls.user')), ] \ No newline at end of file diff --git a/backend/todo/migrations/0001_initial.py b/backend/todo/migrations/0001_initial.py new file mode 100644 index 0000000..11e12f7 --- /dev/null +++ b/backend/todo/migrations/0001_initial.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.10 on 2023-02-12 14:34 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Todo', + fields=[ + ('id', models.AutoField(db_column='todo_id', primary_key=True, serialize=False)), + ('content', models.CharField(max_length=100)), + ('common', models.BooleanField()), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='created_by', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Todo_check', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('check', models.BooleanField()), + ('fg_id', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fg_id', to=settings.AUTH_USER_MODEL)), + ('todo_id', models.ForeignKey(db_column='todo_id', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='todo_id', to='todo.todo')), + ], + ), + ] diff --git a/backend/todo/models.py b/backend/todo/models.py index b7beb79..834298a 100644 --- a/backend/todo/models.py +++ b/backend/todo/models.py @@ -5,7 +5,6 @@ from django.db.models.fields import CharField, AutoField, IntegerField, BooleanField - class Todo(models.Model): id = models.AutoField(db_column='todo_id' ,primary_key= True) created_by = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='created_by' ) diff --git a/backend/todo/serializers.py b/backend/todo/serializers.py index 9723060..7b69dc2 100644 --- a/backend/todo/serializers.py +++ b/backend/todo/serializers.py @@ -5,3 +5,8 @@ class CreateTodoSerializer(serializers.Serializer): content = serializers.CharField() common = serializers.BooleanField() + +class TodoSerializer(serializers.Serializer): + class Meta: + model = Todo + fields = "__all__" \ No newline at end of file diff --git a/backend/todo/urls/admin.py b/backend/todo/urls/admin.py deleted file mode 100644 index 1500611..0000000 --- a/backend/todo/urls/admin.py +++ /dev/null @@ -1 +0,0 @@ -from django.urls import path \ No newline at end of file diff --git a/backend/todo/urls/user.py b/backend/todo/urls/user.py index 7e0d641..57ce4a9 100644 --- a/backend/todo/urls/user.py +++ b/backend/todo/urls/user.py @@ -2,6 +2,6 @@ from ..views.user import Todo_checkAPI, TodoAPI urlpatterns = [ - path('todo/', Todo_checkAPI.as_view(), name="Todocheck_api"), - path('todocheck/', TodoAPI.as_view(), name="Todo_api"), + path('todocheck/', Todo_checkAPI.as_view(), name="Todocheck_api"), + path('todo/', TodoAPI.as_view(), name="Todo_api"), ] \ No newline at end of file diff --git a/frontend/src/component/todo/TodoEdit.tsx b/frontend/src/component/todo/TodoEdit.tsx index adf3375..0b493f4 100644 --- a/frontend/src/component/todo/TodoEdit.tsx +++ b/frontend/src/component/todo/TodoEdit.tsx @@ -2,35 +2,54 @@ import React, { useState } from 'react' import { Box, Checkbox, TextField, IconButton, Alert, AlertTitle, Collapse, Button } from '@mui/material' import { BiCheckCircle as CheckIcon } from 'react-icons/bi' import { TbTrash as DeleteIcon } from 'react-icons/tb' +import { TodoService } from '../../service' +import { useMutation } from 'react-query' +import { useRecoilValue } from 'recoil' +import { accesstoken } from '../../store' +import { Todo } from '../../model' -type Todo = { - id: number - content: string - check: boolean -} type editProp = { addTodo?: Function updateTodo?: Function deleteTodo?: Function todo?: Todo + mode: string } export default function TodoEdit(props: editProp) { const initContent = props.todo ? props.todo.content : '' - const check = props.todo ? props.todo.check : false + // const check = props.todo ? props.todo.check : false + const check = false const id = props.todo ? props.todo.id : null + const common = props.mode === 'common' ? true : false + const token = useRecoilValue(accesstoken) + const [content, setContent] = useState(initContent) const [alertOpen, setAlertOpen] = useState(false) + + + const createTodo = useMutation( + 'createTodo', + async (param: any) => await TodoService.create(param.data, token), + { + onSuccess: () => { + setContent('') + }, + onError: (err: any) => { + alert(err) + } + } + ); const addTodo = () => { // add mode if (!props.todo && props.addTodo) { const newTodo = { content: content.trim(), - check: false, + common: common } - setContent('') - props.addTodo(newTodo) + createTodo.mutate({data:newTodo}) + props.addTodo(common) } } diff --git a/frontend/src/component/todo/TodoSection.tsx b/frontend/src/component/todo/TodoSection.tsx index 9784428..37b712b 100644 --- a/frontend/src/component/todo/TodoSection.tsx +++ b/frontend/src/component/todo/TodoSection.tsx @@ -6,12 +6,12 @@ import TodoElement from './TodoElement' import TodoEdit from './TodoEdit' import { FiPlusCircle as PlusIcon } from 'react-icons/fi' import { TbEditCircle as EditIcon } from 'react-icons/tb' +import { TodoService } from '../../service' +import { useQuery } from 'react-query' +import { useRecoilValue } from 'recoil' +import { accesstoken } from '../../store' +import { Todo } from '../../model' -type Todo = { - id: number - content: string - check: boolean -} type TodoSectionProp = { title: string auth?: boolean //편집 가능 여부 @@ -19,13 +19,27 @@ type TodoSectionProp = { type Mode = 'normal' | 'add' | 'edit' export default function TodoSection(props: TodoSectionProp) { + const token = useRecoilValue(accesstoken) const title = props.title - const data: Todo[] = [ - { id: 1, content: 'todo', check: true }, - { id: 2, content: 'todo', check: false }, + const [common, setCommon] = React.useState(props.title === 'common' ? true : false) + const data: any[] = [ + { id: 1, content: 'todo' }, + { id: 2, content: 'todo' }, ] - const [todoList, setTodoList] = useState(data) + const [todoList, setTodoList] = useState(data) const [mode, setMode] = useState('normal') + const todo = useQuery( + 'getTodo', + async() => await TodoService.get(common, token),{ + onSuccess: () =>{ + console.log(common) + } + } + ) + + const addTodo = (common: boolean) => { + todo.refetch() + } const handleCheck = (id: number, check: boolean) => { const newTodoList: Todo[] = todoList.map((todo: Todo) => { @@ -37,10 +51,7 @@ export default function TodoSection(props: TodoSectionProp) { setTodoList(newTodoList) } - const addTodo = (todo: any) => { - const newTodo = { ...todo, id: todoList.length + 1 } - setTodoList([...todoList, newTodo]) - } + const updateTodo = (id: number, content: string) => { const newTodoList = todoList.map((todo) => { if (todo.id === id) { @@ -83,18 +94,19 @@ export default function TodoSection(props: TodoSectionProp) { )} - {mode !== 'add' && todoList.length === 0 && ( + {todo.isLoading && Loading...} + {!todo.isLoading && mode !== 'add' && todo.data.length === 0 && ( No Todo List )} - {(mode === 'normal' || mode === 'add') && - todoList.length > 0 && - todoList.map((todo) => ( - + {!todo.isLoading &&(mode === 'normal' || mode === 'add') && + todo.data.length > 0 && + todo.data.map((t: Todo) => ( + ))} - {mode === 'edit' && - todoList.length > 0 && - todoList.map((todo) => )} - {mode === 'add' && } + {!todo.isLoading &&mode === 'edit' && + todo.data.length > 0 && + todo.data.map((t: Todo) => )} + {!todo.isLoading &&mode === 'add' && } ) diff --git a/frontend/src/screen/todo/todo.tsx b/frontend/src/screen/todo/todo.tsx index ed1dbb6..c74df98 100644 --- a/frontend/src/screen/todo/todo.tsx +++ b/frontend/src/screen/todo/todo.tsx @@ -9,7 +9,7 @@ export default function TodoScreen() {
- + diff --git a/frontend/src/service/index.ts b/frontend/src/service/index.ts index 17f9fa1..8bc017e 100644 --- a/frontend/src/service/index.ts +++ b/frontend/src/service/index.ts @@ -1,5 +1,7 @@ import UserService from './user.service' import {NoticeService, CommentService} from './notice.service' -export {UserService, NoticeService, CommentService} +import { TodoCheckService, TodoService } from './todo.service' + +export {UserService, NoticeService, CommentService, TodoCheckService, TodoService} export * from './base.service' \ No newline at end of file diff --git a/frontend/src/service/todo.service.ts b/frontend/src/service/todo.service.ts index a0cc0a7..abf73e0 100644 --- a/frontend/src/service/todo.service.ts +++ b/frontend/src/service/todo.service.ts @@ -2,11 +2,11 @@ import {api} from '.' import { Todo,TodoCheck } from '../model'; class TodoServiceClass { - async create(input: Pick){ - return await api.post('todo/', input); + async create(input: Pick, token: string){ + return await api.post('todo/', input, token); } - async get(common:boolean){ - return await api.get(`todo?common=${common}`); + async get(common:boolean, token: string){ + return await api.get(`todo?common=${common}`, token); } async put(input: Pick){ return await api.post('todo/', input); From 2ad5356c71a1fcb36eea47908d6c8566b91daa11 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Mon, 13 Feb 2023 14:01:52 +0900 Subject: [PATCH 16/44] fix: fix rest_framework setting for post request with file --- backend/fnet/settings.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/fnet/settings.py b/backend/fnet/settings.py index 28305ba..cff1ab0 100644 --- a/backend/fnet/settings.py +++ b/backend/fnet/settings.py @@ -119,6 +119,8 @@ ), 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.FormParser', + 'rest_framework.parsers.MultiPartParser' ), 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', From 798fd30077633d526badbb2ed57f1a56467ba00c Mon Sep 17 00:00:00 2001 From: k1g99 Date: Mon, 13 Feb 2023 20:23:11 +0900 Subject: [PATCH 17/44] chore: add user auth to freshman api models --- .../0002_alter_freshman_department.py | 18 +++++++++++++ backend/freshman/urls/user.py | 4 +-- backend/freshman/views/admin.py | 9 +++++++ backend/freshman/views/user.py | 26 ++++++++++++------- frontend/src/component/lc/lcStatus.tsx | 6 +++-- frontend/src/screen/LC/lcMember.tsx | 18 ++++++++----- frontend/src/screen/register/register.tsx | 12 ++++++--- .../src/screen/setting/freshmanSetting.tsx | 24 +++++++++++------ frontend/src/service/user.service.ts | 18 ++++++------- 9 files changed, 93 insertions(+), 42 deletions(-) create mode 100644 backend/freshman/migrations/0002_alter_freshman_department.py diff --git a/backend/freshman/migrations/0002_alter_freshman_department.py b/backend/freshman/migrations/0002_alter_freshman_department.py new file mode 100644 index 0000000..0593b6b --- /dev/null +++ b/backend/freshman/migrations/0002_alter_freshman_department.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.10 on 2023-02-13 05:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('freshman', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='freshman', + name='department', + field=models.CharField(choices=[('NC', '자연과학계열'), ('EN', '공학계열'), ('SS', '사회과학계열'), ('HS', '인문과학계열')], max_length=10), + ), + ] diff --git a/backend/freshman/urls/user.py b/backend/freshman/urls/user.py index 9507c51..f58b48b 100644 --- a/backend/freshman/urls/user.py +++ b/backend/freshman/urls/user.py @@ -1,6 +1,6 @@ from django.urls import path -from ..views.user import getLcMemberList +from ..views.user import getLcMemberListAPI urlpatterns = [ - path("freshman/", getLcMemberList) + path("freshman/", getLcMemberListAPI) ] \ No newline at end of file diff --git a/backend/freshman/views/admin.py b/backend/freshman/views/admin.py index 8ffef6e..c383586 100644 --- a/backend/freshman/views/admin.py +++ b/backend/freshman/views/admin.py @@ -11,6 +11,9 @@ class setFreshmanAPI(APIView): def get(self, request): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + try: queryset = Freshman.objects.all() except Freshman.DoesNotExist: @@ -21,6 +24,9 @@ def get(self, request): return Response({"error": False, "data": data}) def post(self, request): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + # print(request.data) serializer = FreshmanFileUploadSerializer(data = request.data) serializer.is_valid(raise_exception=True) @@ -60,6 +66,9 @@ def post(self, request): return Response({"error":False, "data": post_success_data}) def put(self, request): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + data = request.data serializer = registerFreshmanSerializer(data=data) serializer.is_valid(raise_exception=True) diff --git a/backend/freshman/views/user.py b/backend/freshman/views/user.py index 0d7614c..2faeddc 100644 --- a/backend/freshman/views/user.py +++ b/backend/freshman/views/user.py @@ -6,18 +6,24 @@ from freshman.serializers import FreshmanLCSerializer @api_view(['GET']) -def getLcMemberList(request): +def getLcMemberListAPI(request): + # check login + if not request.user.is_authenticated: + return Response({"error": True, "data": "login required"}) + + fg_id = request.user.id + fg_role = request.user.role + lc_id = request.GET.get("lc") - # lc_nam1e = request.GET.get("lc") - # print(lc_name) - # if not lc_name: - # return Response({"error": True, "data": "LC name is required"}) - # print(lc_name) + if not lc_id: + return Response({"error": True, "data": "LC is required"}) + + try: + lc = LC.objects.get(id=lc_id) + except LC.DoesNotExist: + return Response({"error": True, "data": "LC does not exist"}) - # try: - # lc_id = LC.objects.get(name=lc_name) - # except LC.DoesNotExist: - # return Response({"error": True, "data": "LC does not exist"}) + # Admin 제외 자기 LC만 열람 가능? queryset = Freshman.objects.filter(lc_id=lc_id) data = FreshmanLCSerializer(queryset, many=True).data diff --git a/frontend/src/component/lc/lcStatus.tsx b/frontend/src/component/lc/lcStatus.tsx index b1efb24..8d904fa 100644 --- a/frontend/src/component/lc/lcStatus.tsx +++ b/frontend/src/component/lc/lcStatus.tsx @@ -3,6 +3,7 @@ import React from 'react' import background from '../../image/lc_bgImg.png' type LCStatusType = { + lc: string sReg: number nReg: number eReg: number @@ -13,7 +14,7 @@ type RegisterDetailType = { department: number breakpoint?: boolean } -function LCStatus({ sReg, nReg, eReg, hReg }: LCStatusType) { +function LCStatus({ lc, sReg, nReg, eReg, hReg }: LCStatusType) { const RegisterDetail = ({ campus, department }: RegisterDetailType) => { return ( @@ -69,7 +70,8 @@ function LCStatus({ sReg, nReg, eReg, hReg }: LCStatusType) { }} > - LC09 + LC{Number(lc) < 10 ? '0' + lc : Number(lc)} + {/* {request.GET.get("lc")} */} diff --git a/frontend/src/screen/LC/lcMember.tsx b/frontend/src/screen/LC/lcMember.tsx index 4964971..e62ca26 100644 --- a/frontend/src/screen/LC/lcMember.tsx +++ b/frontend/src/screen/LC/lcMember.tsx @@ -16,6 +16,8 @@ import {Header, LCStatus, Loading} from '../../component' import { UserService } from '../../service' import { useQuery } from 'react-query' import { useParams } from 'react-router-dom' +import { useRecoilValue } from 'recoil' +import { accesstoken } from '../../store' const TableCellTheme = createTheme({ components: { @@ -42,16 +44,18 @@ export default function LcMemberScreen() { let { id } = useParams() const [lcData, setlcData] = useState([]) const [loading, setLoading] = useState(true) - - useQuery(['lcMember', id], () => UserService.getLcMemberList(id as string), { + const token = useRecoilValue(accesstoken) + + useQuery(['lcMember', id], () => UserService.getLcMemberList(id as string, token), { refetchOnWindowFocus: false, onSuccess: data => { + if (data.error) { + alert(data.data) + return + } setlcData(data.data) setLoading(false) - console.log(data.data) - }, - onError: error => { - alert(error) + // console.log(data.data) }, }) @@ -112,7 +116,7 @@ export default function LcMemberScreen() {
- + diff --git a/frontend/src/screen/register/register.tsx b/frontend/src/screen/register/register.tsx index 51c6b03..f2108d7 100644 --- a/frontend/src/screen/register/register.tsx +++ b/frontend/src/screen/register/register.tsx @@ -16,6 +16,8 @@ import { BsSuitHeart, BsSuitHeartFill } from 'react-icons/bs' import { Container } from '@mui/system' import { useMutation, useQuery } from 'react-query' import { UserService } from '../../service' +import { useRecoilValue } from 'recoil' +import { accesstoken } from '../../store' interface registerData { id: number @@ -42,8 +44,10 @@ export default function RegisterScreen() { const [searched, setSearched] = useState('') const [page, setPage] = React.useState(0) const [rowsPerPage, setRowsPerPage] = React.useState(10) + const token = useRecoilValue(accesstoken) - useQuery('registerFreshmans', UserService.getFreshman, { + + useQuery(['registerFreshmans', token], () => UserService.getFreshman(token), { refetchOnWindowFocus: false, onSuccess: data => { setOriginalRows(data.data) @@ -72,11 +76,13 @@ export default function RegisterScreen() { setRows(filteredRows) } - const registerMutate = useMutation(UserService.registerFreshman) + + const registerMutate = useMutation(((param: any) => UserService.registerFreshman(param.id, param.token))) + const handleCheckBox = (event: React.ChangeEvent, id: number) => { const updatedRows = rows.map((data) => { if (data.id === id) { - registerMutate.mutate(id, { + registerMutate.mutate({id: id, token: token}, { onSuccess: data =>{ // alert('반영되었습니다.') }, diff --git a/frontend/src/screen/setting/freshmanSetting.tsx b/frontend/src/screen/setting/freshmanSetting.tsx index 6f04064..c6df31d 100644 --- a/frontend/src/screen/setting/freshmanSetting.tsx +++ b/frontend/src/screen/setting/freshmanSetting.tsx @@ -3,10 +3,13 @@ import { Container, Grid, Box, Button, Divider } from '@mui/material' import { Header, Title, MenuButton, AdminTable, Loading } from '../../component' import { useMutation, useQuery } from 'react-query' import { UserService } from '../../service' +import { useRecoilValue } from 'recoil' +import { accesstoken } from '../../store' export default function FgSettingScreen() { const [freshmanData, setFreshmanData] = useState([]) const [loading, setLoading] = useState(true) + const token = useRecoilValue(accesstoken) const tableColumn = [ { id: 'index', label: '#' }, @@ -16,7 +19,7 @@ export default function FgSettingScreen() { { id: 'register', label: '등록' }, ] - useQuery('freshmans', UserService.getFreshman, { + useQuery(['freshmans', token], () => UserService.getFreshman(token), { refetchOnWindowFocus: false, onSuccess: data => { setFreshmanData(data.data) @@ -28,7 +31,8 @@ export default function FgSettingScreen() { }) - const uploadFileMutate = useMutation(UserService.upLoadFreshman) + const uploadFileMutate = useMutation((param: any) => UserService.upLoadFreshman(param.file, param.token)) + const uploadFile = async (event: React.ChangeEvent) => { event.preventDefault() if (event.target.files != null) { @@ -37,16 +41,20 @@ export default function FgSettingScreen() { formData.append('file', event.target.files[0]) event.target.value = '' - uploadFileMutate.mutate(formData, { + uploadFileMutate.mutate({file: formData, token: token}, { onSuccess: data => { + if (data.error) { + alert(data.data) + return + } setLoading(false) setFreshmanData(data.data) // console.log(freshmanData) - }, - onError: error => { - alert(error) - setLoading(false) - }, + } + // onError: error => { + // alert(error) + // setLoading(false) + // }, }) } else { alert('파일이 선택되지 않았습니다.') diff --git a/frontend/src/service/user.service.ts b/frontend/src/service/user.service.ts index e84eed1..7bb5d34 100644 --- a/frontend/src/service/user.service.ts +++ b/frontend/src/service/user.service.ts @@ -17,21 +17,19 @@ class UserService { return data.data } - async getFreshman() { - return await api.get('admin/freshman/') + async getFreshman(token: string) { + return await api.get('admin/freshman/', token) } - async getLcMemberList(lc_id: string) { - return await api.get(`freshman?lc=${lc_id}`) + async getLcMemberList(lc_id: string, token: string) { + return await api.get(`freshman?lc=${lc_id}`, token) } - async upLoadFreshman(file: any) { - return await api.post('admin/freshman/', file, { - headers: { 'Content-Type': 'text/xml' }, - }) + async upLoadFreshman(file: any, token: string) { + return await api.post('admin/freshman/', file, token) } - async registerFreshman(freshman_id: number) { - return await api.put('admin/freshman/', { freshman_id }) + async registerFreshman(freshman_id: number, token: string) { + return await api.put('admin/freshman/', { freshman_id }, token) } } From 6f2bbb87feed3e0138724de88a50b237d55f6587 Mon Sep 17 00:00:00 2001 From: k1g99 Date: Mon, 13 Feb 2023 20:54:37 +0900 Subject: [PATCH 18/44] refactor: delete unusage codes --- backend/freshman/views/admin.py | 7 ++----- frontend/src/screen/register/register.tsx | 13 ------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/backend/freshman/views/admin.py b/backend/freshman/views/admin.py index c383586..434539d 100644 --- a/backend/freshman/views/admin.py +++ b/backend/freshman/views/admin.py @@ -27,11 +27,10 @@ def post(self, request): if request.user.role != "Admin": return Response({"error": True, "data": "Admin role required"}) - # print(request.data) serializer = FreshmanFileUploadSerializer(data = request.data) serializer.is_valid(raise_exception=True) file = serializer.validated_data['file'] - # print(file) + rows = load_workbook(file).active.rows data_list = [[cell.value for cell in row] for row in rows] # remove header @@ -49,8 +48,6 @@ def post(self, request): raise ParseError("LC does not exist. Check your file again") # return Response({"error": True, "message": "LC does not exist. Check your file again"}, status=status.HTTP_400_BAD_REQUEST) - # if Freshman.objects.filter(name=data[0], phone_number=data[1]).exists(): - # continue freshman_list.append(Freshman(lc=lc, name=data[0], department=department_dict[data[3]], phone_number=data[1])) # 기존 data 모두 삭제 @@ -61,7 +58,7 @@ def post(self, request): Freshman.objects.bulk_create(freshman_list) except IntegrityError as e: raise ParseError({"data": str(e).split("\n")[0]}) - # return Response({"data": str(e).split("\n")[0]}) + post_success_data = FreshmanSerializer(freshman_list, many=True).data return Response({"error":False, "data": post_success_data}) diff --git a/frontend/src/screen/register/register.tsx b/frontend/src/screen/register/register.tsx index f2108d7..482b674 100644 --- a/frontend/src/screen/register/register.tsx +++ b/frontend/src/screen/register/register.tsx @@ -27,16 +27,6 @@ interface registerData { register: boolean } -// const originalRows: registerData[] = [ -// { id: 1, name: '박민서', phone_number: 1243, lc: 'LC23', register: true }, -// { id: 2, name: '이승준', phone_number: 5213, lc: 'LC21', register: true }, -// { id: 3, name: '정노원', phone_number: 2567, lc: 'LC96', register: false }, -// { id: 4, name: '심지연', phone_number: 3426, lc: 'LC43', register: true }, -// { id: 5, name: '장선영', phone_number: 8245, lc: 'LC63', register: false }, -// { id: 6, name: '이창준', phone_number: 1238, lc: 'LC78', register: true }, -// { id: 7, name: '배성빈', phone_number: 1263, lc: 'LC12', register: false }, -// ] - export default function RegisterScreen() { const [loading, setLoading] = useState(true) const [originalRows, setOriginalRows] = useState([]) @@ -83,9 +73,6 @@ export default function RegisterScreen() { const updatedRows = rows.map((data) => { if (data.id === id) { registerMutate.mutate({id: id, token: token}, { - onSuccess: data =>{ - // alert('반영되었습니다.') - }, onError: error => { alert('잠시 후 다시 시도해주세요') setLoading(true) From 7c2ffa87de7021eeae1193b794b9147957090474 Mon Sep 17 00:00:00 2001 From: pinecone28 Date: Wed, 15 Feb 2023 10:01:45 +0900 Subject: [PATCH 19/44] feat: implement todo page --- backend/fnet/settings.py | 2 +- .../migrations/0002_auto_20230214_1303.py | 26 ++++++ backend/todo/models.py | 15 ++-- backend/todo/serializers.py | 26 +++++- backend/todo/views/user.py | 82 ++++++++++++++----- frontend/src/component/todo/TodoEdit.tsx | 79 ++++++++++++------ frontend/src/component/todo/TodoElement.tsx | 23 +++++- frontend/src/component/todo/TodoSection.tsx | 77 ++++------------- frontend/src/screen/todo/todo.tsx | 39 ++++++++- frontend/src/service/todo.service.ts | 9 +- 10 files changed, 258 insertions(+), 120 deletions(-) create mode 100644 backend/todo/migrations/0002_auto_20230214_1303.py diff --git a/backend/fnet/settings.py b/backend/fnet/settings.py index cf950e4..86f6f8a 100644 --- a/backend/fnet/settings.py +++ b/backend/fnet/settings.py @@ -150,7 +150,7 @@ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Asia/Seoul' USE_I18N = True diff --git a/backend/todo/migrations/0002_auto_20230214_1303.py b/backend/todo/migrations/0002_auto_20230214_1303.py new file mode 100644 index 0000000..8c5d621 --- /dev/null +++ b/backend/todo/migrations/0002_auto_20230214_1303.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.10 on 2023-02-14 04:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('todo', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='todo', + name='id', + field=models.AutoField(primary_key=True, serialize=False), + ), + migrations.AlterModelTable( + name='todo', + table='todo', + ), + migrations.AlterModelTable( + name='todo_check', + table='todo_check', + ), + ] diff --git a/backend/todo/models.py b/backend/todo/models.py index 834298a..38b8a94 100644 --- a/backend/todo/models.py +++ b/backend/todo/models.py @@ -2,18 +2,23 @@ from django.db.models.fields.related import ForeignKey from fg.models import FG from django.db.models.deletion import CASCADE -from django.db.models.fields import CharField, AutoField, IntegerField, BooleanField - +from django.db.models.fields import CharField, DateTimeField, AutoField, IntegerField, BooleanField class Todo(models.Model): - id = models.AutoField(db_column='todo_id' ,primary_key= True) + id = models.AutoField(primary_key= True) created_by = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='created_by' ) content = CharField(max_length=100) - common = models.BooleanField() + common = models.BooleanField() + + class Meta: + db_table = 'todo' class Todo_check(models.Model): id = models.AutoField(primary_key= True) - todo_id = ForeignKey(Todo,db_column='todo_id' ,on_delete=CASCADE, null= True, related_name='todo_id' ) + todo_id = ForeignKey(Todo, db_column='todo_id' ,on_delete=CASCADE, related_name='todo_id', null= True ) fg_id = ForeignKey(FG, on_delete=CASCADE, null= True, related_name='fg_id' ) check = models.BooleanField() + + class Meta: + db_table = 'todo_check' diff --git a/backend/todo/serializers.py b/backend/todo/serializers.py index 7b69dc2..411d9e1 100644 --- a/backend/todo/serializers.py +++ b/backend/todo/serializers.py @@ -1,12 +1,32 @@ from django.db import models -from .models import Todo +from .models import Todo, Todo_check from rest_framework import serializers class CreateTodoSerializer(serializers.Serializer): content = serializers.CharField() common = serializers.BooleanField() -class TodoSerializer(serializers.Serializer): +class EditTodoSerializer(serializers.Serializer): + id = serializers.IntegerField() + content = serializers.CharField() + +class TodoSerializer(serializers.ModelSerializer): class Meta: model = Todo - fields = "__all__" \ No newline at end of file + fields = "__all__" + +class TodoCheckSerializer(serializers.ModelSerializer): + class Meta: + model = Todo_check + fields = "__all__" + +class TodoCheckSerializer(serializers.Serializer): + id = serializers.IntegerField() + check = serializers.BooleanField() + +class TodoAllSerializer(serializers.Serializer): + id = serializers.IntegerField() + todo_id = serializers.IntegerField() + content = serializers.CharField() + common = serializers.BooleanField() + check = serializers.BooleanField() diff --git a/backend/todo/views/user.py b/backend/todo/views/user.py index ec4af22..fd51f5e 100644 --- a/backend/todo/views/user.py +++ b/backend/todo/views/user.py @@ -1,5 +1,5 @@ from ..models import Todo, Todo_check -from ..serializers import CreateTodoSerializer +from ..serializers import CreateTodoSerializer, TodoSerializer, EditTodoSerializer, TodoCheckSerializer, TodoAllSerializer from fg.models import FG from rest_framework.response import Response from rest_framework.views import APIView @@ -7,47 +7,89 @@ class TodoAPI(APIView): def get(self, request): - get_common = request.GET.get("common") + get_common = True if request.GET.get("common") == 'true' else False try: - todos = Todo.objects.prefetch_related('todo_id').filter(common = get_common, fg_id = request.user) + if get_common: + todos = Todo.objects.prefetch_related('todo_id') + todos = todos.filter(common = get_common) + else: + todos = Todo.objects.prefetch_related('todo_id') + todos = todos.filter(common = get_common, created_by = request.user) except Todo.DoesNotExist: msg = "Todo does not exist" return Response({"error": True, "data": msg}) - return Response({"error": False, "data" : todos}) + return Response({"error": False, "data" : TodoSerializer(todos, many=True).data}) def post(self, request): - data = request.data - serializer = CreateTodoSerializer(data) - if data.objects.get(common = False): - todo = Todo.objects.create(create_by = request.user, content = data["content"],common = False) - todo_check= Todo_check.objects.create(todo_id = todo["id"], fg_id = request.user, check = False) - return Response({"error": False, "data": todo}) + serializer = CreateTodoSerializer(data = request.data) + serializer.is_valid(raise_exception=True) + data = serializer.data + + if data["common"] == False: + todo = Todo.objects.create(created_by = request.user, + content = data["content"], + common = False) + todo_check= Todo_check.objects.create(todo_id = todo, + fg_id = request.user, + check = False) + return Response({"error": False, "data": TodoSerializer(todo).data}) else: try: - fgs = FG.objects.get() + fgs = FG.objects.all() except FG.DoesNotExist: msg = "FG does not exist" return Response({"error": True, "data": msg}) - for fg in fgs: - todo = Todo.objects.create(create_by = request.user, content = data["content"],common = False) - todo_check= Todo_check.objects.create(todo_id = todo["id"], fg_id = fg["id"], check = False) - return Response({"error": False, "data": todo}) + todo = Todo.objects.create(created_by = request.user, + content = data["content"], + common = True) + for fg in fgs: + todo_check= Todo_check.objects.create(todo_id = todo, + fg_id = fg, + check = False) + return Response({"error": False, "data": TodoSerializer(todo).data}) def put(self, request): - data = request.data + serializer = EditTodoSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + data = serializer.data + try: todo = Todo.objects.get(id = data.pop("id")) except Todo.DoesNotExist: msg = "Todo does not exist" return Response({"error":True, "data": msg}) - todo.centent = data["content"] + todo.content = data["content"] todo.save() - return Response({"error": False, "data": todo}) + return Response({"error": False, "data": TodoSerializer(todo).data}) + + def delete(self, request): + """ + delete notice + """ + error = False + todo_id = request.GET.get("id") + if not todo_id: + msg = "Invalid parameter, id is required" + error = True + return Response({"error": error, "data": msg}) + + try: + todo = Todo.objects.get(id=todo_id) + except Todo.DoesNotExist: + msg = "Todo does not exist" + error = True + return Response({"error": error, "data": msg}) + + todo.delete() + return Response({"error": error, "data": None}) class Todo_checkAPI(APIView): def put(self, request): - data = request.data + serializer = TodoCheckSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + data = serializer.data + try: todo_check = Todo_check.objects.get(id = data["id"]) except Todo_check.DoesNotExist: @@ -56,4 +98,4 @@ def put(self, request): todo_check.check = data["check"] todo_check.save() - return Response({"error": False, "data": todo_check}) \ No newline at end of file + return Response({"error": False, "data": TodoCheckSerializer(todo_check).data}) \ No newline at end of file diff --git a/frontend/src/component/todo/TodoEdit.tsx b/frontend/src/component/todo/TodoEdit.tsx index 0b493f4..704cebb 100644 --- a/frontend/src/component/todo/TodoEdit.tsx +++ b/frontend/src/component/todo/TodoEdit.tsx @@ -9,17 +9,15 @@ import { accesstoken } from '../../store' import { Todo } from '../../model' type editProp = { - addTodo?: Function - updateTodo?: Function - deleteTodo?: Function + refetch?: Function todo?: Todo mode: string } export default function TodoEdit(props: editProp) { const initContent = props.todo ? props.todo.content : '' - // const check = props.todo ? props.todo.check : false const check = false + // const check = props.todo ? props.todo.check : false const id = props.todo ? props.todo.id : null const common = props.mode === 'common' ? true : false const token = useRecoilValue(accesstoken) @@ -34,28 +32,65 @@ export default function TodoEdit(props: editProp) { { onSuccess: () => { setContent('') + if (props.refetch) { + props.refetch(common) + } }, onError: (err: any) => { alert(err) } } ); + const deleteTodo = useMutation( + 'deleteTodo', + async (param: any) => await TodoService.delete(param),{ + onSuccess: () => { + if (props.refetch) { + props.refetch(common) + } + }, + } + ) + const updateTodo = useMutation( + 'updateTodo', + async (param: any) => await TodoService.put(param.data, token), + { + onSuccess: () => { + alert('수정되었습니다') + if (props.refetch) { + props.refetch(common) + } + }, + onError: (err: any) => { + alert(err) + } + } + ) const addTodo = () => { - // add mode - if (!props.todo && props.addTodo) { + if (!props.todo && props.refetch) { + if (content.trim() === '') { + alert('내용을 입력해주세요') + return + } const newTodo = { content: content.trim(), common: common } createTodo.mutate({data:newTodo}) - props.addTodo(common) } } - - const deleteTodo = () => { - if (props.deleteTodo) { - props.deleteTodo(id) + const update = () => { + if (props.todo && props.refetch) { + if (content.trim() === '') { + alert('내용을 입력해주세요') + return + } + const changeTodo = { + id: id, + content: content.trim() + } + updateTodo.mutate({data: changeTodo}) } } @@ -76,12 +111,6 @@ export default function TodoEdit(props: editProp) { } } - const inputEvent = (event: React.KeyboardEvent) => { - if (props.updateTodo) { - props.updateTodo(id, content) - } - } - const DeleteAlert = () => { return ( @@ -92,7 +121,7 @@ export default function TodoEdit(props: editProp) {