diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index b7c0102..cd5604d 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,35 +1,35 @@ -name: Docker Image CI +# name: Docker Image CI -on: - push: - branches: [main] - pull_request: - branches: [main] +# on: +# push: +# branches: [main] +# pull_request: +# branches: [main] -jobs: - build-image: - runs-on: ubuntu-latest +# jobs: +# build-image: +# runs-on: ubuntu-latest - steps: - - name: Checkout to repository - uses: actions/checkout@v2 +# steps: +# - name: Checkout to repository +# uses: actions/checkout@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 +# - name: Set up Docker Buildx +# uses: docker/setup-buildx-action@v2 - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: k1g99 - password: ${{ secrets.CR_PAT }} +# - name: Login to GitHub Container Registry +# uses: docker/login-action@v2 +# with: +# registry: ghcr.io +# username: k1g99 +# password: ${{ secrets.CR_PAT }} - - name: Build and push container image - uses: docker/build-push-action@v4 - with: - context: . - file: ./Dockerfile - push: true - tags: ghcr.io/fgfnet/fnet:latest - cache-from: type=gha - cache-to: type=gha,mode=max +# - name: Build and push container image +# uses: docker/build-push-action@v4 +# with: +# context: . +# file: ./Dockerfile +# push: true +# tags: ghcr.io/fgfnet/fnet:latest +# cache-from: type=gha +# cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 8c2921b..3f42758 100644 --- a/.gitignore +++ b/.gitignore @@ -79,4 +79,5 @@ media *.py[cod] *$py.class -.DS_Store \ No newline at end of file +.DS_Store +.env \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 79ce11e..49c685c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,6 @@ RUN apk add --update --no-cache build-base nginx curl supervisor mysql-client ma pip install --no-cache-dir -r /app/deploy/requirements.txt && \ apk del build-base --purge -COPY --from=builder /build/ /app/dist +COPY --from=builder /build/build/ /app/dist ENTRYPOINT ["sh", "/app/deploy/entrypoint.sh" ] \ No newline at end of file diff --git a/backend/deploy/nginx.conf b/backend/deploy/nginx.conf index ef0c8eb..ee9ec7a 100644 --- a/backend/deploy/nginx.conf +++ b/backend/deploy/nginx.conf @@ -27,7 +27,7 @@ http { access_log /data/log/nginx_access.log main; server { - listen 8000 default_server; + listen 80 default_server; server_name _; location /public { diff --git a/backend/fg/models.py b/backend/fg/models.py index e3bdaf7..641937c 100644 --- a/backend/fg/models.py +++ b/backend/fg/models.py @@ -6,10 +6,10 @@ # Create your models here. class UserManager(BaseUserManager): user_in_migrations = True - def create_user(self, name, student_id): + def create_user(self, name, student_id, campus): try: fg = self.model( - name = name, student_id = student_id + name = name, student_id = student_id, campus= campus ) fg.set_password(student_id) fg.save(using=self._db) @@ -20,7 +20,8 @@ def create_superuser(self, name, password, student_id="root"): try: fg = self.create_user( name=name, - student_id = password if password else student_id + student_id = password if password else student_id, + campus = "n" ) fg.set_password(password) fg.role = 'Admin' diff --git a/backend/fg/serializers.py b/backend/fg/serializers.py index 26bf44f..e06790b 100644 --- a/backend/fg/serializers.py +++ b/backend/fg/serializers.py @@ -5,7 +5,7 @@ class FGSerializer(serializers.ModelSerializer): class Meta: model = FG - fields = ['id', 'name', 'student_id', 'campus', 'role'] + fields = ['id', 'name', 'role', 'student_id', 'campus'] class CreateFGSerializer(serializers.Serializer): name = serializers.CharField() @@ -15,4 +15,7 @@ class CreateFGSerializer(serializers.Serializer): class LoginSerializer(serializers.Serializer): name = serializers.CharField() - password = serializers.CharField() \ No newline at end of file + password = serializers.CharField() + +class FGFileUploadSerializer(serializers.Serializer): + file = serializers.FileField() \ No newline at end of file diff --git a/backend/fg/urls/admin.py b/backend/fg/urls/admin.py index 4a415e0..23d1f05 100644 --- a/backend/fg/urls/admin.py +++ b/backend/fg/urls/admin.py @@ -1,7 +1,8 @@ from django.urls import path -from ..views.admin import FGAPI +from ..views.admin import FGAPI, FGUploadAPI urlpatterns = [ path('fg/', FGAPI.as_view(), name="fg_admin_api"), + path('fg/upload/', FGUploadAPI.as_view(), name="fg_upload_api") ] \ No newline at end of file diff --git a/backend/fg/views/admin.py b/backend/fg/views/admin.py index b3d1420..d1b0f29 100644 --- a/backend/fg/views/admin.py +++ b/backend/fg/views/admin.py @@ -1,12 +1,19 @@ from ..models import FG -from ..serializers import (FGSerializer, CreateFGSerializer) +from ..serializers import (FGSerializer, CreateFGSerializer, FGFileUploadSerializer) +from lc.serializers import LCSerializer +from lc.models import LC + 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 rest_framework.exceptions import ParseError +from django.contrib.auth.hashers import make_password class FGAPI(APIView): def get(self, request): - if request.user.role != ADMIN: + if request.user.role != 'Admin': return Response({"error": True, "data": "Admin role required"}) fg_id = request.GET.get("id") error = False @@ -43,17 +50,55 @@ def post(self, request): student_id=data["student_id"] ) else: - fg = FG.objects.create_user(name=data["name"], + fg = FG.objects.create(name=data["name"], student_id=data["student_id"], campus=data["campus"] ) - # try: - # with transaction.atomic(): - # Freshman.objects.bulk_create() - # except IntegrityError as e: - # return self.Response({"data": str(e).split("\n")[1]}) - data = FGSerializer(fg).data return Response({"error": False, "data": data}) + +class FGUploadAPI(APIView): + def post(self, request): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + + serializer = FGFileUploadSerializer(data = request.data) + serializer.is_valid(raise_exception=True) + file = serializer.validated_data['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 (LCXX), 자과fg, 자과 fg 학번, 자과 fg role, 사과fg, 사과 fg 학번, 사과 fg role, 날짜 + lc_list = [] + + # 기존 data 모두 삭제 + LC.objects.all().delete() + FG.objects.exclude(id=1).delete() + + for data in data_list: + if FG.objects.filter(student_id = data[2]).exists(): + fg_n = FG.objects.get(student_id = data[2]) + else: + fg_n = FG.objects.create(name=data[1], + student_id=data[2], + campus="n", + password=make_password(str(data[2]))) + + if FG.objects.filter(student_id = data[5]).exists(): + fg_s = FG.objects.get(student_id = data[5]) + else: + fg_s = FG.objects.create(name=data[4], + student_id=data[5], + campus="s", + password=make_password(str(data[5]))) + + lc_list.append(LC.objects.create(name=data[0], fg_n_id=fg_n, + fg_s_id=fg_s, total=0, schedule=data[7])) + + lc_success_data = LCSerializer(lc_list, many=True).data + return Response({"error":False, "data": lc_success_data}) \ No newline at end of file diff --git a/backend/fnet/dev_settings.py b/backend/fnet/dev_settings.py index 672fdfb..daf9815 100644 --- a/backend/fnet/dev_settings.py +++ b/backend/fnet/dev_settings.py @@ -9,12 +9,14 @@ 'USER': 'fnet', 'PASSWORD': 'fnet', 'HOST': '127.0.0.1', - 'PORT': 3306, + 'PORT': 1398, } } DEBUG = True +SECRET_KEY = 'django-insecure-8l87s(fcz$l*hni4bs-9)rpdhzrab3=sc(fgz=b5ecau&1k0j9' + ALLOWED_HOSTS = ["*"] DATA_DIR = f"{BASE_DIR}/data/django" \ No newline at end of file diff --git a/backend/fnet/production_settings.py b/backend/fnet/production_settings.py index 8907785..2d7154d 100644 --- a/backend/fnet/production_settings.py +++ b/backend/fnet/production_settings.py @@ -13,6 +13,8 @@ DEBUG = False +SECRET_KEY = get_env("DJANGO_SECRET_KEY") + ALLOWED_HOSTS = ["*"] DATA_DIR = "/data" \ No newline at end of file diff --git a/backend/fnet/settings.py b/backend/fnet/settings.py index cf950e4..3cdcc68 100644 --- a/backend/fnet/settings.py +++ b/backend/fnet/settings.py @@ -27,7 +27,7 @@ # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-8l87s(fcz$l*hni4bs-9)rpdhzrab3=sc(fgz=b5ecau&1k0j9' +# SECRET_KEY = 'django-insecure-8l87s(fcz$l*hni4bs-9)rpdhzrab3=sc(fgz=b5ecau&1k0j9' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -38,8 +38,8 @@ CORS_ALLOW_CREDENTIALS = True CSRF_TRUSTED_ORIGINS = ["127.0.0.1:8000", "localhost:8000"] CORS_ORIGIN_WHITELIST = ( - 'http://localhost:3000', - 'http://127.0.0.1:8000', + 'https://localhost:8000', + 'https://127.0.0.1:8000', ) CORS_ALLOW_HEADERS = ( 'access-control-allow-credentials', @@ -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', @@ -150,7 +152,7 @@ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Asia/Seoul' USE_I18N = True diff --git a/backend/fnet/urls.py b/backend/fnet/urls.py index 3908b63..b49f56e 100644 --- a/backend/fnet/urls.py +++ b/backend/fnet/urls.py @@ -20,12 +20,11 @@ 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('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('freshman.urls.user')), + path('api/admin/', include('freshman.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/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/migrations/0001_initial.py b/backend/freshman/migrations/0001_initial.py new file mode 100644 index 0000000..deebbd9 --- /dev/null +++ b/backend/freshman/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.10 on 2023-02-15 19:04 + +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', '자연과학계열'), ('EN', '공학계열'), ('SS', '사회과학계열'), ('HS', '인문과학계열')], 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..61048ac 100644 --- a/backend/freshman/models.py +++ b/backend/freshman/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, 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, '자연과학계열'), + (ENGINEERING, '공학계열'), + (SOCIAL_SCIENCE, '사회과학계열'), + (HUMANITIES_SCIENCE, '인문과학계열'), + ] + + 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/freshman/serializers.py b/backend/freshman/serializers.py new file mode 100644 index 0000000..978c0b1 --- /dev/null +++ b/backend/freshman/serializers.py @@ -0,0 +1,74 @@ +from django.db import models +from rest_framework import serializers +from .models import Freshman + +class FreshmanSerializer(serializers.ModelSerializer): + id = serializers.IntegerField() + name = serializers.CharField() + phone_number = serializers.SerializerMethodField() + lc = serializers.SerializerMethodField() + department = serializers.SerializerMethodField() + register = serializers.BooleanField() + # register = serializers.SerializerMethodField() + + class Meta: + model = Freshman + fields = ['id', 'name','phone_number', 'lc', 'register', 'department'] + + def get_phone_number(self, obj): + return obj.phone_number[9:13] + + 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 FreshmanLCSerializer(serializers.ModelSerializer): + name = serializers.CharField() + lc = serializers.SerializerMethodField() + department = serializers.SerializerMethodField() + register = serializers.SerializerMethodField() + + class Meta: + model = Freshman + fields = ['name', 'department', 'lc', 'register'] + + 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..4d076f5 100644 --- a/backend/freshman/urls/user.py +++ b/backend/freshman/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 getLcMemberListAPI, getLCCountInfoAPI + +urlpatterns = [ + path("freshman/", getLcMemberListAPI), + path("freshman/count/", getLCCountInfoAPI) +] \ No newline at end of file diff --git a/backend/freshman/views/admin.py b/backend/freshman/views/admin.py index e69de29..86e288e 100644 --- a/backend/freshman/views/admin.py +++ b/backend/freshman/views/admin.py @@ -0,0 +1,80 @@ +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 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): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + + 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): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + + serializer = FreshmanFileUploadSerializer(data = request.data) + serializer.is_valid(raise_exception=True) + file = serializer.validated_data['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 = [] + 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: + # raise ParseError("LC does not exist. Check your file again") + return Response({"error": True, "data": "LC does not exist. Check your file again"}) + + freshman_list.append(Freshman(lc=lc, name=data[0], department=department_dict[data[3]], phone_number=data[1], register=False)) + + # 기존 data 모두 삭제 + Freshman.objects.all().delete() + + try: + with transaction.atomic(): + Freshman.objects.bulk_create(freshman_list) + except IntegrityError as e: + raise ParseError({"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): + 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) + + 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..3027384 100644 --- a/backend/freshman/views/user.py +++ b/backend/freshman/views/user.py @@ -0,0 +1,58 @@ +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 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_name = request.GET.get("lc") + if not lc_name: + return Response({"error": True, "data": "LC is required"}) + + try: + lc = 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 + + return Response({"error": False, "data": data}) + +@api_view(['GET']) +def getLCCountInfoAPI(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_name = request.GET.get("lc") + if not lc_name: + return Response({"error": True, "data": "LC is required"}) + + try: + lc = 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) + total = queryset.count() + register_total = queryset.filter(register=True) + data = { "total" : total, "register" : register_total.count() } + + return Response({"error": False, "data": data}) \ No newline at end of file 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 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 diff --git a/backend/lc/migrations/0001_initial.py b/backend/lc/migrations/0001_initial.py new file mode 100644 index 0000000..3a0c445 --- /dev/null +++ b/backend/lc/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.10 on 2023-02-15 18:51 + +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 71a8362..5daa608 100644 --- a/backend/lc/models.py +++ b/backend/lc/models.py @@ -1,3 +1,19 @@ 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, DateField +from datetime import datetime # 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 = DateField(default=datetime.now) + day = IntegerField() diff --git a/backend/lc/serializers.py b/backend/lc/serializers.py new file mode 100644 index 0000000..0bbb4c4 --- /dev/null +++ b/backend/lc/serializers.py @@ -0,0 +1,22 @@ +from django.db import models +from .models import Schedule, LC +from rest_framework import serializers +from fg.serializers import FGSerializer + +class CreateScheduleSerializer(serializers.Serializer): + date = serializers.DateField() + day = serializers.IntegerField() + +class LCSerializer(serializers.ModelSerializer): + fg_n_id = FGSerializer() + fg_s_id = FGSerializer() + class Meta: + model = LC + fields = "__all__" + + +class ScheduleSerializer(serializers.ModelSerializer): + class Meta: + model = Schedule + fields = "__all__" + \ No newline at end of file diff --git a/backend/lc/urls/admin.py b/backend/lc/urls/admin.py index 1500611..32a17da 100644 --- a/backend/lc/urls/admin.py +++ b/backend/lc/urls/admin.py @@ -1 +1,7 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.admin import ScheduleAPI, LCAPI + +urlpatterns = [ + path('schedule/', ScheduleAPI.as_view(), name="schedule_api"), + path('LC/', LCAPI.as_view(), name="lc_api") +] \ No newline at end of file diff --git a/backend/lc/urls/user.py b/backend/lc/urls/user.py index 1500611..a7f8c0d 100644 --- a/backend/lc/urls/user.py +++ b/backend/lc/urls/user.py @@ -1 +1,8 @@ -from django.urls import path \ No newline at end of file +from django.urls import path +from ..views.user import TodayLCAPI, LCAPI, ScheduleAPI + +urlpatterns = [ + path('schedule/', ScheduleAPI.as_view(), name="schedule_api"), + path('LC/', LCAPI.as_view(), name="LC_api"), + path('TodayLC/', 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 e69de29..30cbfb3 100644 --- a/backend/lc/views/admin.py +++ b/backend/lc/views/admin.py @@ -0,0 +1,47 @@ +from ..models import LC, Schedule +from ..serializers import (CreateScheduleSerializer, ScheduleSerializer, LCSerializer) +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 LCAPI(APIView): + def get(self, request): + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + error = False + lc = LC.objects.all().order_by("id") + return Response({"error": error, "data": LCSerializer(lc, many=True).data}) + +class ScheduleAPI(APIView): + def post(self, request): + print(request.user.role) + if request.user.role != "Admin": + return Response({"error": True, "data": "Admin role required"}) + serializer = CreateScheduleSerializer(request.data) + data = serializer.data + + old = Schedule.objects.filter(day=data["day"]) + old.delete() + + schedule = Schedule.objects.create(date=dateutil.parser.parse(data["date"]).date(), day = data["day"]) + return Response({"error":False, "data": ScheduleSerializer(schedule).data}) + + def put(self, request): + data = Schedule.objects.filter() + + if len(data) > 0: + data.delete() + # if request.user.role != ADMIN:1 + # return Response({"error":True, "data":"not Admin"}) + data = request.data["datelist"] + for d in data: + serializer = CreateScheduleSerializer(d) + n = 1 + for d in data: + 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..6623a14 100644 --- a/backend/lc/views/user.py +++ b/backend/lc/views/user.py @@ -0,0 +1,55 @@ +from ..models import LC, Schedule +from ..serializers import (CreateScheduleSerializer, ScheduleSerializer, LCSerializer) +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.all().order_by("day") + 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: + return Response({"error":False, "data": None}) + if user_campus == "n": + try: + todayLC = LC.objects.filter(schedule = schedule.day, fg_n_id = request.user) + except LC.DoesNotExist: + return Response({"error":False, "data": None}) + else: + try: + todayLC = LC.objects.filter(schedule = schedule.day, fg_s_id = request.user) + except LC.DoesNotExist: + return Response({"error":False, "data": None}) + return Response({"error": False, "data": LCSerializer(todayLC, many=True).data}) + +class LCAPI(APIView): + def get(self, request): + user_campus = request.user.campus + + if user_campus == "n": + try: + todayLC = LC.objects.filter(fg_n_id = request.user) + data = LCSerializer(todayLC, many=True).data + + except LC.DoesNotExist: + return Response({"error":False, "data": None}) + else: + try: + todayLC = LC.objects.filter(fg_s_id = request.user) + data = LCSerializer(todayLC, many=True).data + except LC.DoesNotExist: + return Response({"error":False, "data": None}) + return Response({"error": False, "data": data}) 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/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/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 71a8362..38b8a94 100644 --- a/backend/todo/models.py +++ b/backend/todo/models.py @@ -1,3 +1,24 @@ 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, DateTimeField, AutoField, IntegerField, BooleanField -# Create your models here. +class Todo(models.Model): + 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() + + 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, 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 new file mode 100644 index 0000000..7835733 --- /dev/null +++ b/backend/todo/serializers.py @@ -0,0 +1,39 @@ +from django.db import models +from .models import Todo, Todo_check +from rest_framework import serializers + +class CreateTodoSerializer(serializers.Serializer): + content = serializers.CharField() + common = serializers.BooleanField() + +class EditTodoSerializer(serializers.Serializer): + id = serializers.IntegerField() + content = serializers.CharField() + +class TodoSerializer(serializers.ModelSerializer): + class Meta: + model = Todo + fields = "__all__" + + +class TodoPlusCheckSerializer(serializers.ModelSerializer): + todo_id = TodoSerializer() + class Meta: + model = Todo_check + 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/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 1500611..57ce4a9 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('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/backend/todo/views/user.py b/backend/todo/views/user.py index e69de29..1d84ccd 100644 --- a/backend/todo/views/user.py +++ b/backend/todo/views/user.py @@ -0,0 +1,108 @@ +from ..models import Todo, Todo_check +from ..serializers import TodoPlusCheckSerializer,CreateTodoSerializer, TodoSerializer, EditTodoSerializer, TodoCheckSerializer, TodoAllSerializer +from fg.models import FG +from itertools import chain +from rest_framework.response import Response +from rest_framework.views import APIView + + +class TodoAPI(APIView): + def get(self, request): + get_common = True if request.GET.get("common") == 'true' else False + try: + if get_common: + todos = TodoPlusCheckSerializer(Todo_check.objects.filter(fg_id = request.user),many= True).data + result = [] + for todo in todos: + if todo["todo_id"]["common"] == get_common: + result.append(todo) + else: + todos = TodoPlusCheckSerializer(Todo_check.objects.filter(fg_id = request.user),many= True).data + result = [] + for todo in todos: + if todo["todo_id"]["common"]== get_common: + result.append(todo) + except Todo.DoesNotExist: + msg = "Todo does not exist" + return Response({"error": True, "data": msg}) + return Response({"error": False, "data" : result}) + + def post(self, request): + 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.all() + except FG.DoesNotExist: + msg = "FG does not exist" + return Response({"error": True, "data": msg}) + 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): + 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.content = data["content"] + todo.save() + 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): + 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: + 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": TodoCheckSerializer(todo_check).data}) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index acae2d2..785bd7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,25 +8,28 @@ services: volumes: - ./data/django:/data ports: - - "8000:8000" + - "80:80" + # - "8000:8000" + # - "8080:8080" depends_on: - fnet-mysql environment: - - MYSQL_DATABASE=fnet - - MYSQL_USER=fnet - - MYSQL_PASSWORD=fnet - - DJANGO_SUPERUSER_NAME=root - - DJANGO_SUPERUSER_PASSWORD=rootroot + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} + - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY} + - DJANGO_SUPERUSER_NAME=${DJANGO_SUPERUSER_NAME} + - DJANGO_SUPERUSER_PASSWORD=${DJANGO_SUPERUSER_PASSWORD} fnet-mysql: image: mysql container_name: fnet-mysql - ports: - - "3306:3306" + # ports: + # - "3306:3306" volumes: - ./data/mysql:/var/lib/mysql environment: - - MYSQL_ROOT_PASSWORD=root - - MYSQL_DATABASE=fnet - - MYSQL_USER=fnet - - MYSQL_PASSWORD=fnet + - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} + - MYSQL_DATABASE=${MYSQL_DATABASE} + - MYSQL_USER=${MYSQL_USER} + - MYSQL_PASSWORD=${MYSQL_PASSWORD} diff --git a/frontend/public/index.html b/frontend/public/index.html index babe619..209ce92 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -7,6 +7,7 @@ +