Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ staticfiles/
venv/
.env
.env.*
test/

# Node / Vite
node_modules/
Expand Down
120 changes: 86 additions & 34 deletions ai/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.2.6 on 2025-09-25 13:21
# Generated by Django 5.2.6 on 2025-09-25 21:32

import django.db.models.deletion
import uuid
Expand All @@ -9,55 +9,107 @@ class Migration(migrations.Migration):

initial = True

dependencies = [
('organizations', '0001_initial'),
]
dependencies = []

operations = [
migrations.CreateModel(
name='MotionType',
name="MotionType",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('motion_name', models.CharField(max_length=100, unique=True)),
('description', models.TextField(blank=True)),
('max_dtw_distance', models.FloatField(default=1000.0, help_text='점수 정규화를 위한 최대 DTW 거리')),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("motion_name", models.CharField(max_length=100, unique=True)),
("description", models.TextField(blank=True)),
(
"max_dtw_distance",
models.FloatField(
default=1000.0, help_text="점수 정규화를 위한 최대 DTW 거리"
),
),
],
),
migrations.CreateModel(
name='MotionRecording',
name="SensorDevice",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('recorded_at', models.DateTimeField(auto_now_add=True)),
('data_frames', models.IntegerField()),
('score_category', models.CharField(choices=[('reference', '모범 동작'), ('zero_score', '0점 동작')], max_length=20)),
('sensor_data_json', models.JSONField()),
('motion_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recordings', to='ai.motiontype')),
(
"id",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
),
),
("device_uid", models.CharField(max_length=120)),
("name", models.CharField(blank=True, max_length=200)),
(
"api_key",
models.CharField(
blank=True, db_index=True, max_length=64, unique=True
),
),
("is_active", models.BooleanField(default=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
],
options={
"ordering": ["-created_at", "-id"],
},
),
migrations.CreateModel(
name='UserRecording',
name="UserRecording",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('score', models.FloatField()),
('recorded_at', models.DateTimeField(auto_now_add=True)),
('motion_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ai.motiontype')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='motion_recordings', to='organizations.employee')),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("score", models.FloatField()),
("recorded_at", models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='SensorDevice',
name="MotionRecording",
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('device_uid', models.CharField(max_length=120)),
('name', models.CharField(blank=True, max_length=200)),
('api_key', models.CharField(blank=True, db_index=True, max_length=64, unique=True)),
('is_active', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sensor_devices', to='organizations.company')),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("recorded_at", models.DateTimeField(auto_now_add=True)),
("data_frames", models.IntegerField()),
(
"score_category",
models.CharField(
choices=[
("reference", "모범 동작"),
("zero_score", "0점 동작"),
],
max_length=20,
),
),
("sensor_data_json", models.JSONField()),
(
"motion_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="recordings",
to="ai.motiontype",
),
),
],
options={
'ordering': ['-created_at', '-id'],
'constraints': [models.UniqueConstraint(fields=('company', 'device_uid'), name='uq_ai_sensor_device_company_uid')],
},
),
]
50 changes: 50 additions & 0 deletions ai/migrations/0002_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 5.2.6 on 2025-09-25 21:32

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


class Migration(migrations.Migration):

initial = True

dependencies = [
("ai", "0001_initial"),
("organizations", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.AddField(
model_name="sensordevice",
name="company",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="sensor_devices",
to=settings.AUTH_USER_MODEL,
),
),
migrations.AddField(
model_name="userrecording",
name="motion_type",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="ai.motiontype"
),
),
migrations.AddField(
model_name="userrecording",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="motion_recordings",
to="organizations.employee",
),
),
migrations.AddConstraint(
model_name="sensordevice",
constraint=models.UniqueConstraint(
fields=("company", "device_uid"), name="uq_ai_sensor_device_company_uid"
),
),
]
2 changes: 1 addition & 1 deletion ai/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class UnifiedEvaluationView(APIView):
Unity로부터 센서 데이터를 받아 즉시 평가하고 결과를 반환하는 API
POST /api/ai/evaluate/
"""
permission_classes = [HasValidAPIKey] # 커스텀 권한 클래스로 교체
# permission_classes = [HasValidAPIKey] # 커스텀 권한 클래스로 교체

def _try_get_employee(self, emp_no: str, company: Company):
try:
Expand Down
6 changes: 4 additions & 2 deletions back/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
'NAME': env("DATABASE_NAME"),
'USER': env("DATABASE_USER"),
'PASSWORD': env("DATABASE_PASSWORD"),
'HOST': env("DATABASE_HOST"),
'HOST': 'localhost',
'PORT': '3306',
}
}
Expand Down Expand Up @@ -162,4 +162,6 @@
# 데이터 변경 요청시 허용하는 출처 목록
CSRF_TRUSTED_ORIGINS = [
"http://localhost:5173",
]
]

AUTH_USER_MODEL = "organizations.Company"
26 changes: 15 additions & 11 deletions courses/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
# Generated by Django 5.2.6 on 2025-09-25 13:18
# Generated by Django 5.2.6 on 2025-09-25 21:32

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
('organizations', '0001_initial'),
]
dependencies = []

operations = [
migrations.CreateModel(
name='Course',
name="Course",
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('description', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='courses', to='organizations.company')),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=255)),
("description", models.TextField(blank=True, null=True)),
("created_at", models.DateTimeField(auto_now_add=True)),
],
),
]
27 changes: 27 additions & 0 deletions courses/migrations/0002_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 5.2.6 on 2025-09-25 21:32

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


class Migration(migrations.Migration):

initial = True

dependencies = [
("courses", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.AddField(
model_name="course",
name="company",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="courses",
to=settings.AUTH_USER_MODEL,
),
),
]
17 changes: 15 additions & 2 deletions courses/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from .models import Course
from .serializers import CourseSerializer

class CourseListCreateAPI(generics.ListCreateAPIView):
queryset = Course.objects.all()
serializer_class = CourseSerializer
permission_classes = [IsAuthenticated]

def get_queryset(self):
# JWT 토큰의 user(Company) 정보를 기반으로 강좌를 필터링합니다.
return Course.objects.filter(company=self.request.user)

def perform_create(self, serializer):
# 강좌 생성 시, 요청한 user(Company) 소속으로 자동 설정합니다.
serializer.save(company=self.request.user)


class CourseDetailAPI(generics.RetrieveUpdateDestroyAPIView):
queryset = Course.objects.all()
serializer_class = CourseSerializer
permission_classes = [IsAuthenticated]

def get_queryset(self):
# JWT 토큰의 user(Company) 정보를 기반으로 강좌를 필터링합니다.
return Course.objects.filter(company=self.request.user)
40 changes: 40 additions & 0 deletions enrollments/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 5.2.6 on 2025-09-25 21:32

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
("courses", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="Enrollment",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("enrolled_at", models.DateTimeField(auto_now_add=True)),
("status", models.CharField(default="enrolled", max_length=20)),
(
"course",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="courses",
to="courses.course",
),
),
],
),
]
26 changes: 26 additions & 0 deletions enrollments/migrations/0002_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 5.2.6 on 2025-09-25 21:32

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
("enrollments", "0001_initial"),
("organizations", "0001_initial"),
]

operations = [
migrations.AddField(
model_name="enrollment",
name="employee",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="enrollments",
to="organizations.employee",
),
),
]
Loading