Compare commits
1 Commits
5c03032921
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 813e3d619c |
@@ -1,9 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
coverage
|
|
||||||
.git
|
|
||||||
.env
|
|
||||||
npm-debug.log*
|
|
||||||
Dockerfile
|
|
||||||
docker-compose.yml
|
|
||||||
README.md
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
NODE_ENV=development
|
|
||||||
PORT=4400
|
|
||||||
CORS_ORIGIN=http://localhost:5173,http://localhost:5174,http://localhost:5175,http://localhost:5176
|
|
||||||
DATABASE_URL=postgresql://lms:future1234!%40@localhost:5432/lms?schema=public
|
|
||||||
AUTH_SERVER_URL=http://localhost:4100
|
|
||||||
GITEA_BASE_URL=http://localhost:3000
|
|
||||||
GITEA_ADMIN_TOKEN=
|
|
||||||
GITEA_STUDENT_OWNER=student
|
|
||||||
PLATFORM_GATEWAY_URL=http://localhost:4500
|
|
||||||
PLATFORM_GATEWAY_TOKEN=change-me
|
|
||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
coverage
|
|
||||||
*.tsbuildinfo
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "all"
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
FROM node:22-alpine AS deps
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm ci
|
|
||||||
|
|
||||||
FROM node:22-alpine AS build
|
|
||||||
WORKDIR /app
|
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm ci
|
|
||||||
COPY . .
|
|
||||||
RUN npm run build
|
|
||||||
RUN npm prune --omit=dev
|
|
||||||
|
|
||||||
FROM node:22-alpine AS runtime
|
|
||||||
WORKDIR /app
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
COPY --from=build /app/node_modules ./node_modules
|
|
||||||
COPY --from=build /app/dist ./dist
|
|
||||||
COPY --from=build /app/prisma ./prisma
|
|
||||||
COPY package*.json ./
|
|
||||||
EXPOSE 4400
|
|
||||||
CMD ["node", "dist/main.js"]
|
|
||||||
@@ -1,40 +1,3 @@
|
|||||||
# lms-be
|
# lms-be
|
||||||
|
|
||||||
Nest.js backend API for the LMS Electron frontend.
|
lms 백엔드
|
||||||
|
|
||||||
## Local development
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
cp .env.example .env
|
|
||||||
npm run prisma:generate
|
|
||||||
npm run start:dev
|
|
||||||
```
|
|
||||||
|
|
||||||
The API listens on `http://localhost:4400`.
|
|
||||||
|
|
||||||
- `GET /health`
|
|
||||||
- `GET /health/db`
|
|
||||||
- `GET /api`
|
|
||||||
|
|
||||||
## Database
|
|
||||||
|
|
||||||
Local PostgreSQL is included in Docker Compose.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose up -d postgres
|
|
||||||
npm run prisma:migrate
|
|
||||||
npm run start:dev
|
|
||||||
```
|
|
||||||
|
|
||||||
For a separated database server, set `DATABASE_URL` to the remote PostgreSQL host:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
DATABASE_URL=postgresql://USER:PASSWORD@DB_HOST:5432/DB_NAME?schema=public
|
|
||||||
```
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose up --build
|
|
||||||
```
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
services:
|
|
||||||
lms-be:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
container_name: lms-be
|
|
||||||
environment:
|
|
||||||
NODE_ENV: production
|
|
||||||
PORT: 4400
|
|
||||||
CORS_ORIGIN: ${CORS_ORIGIN:-*}
|
|
||||||
DATABASE_URL: ${DATABASE_URL:-postgresql://lms:future1234!%40@postgres:5432/lms?schema=public}
|
|
||||||
AUTH_SERVER_URL: ${AUTH_SERVER_URL:-http://host.docker.internal:4100}
|
|
||||||
ports:
|
|
||||||
- "${PORT:-4400}:4400"
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:4400/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 3
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
postgres:
|
|
||||||
image: postgres:17-alpine
|
|
||||||
container_name: lms-postgres
|
|
||||||
command: postgres -c timezone=Asia/Seoul
|
|
||||||
environment:
|
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-lms}
|
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-lms}
|
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-future1234!@}
|
|
||||||
ports:
|
|
||||||
- "${POSTGRES_PORT:-5432}:5432"
|
|
||||||
volumes:
|
|
||||||
- postgres_data:/var/lib/postgresql/data
|
|
||||||
restart: unless-stopped
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-lms} -d ${POSTGRES_DB:-lms}"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
postgres_data:
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import eslint from '@eslint/js';
|
|
||||||
import prettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
||||||
import globals from 'globals';
|
|
||||||
import tseslint from 'typescript-eslint';
|
|
||||||
|
|
||||||
export default tseslint.config(
|
|
||||||
{
|
|
||||||
ignores: ['eslint.config.mjs', 'dist/**', 'node_modules/**', 'coverage/**'],
|
|
||||||
},
|
|
||||||
eslint.configs.recommended,
|
|
||||||
...tseslint.configs.recommendedTypeChecked,
|
|
||||||
prettierRecommended,
|
|
||||||
{
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.node,
|
|
||||||
...globals.jest,
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
projectService: true,
|
|
||||||
tsconfigRootDir: import.meta.dirname,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.spec.ts', 'test/**/*.ts'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-unsafe-argument': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-call': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: Middleware
|
|
||||||
metadata:
|
|
||||||
name: lms-api-strip
|
|
||||||
spec:
|
|
||||||
stripPrefix:
|
|
||||||
prefixes:
|
|
||||||
- /lms-api
|
|
||||||
---
|
|
||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: IngressRoute
|
|
||||||
metadata:
|
|
||||||
name: lms-api
|
|
||||||
spec:
|
|
||||||
entryPoints:
|
|
||||||
- web
|
|
||||||
routes:
|
|
||||||
- match: Host(`workspace.autonomystudio.net`) && PathPrefix(`/lms-api`)
|
|
||||||
kind: Rule
|
|
||||||
middlewares:
|
|
||||||
- name: lms-api-strip
|
|
||||||
services:
|
|
||||||
- name: lms-be
|
|
||||||
port: 4400
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/nest-cli",
|
|
||||||
"collection": "@nestjs/schematics",
|
|
||||||
"sourceRoot": "src",
|
|
||||||
"compilerOptions": {
|
|
||||||
"deleteOutDir": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Generated
-11082
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "lms-be",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "Backend API for LMS",
|
|
||||||
"license": "UNLICENSED",
|
|
||||||
"scripts": {
|
|
||||||
"build": "prisma generate && nest build",
|
|
||||||
"start": "nest start",
|
|
||||||
"start:dev": "nest start --watch",
|
|
||||||
"start:prod": "node dist/main.js",
|
|
||||||
"lint": "eslint \"{src,test}/**/*.ts\" --fix",
|
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
|
||||||
"prisma:generate": "prisma generate",
|
|
||||||
"prisma:migrate": "prisma migrate dev",
|
|
||||||
"prisma:deploy": "prisma migrate deploy",
|
|
||||||
"prisma:studio": "prisma studio",
|
|
||||||
"test": "jest",
|
|
||||||
"test:watch": "jest --watch",
|
|
||||||
"test:cov": "jest --coverage"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-sdk/client-s3": "^3.1081.0",
|
|
||||||
"@nestjs/common": "^11.1.24",
|
|
||||||
"@nestjs/config": "^4.0.4",
|
|
||||||
"@nestjs/core": "^11.1.24",
|
|
||||||
"@nestjs/platform-express": "^11.1.24",
|
|
||||||
"@nestjs/platform-socket.io": "^11.1.24",
|
|
||||||
"@nestjs/websockets": "^11.1.24",
|
|
||||||
"@prisma/client": "^6.19.3",
|
|
||||||
"class-transformer": "^0.5.1",
|
|
||||||
"class-validator": "^0.15.1",
|
|
||||||
"reflect-metadata": "^0.2.2",
|
|
||||||
"rxjs": "^7.8.2",
|
|
||||||
"socket.io": "^4.8.3"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/js": "^10.0.1",
|
|
||||||
"@nestjs/cli": "^11.0.21",
|
|
||||||
"@nestjs/schematics": "^11.1.0",
|
|
||||||
"@nestjs/testing": "^11.1.24",
|
|
||||||
"@types/express": "^5.0.6",
|
|
||||||
"@types/jest": "^30.0.0",
|
|
||||||
"@types/node": "^25.9.1",
|
|
||||||
"@types/supertest": "^7.2.0",
|
|
||||||
"eslint": "^10.4.1",
|
|
||||||
"eslint-config-prettier": "^10.1.8",
|
|
||||||
"eslint-plugin-prettier": "^5.5.6",
|
|
||||||
"globals": "^17.6.0",
|
|
||||||
"jest": "^30.4.2",
|
|
||||||
"prettier": "^3.8.3",
|
|
||||||
"prisma": "^6.19.3",
|
|
||||||
"source-map-support": "^0.5.21",
|
|
||||||
"supertest": "^7.2.2",
|
|
||||||
"ts-jest": "^29.4.11",
|
|
||||||
"ts-loader": "^9.6.0",
|
|
||||||
"ts-node": "^10.9.2",
|
|
||||||
"tsconfig-paths": "^4.2.0",
|
|
||||||
"typescript": "^6.0.3",
|
|
||||||
"typescript-eslint": "^8.60.0"
|
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"rootDir": ".",
|
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"src/**/*.(t|j)s"
|
|
||||||
],
|
|
||||||
"coverageDirectory": "coverage",
|
|
||||||
"testEnvironment": "node"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "users" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"email" TEXT NOT NULL,
|
|
||||||
"name" TEXT,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
-- CreateTable
|
|
||||||
CREATE TABLE "lms_notifications" (
|
|
||||||
"id" TEXT NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"body" TEXT NOT NULL,
|
|
||||||
"category" TEXT NOT NULL DEFAULT 'notice',
|
|
||||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "lms_notifications_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "lms_notifications_is_active_created_at_idx" ON "lms_notifications"("is_active", "created_at");
|
|
||||||
|
|
||||||
-- Seed
|
|
||||||
INSERT INTO "lms_notifications" ("id", "title", "body", "category", "created_at", "updated_at")
|
|
||||||
VALUES
|
|
||||||
('lms-notification-course-review', '신규 과제 검토 요청', '자율주행 인지 실습 과정에 제출된 과제 3건의 검토가 필요합니다.', 'assignment', '2026-06-02T00:00:00.000Z', CURRENT_TIMESTAMP),
|
|
||||||
('lms-notification-live-session', '라이브 세션 일정 안내', '오늘 15:00에 SLAM 기초 라이브 세션이 예정되어 있습니다.', 'schedule', '2026-06-01T00:00:00.000Z', CURRENT_TIMESTAMP),
|
|
||||||
('lms-notification-system-maintenance', '시스템 점검 안내', '6월 5일 02:00부터 03:00까지 LMS 점검이 진행됩니다.', 'system', '2026-05-31T00:00:00.000Z', CURRENT_TIMESTAMP);
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
-- AlterTable
|
|
||||||
ALTER TABLE "lms_notifications" ADD COLUMN "audience" TEXT NOT NULL DEFAULT 'all';
|
|
||||||
|
|
||||||
-- Existing admin messages are instructor-facing.
|
|
||||||
UPDATE "lms_notifications"
|
|
||||||
SET "audience" = 'instructor'
|
|
||||||
WHERE "id" IN (
|
|
||||||
'lms-notification-course-review',
|
|
||||||
'lms-notification-live-session',
|
|
||||||
'lms-notification-system-maintenance'
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Seed student messages.
|
|
||||||
INSERT INTO "lms_notifications" ("id", "title", "body", "category", "audience", "created_at", "updated_at")
|
|
||||||
VALUES
|
|
||||||
('lms-student-notification-assignment-due', '과제 제출 마감 안내', '인지 실습 리포트 제출 마감이 오늘 18:00까지입니다.', 'assignment', 'student', '2026-06-02T00:00:00.000Z', CURRENT_TIMESTAMP),
|
|
||||||
('lms-student-notification-new-lesson', '신규 강의 공개', '자율주행 센서 융합 기초 강의가 새로 공개되었습니다.', 'course', 'student', '2026-06-01T00:00:00.000Z', CURRENT_TIMESTAMP),
|
|
||||||
('lms-student-notification-session', '실습 세션 안내', '내일 10:00에 ROS2 실습 세션이 예정되어 있습니다.', 'schedule', 'student', '2026-05-31T00:00:00.000Z', CURRENT_TIMESTAMP);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE INDEX "lms_notifications_audience_is_active_created_at_idx" ON "lms_notifications"("audience", "is_active", "created_at");
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
COMMENT ON TABLE "users" IS 'LMS 내부 사용자 기본 정보';
|
|
||||||
COMMENT ON COLUMN "users"."id" IS 'LMS 사용자 고유 ID';
|
|
||||||
COMMENT ON COLUMN "users"."email" IS '사용자 이메일';
|
|
||||||
COMMENT ON COLUMN "users"."name" IS '사용자 이름';
|
|
||||||
COMMENT ON COLUMN "users"."createdAt" IS '사용자 생성 일시';
|
|
||||||
COMMENT ON COLUMN "users"."updatedAt" IS '사용자 수정 일시';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "lms_notifications" IS 'LMS 학생/강사/전체 대상 알림 메시지';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."id" IS 'LMS 알림 고유 ID';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."title" IS '알림 제목';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."body" IS '알림 본문';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."category" IS '알림 분류';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."audience" IS '알림 대상. student, instructor, all 등을 사용';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."is_active" IS '알림 활성 여부';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."created_at" IS '알림 생성 일시';
|
|
||||||
COMMENT ON COLUMN "lms_notifications"."updated_at" IS '알림 수정 일시';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
||||||
|
|
||||||
UPDATE "users"
|
|
||||||
SET "id" = gen_random_uuid()::text
|
|
||||||
WHERE "id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
|
|
||||||
|
|
||||||
UPDATE "lms_notifications"
|
|
||||||
SET "id" = gen_random_uuid()::text
|
|
||||||
WHERE "id" !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
|
|
||||||
|
|
||||||
ALTER TABLE "users"
|
|
||||||
ALTER COLUMN "id" TYPE uuid USING "id"::uuid;
|
|
||||||
|
|
||||||
ALTER TABLE "lms_notifications"
|
|
||||||
ALTER COLUMN "id" TYPE uuid USING "id"::uuid;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
ALTER TABLE "users"
|
|
||||||
ADD COLUMN "auth_user_id" uuid;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "users_auth_user_id_key" ON "users"("auth_user_id");
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "users"."auth_user_id" IS '중앙 auth DB users.id와 매핑되는 사용자 ID';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS "lms_notifications";
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
CREATE TABLE "courses" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"slug" TEXT NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"subtitle" TEXT,
|
|
||||||
"description" TEXT,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"teacher_id" UUID NOT NULL,
|
|
||||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "courses_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "course_weeks" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"tags" TEXT[] NOT NULL,
|
|
||||||
"progress" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"state" TEXT NOT NULL,
|
|
||||||
"duration" TEXT NOT NULL,
|
|
||||||
"github" TEXT NOT NULL,
|
|
||||||
"overview" TEXT NOT NULL,
|
|
||||||
"deliverable" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "course_weeks_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "course_week_topics" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"detail" TEXT NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "course_week_topics_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "course_week_practices" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"description" TEXT NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "course_week_practices_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "courses_slug_key" ON "courses"("slug");
|
|
||||||
CREATE INDEX "courses_is_active_idx" ON "courses"("is_active");
|
|
||||||
CREATE INDEX "courses_teacher_id_idx" ON "courses"("teacher_id");
|
|
||||||
CREATE UNIQUE INDEX "course_weeks_course_id_week_key" ON "course_weeks"("course_id", "week");
|
|
||||||
CREATE INDEX "course_weeks_course_id_idx" ON "course_weeks"("course_id");
|
|
||||||
CREATE UNIQUE INDEX "course_week_topics_week_id_sort_order_key" ON "course_week_topics"("week_id", "sort_order");
|
|
||||||
CREATE INDEX "course_week_topics_week_id_idx" ON "course_week_topics"("week_id");
|
|
||||||
CREATE UNIQUE INDEX "course_week_practices_week_id_sort_order_key" ON "course_week_practices"("week_id", "sort_order");
|
|
||||||
CREATE INDEX "course_week_practices_week_id_idx" ON "course_week_practices"("week_id");
|
|
||||||
|
|
||||||
ALTER TABLE "courses" ADD CONSTRAINT "courses_teacher_id_fkey" FOREIGN KEY ("teacher_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_weeks" ADD CONSTRAINT "course_weeks_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_week_topics" ADD CONSTRAINT "course_week_topics_week_id_fkey" FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_week_practices" ADD CONSTRAINT "course_week_practices_week_id_fkey" FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "courses" IS 'LMS 강의 마스터. 소속과 강사 기준으로 전체 강의 정보를 관리한다.';
|
|
||||||
COMMENT ON TABLE "course_weeks" IS '강의별 주차 커리큘럼 정보.';
|
|
||||||
COMMENT ON TABLE "course_week_topics" IS '주차별 학습 주제와 상세 설명.';
|
|
||||||
COMMENT ON TABLE "course_week_practices" IS '주차별 실습 내용.';
|
|
||||||
COMMENT ON COLUMN "courses"."organization_name" IS '강의를 등록한 소속명. 현재 퓨처드라이브 기준.';
|
|
||||||
COMMENT ON COLUMN "courses"."teacher_id" IS '강의를 등록한 강사 사용자 UUID.';
|
|
||||||
|
|
||||||
INSERT INTO "users" ("id", "email", "name", "createdAt", "updatedAt")
|
|
||||||
VALUES (gen_random_uuid(), 'teacher@futuredrive.net', '퓨처드라이브 강사', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
||||||
ON CONFLICT ("email") DO UPDATE
|
|
||||||
SET "name" = EXCLUDED."name",
|
|
||||||
"updatedAt" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH teacher AS (
|
|
||||||
SELECT "id" FROM "users" WHERE "email" = 'teacher@futuredrive.net'
|
|
||||||
)
|
|
||||||
INSERT INTO "courses" ("slug", "title", "subtitle", "description", "organization_name", "teacher_id", "updated_at")
|
|
||||||
SELECT
|
|
||||||
'futuredrive-autonomy-basic',
|
|
||||||
'단계별 자율주행 알고리즘 학습 과정',
|
|
||||||
'주차별 커리큘럼',
|
|
||||||
'ROS2, 센서 처리, SLAM, 객체 인식, 경로 계획까지 자율주행 알고리즘 개발 흐름을 주차별로 학습합니다.',
|
|
||||||
'퓨처드라이브',
|
|
||||||
teacher."id",
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM teacher
|
|
||||||
ON CONFLICT ("slug") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"subtitle" = EXCLUDED."subtitle",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"organization_name" = EXCLUDED."organization_name",
|
|
||||||
"teacher_id" = EXCLUDED."teacher_id",
|
|
||||||
"is_active" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH course AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
)
|
|
||||||
INSERT INTO "course_weeks" ("course_id", "week", "title", "tags", "progress", "state", "duration", "github", "overview", "deliverable", "updated_at")
|
|
||||||
SELECT course."id", data."week", data."title", data."tags", data."progress", data."state", data."duration", data."github", data."overview", data."deliverable", CURRENT_TIMESTAMP
|
|
||||||
FROM course
|
|
||||||
CROSS JOIN (
|
|
||||||
VALUES
|
|
||||||
(1, 'ROS2 기초 & 개발 환경 설정', ARRAY['시뮬레이션','완료']::TEXT[], 100, 'done', '8-10시간', '완료', 'ROS2 패키지 구조와 노드 실행 흐름을 이해하고, 실습 환경에서 기본 통신 구조를 직접 구성합니다.', '실습 노드 코드, 실행 로그, README 정리본을 GitHub에 제출'),
|
|
||||||
(2, '센서 데이터 수집 및 처리', ARRAY['MK-mini','완료']::TEXT[], 100, 'done', '8-10시간', '완료', '자율주행 시스템에서 사용하는 LiDAR와 카메라 데이터를 수집하고, ROS2 토픽 기반으로 전처리하는 흐름을 학습합니다.', '센서 토픽 분석표, 전처리 코드, 결과 캡처 이미지를 포함한 제출물'),
|
|
||||||
(3, 'SLAM & 지도 생성', ARRAY['MK-mini','진행중']::TEXT[], 65, 'current', '8-10시간', '미제출', 'Cartographer 기반 SLAM을 실행해 주행 환경 지도를 생성하고, 저장된 맵을 이용해 Localization 흐름을 검증합니다.', '생성된 map 파일, SLAM 실행 로그, Localization 검증 결과를 GitHub에 제출'),
|
|
||||||
(4, '객체 인식 & 딥러닝', ARRAY['시뮬레이션','진행예정']::TEXT[], 0, 'locked', '-', '진행예정', '카메라 기반 객체 인식 모델을 실행하고, 인식 결과를 주행 판단에 연결하는 과정을 학습합니다.', '모델 실행 로그와 인식 결과 리포트 제출'),
|
|
||||||
(5, '경로 계획 알고리즘', ARRAY['f1tenth','진행예정']::TEXT[], 0, 'locked', '-', '진행예정', '생성된 지도와 차량 상태를 기반으로 경로를 계획하고, 제어 명령으로 연결하는 전체 흐름을 정리합니다.', '경로 계획 설정값, 주행 결과 영상, 개선 사항 정리본 제출')
|
|
||||||
) AS data("week", "title", "tags", "progress", "state", "duration", "github", "overview", "deliverable")
|
|
||||||
ON CONFLICT ("course_id", "week") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"tags" = EXCLUDED."tags",
|
|
||||||
"progress" = EXCLUDED."progress",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"duration" = EXCLUDED."duration",
|
|
||||||
"github" = EXCLUDED."github",
|
|
||||||
"overview" = EXCLUDED."overview",
|
|
||||||
"deliverable" = EXCLUDED."deliverable",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH rows AS (
|
|
||||||
SELECT w."id" AS week_id, topic.*
|
|
||||||
FROM "course_weeks" w
|
|
||||||
JOIN "courses" c ON c."id" = w."course_id"
|
|
||||||
JOIN LATERAL (
|
|
||||||
VALUES
|
|
||||||
(1, 1, 'ROS2 노드 생성', '패키지 생성, 노드 실행, 로그 확인까지 ROS2 개발의 기본 흐름을 실습합니다.'),
|
|
||||||
(1, 2, '토픽 통신', 'Publisher와 Subscriber를 구성하고 메시지 타입, 주기, 토픽 이름을 확인합니다.'),
|
|
||||||
(1, 3, '파라미터 설정', '노드 파라미터를 외부에서 변경하고 launch 파일로 실행 구성을 정리합니다.'),
|
|
||||||
(2, 1, 'LiDAR 포인트클라우드', 'LiDAR 토픽을 확인하고 포인트클라우드 메시지 구조와 좌표 범위를 분석합니다.'),
|
|
||||||
(2, 2, '카메라 이미지 처리', '카메라 프레임을 수신해 기본 전처리와 시각화 결과를 확인합니다.'),
|
|
||||||
(2, 3, '센서 퓨전 기초', 'LiDAR와 카메라 데이터를 같은 주행 상황 기준으로 비교하고 센서 결합 조건을 정리합니다.'),
|
|
||||||
(3, 1, 'Cartographer SLAM', 'Cartographer 설정을 확인하고 센서 입력 조건에 맞춰 SLAM launch를 실행합니다.'),
|
|
||||||
(3, 2, '맵 저장/로드', '생성된 지도를 저장한 뒤 재로딩 과정에서 맵 파일과 설정값을 검증합니다.'),
|
|
||||||
(3, 3, 'Localization', '저장된 맵 위에서 차량 위치 추정이 안정적으로 동작하는지 확인합니다.'),
|
|
||||||
(4, 1, '객체 인식 모델 실행', '객체 인식 모델의 입력 이미지와 출력 박스 구조를 확인합니다.'),
|
|
||||||
(4, 2, 'ROS2 메시지 변환', '검출 결과를 ROS2 메시지로 변환하고 후속 노드에서 사용할 수 있게 정리합니다.'),
|
|
||||||
(4, 3, '인식 결과 시각화', '시뮬레이션 장면에서 인식 결과를 시각화하고 실패 케이스를 기록합니다.'),
|
|
||||||
(5, 1, '전역 경로 생성', '지도 기반 전역 경로를 생성하고 목적지까지의 기준 경로를 확인합니다.'),
|
|
||||||
(5, 2, '지역 경로 갱신', '주변 장애물과 차량 상태를 반영해 지역 경로를 갱신합니다.'),
|
|
||||||
(5, 3, '제어 명령 연결', '계획된 경로를 제어 명령으로 연결하고 주행 결과를 비교합니다.')
|
|
||||||
) AS topic(week_no, sort_order, title, detail) ON topic.week_no = w."week"
|
|
||||||
WHERE c."slug" = 'futuredrive-autonomy-basic'
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_topics" ("week_id", "sort_order", "title", "detail")
|
|
||||||
SELECT week_id, sort_order, title, detail FROM rows
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"detail" = EXCLUDED."detail";
|
|
||||||
|
|
||||||
WITH week3 AS (
|
|
||||||
SELECT w."id" AS week_id
|
|
||||||
FROM "course_weeks" w
|
|
||||||
JOIN "courses" c ON c."id" = w."course_id"
|
|
||||||
WHERE c."slug" = 'futuredrive-autonomy-basic' AND w."week" = 3
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_practices" ("week_id", "sort_order", "title", "description")
|
|
||||||
SELECT week3.week_id, data.sort_order, data.title, data.description
|
|
||||||
FROM week3
|
|
||||||
CROSS JOIN (
|
|
||||||
VALUES
|
|
||||||
(1, 'SLAM 개요', '동시적 위치추정 및 지도작성의 기본 개념'),
|
|
||||||
(2, 'Cartographer 설정', 'ROS2 Cartographer 패키지 설정 방법'),
|
|
||||||
(3, '실습: 맵 생성', 'LiDAR 데이터를 활용한 실시간 맵핑'),
|
|
||||||
(4, '위치 추정', '생성된 맵에서 로봇 위치 인식')
|
|
||||||
) AS data(sort_order, title, description)
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"description" = EXCLUDED."description";
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
CREATE TABLE "platforms" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"platform_key" TEXT NOT NULL,
|
|
||||||
"display_name" TEXT NOT NULL,
|
|
||||||
"description" TEXT NOT NULL,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"icon_key" TEXT NOT NULL,
|
|
||||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "platforms_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "course_platforms" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"platform_id" UUID NOT NULL,
|
|
||||||
"status" TEXT NOT NULL,
|
|
||||||
"action" TEXT NOT NULL,
|
|
||||||
"state" TEXT NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "course_platforms_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "course_student_progress" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"completed_weeks" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"total_weeks" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"learning_hours" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"commit_count" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"average_score" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "course_student_progress_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "platforms_platform_key_key" ON "platforms"("platform_key");
|
|
||||||
CREATE INDEX "platforms_organization_name_idx" ON "platforms"("organization_name");
|
|
||||||
CREATE INDEX "platforms_is_active_idx" ON "platforms"("is_active");
|
|
||||||
CREATE UNIQUE INDEX "course_platforms_course_id_platform_id_key" ON "course_platforms"("course_id", "platform_id");
|
|
||||||
CREATE INDEX "course_platforms_course_id_sort_order_idx" ON "course_platforms"("course_id", "sort_order");
|
|
||||||
CREATE INDEX "course_platforms_platform_id_idx" ON "course_platforms"("platform_id");
|
|
||||||
CREATE UNIQUE INDEX "course_student_progress_course_id_user_id_key" ON "course_student_progress"("course_id", "user_id");
|
|
||||||
CREATE INDEX "course_student_progress_user_id_idx" ON "course_student_progress"("user_id");
|
|
||||||
|
|
||||||
ALTER TABLE "course_platforms" ADD CONSTRAINT "course_platforms_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_platforms" ADD CONSTRAINT "course_platforms_platform_id_fkey" FOREIGN KEY ("platform_id") REFERENCES "platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_student_progress" ADD CONSTRAINT "course_student_progress_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_student_progress" ADD CONSTRAINT "course_student_progress_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "platforms" IS '최고관리자가 등록하는 실습 플랫폼 마스터. 학교/기관별 개별 장비를 구분한다.';
|
|
||||||
COMMENT ON TABLE "course_platforms" IS '강사가 강의에 사용할 플랫폼을 선택해 배정한 목록.';
|
|
||||||
COMMENT ON TABLE "course_student_progress" IS '학생별 강의 학습 현황 통계.';
|
|
||||||
|
|
||||||
INSERT INTO "users" ("id", "email", "name", "createdAt", "updatedAt")
|
|
||||||
VALUES (gen_random_uuid(), 'student@futuredrive.net', '퓨처드라이브 학생', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
||||||
ON CONFLICT ("email") DO UPDATE
|
|
||||||
SET "name" = EXCLUDED."name",
|
|
||||||
"updatedAt" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
INSERT INTO "platforms" ("platform_key", "display_name", "description", "organization_name", "icon_key", "updated_at")
|
|
||||||
VALUES
|
|
||||||
('futuredrive-local-pc', '로컬 PC', '내 PC에서 직접 개발 환경을 실행합니다.', '퓨처드라이브', 'laptop', CURRENT_TIMESTAMP),
|
|
||||||
('futuredrive-simulator-01', '시뮬레이터', '가상 환경에서 안전하게 테스트합니다.', '퓨처드라이브', 'monitor', CURRENT_TIMESTAMP),
|
|
||||||
('futuredrive-f1tenth-01', 'F1Tenth #01', '고속 주행 및 회피 알고리즘 실습 장비입니다.', '퓨처드라이브', 'car', CURRENT_TIMESTAMP),
|
|
||||||
('futuredrive-mkmini-02', 'MK-mini #02', '기초 SLAM 및 자율주행 실습 장비입니다.', '퓨처드라이브', 'car', CURRENT_TIMESTAMP)
|
|
||||||
ON CONFLICT ("platform_key") DO UPDATE
|
|
||||||
SET "display_name" = EXCLUDED."display_name",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"organization_name" = EXCLUDED."organization_name",
|
|
||||||
"icon_key" = EXCLUDED."icon_key",
|
|
||||||
"is_active" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH course AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
), data AS (
|
|
||||||
SELECT *
|
|
||||||
FROM (VALUES
|
|
||||||
('futuredrive-local-pc', '클라이언트 실행 시 제어권이 부여됩니다', '연결', 'ready', 1),
|
|
||||||
('futuredrive-simulator-01', '사용 가능', '제어권 요청', 'ready', 2),
|
|
||||||
('futuredrive-f1tenth-01', '타 학생 사용 중', '대기 예약', 'reserved', 3),
|
|
||||||
('futuredrive-mkmini-02', '접근 권한 없음', '강사에게 권한 요청', 'locked', 4)
|
|
||||||
) AS v(platform_key, status, action, state, sort_order)
|
|
||||||
)
|
|
||||||
INSERT INTO "course_platforms" ("course_id", "platform_id", "status", "action", "state", "sort_order", "updated_at")
|
|
||||||
SELECT course."id", p."id", data.status, data.action, data.state, data.sort_order, CURRENT_TIMESTAMP
|
|
||||||
FROM course
|
|
||||||
JOIN data ON true
|
|
||||||
JOIN "platforms" p ON p."platform_key" = data.platform_key
|
|
||||||
ON CONFLICT ("course_id", "platform_id") DO UPDATE
|
|
||||||
SET "status" = EXCLUDED."status",
|
|
||||||
"action" = EXCLUDED."action",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH course AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
), student AS (
|
|
||||||
SELECT "id" FROM "users" WHERE "email" = 'student@futuredrive.net'
|
|
||||||
)
|
|
||||||
INSERT INTO "course_student_progress" ("course_id", "user_id", "completed_weeks", "total_weeks", "learning_hours", "commit_count", "average_score", "updated_at")
|
|
||||||
SELECT course."id", student."id", 2, 5, 42, 18, 85, CURRENT_TIMESTAMP
|
|
||||||
FROM course, student
|
|
||||||
ON CONFLICT ("course_id", "user_id") DO UPDATE
|
|
||||||
SET "completed_weeks" = EXCLUDED."completed_weeks",
|
|
||||||
"total_weeks" = EXCLUDED."total_weeks",
|
|
||||||
"learning_hours" = EXCLUDED."learning_hours",
|
|
||||||
"commit_count" = EXCLUDED."commit_count",
|
|
||||||
"average_score" = EXCLUDED."average_score",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
COMMENT ON TABLE "users" IS 'LMS 내부 사용자 기본 정보. 중앙 auth 사용자와 동기화해 앱별 데이터를 연결한다.';
|
|
||||||
COMMENT ON COLUMN "users"."id" IS 'LMS 사용자 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "users"."auth_user_id" IS '중앙 auth DB users.id와 매핑되는 사용자 UUID.';
|
|
||||||
COMMENT ON COLUMN "users"."email" IS '사용자 로그인 이메일.';
|
|
||||||
COMMENT ON COLUMN "users"."name" IS '사용자 표시 이름.';
|
|
||||||
COMMENT ON COLUMN "users"."createdAt" IS '사용자 데이터 생성 일시.';
|
|
||||||
COMMENT ON COLUMN "users"."updatedAt" IS '사용자 데이터 수정 일시.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "courses" IS 'LMS 강의 마스터. 소속과 강사 기준으로 전체 강의 정보를 관리한다.';
|
|
||||||
COMMENT ON COLUMN "courses"."id" IS '강의 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "courses"."slug" IS '강의 식별용 문자열 키.';
|
|
||||||
COMMENT ON COLUMN "courses"."title" IS '강의 제목.';
|
|
||||||
COMMENT ON COLUMN "courses"."subtitle" IS '강의 부제목.';
|
|
||||||
COMMENT ON COLUMN "courses"."description" IS '강의 설명.';
|
|
||||||
COMMENT ON COLUMN "courses"."organization_name" IS '강의를 운영하는 소속명.';
|
|
||||||
COMMENT ON COLUMN "courses"."teacher_id" IS '강의를 등록하거나 운영하는 강사 사용자 UUID.';
|
|
||||||
COMMENT ON COLUMN "courses"."is_active" IS '강의 활성 여부.';
|
|
||||||
COMMENT ON COLUMN "courses"."created_at" IS '강의 생성 일시.';
|
|
||||||
COMMENT ON COLUMN "courses"."updated_at" IS '강의 수정 일시.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_weeks" IS '강의별 주차 커리큘럼 정보.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."id" IS '주차 커리큘럼 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."course_id" IS '소속 강의 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."week" IS '강의 내 주차 번호.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."title" IS '주차 제목.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."tags" IS '주차 상태 및 분류 태그 목록.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."progress" IS '주차 기준 진행률.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."state" IS '주차 상태. done, current, locked 등을 사용한다.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."duration" IS '예상 학습 시간 표시값.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."github" IS 'GitHub 제출 상태 표시값.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."overview" IS '주차 개요 설명.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."deliverable" IS '주차 제출 기준.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."created_at" IS '주차 커리큘럼 생성 일시.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."updated_at" IS '주차 커리큘럼 수정 일시.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_topics" IS '주차별 학습 주제와 상세 설명.';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."id" IS '주차 학습 주제 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."week_id" IS '소속 주차 커리큘럼 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."title" IS '학습 주제 제목.';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."detail" IS '학습 주제 상세 설명.';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."sort_order" IS '주차 내 학습 주제 표시 순서.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_practices" IS '주차별 실습 내용.';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."id" IS '주차 실습 내용 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."week_id" IS '소속 주차 커리큘럼 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."title" IS '실습 항목 제목.';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."description" IS '실습 항목 설명.';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."sort_order" IS '주차 내 실습 항목 표시 순서.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "platforms" IS '최고관리자가 등록하는 실습 플랫폼 마스터. 학교/기관별 개별 장비를 구분한다.';
|
|
||||||
COMMENT ON COLUMN "platforms"."id" IS '실습 플랫폼 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "platforms"."platform_key" IS '플랫폼 식별용 문자열 키. 기관과 장비를 구분할 수 있어야 한다.';
|
|
||||||
COMMENT ON COLUMN "platforms"."display_name" IS '화면에 표시할 플랫폼 이름.';
|
|
||||||
COMMENT ON COLUMN "platforms"."description" IS '플랫폼 설명.';
|
|
||||||
COMMENT ON COLUMN "platforms"."organization_name" IS '플랫폼을 소유하거나 운영하는 소속명.';
|
|
||||||
COMMENT ON COLUMN "platforms"."icon_key" IS '프론트엔드에서 사용할 아이콘 키.';
|
|
||||||
COMMENT ON COLUMN "platforms"."is_active" IS '플랫폼 활성 여부.';
|
|
||||||
COMMENT ON COLUMN "platforms"."created_at" IS '플랫폼 생성 일시.';
|
|
||||||
COMMENT ON COLUMN "platforms"."updated_at" IS '플랫폼 수정 일시.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_platforms" IS '강사가 강의에 사용할 플랫폼을 선택해 배정한 목록.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."id" IS '강의-플랫폼 배정 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."course_id" IS '플랫폼을 배정한 강의 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."platform_id" IS '강의에 배정된 플랫폼 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."status" IS '플랫폼 사용 가능 상태 설명.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."action" IS '플랫폼 버튼에 표시할 작업명.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."state" IS '플랫폼 상태. ready, reserved, locked 등을 사용한다.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."sort_order" IS '강의 내 플랫폼 표시 순서.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."created_at" IS '강의-플랫폼 배정 생성 일시.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."updated_at" IS '강의-플랫폼 배정 수정 일시.';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_student_progress" IS '학생별 강의 학습 현황 통계.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."id" IS '학생 학습 현황 고유 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."course_id" IS '학습 현황이 속한 강의 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."user_id" IS '학습 현황 대상 학생 사용자 UUID.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."completed_weeks" IS '완료한 주차 수.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."total_weeks" IS '전체 주차 수.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."learning_hours" IS '누적 학습 시간.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."commit_count" IS '누적 커밋 수.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."average_score" IS '평균 점수.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."created_at" IS '학생 학습 현황 생성 일시.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."updated_at" IS '학생 학습 현황 수정 일시.';
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
UPDATE "users"
|
|
||||||
SET "auth_user_id" = '54803583-0a04-44f4-9398-78478239c566',
|
|
||||||
"name" = 'ATNMY Studio Admin',
|
|
||||||
"updatedAt" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "email" = 'teacher@futuredrive.net';
|
|
||||||
|
|
||||||
INSERT INTO "users" ("id", "auth_user_id", "email", "name", "createdAt", "updatedAt")
|
|
||||||
VALUES (
|
|
||||||
gen_random_uuid(),
|
|
||||||
'a8e7f37e-ef63-4912-a412-b4874b91ee67',
|
|
||||||
'chyoo@futuredrive.net',
|
|
||||||
'유창훈',
|
|
||||||
CURRENT_TIMESTAMP,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
ON CONFLICT ("email") DO UPDATE
|
|
||||||
SET "auth_user_id" = EXCLUDED."auth_user_id",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"updatedAt" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH course AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
), learner AS (
|
|
||||||
SELECT "id" FROM "users" WHERE "email" = 'chyoo@futuredrive.net'
|
|
||||||
)
|
|
||||||
INSERT INTO "course_student_progress" ("course_id", "user_id", "completed_weeks", "total_weeks", "learning_hours", "commit_count", "average_score", "updated_at")
|
|
||||||
SELECT course."id", learner."id", 2, 5, 42, 18, 85, CURRENT_TIMESTAMP
|
|
||||||
FROM course, learner
|
|
||||||
ON CONFLICT ("course_id", "user_id") DO UPDATE
|
|
||||||
SET "completed_weeks" = EXCLUDED."completed_weeks",
|
|
||||||
"total_weeks" = EXCLUDED."total_weeks",
|
|
||||||
"learning_hours" = EXCLUDED."learning_hours",
|
|
||||||
"commit_count" = EXCLUDED."commit_count",
|
|
||||||
"average_score" = EXCLUDED."average_score",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
DELETE FROM "course_student_progress"
|
|
||||||
WHERE "user_id" IN (
|
|
||||||
SELECT "id" FROM "users" WHERE "email" = 'student@futuredrive.net'
|
|
||||||
);
|
|
||||||
|
|
||||||
DELETE FROM "users"
|
|
||||||
WHERE "email" = 'student@futuredrive.net';
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
UPDATE "users"
|
|
||||||
SET "auth_user_id" = NULL,
|
|
||||||
"updatedAt" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "email" IN ('teacher@futuredrive.net', 'chyoo@futuredrive.net');
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
DELETE FROM "course_student_progress"
|
|
||||||
WHERE "user_id" IN (
|
|
||||||
SELECT "id" FROM "users" WHERE "email" = 'chyoo@futuredrive.net'
|
|
||||||
);
|
|
||||||
|
|
||||||
DELETE FROM "users"
|
|
||||||
WHERE "email" = 'chyoo@futuredrive.net';
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
CREATE TYPE "ChatThreadType" AS ENUM ('AI', 'INSTRUCTOR', 'MIXED');
|
|
||||||
CREATE TYPE "ChatThreadStatus" AS ENUM ('OPEN', 'WAITING_INSTRUCTOR', 'RESOLVED', 'ARCHIVED');
|
|
||||||
CREATE TYPE "ChatSenderType" AS ENUM ('STUDENT', 'AI', 'INSTRUCTOR', 'SYSTEM');
|
|
||||||
CREATE TYPE "ChatContentFormat" AS ENUM ('TEXT', 'MARKDOWN');
|
|
||||||
CREATE TYPE "AiChatRunStatus" AS ENUM ('SUCCEEDED', 'FAILED');
|
|
||||||
|
|
||||||
CREATE TABLE "chat_threads" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week_id" UUID,
|
|
||||||
"student_id" UUID NOT NULL,
|
|
||||||
"assigned_instructor_id" UUID,
|
|
||||||
"type" "ChatThreadType" NOT NULL,
|
|
||||||
"status" "ChatThreadStatus" NOT NULL DEFAULT 'OPEN',
|
|
||||||
"title" TEXT,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
|
||||||
"last_message_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "chat_threads_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "chat_messages" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"thread_id" UUID NOT NULL,
|
|
||||||
"sender_type" "ChatSenderType" NOT NULL,
|
|
||||||
"sender_user_id" UUID,
|
|
||||||
"content" TEXT NOT NULL,
|
|
||||||
"content_format" "ChatContentFormat" NOT NULL DEFAULT 'TEXT',
|
|
||||||
"metadata" JSONB,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"deleted_at" TIMESTAMP(3),
|
|
||||||
|
|
||||||
CONSTRAINT "chat_messages_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "ai_chat_runs" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"message_id" UUID NOT NULL,
|
|
||||||
"model" TEXT NOT NULL,
|
|
||||||
"prompt_tokens" INTEGER,
|
|
||||||
"completion_tokens" INTEGER,
|
|
||||||
"total_tokens" INTEGER,
|
|
||||||
"latency_ms" INTEGER,
|
|
||||||
"status" "AiChatRunStatus" NOT NULL,
|
|
||||||
"error" TEXT,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "ai_chat_runs_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX "chat_threads_course_id_type_last_message_at_idx" ON "chat_threads"("course_id", "type", "last_message_at");
|
|
||||||
CREATE INDEX "chat_threads_student_id_type_last_message_at_idx" ON "chat_threads"("student_id", "type", "last_message_at");
|
|
||||||
CREATE INDEX "chat_threads_week_id_idx" ON "chat_threads"("week_id");
|
|
||||||
CREATE INDEX "chat_threads_assigned_instructor_id_idx" ON "chat_threads"("assigned_instructor_id");
|
|
||||||
CREATE INDEX "chat_messages_thread_id_created_at_idx" ON "chat_messages"("thread_id", "created_at");
|
|
||||||
CREATE INDEX "chat_messages_sender_user_id_idx" ON "chat_messages"("sender_user_id");
|
|
||||||
CREATE UNIQUE INDEX "ai_chat_runs_message_id_key" ON "ai_chat_runs"("message_id");
|
|
||||||
CREATE INDEX "ai_chat_runs_status_created_at_idx" ON "ai_chat_runs"("status", "created_at");
|
|
||||||
|
|
||||||
ALTER TABLE "chat_threads" ADD CONSTRAINT "chat_threads_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "chat_threads" ADD CONSTRAINT "chat_threads_week_id_fkey" FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "chat_threads" ADD CONSTRAINT "chat_threads_student_id_fkey" FOREIGN KEY ("student_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "chat_threads" ADD CONSTRAINT "chat_threads_assigned_instructor_id_fkey" FOREIGN KEY ("assigned_instructor_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "chat_messages" ADD CONSTRAINT "chat_messages_thread_id_fkey" FOREIGN KEY ("thread_id") REFERENCES "chat_threads"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "chat_messages" ADD CONSTRAINT "chat_messages_sender_user_id_fkey" FOREIGN KEY ("sender_user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "ai_chat_runs" ADD CONSTRAINT "ai_chat_runs_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "chat_messages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "chat_threads" IS '학생 질의응답 대화방. AI, 강사, 혼합 대화의 상태와 연결 주차를 관리한다.';
|
|
||||||
COMMENT ON TABLE "chat_messages" IS '질의응답 대화방의 개별 메시지 기록.';
|
|
||||||
COMMENT ON TABLE "ai_chat_runs" IS 'AI 응답 생성 실행 기록. 모델, 토큰, 지연 시간, 오류를 추적한다.';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."metadata" IS '코드 파일, 터미널 출력, 선택 모듈 등 메시지와 함께 보낸 추가 컨텍스트.';
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
DO $$
|
|
||||||
DECLARE
|
|
||||||
column_record record;
|
|
||||||
BEGIN
|
|
||||||
FOR column_record IN
|
|
||||||
SELECT table_schema, table_name, column_name
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_schema = 'public'
|
|
||||||
AND data_type = 'timestamp without time zone'
|
|
||||||
LOOP
|
|
||||||
EXECUTE format(
|
|
||||||
'ALTER TABLE %I.%I ALTER COLUMN %I TYPE timestamptz USING %I AT TIME ZONE ''UTC''',
|
|
||||||
column_record.table_schema,
|
|
||||||
column_record.table_name,
|
|
||||||
column_record.column_name,
|
|
||||||
column_record.column_name
|
|
||||||
);
|
|
||||||
END LOOP;
|
|
||||||
END $$;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
ALTER TABLE "users"
|
|
||||||
ADD COLUMN "auth_role" TEXT,
|
|
||||||
ADD COLUMN "primary_organization_id" UUID,
|
|
||||||
ADD COLUMN "primary_organization_name" TEXT,
|
|
||||||
ADD COLUMN "primary_organization_type" TEXT,
|
|
||||||
ADD COLUMN "department" TEXT,
|
|
||||||
ADD COLUMN "position" TEXT;
|
|
||||||
|
|
||||||
CREATE INDEX "users_auth_role_idx" ON "users"("auth_role");
|
|
||||||
CREATE INDEX "users_primary_organization_id_idx" ON "users"("primary_organization_id");
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
CREATE TABLE "student_groups" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL DEFAULT 1,
|
|
||||||
"status" TEXT NOT NULL DEFAULT '진행중',
|
|
||||||
"platform_name" TEXT,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
CONSTRAINT "student_groups_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "student_group_members" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"group_id" UUID NOT NULL,
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
CONSTRAINT "student_group_members_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_groups_organization_name_name_key" ON "student_groups"("organization_name", "name");
|
|
||||||
CREATE INDEX "student_groups_organization_id_idx" ON "student_groups"("organization_id");
|
|
||||||
CREATE INDEX "student_groups_organization_name_idx" ON "student_groups"("organization_name");
|
|
||||||
CREATE UNIQUE INDEX "student_group_members_group_id_user_id_key" ON "student_group_members"("group_id", "user_id");
|
|
||||||
CREATE INDEX "student_group_members_user_id_idx" ON "student_group_members"("user_id");
|
|
||||||
|
|
||||||
ALTER TABLE "student_group_members"
|
|
||||||
ADD CONSTRAINT "student_group_members_group_id_fkey"
|
|
||||||
FOREIGN KEY ("group_id") REFERENCES "student_groups"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "student_group_members"
|
|
||||||
ADD CONSTRAINT "student_group_members_user_id_fkey"
|
|
||||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
INSERT INTO "student_groups" ("organization_id", "organization_name", "name", "week", "status", "platform_name")
|
|
||||||
SELECT org."organization_id", org."organization_name", seed."name", seed."week", '진행중', seed."platform_name"
|
|
||||||
FROM (
|
|
||||||
VALUES
|
|
||||||
('그룹 A', 3, 'MK-mini #01'),
|
|
||||||
('그룹 B', 2, '시뮬레이션'),
|
|
||||||
('그룹 C', 4, 'f1tenth #01')
|
|
||||||
) AS seed("name", "week", "platform_name")
|
|
||||||
CROSS JOIN (
|
|
||||||
SELECT DISTINCT "primary_organization_id" AS "organization_id", "primary_organization_name" AS "organization_name"
|
|
||||||
FROM "users"
|
|
||||||
WHERE "primary_organization_name" IS NOT NULL
|
|
||||||
) org
|
|
||||||
ON CONFLICT ("organization_name", "name") DO NOTHING;
|
|
||||||
|
|
||||||
INSERT INTO "student_group_members" ("group_id", "user_id")
|
|
||||||
SELECT g."id", u."id"
|
|
||||||
FROM "student_groups" g
|
|
||||||
JOIN "users" u ON u."primary_organization_name" = g."organization_name"
|
|
||||||
WHERE g."name" = '그룹 A'
|
|
||||||
AND u."email" IN ('kms@yu.ac.kr', 'lsy@yu.ac.kr', 'cyj@yu.ac.kr')
|
|
||||||
ON CONFLICT ("group_id", "user_id") DO NOTHING;
|
|
||||||
|
|
||||||
INSERT INTO "student_group_members" ("group_id", "user_id")
|
|
||||||
SELECT g."id", u."id"
|
|
||||||
FROM "student_groups" g
|
|
||||||
JOIN "users" u ON u."primary_organization_name" = g."organization_name"
|
|
||||||
WHERE g."name" = '그룹 B'
|
|
||||||
AND u."email" IN ('jyr@yu.ac.kr', 'pjh@yu.ac.kr')
|
|
||||||
ON CONFLICT ("group_id", "user_id") DO NOTHING;
|
|
||||||
|
|
||||||
INSERT INTO "student_group_members" ("group_id", "user_id")
|
|
||||||
SELECT g."id", u."id"
|
|
||||||
FROM "student_groups" g
|
|
||||||
JOIN "users" u ON u."primary_organization_name" = g."organization_name"
|
|
||||||
WHERE g."name" = '그룹 C'
|
|
||||||
AND u."email" IN ('hdy@yu.ac.kr', 'ojh@yu.ac.kr', 'ysj@yu.ac.kr', 'kdh@yu.ac.kr')
|
|
||||||
ON CONFLICT ("group_id", "user_id") DO NOTHING;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
CREATE TABLE "student_enrollments" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"status" TEXT NOT NULL DEFAULT 'ACTIVE',
|
|
||||||
"removed_at" TIMESTAMP(3),
|
|
||||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "student_enrollments_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_enrollments_user_id_organization_name_key" ON "student_enrollments"("user_id", "organization_name");
|
|
||||||
CREATE INDEX "student_enrollments_organization_id_status_idx" ON "student_enrollments"("organization_id", "status");
|
|
||||||
CREATE INDEX "student_enrollments_organization_name_status_idx" ON "student_enrollments"("organization_name", "status");
|
|
||||||
|
|
||||||
ALTER TABLE "student_enrollments" ADD CONSTRAINT "student_enrollments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
INSERT INTO "student_enrollments" ("user_id", "organization_id", "organization_name", "status")
|
|
||||||
SELECT
|
|
||||||
u."id",
|
|
||||||
u."primary_organization_id",
|
|
||||||
u."primary_organization_name",
|
|
||||||
'ACTIVE'
|
|
||||||
FROM "users" u
|
|
||||||
WHERE u."primary_organization_name" IS NOT NULL
|
|
||||||
AND (u."auth_role" IS NULL OR u."auth_role" IN ('USER', 'STUDENT'))
|
|
||||||
ON CONFLICT ("user_id", "organization_name") DO NOTHING;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
CREATE TABLE "instructor_platforms" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"platform_id" UUID NOT NULL,
|
|
||||||
"display_name" TEXT NOT NULL,
|
|
||||||
"instance_no" INTEGER NOT NULL,
|
|
||||||
"status" TEXT NOT NULL DEFAULT '대기',
|
|
||||||
"battery" INTEGER NOT NULL DEFAULT 100,
|
|
||||||
"uptime_minutes" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"speed_meters" DECIMAL(6, 2) NOT NULL DEFAULT 0,
|
|
||||||
"error_count" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"emergency_stop" BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "instructor_platforms_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_platforms_organization_name_display_name_key"
|
|
||||||
ON "instructor_platforms"("organization_name", "display_name");
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_platforms_organization_name_platform_id_instance_no_key"
|
|
||||||
ON "instructor_platforms"("organization_name", "platform_id", "instance_no");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_platforms_organization_id_idx"
|
|
||||||
ON "instructor_platforms"("organization_id");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_platforms_organization_name_idx"
|
|
||||||
ON "instructor_platforms"("organization_name");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_platforms_platform_id_idx"
|
|
||||||
ON "instructor_platforms"("platform_id");
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_platforms"
|
|
||||||
ADD CONSTRAINT "instructor_platforms_platform_id_fkey"
|
|
||||||
FOREIGN KEY ("platform_id") REFERENCES "platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
CREATE TABLE "instructor_curriculum_settings" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"active_week" INTEGER NOT NULL DEFAULT 1,
|
|
||||||
"active_learning_step" TEXT,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "instructor_curriculum_settings_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_curriculum_settings_organization_name_key"
|
|
||||||
ON "instructor_curriculum_settings"("organization_name");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_settings_organization_id_idx"
|
|
||||||
ON "instructor_curriculum_settings"("organization_id");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_settings_course_id_idx"
|
|
||||||
ON "instructor_curriculum_settings"("course_id");
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_curriculum_settings"
|
|
||||||
ADD CONSTRAINT "instructor_curriculum_settings_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
UPDATE "courses"
|
|
||||||
SET "organization_name" = '공통'
|
|
||||||
WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
AND "organization_name" = '퓨처드라이브';
|
|
||||||
|
|
||||||
CREATE TABLE "instructor_curriculum_week_overrides" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"title" TEXT,
|
|
||||||
"overview" TEXT,
|
|
||||||
"deliverable" TEXT,
|
|
||||||
"topics" JSONB,
|
|
||||||
"topic_details" JSONB,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "instructor_curriculum_week_overrides_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_curriculum_week_overrides_organization_name_course_id_week_key"
|
|
||||||
ON "instructor_curriculum_week_overrides"("organization_name", "course_id", "week");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_week_overrides_organization_id_idx"
|
|
||||||
ON "instructor_curriculum_week_overrides"("organization_id");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_week_overrides_organization_name_idx"
|
|
||||||
ON "instructor_curriculum_week_overrides"("organization_name");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_week_overrides_course_id_idx"
|
|
||||||
ON "instructor_curriculum_week_overrides"("course_id");
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD CONSTRAINT "instructor_curriculum_week_overrides_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN "duration" TEXT;
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
UPDATE "student_groups"
|
|
||||||
SET "platform_name" = NULL
|
|
||||||
WHERE "platform_name" IN ('로컬 PC', '시뮬레이션', '시뮬레이터', 'Simulator');
|
|
||||||
|
|
||||||
DELETE FROM "instructor_platforms"
|
|
||||||
WHERE "platform_id" IN (
|
|
||||||
SELECT "id"
|
|
||||||
FROM "platforms"
|
|
||||||
WHERE "platform_key" ILIKE '%local%'
|
|
||||||
OR "platform_key" ILIKE '%simulator%'
|
|
||||||
OR "display_name" ILIKE '%로컬%'
|
|
||||||
OR "display_name" ILIKE '%시뮬%'
|
|
||||||
OR "display_name" ILIKE '%simulator%'
|
|
||||||
);
|
|
||||||
|
|
||||||
DELETE FROM "course_platforms"
|
|
||||||
WHERE "platform_id" IN (
|
|
||||||
SELECT "id"
|
|
||||||
FROM "platforms"
|
|
||||||
WHERE "platform_key" ILIKE '%local%'
|
|
||||||
OR "platform_key" ILIKE '%simulator%'
|
|
||||||
OR "display_name" ILIKE '%로컬%'
|
|
||||||
OR "display_name" ILIKE '%시뮬%'
|
|
||||||
OR "display_name" ILIKE '%simulator%'
|
|
||||||
);
|
|
||||||
|
|
||||||
DELETE FROM "platforms"
|
|
||||||
WHERE "platform_key" ILIKE '%local%'
|
|
||||||
OR "platform_key" ILIKE '%simulator%'
|
|
||||||
OR "display_name" ILIKE '%로컬%'
|
|
||||||
OR "display_name" ILIKE '%시뮬%'
|
|
||||||
OR "display_name" ILIKE '%simulator%';
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
INSERT INTO "platforms" ("platform_key", "display_name", "description", "organization_name", "icon_key", "is_active", "updated_at")
|
|
||||||
VALUES
|
|
||||||
('futuredrive-local-pc', '로컬 PC', '내 PC에서 직접 개발 환경을 실행합니다.', '퓨처드라이브', 'laptop', true, CURRENT_TIMESTAMP),
|
|
||||||
('futuredrive-simulator-01', '시뮬레이터', '가상 환경에서 안전하게 테스트합니다.', '퓨처드라이브', 'monitor', true, CURRENT_TIMESTAMP)
|
|
||||||
ON CONFLICT ("platform_key") DO UPDATE
|
|
||||||
SET "display_name" = EXCLUDED."display_name",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"organization_name" = EXCLUDED."organization_name",
|
|
||||||
"icon_key" = EXCLUDED."icon_key",
|
|
||||||
"is_active" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH course AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
), data AS (
|
|
||||||
SELECT *
|
|
||||||
FROM (VALUES
|
|
||||||
('futuredrive-local-pc', '클라이언트 실행 시 제어권이 부여됩니다', '연결', 'ready', 1),
|
|
||||||
('futuredrive-simulator-01', '사용 가능', '제어권 요청', 'ready', 2)
|
|
||||||
) AS v(platform_key, status, action, state, sort_order)
|
|
||||||
)
|
|
||||||
INSERT INTO "course_platforms" ("course_id", "platform_id", "status", "action", "state", "sort_order", "updated_at")
|
|
||||||
SELECT course."id", p."id", data.status, data.action, data.state, data.sort_order, CURRENT_TIMESTAMP
|
|
||||||
FROM course
|
|
||||||
JOIN data ON true
|
|
||||||
JOIN "platforms" p ON p."platform_key" = data.platform_key
|
|
||||||
ON CONFLICT ("course_id", "platform_id") DO UPDATE
|
|
||||||
SET "status" = EXCLUDED."status",
|
|
||||||
"action" = EXCLUDED."action",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
UPDATE platforms
|
|
||||||
SET display_name = 'F1Tenth'
|
|
||||||
WHERE platform_key = 'futuredrive-f1tenth-01';
|
|
||||||
|
|
||||||
UPDATE platforms
|
|
||||||
SET display_name = 'MK-mini'
|
|
||||||
WHERE platform_key = 'futuredrive-mkmini-02';
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
ALTER TABLE "instructor_platforms"
|
|
||||||
ADD COLUMN "control_duration_minutes" INTEGER NOT NULL DEFAULT 30,
|
|
||||||
ADD COLUMN "control_warning_minutes" INTEGER NOT NULL DEFAULT 5;
|
|
||||||
|
|
||||||
CREATE TABLE "platform_control_sessions" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"platform_id" UUID,
|
|
||||||
"instructor_platform_id" UUID,
|
|
||||||
"holder_user_id" UUID NOT NULL,
|
|
||||||
"group_id" UUID,
|
|
||||||
"status" TEXT NOT NULL DEFAULT 'ACTIVE',
|
|
||||||
"started_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"ends_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
"warning_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
"warning_sent_at" TIMESTAMPTZ(3),
|
|
||||||
"released_at" TIMESTAMPTZ(3),
|
|
||||||
"end_reason" TEXT,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "platform_control_sessions_pkey" PRIMARY KEY ("id"),
|
|
||||||
CONSTRAINT "platform_control_sessions_platform_id_fkey" FOREIGN KEY ("platform_id") REFERENCES "platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT "platform_control_sessions_instructor_platform_id_fkey" FOREIGN KEY ("instructor_platform_id") REFERENCES "instructor_platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT "platform_control_sessions_holder_user_id_fkey" FOREIGN KEY ("holder_user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT "platform_control_sessions_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "student_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "platform_control_queue_entries" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"organization_id" UUID,
|
|
||||||
"organization_name" TEXT NOT NULL,
|
|
||||||
"platform_id" UUID,
|
|
||||||
"instructor_platform_id" UUID,
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"group_id" UUID,
|
|
||||||
"status" TEXT NOT NULL DEFAULT 'WAITING',
|
|
||||||
"requested_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"granted_at" TIMESTAMPTZ(3),
|
|
||||||
"canceled_at" TIMESTAMPTZ(3),
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "platform_control_queue_entries_pkey" PRIMARY KEY ("id"),
|
|
||||||
CONSTRAINT "platform_control_queue_entries_platform_id_fkey" FOREIGN KEY ("platform_id") REFERENCES "platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT "platform_control_queue_entries_instructor_platform_id_fkey" FOREIGN KEY ("instructor_platform_id") REFERENCES "instructor_platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT "platform_control_queue_entries_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
CONSTRAINT "platform_control_queue_entries_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "student_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX "platform_control_sessions_organization_id_idx" ON "platform_control_sessions"("organization_id");
|
|
||||||
CREATE INDEX "platform_control_sessions_organization_name_idx" ON "platform_control_sessions"("organization_name");
|
|
||||||
CREATE INDEX "platform_control_sessions_platform_id_status_idx" ON "platform_control_sessions"("platform_id", "status");
|
|
||||||
CREATE INDEX "platform_control_sessions_instructor_platform_id_status_idx" ON "platform_control_sessions"("instructor_platform_id", "status");
|
|
||||||
CREATE INDEX "platform_control_sessions_holder_user_id_status_idx" ON "platform_control_sessions"("holder_user_id", "status");
|
|
||||||
CREATE INDEX "platform_control_sessions_ends_at_status_idx" ON "platform_control_sessions"("ends_at", "status");
|
|
||||||
|
|
||||||
CREATE INDEX "platform_control_queue_entries_organization_id_idx" ON "platform_control_queue_entries"("organization_id");
|
|
||||||
CREATE INDEX "platform_control_queue_entries_organization_name_idx" ON "platform_control_queue_entries"("organization_name");
|
|
||||||
CREATE INDEX "platform_control_queue_entries_platform_id_status_requested_at_idx" ON "platform_control_queue_entries"("platform_id", "status", "requested_at");
|
|
||||||
CREATE INDEX "platform_control_queue_entries_instructor_platform_id_status_requested_at_idx" ON "platform_control_queue_entries"("instructor_platform_id", "status", "requested_at");
|
|
||||||
CREATE INDEX "platform_control_queue_entries_user_id_status_idx" ON "platform_control_queue_entries"("user_id", "status");
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
ALTER TABLE "platforms"
|
|
||||||
ADD COLUMN "vehicle_type" TEXT NOT NULL DEFAULT 'MK-mini',
|
|
||||||
ADD COLUMN "wheelbase_meters" DECIMAL(6, 3) NOT NULL DEFAULT 0.325,
|
|
||||||
ADD COLUMN "max_speed_meters_per_second" DECIMAL(6, 2) NOT NULL DEFAULT 7.0,
|
|
||||||
ADD COLUMN "max_steering_angle_degrees" DECIMAL(6, 2) NOT NULL DEFAULT 30,
|
|
||||||
ADD COLUMN "sensors" JSONB NOT NULL DEFAULT '[]',
|
|
||||||
ADD COLUMN "drivers" JSONB NOT NULL DEFAULT '[]',
|
|
||||||
ADD COLUMN "algorithms" JSONB NOT NULL DEFAULT '{}',
|
|
||||||
ADD COLUMN "control_duration_minutes" INTEGER NOT NULL DEFAULT 30,
|
|
||||||
ADD COLUMN "control_warning_minutes" INTEGER NOT NULL DEFAULT 5;
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_platforms"
|
|
||||||
ADD COLUMN "vehicle_type" TEXT NOT NULL DEFAULT 'MK-mini',
|
|
||||||
ADD COLUMN "wheelbase_meters" DECIMAL(6, 3) NOT NULL DEFAULT 0.325,
|
|
||||||
ADD COLUMN "max_speed_meters_per_second" DECIMAL(6, 2) NOT NULL DEFAULT 7.0,
|
|
||||||
ADD COLUMN "max_steering_angle_degrees" DECIMAL(6, 2) NOT NULL DEFAULT 30,
|
|
||||||
ADD COLUMN "sensors" JSONB NOT NULL DEFAULT '[]',
|
|
||||||
ADD COLUMN "drivers" JSONB NOT NULL DEFAULT '[]',
|
|
||||||
ADD COLUMN "algorithms" JSONB NOT NULL DEFAULT '{}';
|
|
||||||
|
|
||||||
UPDATE "platforms"
|
|
||||||
SET
|
|
||||||
"vehicle_type" = CASE
|
|
||||||
WHEN "display_name" ILIKE '%F1Tenth%' THEN 'F1tenth'
|
|
||||||
WHEN "display_name" ILIKE '%시뮬%' OR "display_name" ILIKE '%Simulator%' THEN 'Simulator'
|
|
||||||
ELSE 'MK-mini'
|
|
||||||
END,
|
|
||||||
"sensors" = '[{"title":"LiDAR","description":"Robosense Helios 16채널"},{"title":"RGB-D 카메라","description":"Intel Realsense D435"},{"title":"IMU 센서","description":"9축 관성측정장치"}]'::jsonb,
|
|
||||||
"drivers" = '[{"title":"LiDAR 드라이버","description":"robosense_driver v1.5.0"},{"title":"Camera 드라이버","description":"realsense2_camera v4.55.1"},{"title":"IMU 드라이버","description":"xsens_driver v2.2.0"}]'::jsonb,
|
|
||||||
"algorithms" = '{"slam":"Cartographer","planning":"A* + DWA","control":"PID Control"}'::jsonb;
|
|
||||||
|
|
||||||
UPDATE "instructor_platforms" AS ip
|
|
||||||
SET
|
|
||||||
"vehicle_type" = p."vehicle_type",
|
|
||||||
"wheelbase_meters" = p."wheelbase_meters",
|
|
||||||
"max_speed_meters_per_second" = p."max_speed_meters_per_second",
|
|
||||||
"max_steering_angle_degrees" = p."max_steering_angle_degrees",
|
|
||||||
"sensors" = p."sensors",
|
|
||||||
"drivers" = p."drivers",
|
|
||||||
"algorithms" = p."algorithms",
|
|
||||||
"control_duration_minutes" = p."control_duration_minutes",
|
|
||||||
"control_warning_minutes" = p."control_warning_minutes"
|
|
||||||
FROM "platforms" AS p
|
|
||||||
WHERE p."id" = ip."platform_id";
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
UPDATE "platforms"
|
|
||||||
SET "sensors" = (
|
|
||||||
SELECT COALESCE(jsonb_agg(sensor || jsonb_build_object('enabled', COALESCE((sensor->>'enabled')::boolean, true))), '[]'::jsonb)
|
|
||||||
FROM jsonb_array_elements("platforms"."sensors") AS sensor
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE "instructor_platforms"
|
|
||||||
SET "sensors" = (
|
|
||||||
SELECT COALESCE(jsonb_agg(sensor || jsonb_build_object('enabled', COALESCE((sensor->>'enabled')::boolean, true))), '[]'::jsonb)
|
|
||||||
FROM jsonb_array_elements("instructor_platforms"."sensors") AS sensor
|
|
||||||
);
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
CREATE TABLE "course_week_modules" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL,
|
|
||||||
"category" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"folder_name" TEXT NOT NULL,
|
|
||||||
"template_repository_url" TEXT,
|
|
||||||
"docker_image" TEXT,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "course_week_modules_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "course_week_modules_week_id_folder_name_key" ON "course_week_modules"("week_id", "folder_name");
|
|
||||||
CREATE UNIQUE INDEX "course_week_modules_week_id_sort_order_key" ON "course_week_modules"("week_id", "sort_order");
|
|
||||||
CREATE INDEX "course_week_modules_week_id_idx" ON "course_week_modules"("week_id");
|
|
||||||
|
|
||||||
ALTER TABLE "course_week_modules"
|
|
||||||
ADD CONSTRAINT "course_week_modules_week_id_fkey"
|
|
||||||
FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_modules" IS '주차별 개발 스튜디오 모듈 목록.';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."folder_name" IS '관리자 템플릿 또는 로컬 캐시의 원본 모듈 폴더명.';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."template_repository_url" IS '추후 Gitea 템플릿 저장소 연동 시 사용할 원본 repo URL.';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."docker_image" IS '모듈 실행에 사용할 Docker 이미지.';
|
|
||||||
|
|
||||||
WITH target_week AS (
|
|
||||||
SELECT w."id" AS week_id
|
|
||||||
FROM "course_weeks" w
|
|
||||||
JOIN "courses" c ON c."id" = w."course_id"
|
|
||||||
WHERE c."slug" = 'futuredrive-autonomy-basic' AND w."week" = 3
|
|
||||||
),
|
|
||||||
seed(sort_order, category, name, folder_name) AS (
|
|
||||||
VALUES
|
|
||||||
(1, 'Gui', 'AW-Rviz', '[furive-gui]AW-Rviz_v1.0.0_ws'),
|
|
||||||
(2, 'Map', 'AW-SampleMap', '[furive-map]AW-SampleMap_v1.0.0_ws'),
|
|
||||||
(3, 'Simulator', 'AW-PlanningSim', '[furive-simulator]AW-PlanningSim_v1.0.0_ws'),
|
|
||||||
(4, 'Map', 'DGIST-Campus', '[furive-map]DGIST-Campus_v1.0.0_ws'),
|
|
||||||
(5, 'Planning', 'AW-Planning', '[furive-planning]AW-Planning_v1.0.0_ws'),
|
|
||||||
(6, 'Bag', 'AW-SampleBag', '[furive-bag]AW-SampleBag_v1.0.0_ws'),
|
|
||||||
(7, 'Perception', 'AW-Perception', '[furive-perception]AW-Perception_v1.0.0_ws'),
|
|
||||||
(8, 'Sensing', 'test2', '[furive-sensing]test2_v1.0.0_ws'),
|
|
||||||
(9, 'Localization', 'AW-Localization', '[furive-localization]AW-Localization_v1.0.0_ws'),
|
|
||||||
(10, 'System', 'AW-System', '[furive-system]AW-System_v1.0.0_ws'),
|
|
||||||
(11, 'Vehicle', 'AW-TestSampleVehicle', '[furive-vehicle]AW-TestSampleVehicle_v1.0.0_ws'),
|
|
||||||
(12, 'Module', 'vehicle-f1', 'vehicle-f1_v1.0.0_ws'),
|
|
||||||
(13, 'Vehicle', 'AW-SampleVehicle', '[furive-vehicle]AW-SampleVehicle_v1.0.0_ws'),
|
|
||||||
(14, 'Control', 'AW-Control', '[furive-control]AW-Control_v1.0.0_ws'),
|
|
||||||
(15, 'Map', 'AW-SampleMapRosBag', '[furive-map]AW-SampleMapRosBag_v1.0.0_ws'),
|
|
||||||
(16, 'Sensing', 'AW-SampleSensor', '[furive-sensing]AW-SampleSensor_v1.0.0_ws')
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_modules" ("week_id", "sort_order", "category", "name", "folder_name")
|
|
||||||
SELECT target_week.week_id, seed.sort_order, seed.category, seed.name, seed.folder_name
|
|
||||||
FROM target_week
|
|
||||||
CROSS JOIN seed
|
|
||||||
ON CONFLICT ("week_id", "folder_name") DO UPDATE
|
|
||||||
SET "sort_order" = EXCLUDED."sort_order",
|
|
||||||
"category" = EXCLUDED."category",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
DELETE FROM "course_week_modules";
|
|
||||||
|
|
||||||
WITH seed(sort_order, category, name, folder_name) AS (
|
|
||||||
VALUES
|
|
||||||
(1, 'Gui', 'AW-Rviz', '[furive-gui]AW-Rviz_v1.0.0_ws'),
|
|
||||||
(2, 'Map', 'AW-SampleMap', '[furive-map]AW-SampleMap_v1.0.0_ws'),
|
|
||||||
(3, 'Planning', 'AW-Planning', '[furive-planning]AW-Planning_v1.0.0_ws'),
|
|
||||||
(4, 'Perception', 'AW-Perception', '[furive-perception]AW-Perception_v1.0.0_ws'),
|
|
||||||
(5, 'Localization', 'AW-Localization', '[furive-localization]AW-Localization_v1.0.0_ws'),
|
|
||||||
(6, 'System', 'AW-System', '[furive-system]AW-System_v1.0.0_ws'),
|
|
||||||
(7, 'Vehicle', 'AW-SampleVehicle', '[furive-vehicle]AW-SampleVehicle_v1.0.0_ws'),
|
|
||||||
(8, 'Control', 'AW-Control', '[furive-control]AW-Control_v1.0.0_ws'),
|
|
||||||
(9, 'Sensing', 'AW-SampleSensor', '[furive-sensing]AW-SampleSensor_v1.0.0_ws')
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_modules" ("week_id", "sort_order", "category", "name", "folder_name")
|
|
||||||
SELECT w."id", seed.sort_order, seed.category, seed.name, seed.folder_name
|
|
||||||
FROM "course_weeks" w
|
|
||||||
CROSS JOIN seed;
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
UPDATE "course_week_modules"
|
|
||||||
SET "template_repository_url" = CASE "name"
|
|
||||||
WHEN 'AW-Rviz' THEN 'http://localhost:3000/templates/aw-rviz.git'
|
|
||||||
WHEN 'AW-SampleMap' THEN 'http://localhost:3000/templates/aw-samplemap.git'
|
|
||||||
WHEN 'AW-Planning' THEN 'http://localhost:3000/templates/aw-planning.git'
|
|
||||||
WHEN 'AW-Perception' THEN 'http://localhost:3000/templates/aw-perception.git'
|
|
||||||
WHEN 'AW-Localization' THEN 'http://localhost:3000/templates/aw-localization.git'
|
|
||||||
WHEN 'AW-System' THEN 'http://localhost:3000/templates/aw-system.git'
|
|
||||||
WHEN 'AW-SampleVehicle' THEN 'http://localhost:3000/templates/aw-samplevehicle.git'
|
|
||||||
WHEN 'AW-Control' THEN 'http://localhost:3000/templates/aw-control.git'
|
|
||||||
WHEN 'AW-SampleSensor' THEN 'http://localhost:3000/templates/aw-samplesensor.git'
|
|
||||||
ELSE "template_repository_url"
|
|
||||||
END
|
|
||||||
WHERE "name" IN (
|
|
||||||
'AW-Rviz',
|
|
||||||
'AW-SampleMap',
|
|
||||||
'AW-Planning',
|
|
||||||
'AW-Perception',
|
|
||||||
'AW-Localization',
|
|
||||||
'AW-System',
|
|
||||||
'AW-SampleVehicle',
|
|
||||||
'AW-Control',
|
|
||||||
'AW-SampleSensor'
|
|
||||||
);
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
CREATE TABLE "studio_repositories" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"module_id" UUID NOT NULL,
|
|
||||||
"repo_owner" TEXT NOT NULL,
|
|
||||||
"repo_name" TEXT NOT NULL,
|
|
||||||
"repository_url" TEXT NOT NULL,
|
|
||||||
"template_repository_url" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "studio_repositories_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "studio_repositories_user_id_module_id_key" ON "studio_repositories"("user_id", "module_id");
|
|
||||||
CREATE INDEX "studio_repositories_week_id_idx" ON "studio_repositories"("week_id");
|
|
||||||
CREATE INDEX "studio_repositories_repo_owner_repo_name_idx" ON "studio_repositories"("repo_owner", "repo_name");
|
|
||||||
|
|
||||||
ALTER TABLE "studio_repositories"
|
|
||||||
ADD CONSTRAINT "studio_repositories_user_id_fkey"
|
|
||||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "studio_repositories"
|
|
||||||
ADD CONSTRAINT "studio_repositories_module_id_fkey"
|
|
||||||
FOREIGN KEY ("module_id") REFERENCES "course_week_modules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "studio_repositories" IS '개발 스튜디오 사용자별 모듈 Git 저장소 매핑.';
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
WITH target_weeks AS (
|
|
||||||
SELECT
|
|
||||||
w."id" AS week_id,
|
|
||||||
COALESCE(MAX(m."sort_order"), 0) + 1 AS next_sort_order
|
|
||||||
FROM "course_weeks" w
|
|
||||||
LEFT JOIN "course_week_modules" m ON m."week_id" = w."id"
|
|
||||||
GROUP BY w."id"
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_modules" (
|
|
||||||
"week_id",
|
|
||||||
"sort_order",
|
|
||||||
"category",
|
|
||||||
"name",
|
|
||||||
"folder_name",
|
|
||||||
"template_repository_url"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
target_weeks.week_id,
|
|
||||||
target_weeks.next_sort_order,
|
|
||||||
'Simulator',
|
|
||||||
'AW-PlanningSim',
|
|
||||||
'[furive-simulator]AW-PlanningSim_v1.0.0_ws',
|
|
||||||
'http://localhost:3000/templates/aw-planningsim'
|
|
||||||
FROM target_weeks
|
|
||||||
ON CONFLICT ("week_id", "folder_name") DO UPDATE
|
|
||||||
SET
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"category" = EXCLUDED."category",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"template_repository_url" = EXCLUDED."template_repository_url",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
DELETE FROM "course_week_modules" m
|
|
||||||
USING "course_weeks" w
|
|
||||||
WHERE m."week_id" = w."id"
|
|
||||||
AND w."week" = 3
|
|
||||||
AND m."name" IN (
|
|
||||||
'AW-Perception',
|
|
||||||
'AW-Localization',
|
|
||||||
'AW-SampleSensor'
|
|
||||||
);
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
ALTER TABLE "platforms"
|
|
||||||
ADD COLUMN "default_studio_settings" JSONB NOT NULL DEFAULT '{"workspaceRoot":"~/autonomy-studio/workspaces","giteaBaseUrl":"http://localhost:3000","giteaOwner":"students"}';
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_platforms"
|
|
||||||
ADD COLUMN "studio_settings_override" JSONB;
|
|
||||||
|
|
||||||
UPDATE "instructor_platforms" AS ip
|
|
||||||
SET "studio_settings_override" = p."default_studio_settings"
|
|
||||||
FROM "platforms" AS p
|
|
||||||
WHERE ip."platform_id" = p."id"
|
|
||||||
AND ip."studio_settings_override" IS NULL;
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
WITH priority(name, folder_name, sort_order) AS (
|
|
||||||
VALUES
|
|
||||||
('AW-SampleVehicle', '[furive-vehicle]AW-SampleVehicle_v1.0.0_ws', 1),
|
|
||||||
('AW-SampleSensor', '[furive-sensing]AW-SampleSensor_v1.0.0_ws', 2),
|
|
||||||
('AW-SampleMap', '[furive-map]AW-SampleMap_v1.0.0_ws', 3),
|
|
||||||
('AW-Localization', '[furive-localization]AW-Localization_v1.0.0_ws', 4),
|
|
||||||
('AW-Perception', '[furive-perception]AW-Perception_v1.0.0_ws', 5),
|
|
||||||
('AW-Planning', '[furive-planning]AW-Planning_v1.0.0_ws', 6),
|
|
||||||
('AW-Control', '[furive-control]AW-Control_v1.0.0_ws', 7),
|
|
||||||
('AW-System', '[furive-system]AW-System_v1.0.0_ws', 8),
|
|
||||||
('AW-Rviz', '[furive-gui]AW-Rviz_v1.0.0_ws', 9),
|
|
||||||
('AW-PlanningSim', '[furive-simulator]AW-PlanningSim_v1.0.0_ws', 10)
|
|
||||||
)
|
|
||||||
UPDATE "course_week_modules" module
|
|
||||||
SET "sort_order" = priority.sort_order + 1000,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM priority
|
|
||||||
WHERE module."folder_name" = priority.folder_name;
|
|
||||||
|
|
||||||
WITH priority(name, folder_name, sort_order) AS (
|
|
||||||
VALUES
|
|
||||||
('AW-SampleVehicle', '[furive-vehicle]AW-SampleVehicle_v1.0.0_ws', 1),
|
|
||||||
('AW-SampleSensor', '[furive-sensing]AW-SampleSensor_v1.0.0_ws', 2),
|
|
||||||
('AW-SampleMap', '[furive-map]AW-SampleMap_v1.0.0_ws', 3),
|
|
||||||
('AW-Localization', '[furive-localization]AW-Localization_v1.0.0_ws', 4),
|
|
||||||
('AW-Perception', '[furive-perception]AW-Perception_v1.0.0_ws', 5),
|
|
||||||
('AW-Planning', '[furive-planning]AW-Planning_v1.0.0_ws', 6),
|
|
||||||
('AW-Control', '[furive-control]AW-Control_v1.0.0_ws', 7),
|
|
||||||
('AW-System', '[furive-system]AW-System_v1.0.0_ws', 8),
|
|
||||||
('AW-Rviz', '[furive-gui]AW-Rviz_v1.0.0_ws', 9),
|
|
||||||
('AW-PlanningSim', '[furive-simulator]AW-PlanningSim_v1.0.0_ws', 10)
|
|
||||||
)
|
|
||||||
UPDATE "course_week_modules" module
|
|
||||||
SET "sort_order" = priority.sort_order,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM priority
|
|
||||||
WHERE module."folder_name" = priority.folder_name;
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
CREATE TABLE "course_environment_profiles" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"platform_key" TEXT NOT NULL DEFAULT 'futuredrive-local-pc',
|
|
||||||
"profile_type" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"config" JSONB NOT NULL,
|
|
||||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
CONSTRAINT "course_environment_profiles_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "course_environment_profiles_course_id_week_platform_key_key"
|
|
||||||
ON "course_environment_profiles"("course_id", "week", "platform_key");
|
|
||||||
CREATE INDEX "course_environment_profiles_course_id_week_idx"
|
|
||||||
ON "course_environment_profiles"("course_id", "week");
|
|
||||||
CREATE INDEX "course_environment_profiles_profile_type_idx"
|
|
||||||
ON "course_environment_profiles"("profile_type");
|
|
||||||
|
|
||||||
ALTER TABLE "course_environment_profiles"
|
|
||||||
ADD CONSTRAINT "course_environment_profiles_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id")
|
|
||||||
REFERENCES "courses"("id")
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_environment_profiles" IS '강좌/주차/플랫폼별 개발환경 실행 프로파일.';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."config" IS 'Docker runtime, ROS launch, bridge, viewer 실행 설정 JSON.';
|
|
||||||
|
|
||||||
INSERT INTO "course_environment_profiles" (
|
|
||||||
"course_id",
|
|
||||||
"week",
|
|
||||||
"platform_key",
|
|
||||||
"profile_type",
|
|
||||||
"name",
|
|
||||||
"config",
|
|
||||||
"updated_at"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
course."id",
|
|
||||||
3,
|
|
||||||
'futuredrive-local-pc',
|
|
||||||
'SLAM',
|
|
||||||
'TurtleBot3 SLAM 로컬 시뮬레이션',
|
|
||||||
'{
|
|
||||||
"type": "SLAM",
|
|
||||||
"viewer": {
|
|
||||||
"type": "slam",
|
|
||||||
"url": "/slam-viewer/index.html",
|
|
||||||
"healthUrl": "http://localhost:8765/health"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "nav2-slam",
|
|
||||||
"image": "nav2-slam:humble",
|
|
||||||
"network": "host",
|
|
||||||
"privileged": true,
|
|
||||||
"env": {
|
|
||||||
"DISPLAY": ":1",
|
|
||||||
"QT_X11_NO_MITSHM": "1",
|
|
||||||
"LIBGL_ALWAYS_SOFTWARE": "1",
|
|
||||||
"TURTLEBOT3_MODEL": "burger"
|
|
||||||
},
|
|
||||||
"volumes": [
|
|
||||||
"/tmp/.X11-unix:/tmp/.X11-unix",
|
|
||||||
"/dev/shm:/dev/shm",
|
|
||||||
"~/.gazebo:/root/.gazebo",
|
|
||||||
"~/.cache:/root/.cache"
|
|
||||||
],
|
|
||||||
"command": "tail -f /dev/null"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"container": "nav2-slam",
|
|
||||||
"mode": "execDetached",
|
|
||||||
"delayMs": 0,
|
|
||||||
"command": "ros2 launch gazebo_ros gzserver.launch.py world:=/opt/ros/humble/share/turtlebot3_gazebo/worlds/turtlebot3_world.world & ros2 launch turtlebot3_gazebo robot_state_publisher.launch.py use_sim_time:=true & sleep 6; ros2 launch turtlebot3_gazebo spawn_turtlebot3.launch.py x_pose:=-2.0 y_pose:=-0.5; wait",
|
|
||||||
"logFile": "/tmp/turtlebot3_gazebo.log"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"container": "nav2-slam",
|
|
||||||
"mode": "execDetached",
|
|
||||||
"delayMs": 6000,
|
|
||||||
"command": "ros2 launch slam_toolbox online_async_launch.py use_sim_time:=true",
|
|
||||||
"logFile": "/tmp/slam_toolbox.log"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"container": "nav2-slam",
|
|
||||||
"mode": "execDetached",
|
|
||||||
"delayMs": 3000,
|
|
||||||
"command": "ros2 launch nav2_bringup navigation_launch.py use_sim_time:=true autostart:=true",
|
|
||||||
"logFile": "/tmp/nav2.log"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bridge": {
|
|
||||||
"name": "slam-ros-bridge",
|
|
||||||
"image": "slam-ros-bridge",
|
|
||||||
"buildContext": "resources/slam-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ODOM_TOPIC": "/odom",
|
|
||||||
"SCAN_TOPIC": "/scan",
|
|
||||||
"MAP_TOPIC": "/map",
|
|
||||||
"CMD_VEL_TOPIC": "/cmd_vel",
|
|
||||||
"NAVIGATE_TO_POSE_ACTION": "/navigate_to_pose"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}'::jsonb,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM "courses" AS course
|
|
||||||
ON CONFLICT ("course_id", "week", "platform_key") DO UPDATE
|
|
||||||
SET
|
|
||||||
"profile_type" = EXCLUDED."profile_type",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"config" = EXCLUDED."config",
|
|
||||||
"enabled" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
INSERT INTO "course_weeks" (
|
|
||||||
"course_id",
|
|
||||||
"week",
|
|
||||||
"title",
|
|
||||||
"tags",
|
|
||||||
"progress",
|
|
||||||
"state",
|
|
||||||
"duration",
|
|
||||||
"github",
|
|
||||||
"overview",
|
|
||||||
"deliverable",
|
|
||||||
"updated_at"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
course."id",
|
|
||||||
6,
|
|
||||||
'Autoware 기반 자율주행',
|
|
||||||
ARRAY['Autoware','진행예정']::TEXT[],
|
|
||||||
0,
|
|
||||||
'locked',
|
|
||||||
'10-12시간',
|
|
||||||
'미제출',
|
|
||||||
'Autoware 모듈을 실행하고 시각화 화면에서 초기 위치와 목표 포즈를 설정해 차량이 경로를 따라 주행하는 흐름을 검증합니다.',
|
|
||||||
'초기 위치/목표 포즈 설정값, 주행 로그, 경로 계획 및 제어 결과를 정리해 제출',
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM "courses" AS course
|
|
||||||
ON CONFLICT ("course_id", "week") DO UPDATE
|
|
||||||
SET
|
|
||||||
"title" = EXCLUDED."title",
|
|
||||||
"tags" = EXCLUDED."tags",
|
|
||||||
"progress" = EXCLUDED."progress",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"duration" = EXCLUDED."duration",
|
|
||||||
"github" = EXCLUDED."github",
|
|
||||||
"overview" = EXCLUDED."overview",
|
|
||||||
"deliverable" = EXCLUDED."deliverable",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH target_weeks AS (
|
|
||||||
SELECT w."id" AS week_id
|
|
||||||
FROM "course_weeks" w
|
|
||||||
WHERE w."week" = 6
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_topics" ("week_id", "sort_order", "title", "detail")
|
|
||||||
SELECT target_weeks.week_id, topic.sort_order, topic.title, topic.detail
|
|
||||||
FROM target_weeks
|
|
||||||
CROSS JOIN (
|
|
||||||
VALUES
|
|
||||||
(1, 'Autoware 실행 구조', '차량, 센서, 지도, 위치추정, 인지, 계획, 제어 모듈의 실행 순서와 ROS2 토픽 흐름을 확인합니다.'),
|
|
||||||
(2, '초기 위치 설정', '시각화 화면에서 초기 위치를 지정하고 Localization 상태와 차량 pose 갱신을 검증합니다.'),
|
|
||||||
(3, '목표 포즈와 경로 계획', '목표 포즈를 지정해 mission planning과 behavior planning 결과 경로를 확인합니다.'),
|
|
||||||
(4, '제어 명령과 주행 검증', '계획된 경로를 따라 차량이 움직이는지 제어 명령, 상태 토픽, 로그를 기준으로 점검합니다.')
|
|
||||||
) AS topic(sort_order, title, detail)
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET
|
|
||||||
"title" = EXCLUDED."title",
|
|
||||||
"detail" = EXCLUDED."detail";
|
|
||||||
|
|
||||||
WITH target_weeks AS (
|
|
||||||
SELECT w."id" AS week_id
|
|
||||||
FROM "course_weeks" w
|
|
||||||
WHERE w."week" = 6
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_practices" ("week_id", "sort_order", "title", "description")
|
|
||||||
SELECT target_weeks.week_id, practice.sort_order, practice.title, practice.description
|
|
||||||
FROM target_weeks
|
|
||||||
CROSS JOIN (
|
|
||||||
VALUES
|
|
||||||
(1, 'Autoware 모듈 실행', '주차에 등록된 Autoware 모듈을 순서대로 실행하고 컨테이너 상태를 확인합니다.'),
|
|
||||||
(2, '초기 위치 지정', '시각화에서 초기 위치를 지정하고 차량 위치가 지도 좌표계에 정상 반영되는지 확인합니다.'),
|
|
||||||
(3, '목표 포즈 지정', '목표 지점을 설정하고 생성된 route와 주행 상태를 비교합니다.'),
|
|
||||||
(4, '주행 결과 제출', '수정한 소스, 실행 로그, 주행 검증 결과를 제출합니다.')
|
|
||||||
) AS practice(sort_order, title, description)
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET
|
|
||||||
"title" = EXCLUDED."title",
|
|
||||||
"description" = EXCLUDED."description";
|
|
||||||
|
|
||||||
WITH week_pairs AS (
|
|
||||||
SELECT
|
|
||||||
source_week."id" AS source_week_id,
|
|
||||||
target_week."id" AS target_week_id
|
|
||||||
FROM "course_weeks" source_week
|
|
||||||
JOIN "course_weeks" target_week
|
|
||||||
ON target_week."course_id" = source_week."course_id"
|
|
||||||
AND target_week."week" = 6
|
|
||||||
WHERE source_week."week" = 3
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_modules" (
|
|
||||||
"week_id",
|
|
||||||
"sort_order",
|
|
||||||
"category",
|
|
||||||
"name",
|
|
||||||
"folder_name",
|
|
||||||
"template_repository_url",
|
|
||||||
"docker_image",
|
|
||||||
"updated_at"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
week_pairs.target_week_id,
|
|
||||||
source_module."sort_order",
|
|
||||||
source_module."category",
|
|
||||||
source_module."name",
|
|
||||||
source_module."folder_name",
|
|
||||||
source_module."template_repository_url",
|
|
||||||
source_module."docker_image",
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM week_pairs
|
|
||||||
JOIN "course_week_modules" source_module ON source_module."week_id" = week_pairs.source_week_id
|
|
||||||
ON CONFLICT ("week_id", "folder_name") DO UPDATE
|
|
||||||
SET
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"category" = EXCLUDED."category",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"template_repository_url" = EXCLUDED."template_repository_url",
|
|
||||||
"docker_image" = EXCLUDED."docker_image",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
INSERT INTO "course_environment_profiles" (
|
|
||||||
"course_id",
|
|
||||||
"week",
|
|
||||||
"platform_key",
|
|
||||||
"profile_type",
|
|
||||||
"name",
|
|
||||||
"config",
|
|
||||||
"updated_at"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
course."id",
|
|
||||||
6,
|
|
||||||
'futuredrive-local-pc',
|
|
||||||
'AUTOWARE',
|
|
||||||
'Autoware 로컬 자율주행',
|
|
||||||
'{
|
|
||||||
"type": "autoware",
|
|
||||||
"viewer": {
|
|
||||||
"type": "autoware",
|
|
||||||
"url": "/autoware-viewer/index.html",
|
|
||||||
"healthUrl": "http://localhost:8765/health"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"containers": [],
|
|
||||||
"steps": []
|
|
||||||
},
|
|
||||||
"bridge": {
|
|
||||||
"name": "autoware-ros-bridge",
|
|
||||||
"image": "autoware-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "autoware",
|
|
||||||
"MAP_POINT_STRIDE": "8",
|
|
||||||
"MAP_POINT_LIMIT": "80000",
|
|
||||||
"AUTOWARE_ROUTE_INPUT": "rviz_topic",
|
|
||||||
"INITIAL_POSE_TOPIC": "/initialpose",
|
|
||||||
"GOAL_POSE_TOPIC": "/planning/mission_planning/goal",
|
|
||||||
"VEHICLE_POSE_TOPIC": "/localization/kinematic_state",
|
|
||||||
"TRAJECTORY_TOPIC": "/planning/scenario_planning/trajectory",
|
|
||||||
"ROUTE_TOPIC": "/planning/mission_planning/route"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}'::jsonb,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM "courses" AS course
|
|
||||||
ON CONFLICT ("course_id", "week", "platform_key") DO UPDATE
|
|
||||||
SET
|
|
||||||
"profile_type" = EXCLUDED."profile_type",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"config" = EXCLUDED."config",
|
|
||||||
"enabled" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
UPDATE "course_student_progress"
|
|
||||||
SET
|
|
||||||
"total_weeks" = GREATEST("total_weeks", 6),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_settings"
|
|
||||||
ADD COLUMN "group_id" UUID;
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_curriculum_settings"
|
|
||||||
ADD CONSTRAINT "instructor_curriculum_settings_group_id_fkey"
|
|
||||||
FOREIGN KEY ("group_id")
|
|
||||||
REFERENCES "student_groups"("id")
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS "instructor_curriculum_settings_organization_name_key";
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_curriculum_settings_organization_default_key"
|
|
||||||
ON "instructor_curriculum_settings"("organization_name")
|
|
||||||
WHERE "group_id" IS NULL;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_curriculum_settings_organization_group_key"
|
|
||||||
ON "instructor_curriculum_settings"("organization_name", "group_id")
|
|
||||||
WHERE "group_id" IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_settings_organization_name_idx"
|
|
||||||
ON "instructor_curriculum_settings"("organization_name");
|
|
||||||
|
|
||||||
CREATE INDEX "instructor_curriculum_settings_group_id_idx"
|
|
||||||
ON "instructor_curriculum_settings"("group_id");
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
ALTER TABLE "student_enrollments"
|
|
||||||
ADD COLUMN "course_id" UUID;
|
|
||||||
|
|
||||||
UPDATE "student_enrollments" enrollment
|
|
||||||
SET "course_id" = course."id"
|
|
||||||
FROM "courses" course
|
|
||||||
WHERE course."slug" = 'futuredrive-autonomy-basic'
|
|
||||||
AND course."is_active" = true
|
|
||||||
AND enrollment."course_id" IS NULL;
|
|
||||||
|
|
||||||
ALTER TABLE "student_enrollments"
|
|
||||||
ALTER COLUMN "course_id" SET NOT NULL;
|
|
||||||
|
|
||||||
ALTER TABLE "student_enrollments"
|
|
||||||
ADD CONSTRAINT "student_enrollments_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id")
|
|
||||||
REFERENCES "courses"("id")
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS "student_enrollments_user_id_organization_name_key";
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_enrollments_user_id_course_id_key"
|
|
||||||
ON "student_enrollments"("user_id", "course_id");
|
|
||||||
|
|
||||||
CREATE INDEX "student_enrollments_course_id_status_idx"
|
|
||||||
ON "student_enrollments"("course_id", "status");
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS "instructor_curriculum_settings_organization_default_key";
|
|
||||||
DROP INDEX IF EXISTS "instructor_curriculum_settings_organization_group_key";
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_curriculum_settings_course_default_key"
|
|
||||||
ON "instructor_curriculum_settings"("organization_name", "course_id")
|
|
||||||
WHERE "group_id" IS NULL;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "instructor_curriculum_settings_course_group_key"
|
|
||||||
ON "instructor_curriculum_settings"("organization_name", "course_id", "group_id")
|
|
||||||
WHERE "group_id" IS NOT NULL;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
WITH week3 AS (
|
|
||||||
SELECT "id" AS week_id
|
|
||||||
FROM "course_weeks"
|
|
||||||
WHERE "week" = 3
|
|
||||||
),
|
|
||||||
desired(sort_order, title, detail) AS (
|
|
||||||
VALUES
|
|
||||||
(1, 'SLAM 개요', '동시적 위치추정 및 지도작성의 기본 개념'),
|
|
||||||
(2, 'Cartographer 설정', 'ROS2 Cartographer 패키지 설정 방법'),
|
|
||||||
(3, '실습: 맵 생성', 'LiDAR 데이터를 활용한 실시간 맵핑'),
|
|
||||||
(4, '위치 추정', '생성된 맵에서 로봇 위치 인식')
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_topics" ("week_id", "sort_order", "title", "detail")
|
|
||||||
SELECT week3.week_id, desired.sort_order, desired.title, desired.detail
|
|
||||||
FROM week3
|
|
||||||
CROSS JOIN desired
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET
|
|
||||||
"title" = EXCLUDED."title",
|
|
||||||
"detail" = EXCLUDED."detail";
|
|
||||||
|
|
||||||
DELETE FROM "course_week_topics" topic
|
|
||||||
USING "course_weeks" week
|
|
||||||
WHERE topic."week_id" = week."id"
|
|
||||||
AND week."week" = 3
|
|
||||||
AND topic."sort_order" > 4;
|
|
||||||
|
|
||||||
UPDATE "instructor_curriculum_week_overrides"
|
|
||||||
SET
|
|
||||||
"topics" = '["SLAM 개요","Cartographer 설정","실습: 맵 생성","위치 추정"]'::jsonb,
|
|
||||||
"topic_details" = '["동시적 위치추정 및 지도작성의 기본 개념","ROS2 Cartographer 패키지 설정 방법","LiDAR 데이터를 활용한 실시간 맵핑","생성된 맵에서 로봇 위치 인식"]'::jsonb,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "week" = 3;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
UPDATE "courses"
|
|
||||||
SET "description" = NULL,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "description" IS NOT NULL;
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
UPDATE "courses"
|
|
||||||
SET "organization_name" = '퓨처드라이브',
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
AND "organization_name" = '공통';
|
|
||||||
|
|
||||||
CREATE TEMP TABLE "_course_split_source" AS
|
|
||||||
SELECT "id" AS source_course_id
|
|
||||||
FROM "courses"
|
|
||||||
WHERE "slug" = 'futuredrive-autonomy-basic'
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
INSERT INTO "courses" ("slug", "title", "subtitle", "description", "organization_name", "teacher_id", "is_active", "updated_at")
|
|
||||||
SELECT
|
|
||||||
'yeungnam-autonomy-basic',
|
|
||||||
source."title",
|
|
||||||
source."subtitle",
|
|
||||||
source."description",
|
|
||||||
'영남대학교',
|
|
||||||
teacher."id",
|
|
||||||
true,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM "_course_split_source" split
|
|
||||||
JOIN "courses" source ON source."id" = split.source_course_id
|
|
||||||
JOIN "users" teacher ON teacher."email" = 'teacher@yu.ac.kr'
|
|
||||||
ON CONFLICT ("slug") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"subtitle" = EXCLUDED."subtitle",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"organization_name" = EXCLUDED."organization_name",
|
|
||||||
"teacher_id" = EXCLUDED."teacher_id",
|
|
||||||
"is_active" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
CREATE TEMP TABLE "_course_split_target" AS
|
|
||||||
SELECT "id" AS target_course_id
|
|
||||||
FROM "courses"
|
|
||||||
WHERE "slug" = 'yeungnam-autonomy-basic'
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
INSERT INTO "course_platforms" ("course_id", "platform_id", "status", "action", "state", "sort_order", "updated_at")
|
|
||||||
SELECT target.target_course_id, platform."platform_id", platform."status", platform."action", platform."state", platform."sort_order", CURRENT_TIMESTAMP
|
|
||||||
FROM "course_platforms" platform
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = platform."course_id"
|
|
||||||
CROSS JOIN "_course_split_target" target
|
|
||||||
ON CONFLICT ("course_id", "platform_id") DO UPDATE
|
|
||||||
SET "status" = EXCLUDED."status",
|
|
||||||
"action" = EXCLUDED."action",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
INSERT INTO "course_environment_profiles" ("course_id", "week", "platform_key", "profile_type", "name", "config", "enabled", "updated_at")
|
|
||||||
SELECT target.target_course_id, profile."week", profile."platform_key", profile."profile_type", profile."name", profile."config", profile."enabled", CURRENT_TIMESTAMP
|
|
||||||
FROM "course_environment_profiles" profile
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = profile."course_id"
|
|
||||||
CROSS JOIN "_course_split_target" target
|
|
||||||
ON CONFLICT ("course_id", "week", "platform_key") DO UPDATE
|
|
||||||
SET "profile_type" = EXCLUDED."profile_type",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"config" = EXCLUDED."config",
|
|
||||||
"enabled" = EXCLUDED."enabled",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
INSERT INTO "course_weeks" ("course_id", "week", "title", "tags", "progress", "state", "duration", "github", "overview", "deliverable", "updated_at")
|
|
||||||
SELECT target.target_course_id, week."week", week."title", week."tags", week."progress", week."state", week."duration", week."github", week."overview", week."deliverable", CURRENT_TIMESTAMP
|
|
||||||
FROM "course_weeks" week
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = week."course_id"
|
|
||||||
CROSS JOIN "_course_split_target" target
|
|
||||||
ON CONFLICT ("course_id", "week") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"tags" = EXCLUDED."tags",
|
|
||||||
"progress" = EXCLUDED."progress",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"duration" = EXCLUDED."duration",
|
|
||||||
"github" = EXCLUDED."github",
|
|
||||||
"overview" = EXCLUDED."overview",
|
|
||||||
"deliverable" = EXCLUDED."deliverable",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
CREATE TEMP TABLE "_course_split_week_map" AS
|
|
||||||
SELECT source_week."id" AS source_week_id, target_week."id" AS target_week_id, source_week."week"
|
|
||||||
FROM "course_weeks" source_week
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = source_week."course_id"
|
|
||||||
JOIN "_course_split_target" target ON true
|
|
||||||
JOIN "course_weeks" target_week ON target_week."course_id" = target.target_course_id AND target_week."week" = source_week."week";
|
|
||||||
|
|
||||||
INSERT INTO "course_week_topics" ("week_id", "sort_order", "title", "detail")
|
|
||||||
SELECT week_map.target_week_id, topic."sort_order", topic."title", topic."detail"
|
|
||||||
FROM "course_week_topics" topic
|
|
||||||
JOIN "_course_split_week_map" week_map ON week_map.source_week_id = topic."week_id"
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"detail" = EXCLUDED."detail";
|
|
||||||
|
|
||||||
INSERT INTO "course_week_practices" ("week_id", "sort_order", "title", "description")
|
|
||||||
SELECT week_map.target_week_id, practice."sort_order", practice."title", practice."description"
|
|
||||||
FROM "course_week_practices" practice
|
|
||||||
JOIN "_course_split_week_map" week_map ON week_map.source_week_id = practice."week_id"
|
|
||||||
ON CONFLICT ("week_id", "sort_order") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"description" = EXCLUDED."description";
|
|
||||||
|
|
||||||
INSERT INTO "course_week_modules" ("week_id", "sort_order", "category", "name", "folder_name", "template_repository_url", "docker_image", "updated_at")
|
|
||||||
SELECT week_map.target_week_id, module."sort_order", module."category", module."name", module."folder_name", module."template_repository_url", module."docker_image", CURRENT_TIMESTAMP
|
|
||||||
FROM "course_week_modules" module
|
|
||||||
JOIN "_course_split_week_map" week_map ON week_map.source_week_id = module."week_id"
|
|
||||||
ON CONFLICT ("week_id", "folder_name") DO UPDATE
|
|
||||||
SET "sort_order" = EXCLUDED."sort_order",
|
|
||||||
"category" = EXCLUDED."category",
|
|
||||||
"name" = EXCLUDED."name",
|
|
||||||
"template_repository_url" = EXCLUDED."template_repository_url",
|
|
||||||
"docker_image" = EXCLUDED."docker_image",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
CREATE TEMP TABLE "_course_split_module_map" AS
|
|
||||||
SELECT source_module."id" AS source_module_id, target_module."id" AS target_module_id, week_map.source_week_id, week_map.target_week_id
|
|
||||||
FROM "course_week_modules" source_module
|
|
||||||
JOIN "_course_split_week_map" week_map ON week_map.source_week_id = source_module."week_id"
|
|
||||||
JOIN "course_week_modules" target_module ON target_module."week_id" = week_map.target_week_id AND target_module."folder_name" = source_module."folder_name";
|
|
||||||
|
|
||||||
INSERT INTO "student_enrollments" ("user_id", "course_id", "organization_id", "organization_name", "status", "removed_at", "updated_at")
|
|
||||||
SELECT enrollment."user_id", target.target_course_id, enrollment."organization_id", enrollment."organization_name", enrollment."status", enrollment."removed_at", CURRENT_TIMESTAMP
|
|
||||||
FROM "student_enrollments" enrollment
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = enrollment."course_id"
|
|
||||||
CROSS JOIN "_course_split_target" target
|
|
||||||
WHERE enrollment."organization_name" = '영남대학교'
|
|
||||||
ON CONFLICT ("user_id", "course_id") DO UPDATE
|
|
||||||
SET "organization_id" = EXCLUDED."organization_id",
|
|
||||||
"organization_name" = EXCLUDED."organization_name",
|
|
||||||
"status" = EXCLUDED."status",
|
|
||||||
"removed_at" = EXCLUDED."removed_at",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
DELETE FROM "student_enrollments" enrollment
|
|
||||||
USING "_course_split_source" source
|
|
||||||
WHERE enrollment."course_id" = source.source_course_id
|
|
||||||
AND enrollment."organization_name" = '영남대학교';
|
|
||||||
|
|
||||||
INSERT INTO "course_student_progress" ("course_id", "user_id", "completed_weeks", "total_weeks", "learning_hours", "commit_count", "average_score", "updated_at")
|
|
||||||
SELECT target.target_course_id, progress."user_id", progress."completed_weeks", progress."total_weeks", progress."learning_hours", progress."commit_count", progress."average_score", CURRENT_TIMESTAMP
|
|
||||||
FROM "course_student_progress" progress
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = progress."course_id"
|
|
||||||
JOIN "users" student ON student."id" = progress."user_id"
|
|
||||||
CROSS JOIN "_course_split_target" target
|
|
||||||
WHERE student."primary_organization_name" = '영남대학교'
|
|
||||||
ON CONFLICT ("course_id", "user_id") DO UPDATE
|
|
||||||
SET "completed_weeks" = EXCLUDED."completed_weeks",
|
|
||||||
"total_weeks" = EXCLUDED."total_weeks",
|
|
||||||
"learning_hours" = EXCLUDED."learning_hours",
|
|
||||||
"commit_count" = EXCLUDED."commit_count",
|
|
||||||
"average_score" = EXCLUDED."average_score",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
DELETE FROM "course_student_progress" progress
|
|
||||||
USING "_course_split_source" source, "users" student
|
|
||||||
WHERE progress."course_id" = source.source_course_id
|
|
||||||
AND student."id" = progress."user_id"
|
|
||||||
AND student."primary_organization_name" = '영남대학교';
|
|
||||||
|
|
||||||
UPDATE "instructor_curriculum_settings" setting
|
|
||||||
SET "course_id" = target.target_course_id,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM "_course_split_source" source, "_course_split_target" target
|
|
||||||
WHERE setting."course_id" = source.source_course_id
|
|
||||||
AND setting."organization_name" = '영남대학교';
|
|
||||||
|
|
||||||
INSERT INTO "instructor_curriculum_week_overrides" ("organization_id", "organization_name", "course_id", "week", "title", "duration", "overview", "deliverable", "topics", "topic_details", "updated_at")
|
|
||||||
SELECT override."organization_id", override."organization_name", target.target_course_id, override."week", override."title", override."duration", override."overview", override."deliverable", override."topics", override."topic_details", CURRENT_TIMESTAMP
|
|
||||||
FROM "instructor_curriculum_week_overrides" override
|
|
||||||
JOIN "_course_split_source" source ON source.source_course_id = override."course_id"
|
|
||||||
CROSS JOIN "_course_split_target" target
|
|
||||||
WHERE override."organization_name" = '영남대학교'
|
|
||||||
ON CONFLICT ("organization_name", "course_id", "week") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"duration" = EXCLUDED."duration",
|
|
||||||
"overview" = EXCLUDED."overview",
|
|
||||||
"deliverable" = EXCLUDED."deliverable",
|
|
||||||
"topics" = EXCLUDED."topics",
|
|
||||||
"topic_details" = EXCLUDED."topic_details",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
DELETE FROM "instructor_curriculum_week_overrides" override
|
|
||||||
USING "_course_split_source" source
|
|
||||||
WHERE override."course_id" = source.source_course_id
|
|
||||||
AND override."organization_name" = '영남대학교';
|
|
||||||
|
|
||||||
UPDATE "studio_repositories" repository
|
|
||||||
SET "week_id" = module_map.target_week_id,
|
|
||||||
"module_id" = module_map.target_module_id,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM "_course_split_module_map" module_map, "users" student
|
|
||||||
WHERE repository."module_id" = module_map.source_module_id
|
|
||||||
AND repository."user_id" = student."id"
|
|
||||||
AND student."primary_organization_name" = '영남대학교';
|
|
||||||
|
|
||||||
UPDATE "chat_threads" thread
|
|
||||||
SET "course_id" = target.target_course_id,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM "_course_split_source" source
|
|
||||||
JOIN "_course_split_target" target ON true
|
|
||||||
JOIN "users" student ON true
|
|
||||||
WHERE thread."course_id" = source.source_course_id
|
|
||||||
AND student."id" = thread."student_id"
|
|
||||||
AND student."primary_organization_name" = '영남대학교';
|
|
||||||
|
|
||||||
UPDATE "chat_threads" thread
|
|
||||||
SET "week_id" = week_map.target_week_id,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM "_course_split_week_map" week_map
|
|
||||||
JOIN "_course_split_target" target ON true
|
|
||||||
JOIN "users" student ON true
|
|
||||||
WHERE thread."course_id" = target.target_course_id
|
|
||||||
AND thread."week_id" = week_map.source_week_id
|
|
||||||
AND student."id" = thread."student_id"
|
|
||||||
AND student."primary_organization_name" = '영남대학교';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN "practice_contents" JSONB;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
ALTER TABLE "course_weeks"
|
|
||||||
ADD COLUMN IF NOT EXISTS "requires_source_submission" BOOLEAN NOT NULL DEFAULT true;
|
|
||||||
|
|
||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN IF NOT EXISTS "requires_source_submission" BOOLEAN DEFAULT true;
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "course_weeks"."requires_source_submission" IS '주차 제출 시 소스 커밋/푸시가 필요한지 여부.';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."requires_source_submission" IS '강사별 주차 소스 제출 필요 여부 오버라이드.';
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
CREATE TABLE IF NOT EXISTS "course_student_week_progress" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"submitted_at" TIMESTAMPTZ(3),
|
|
||||||
"commit_count" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
CONSTRAINT "course_student_week_progress_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "course_student_week_progress_course_user_week_key"
|
|
||||||
ON "course_student_week_progress"("course_id", "user_id", "week");
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS "course_student_week_progress_user_idx"
|
|
||||||
ON "course_student_week_progress"("user_id");
|
|
||||||
|
|
||||||
ALTER TABLE "course_student_week_progress"
|
|
||||||
ADD CONSTRAINT "course_student_week_progress_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "course_student_week_progress"
|
|
||||||
ADD CONSTRAINT "course_student_week_progress_user_id_fkey"
|
|
||||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_student_week_progress" IS '학생별 주차 학습 및 제출 요약.';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."submitted_at" IS '해당 주차 소스 제출 완료 시각.';
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
WITH week6_topics AS (
|
|
||||||
SELECT
|
|
||||||
w."course_id",
|
|
||||||
jsonb_agg(t."title" ORDER BY t."sort_order") AS topics,
|
|
||||||
jsonb_agg(t."detail" ORDER BY t."sort_order") AS topic_details
|
|
||||||
FROM "course_weeks" w
|
|
||||||
JOIN "course_week_topics" t ON t."week_id" = w."id"
|
|
||||||
WHERE w."week" = 6
|
|
||||||
GROUP BY w."course_id"
|
|
||||||
)
|
|
||||||
UPDATE "instructor_curriculum_week_overrides" override
|
|
||||||
SET
|
|
||||||
"topics" = week6_topics.topics,
|
|
||||||
"topic_details" = week6_topics.topic_details,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM week6_topics
|
|
||||||
WHERE override."course_id" = week6_topics."course_id"
|
|
||||||
AND override."week" = 6;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
WITH week6_content AS (
|
|
||||||
SELECT
|
|
||||||
w."course_id",
|
|
||||||
w."overview",
|
|
||||||
w."deliverable",
|
|
||||||
jsonb_agg(
|
|
||||||
jsonb_build_object(
|
|
||||||
'title', p."title",
|
|
||||||
'description', p."description"
|
|
||||||
)
|
|
||||||
ORDER BY p."sort_order"
|
|
||||||
) FILTER (WHERE p."id" IS NOT NULL) AS practice_contents
|
|
||||||
FROM "course_weeks" w
|
|
||||||
LEFT JOIN "course_week_practices" p ON p."week_id" = w."id"
|
|
||||||
WHERE w."week" = 6
|
|
||||||
GROUP BY w."course_id", w."overview", w."deliverable"
|
|
||||||
)
|
|
||||||
UPDATE "instructor_curriculum_week_overrides" override
|
|
||||||
SET
|
|
||||||
"overview" = week6_content."overview",
|
|
||||||
"deliverable" = week6_content."deliverable",
|
|
||||||
"practice_contents" = COALESCE(week6_content.practice_contents, '[]'::jsonb),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
FROM week6_content
|
|
||||||
WHERE override."course_id" = week6_content."course_id"
|
|
||||||
AND override."week" = 6;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
CREATE TABLE "course_week_references" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"title" TEXT NOT NULL,
|
|
||||||
"original_file_name" TEXT NOT NULL,
|
|
||||||
"stored_file_name" TEXT NOT NULL,
|
|
||||||
"content_type" TEXT,
|
|
||||||
"file_size_bytes" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "course_week_references_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX "course_week_references_week_id_idx" ON "course_week_references"("week_id");
|
|
||||||
CREATE UNIQUE INDEX "course_week_references_week_id_stored_file_name_key" ON "course_week_references"("week_id", "stored_file_name");
|
|
||||||
|
|
||||||
ALTER TABLE "course_week_references"
|
|
||||||
ADD CONSTRAINT "course_week_references_week_id_fkey"
|
|
||||||
FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN "disabled_module_ids" JSONB;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
CREATE TABLE "submission_scores" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"module_id" UUID NOT NULL,
|
|
||||||
"student_id" UUID NOT NULL,
|
|
||||||
"score" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "submission_scores_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "submission_scores_student_id_module_id_key" ON "submission_scores"("student_id", "module_id");
|
|
||||||
CREATE INDEX "submission_scores_course_id_idx" ON "submission_scores"("course_id");
|
|
||||||
CREATE INDEX "submission_scores_week_id_idx" ON "submission_scores"("week_id");
|
|
||||||
CREATE INDEX "submission_scores_module_id_idx" ON "submission_scores"("module_id");
|
|
||||||
|
|
||||||
ALTER TABLE "submission_scores" ADD CONSTRAINT "submission_scores_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "submission_scores" ADD CONSTRAINT "submission_scores_week_id_fkey" FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "submission_scores" ADD CONSTRAINT "submission_scores_module_id_fkey" FOREIGN KEY ("module_id") REFERENCES "course_week_modules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "submission_scores" ADD CONSTRAINT "submission_scores_student_id_fkey" FOREIGN KEY ("student_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
CREATE TABLE "submission_total_scores" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"student_id" UUID NOT NULL,
|
|
||||||
"score" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "submission_total_scores_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "submission_total_scores_student_id_week_id_key" ON "submission_total_scores"("student_id", "week_id");
|
|
||||||
CREATE INDEX "submission_total_scores_course_id_idx" ON "submission_total_scores"("course_id");
|
|
||||||
CREATE INDEX "submission_total_scores_week_id_idx" ON "submission_total_scores"("week_id");
|
|
||||||
|
|
||||||
ALTER TABLE "submission_total_scores" ADD CONSTRAINT "submission_total_scores_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "submission_total_scores" ADD CONSTRAINT "submission_total_scores_week_id_fkey" FOREIGN KEY ("week_id") REFERENCES "course_weeks"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "submission_total_scores" ADD CONSTRAINT "submission_total_scores_student_id_fkey" FOREIGN KEY ("student_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
CREATE TABLE "visualization_platforms" (
|
|
||||||
"platform_key" TEXT NOT NULL,
|
|
||||||
"type" TEXT NOT NULL,
|
|
||||||
"display_name" TEXT NOT NULL,
|
|
||||||
"description" TEXT,
|
|
||||||
"default_config" JSONB NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "visualization_platforms_pkey" PRIMARY KEY ("platform_key")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "visualization_platforms_type_key" ON "visualization_platforms"("type");
|
|
||||||
CREATE INDEX "visualization_platforms_enabled_sort_order_idx" ON "visualization_platforms"("enabled", "sort_order");
|
|
||||||
|
|
||||||
CREATE TABLE "course_week_visualization_platforms" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"platform_key" TEXT NOT NULL,
|
|
||||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "course_week_visualization_platforms_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "course_week_visualization_platforms_course_id_week_platform_key_key"
|
|
||||||
ON "course_week_visualization_platforms"("course_id", "week", "platform_key");
|
|
||||||
CREATE INDEX "course_week_visualization_platforms_course_id_week_idx"
|
|
||||||
ON "course_week_visualization_platforms"("course_id", "week");
|
|
||||||
CREATE INDEX "course_week_visualization_platforms_platform_key_idx"
|
|
||||||
ON "course_week_visualization_platforms"("platform_key");
|
|
||||||
|
|
||||||
ALTER TABLE "course_week_visualization_platforms"
|
|
||||||
ADD CONSTRAINT "course_week_visualization_platforms_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "course_week_visualization_platforms"
|
|
||||||
ADD CONSTRAINT "course_week_visualization_platforms_platform_key_fkey"
|
|
||||||
FOREIGN KEY ("platform_key") REFERENCES "visualization_platforms"("platform_key") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
INSERT INTO "visualization_platforms" ("platform_key", "type", "display_name", "description", "default_config", "sort_order", "updated_at")
|
|
||||||
VALUES
|
|
||||||
(
|
|
||||||
'AUTOWARE',
|
|
||||||
'AUTOWARE',
|
|
||||||
'Autoware',
|
|
||||||
'Autoware 기반 자율주행 시각화와 제어 실습 환경',
|
|
||||||
'{
|
|
||||||
"type": "AUTOWARE",
|
|
||||||
"runtime": { "containers": [], "steps": [] },
|
|
||||||
"bridge": {
|
|
||||||
"name": "autoware-ros-bridge",
|
|
||||||
"image": "autoware-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "autoware",
|
|
||||||
"MAP_POINT_STRIDE": "8",
|
|
||||||
"MAP_POINT_LIMIT": "80000",
|
|
||||||
"AUTOWARE_ROUTE_INPUT": "rviz_topic",
|
|
||||||
"INITIAL_POSE_TOPIC": "/initialpose",
|
|
||||||
"GOAL_POSE_TOPIC": "/planning/mission_planning/goal",
|
|
||||||
"VEHICLE_POSE_TOPIC": "/localization/kinematic_state",
|
|
||||||
"TRAJECTORY_TOPIC": "/planning/scenario_planning/trajectory",
|
|
||||||
"ROUTE_TOPIC": "/planning/mission_planning/route"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"viewer": { "stack": "autoware" }
|
|
||||||
}'::jsonb,
|
|
||||||
1,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'NAV2',
|
|
||||||
'NAV2',
|
|
||||||
'Nav2',
|
|
||||||
'Nav2 기반 주행 알고리즘 시각화 실습 환경',
|
|
||||||
'{
|
|
||||||
"type": "NAV2",
|
|
||||||
"runtime": { "containers": [], "steps": [] },
|
|
||||||
"bridge": {
|
|
||||||
"name": "nav2-ros-bridge",
|
|
||||||
"image": "nav2-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "nav2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"viewer": { "stack": "nav2" }
|
|
||||||
}'::jsonb,
|
|
||||||
2,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'SLAM',
|
|
||||||
'SLAM',
|
|
||||||
'SLAM',
|
|
||||||
'TurtleBot3 SLAM 지도 생성 시각화 실습 환경',
|
|
||||||
'{
|
|
||||||
"type": "SLAM",
|
|
||||||
"runtime": {
|
|
||||||
"containers": [
|
|
||||||
{ "name": "nav2-slam", "image": "nav2-slam:humble", "network": "host" },
|
|
||||||
{ "name": "slam-ros-bridge", "image": "slam-ros-bridge", "network": "host" }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bridge": { "url": "http://localhost:8765" },
|
|
||||||
"viewer": { "stack": "slam" },
|
|
||||||
"ros": {
|
|
||||||
"turtlebotModel": "burger"
|
|
||||||
}
|
|
||||||
}'::jsonb,
|
|
||||||
3,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
ON CONFLICT ("platform_key") DO UPDATE
|
|
||||||
SET "type" = EXCLUDED."type",
|
|
||||||
"display_name" = EXCLUDED."display_name",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"default_config" = EXCLUDED."default_config",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"enabled" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
INSERT INTO "course_week_visualization_platforms" ("course_id", "week", "platform_key", "enabled", "updated_at")
|
|
||||||
SELECT DISTINCT profile."course_id", profile."week", upper(profile."profile_type"), true, CURRENT_TIMESTAMP
|
|
||||||
FROM "course_environment_profiles" profile
|
|
||||||
JOIN "visualization_platforms" platform ON platform."platform_key" = upper(profile."profile_type")
|
|
||||||
WHERE profile."enabled" = true
|
|
||||||
ON CONFLICT ("course_id", "week", "platform_key") DO UPDATE
|
|
||||||
SET "enabled" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = '{
|
|
||||||
"type": "AUTOWARE",
|
|
||||||
"runtime": { "containers": [], "steps": [] },
|
|
||||||
"bridge": {
|
|
||||||
"name": "autoware-ros-bridge",
|
|
||||||
"image": "autoware-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "autoware",
|
|
||||||
"MAP_POINT_STRIDE": "8",
|
|
||||||
"MAP_POINT_LIMIT": "80000",
|
|
||||||
"AUTOWARE_ROUTE_INPUT": "rviz_topic",
|
|
||||||
"INITIAL_POSE_TOPIC": "/initialpose",
|
|
||||||
"GOAL_POSE_TOPIC": "/planning/mission_planning/goal",
|
|
||||||
"VEHICLE_POSE_TOPIC": "/localization/kinematic_state",
|
|
||||||
"TRAJECTORY_TOPIC": "/planning/scenario_planning/trajectory",
|
|
||||||
"ROUTE_TOPIC": "/planning/mission_planning/route"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"viewer": { "stack": "autoware" }
|
|
||||||
}'::jsonb,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'AUTOWARE';
|
|
||||||
|
|
||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = '{
|
|
||||||
"type": "NAV2",
|
|
||||||
"runtime": { "containers": [], "steps": [] },
|
|
||||||
"bridge": {
|
|
||||||
"name": "nav2-ros-bridge",
|
|
||||||
"image": "nav2-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "nav2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"viewer": { "stack": "nav2" }
|
|
||||||
}'::jsonb,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'NAV2';
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = '{
|
|
||||||
"type": "AUTOWARE",
|
|
||||||
"target": "local",
|
|
||||||
"runtime": { "containers": [], "steps": [] },
|
|
||||||
"bridge": {
|
|
||||||
"name": "autoware-ros-bridge",
|
|
||||||
"image": "autoware-ros-bridge",
|
|
||||||
"buildContext": "resources/autoware-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "autoware",
|
|
||||||
"MAP_POINT_STRIDE": "8",
|
|
||||||
"MAP_POINT_LIMIT": "80000",
|
|
||||||
"AUTOWARE_ROUTE_INPUT": "rviz_topic",
|
|
||||||
"INITIAL_POSE_TOPIC": "/initialpose",
|
|
||||||
"GOAL_POSE_TOPIC": "/planning/mission_planning/goal",
|
|
||||||
"VEHICLE_POSE_TOPIC": "/localization/kinematic_state",
|
|
||||||
"TRAJECTORY_TOPIC": "/planning/scenario_planning/trajectory",
|
|
||||||
"ROUTE_TOPIC": "/planning/mission_planning/route"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"viewer": { "stack": "autoware" }
|
|
||||||
}'::jsonb,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'AUTOWARE';
|
|
||||||
|
|
||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = '{
|
|
||||||
"type": "NAV2",
|
|
||||||
"target": "local",
|
|
||||||
"runtime": { "containers": [], "steps": [] },
|
|
||||||
"bridge": {
|
|
||||||
"name": "nav2-ros-bridge",
|
|
||||||
"image": "nav2-ros-bridge",
|
|
||||||
"buildContext": "resources/nav2-ros-bridge",
|
|
||||||
"port": 8765,
|
|
||||||
"env": {
|
|
||||||
"ROBOT_STACK": "nav2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"viewer": { "stack": "nav2" }
|
|
||||||
}'::jsonb,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'NAV2';
|
|
||||||
|
|
||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = jsonb_set("default_config", '{target}', '"local"'::jsonb, true),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'SLAM';
|
|
||||||
|
|
||||||
UPDATE "course_environment_profiles"
|
|
||||||
SET "config" = jsonb_set(
|
|
||||||
jsonb_set(
|
|
||||||
jsonb_set("config", '{target}', '"local"'::jsonb, true),
|
|
||||||
'{bridge,buildContext}',
|
|
||||||
'"resources/autoware-ros-bridge"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
'{bridge,image}',
|
|
||||||
'"autoware-ros-bridge"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE upper("profile_type") = 'AUTOWARE';
|
|
||||||
|
|
||||||
UPDATE "course_environment_profiles"
|
|
||||||
SET "config" = jsonb_set(
|
|
||||||
jsonb_set(
|
|
||||||
jsonb_set("config", '{target}', '"local"'::jsonb, true),
|
|
||||||
'{bridge,buildContext}',
|
|
||||||
'"resources/nav2-ros-bridge"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
'{bridge,image}',
|
|
||||||
'"nav2-ros-bridge"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE upper("profile_type") = 'NAV2';
|
|
||||||
|
|
||||||
UPDATE "course_environment_profiles"
|
|
||||||
SET "config" = jsonb_set("config", '{target}', '"local"'::jsonb, true),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE upper("profile_type") = 'SLAM';
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
CREATE TABLE "course_week_platforms" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"platform_id" UUID NOT NULL,
|
|
||||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "course_week_platforms_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "course_week_platforms_course_id_week_platform_id_key" ON "course_week_platforms"("course_id", "week", "platform_id");
|
|
||||||
CREATE INDEX "course_week_platforms_course_id_week_idx" ON "course_week_platforms"("course_id", "week");
|
|
||||||
CREATE INDEX "course_week_platforms_platform_id_idx" ON "course_week_platforms"("platform_id");
|
|
||||||
|
|
||||||
ALTER TABLE "course_week_platforms"
|
|
||||||
ADD CONSTRAINT "course_week_platforms_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "course_week_platforms"
|
|
||||||
ADD CONSTRAINT "course_week_platforms_platform_id_fkey"
|
|
||||||
FOREIGN KEY ("platform_id") REFERENCES "platforms"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
INSERT INTO "course_week_platforms" ("course_id", "week", "platform_id", "enabled")
|
|
||||||
SELECT cw."course_id", cw."week", cp."platform_id", true
|
|
||||||
FROM "course_weeks" cw
|
|
||||||
JOIN "course_platforms" cp ON cp."course_id" = cw."course_id"
|
|
||||||
ON CONFLICT ("course_id", "week", "platform_id") DO NOTHING;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN IF NOT EXISTS "tag_label" TEXT;
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS "course_week_platforms";
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
CREATE TABLE "curriculum_tag_options" (
|
|
||||||
"option_key" TEXT NOT NULL,
|
|
||||||
"label" TEXT NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
CONSTRAINT "curriculum_tag_options_pkey" PRIMARY KEY ("option_key")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "curriculum_tag_options_label_key" ON "curriculum_tag_options"("label");
|
|
||||||
CREATE INDEX "curriculum_tag_options_enabled_sort_order_idx" ON "curriculum_tag_options"("enabled", "sort_order");
|
|
||||||
|
|
||||||
INSERT INTO "curriculum_tag_options" ("option_key", "label", "sort_order", "enabled")
|
|
||||||
VALUES
|
|
||||||
('none', '없음', 0, true),
|
|
||||||
('simulation', '시뮬레이션', 10, true),
|
|
||||||
('mk-mini', 'MK-mini', 20, true),
|
|
||||||
('f1tenth', 'f1tenth', 30, true)
|
|
||||||
ON CONFLICT ("option_key") DO UPDATE SET
|
|
||||||
"label" = EXCLUDED."label",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"enabled" = EXCLUDED."enabled",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
ALTER TABLE "platforms"
|
|
||||||
ADD COLUMN IF NOT EXISTS "curriculum_tag_only" BOOLEAN NOT NULL DEFAULT false;
|
|
||||||
|
|
||||||
UPDATE "platforms"
|
|
||||||
SET "curriculum_tag_only" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE lower("platform_key") LIKE '%simulator%'
|
|
||||||
OR "display_name" ILIKE '%시뮬%'
|
|
||||||
OR lower("display_name") LIKE '%simulator%';
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS "curriculum_tag_options";
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
ALTER TABLE "platforms"
|
|
||||||
ALTER COLUMN "default_studio_settings"
|
|
||||||
SET DEFAULT '{"workspaceRoot":"~/autonomy-studio/workspaces","giteaBaseUrl":"http://localhost:3000","giteaOwner":"student"}';
|
|
||||||
|
|
||||||
UPDATE "platforms"
|
|
||||||
SET "default_studio_settings" = jsonb_set(
|
|
||||||
COALESCE("default_studio_settings", '{}'::jsonb),
|
|
||||||
'{giteaOwner}',
|
|
||||||
'"student"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE COALESCE("default_studio_settings"->>'giteaOwner', '') IN ('', 'students');
|
|
||||||
|
|
||||||
UPDATE "instructor_platforms"
|
|
||||||
SET "studio_settings_override" = jsonb_set(
|
|
||||||
"studio_settings_override",
|
|
||||||
'{giteaOwner}',
|
|
||||||
'"student"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "studio_settings_override" IS NOT NULL
|
|
||||||
AND "studio_settings_override"->>'giteaOwner' = 'students';
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
UPDATE "studio_repositories"
|
|
||||||
SET "repo_owner" = 'student',
|
|
||||||
"repository_url" = replace("repository_url", '/students/', '/student/')
|
|
||||||
WHERE "repo_owner" = 'students';
|
|
||||||
@@ -1,350 +0,0 @@
|
|||||||
COMMENT ON TABLE "users" IS 'LMS 내부 사용자 프로필. 인증 서버 사용자와 LMS 수강/강의/채팅 데이터를 연결한다.';
|
|
||||||
COMMENT ON COLUMN "users"."id" IS 'LMS 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "users"."auth_user_id" IS '인증 서버 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "users"."email" IS '사용자 이메일';
|
|
||||||
COMMENT ON COLUMN "users"."name" IS '사용자 이름';
|
|
||||||
COMMENT ON COLUMN "users"."auth_role" IS '인증 서버 기준 역할';
|
|
||||||
COMMENT ON COLUMN "users"."primary_organization_id" IS '대표 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "users"."primary_organization_name" IS '대표 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "users"."primary_organization_type" IS '대표 소속 조직 유형';
|
|
||||||
COMMENT ON COLUMN "users"."department" IS '부서 또는 학과';
|
|
||||||
COMMENT ON COLUMN "users"."position" IS '직책 또는 직위';
|
|
||||||
COMMENT ON COLUMN "users"."createdAt" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "users"."updatedAt" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "student_enrollments" IS '수강생과 강좌의 등록 관계. 기존 사용자는 새 계정 생성 없이 강좌에 연결된다.';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."id" IS '수강 등록 ID';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."user_id" IS '수강생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."course_id" IS '등록 강좌 ID';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."organization_id" IS '등록 기준 조직 ID';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."organization_name" IS '등록 기준 조직명';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."status" IS '수강 등록 상태';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."removed_at" IS '수강 등록 해제 시각';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "student_enrollments"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "student_groups" IS '강사가 관리하는 수강생 그룹. 강의 진행 상태와 플랫폼 배정의 기준이다.';
|
|
||||||
COMMENT ON COLUMN "student_groups"."id" IS '수강생 그룹 ID';
|
|
||||||
COMMENT ON COLUMN "student_groups"."organization_id" IS '그룹 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "student_groups"."organization_name" IS '그룹 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "student_groups"."name" IS '그룹명';
|
|
||||||
COMMENT ON COLUMN "student_groups"."week" IS '그룹 기본 주차 값';
|
|
||||||
COMMENT ON COLUMN "student_groups"."status" IS '그룹 운영 상태';
|
|
||||||
COMMENT ON COLUMN "student_groups"."platform_name" IS '그룹에 배정된 플랫폼 표시명';
|
|
||||||
COMMENT ON COLUMN "student_groups"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "student_groups"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "student_group_members" IS '수강생 그룹과 사용자 간의 멤버십.';
|
|
||||||
COMMENT ON COLUMN "student_group_members"."id" IS '그룹 멤버 ID';
|
|
||||||
COMMENT ON COLUMN "student_group_members"."group_id" IS '수강생 그룹 ID';
|
|
||||||
COMMENT ON COLUMN "student_group_members"."user_id" IS '그룹에 포함된 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "student_group_members"."created_at" IS '레코드 생성 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "courses" IS 'LMS 강좌 마스터. 주차별 커리큘럼과 수강생 등록의 상위 단위이다.';
|
|
||||||
COMMENT ON COLUMN "courses"."id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "courses"."slug" IS '강좌 식별 슬러그';
|
|
||||||
COMMENT ON COLUMN "courses"."title" IS '강좌명';
|
|
||||||
COMMENT ON COLUMN "courses"."subtitle" IS '강좌 부제목';
|
|
||||||
COMMENT ON COLUMN "courses"."description" IS '강좌 설명';
|
|
||||||
COMMENT ON COLUMN "courses"."organization_name" IS '강좌 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "courses"."teacher_id" IS '담당 강사 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "courses"."is_active" IS '강좌 활성 여부';
|
|
||||||
COMMENT ON COLUMN "courses"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "courses"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_weeks" IS '강좌별 주차 커리큘럼 기본 데이터.';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."course_id" IS '소속 강좌 ID';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."week" IS '주차 번호';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."title" IS '주차 제목';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."tags" IS '주차 태그 목록';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."progress" IS '기본 진행률';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."state" IS '주차 상태';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."duration" IS '예상 학습 시간';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."github" IS '관련 GitHub 또는 저장소 정보';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."overview" IS '주차 개요';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."deliverable" IS '제출 기준 또는 산출물 설명';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."requires_source_submission" IS '소스 제출 필요 여부';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_weeks"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_topics" IS '주차별 학습단계 목록.';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."id" IS '학습단계 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."title" IS '학습단계 제목';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."detail" IS '학습단계 상세 내용';
|
|
||||||
COMMENT ON COLUMN "course_week_topics"."sort_order" IS '학습단계 표시 순서';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_practices" IS '주차별 실습내용 목록.';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."id" IS '실습내용 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."title" IS '실습내용 제목';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."description" IS '실습내용 설명';
|
|
||||||
COMMENT ON COLUMN "course_week_practices"."sort_order" IS '실습내용 표시 순서';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_references" IS '주차별 참고자료 파일 메타데이터.';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."id" IS '참고자료 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."title" IS '참고자료 제목';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."original_file_name" IS '업로드 원본 파일명';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."stored_file_name" IS '서버 저장 파일명';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."content_type" IS '파일 MIME 타입';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."file_size_bytes" IS '파일 크기 바이트';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."sort_order" IS '참고자료 표시 순서';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_week_references"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_modules" IS '주차별 개발 스튜디오 모듈 목록.';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."id" IS '주차 모듈 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."sort_order" IS '모듈 표시 및 실행 순서';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."category" IS '모듈 분류';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."name" IS '모듈명';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."folder_name" IS '워크스페이스 폴더명';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."template_repository_url" IS '모듈 템플릿 저장소 URL';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."docker_image" IS '모듈 실행 Docker 이미지';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "instructor_curriculum_settings" IS '강사/그룹별 현재 진행 중인 주차와 학습단계 설정.';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."id" IS '강의 진행 설정 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."organization_id" IS '설정 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."organization_name" IS '설정 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."group_id" IS '진행 상태가 적용되는 수강생 그룹 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."active_week" IS '현재 진행 주차';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."active_learning_step" IS '현재 진행 학습단계 식별값';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_settings"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "instructor_curriculum_week_overrides" IS '강사 소속/강좌별 주차 커리큘럼 수정값.';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."id" IS '주차 커리큘럼 수정값 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."organization_id" IS '수정 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."organization_name" IS '수정 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."week" IS '주차 번호';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."tag_label" IS '주차 플랫폼/태그 표시 라벨';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."title" IS '수정된 주차 제목';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."duration" IS '수정된 예상 학습 시간';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."overview" IS '수정된 주차 개요';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."deliverable" IS '수정된 제출 기준';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."requires_source_submission" IS '소스 제출 필요 여부 수정값';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."topics" IS '수정된 학습단계 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."topic_details" IS '수정된 학습단계 상세 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."practice_contents" IS '수정된 실습내용 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."disabled_module_ids" IS '비활성화한 주차 모듈 ID 목록 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "platforms" IS '관리자가 정의하는 플랫폼 마스터. 강좌 태그와 강사 등록 플랫폼의 기준이다.';
|
|
||||||
COMMENT ON COLUMN "platforms"."id" IS '플랫폼 마스터 ID';
|
|
||||||
COMMENT ON COLUMN "platforms"."platform_key" IS '플랫폼 고유 키';
|
|
||||||
COMMENT ON COLUMN "platforms"."display_name" IS '플랫폼 표시명';
|
|
||||||
COMMENT ON COLUMN "platforms"."description" IS '플랫폼 설명';
|
|
||||||
COMMENT ON COLUMN "platforms"."organization_name" IS '플랫폼 기본 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "platforms"."icon_key" IS '플랫폼 아이콘 키';
|
|
||||||
COMMENT ON COLUMN "platforms"."is_active" IS '플랫폼 활성 여부';
|
|
||||||
COMMENT ON COLUMN "platforms"."curriculum_tag_only" IS '커리큘럼 태그 전용 여부';
|
|
||||||
COMMENT ON COLUMN "platforms"."vehicle_type" IS '차량 또는 장비 유형';
|
|
||||||
COMMENT ON COLUMN "platforms"."wheelbase_meters" IS '휠베이스 미터 단위';
|
|
||||||
COMMENT ON COLUMN "platforms"."max_speed_meters_per_second" IS '최대 속도 m/s';
|
|
||||||
COMMENT ON COLUMN "platforms"."max_steering_angle_degrees" IS '최대 조향각 도 단위';
|
|
||||||
COMMENT ON COLUMN "platforms"."sensors" IS '기본 센서 구성 JSON';
|
|
||||||
COMMENT ON COLUMN "platforms"."drivers" IS '기본 드라이버 구성 JSON';
|
|
||||||
COMMENT ON COLUMN "platforms"."algorithms" IS '기본 알고리즘 구성 JSON';
|
|
||||||
COMMENT ON COLUMN "platforms"."default_studio_settings" IS '강사 플랫폼 등록 시 복사되는 기본 개발 스튜디오 설정 JSON';
|
|
||||||
COMMENT ON COLUMN "platforms"."control_duration_minutes" IS '기본 제어권 유지 시간 분';
|
|
||||||
COMMENT ON COLUMN "platforms"."control_warning_minutes" IS '기본 제어권 만료 경고 시간 분';
|
|
||||||
COMMENT ON COLUMN "platforms"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "platforms"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "instructor_platforms" IS '강사가 실제 수업에 등록한 플랫폼 인스턴스.';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."id" IS '강사 등록 플랫폼 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."organization_id" IS '등록 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."organization_name" IS '등록 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."platform_id" IS '플랫폼 마스터 ID';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."display_name" IS '수업에서 사용할 플랫폼 표시명';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."instance_no" IS '동일 플랫폼 내 인스턴스 번호';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."status" IS '플랫폼 운영 상태';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."battery" IS '배터리 잔량 퍼센트';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."uptime_minutes" IS '가동 시간 분';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."speed_meters" IS '현재 속도 m/s';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."error_count" IS '오류 발생 수';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."emergency_stop" IS '긴급정지 여부';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."vehicle_type" IS '차량 또는 장비 유형';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."wheelbase_meters" IS '휠베이스 미터 단위';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."max_speed_meters_per_second" IS '최대 속도 m/s';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."max_steering_angle_degrees" IS '최대 조향각 도 단위';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."sensors" IS '강사 플랫폼 센서 설정 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."drivers" IS '강사 플랫폼 드라이버 정보 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."algorithms" IS '강사 플랫폼 알고리즘 설정 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."studio_settings_override" IS '플랫폼별 개발 스튜디오 설정 오버라이드 JSON';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."control_duration_minutes" IS '제어권 유지 시간 분';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."control_warning_minutes" IS '제어권 만료 경고 시간 분';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "instructor_platforms"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_platforms" IS '강좌에서 사용 가능한 플랫폼 목록.';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."id" IS '강좌 플랫폼 ID';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."platform_id" IS '플랫폼 마스터 ID';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."status" IS '강좌 내 플랫폼 표시 상태';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."action" IS '강좌 내 플랫폼 기본 액션 문구';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."state" IS '강좌 내 플랫폼 사용 상태 키';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."sort_order" IS '플랫폼 표시 순서';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_platforms"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_environment_profiles" IS '강좌/주차/플랫폼별 개발환경 실행 프로파일.';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."id" IS '개발환경 프로파일 ID';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."week" IS '주차 번호';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."platform_key" IS '대상 플랫폼 키';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."profile_type" IS '환경 프로파일 유형';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."name" IS '프로파일 이름';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."config" IS '컨테이너, 브릿지, 뷰어 실행 설정 JSON';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."enabled" IS '프로파일 사용 여부';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_environment_profiles"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "visualization_platforms" IS '시각화 화면에서 선택 가능한 주행/시뮬레이션 스택 마스터.';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."platform_key" IS '시각화 플랫폼 키';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."type" IS '시각화 스택 유형. AUTOWARE, NAV2, SLAM 등';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."display_name" IS '시각화 플랫폼 표시명';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."description" IS '시각화 플랫폼 설명';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."default_config" IS '기본 시각화 실행 설정 JSON';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."sort_order" IS '시각화 플랫폼 표시 순서';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."enabled" IS '시각화 플랫폼 사용 여부';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "visualization_platforms"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_week_visualization_platforms" IS '강좌 주차별 허용 시각화 플랫폼 선택값.';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."id" IS '주차 시각화 플랫폼 선택 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."week" IS '주차 번호';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."platform_key" IS '시각화 플랫폼 키';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."enabled" IS '주차에서 해당 시각화 플랫폼 사용 여부';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_week_visualization_platforms"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "platform_control_sessions" IS '플랫폼 제어권을 실제로 점유 중이거나 종료된 세션.';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."id" IS '제어권 세션 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."organization_id" IS '세션 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."organization_name" IS '세션 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."platform_id" IS '플랫폼 마스터 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."instructor_platform_id" IS '강사 등록 플랫폼 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."holder_user_id" IS '제어권 보유 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."group_id" IS '제어권 대상 수강생 그룹 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."status" IS '제어권 세션 상태';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."started_at" IS '제어권 시작 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."ends_at" IS '제어권 만료 예정 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."warning_at" IS '만료 경고 예정 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."warning_sent_at" IS '만료 경고 발송 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."released_at" IS '제어권 반납 또는 종료 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."end_reason" IS '제어권 종료 사유';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_sessions"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "platform_control_queue_entries" IS '플랫폼 제어권 대기 예약 목록.';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."id" IS '제어권 대기 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."organization_id" IS '대기 소속 조직 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."organization_name" IS '대기 소속 조직명';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."platform_id" IS '플랫폼 마스터 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."instructor_platform_id" IS '강사 등록 플랫폼 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."user_id" IS '대기 신청 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."group_id" IS '대기 대상 수강생 그룹 ID';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."status" IS '대기 예약 상태';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."requested_at" IS '대기 신청 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."granted_at" IS '제어권 배정 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."canceled_at" IS '대기 취소 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "platform_control_queue_entries"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_student_progress" IS '학생별 강좌 전체 학습 현황 집계.';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."id" IS '강좌 학습 현황 ID';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."user_id" IS '학생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."completed_weeks" IS '완료한 주차 수';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."total_weeks" IS '전체 주차 수';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."learning_hours" IS '누적 학습 시간 분';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."commit_count" IS '소스 제출 커밋 수';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."average_score" IS '평균 점수';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_student_progress"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "course_student_week_progress" IS '학생별 주차 제출 및 진행 현황.';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."id" IS '주차 학습 현황 ID';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."user_id" IS '학생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."week" IS '주차 번호';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."submitted_at" IS '학생 제출 완료 시각';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."commit_count" IS '해당 주차 제출 커밋 수';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "course_student_week_progress"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "studio_repositories" IS '학생별 주차 모듈 Gitea 저장소 매핑.';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."id" IS '스튜디오 저장소 매핑 ID';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."user_id" IS '학생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."module_id" IS '주차 모듈 ID';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."repo_owner" IS 'Gitea 저장소 owner 또는 조직';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."repo_name" IS 'Gitea 저장소명';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."repository_url" IS '학생 작업 저장소 URL';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."template_repository_url" IS '원본 템플릿 저장소 URL';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "studio_repositories"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "submission_scores" IS '학생 제출물의 모듈별 점수.';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."id" IS '모듈별 제출 점수 ID';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."module_id" IS '주차 모듈 ID';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."student_id" IS '학생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."score" IS '모듈별 점수';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "submission_scores"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "submission_total_scores" IS '학생 제출물의 주차별 총점.';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."id" IS '주차 제출 총점 ID';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."course_id" IS '강좌 ID';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."week_id" IS '주차 커리큘럼 ID';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."student_id" IS '학생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."score" IS '주차 제출 총점';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "submission_total_scores"."updated_at" IS '레코드 수정 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "chat_threads" IS '개발 스튜디오 질의응답 채팅방.';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."id" IS '채팅방 ID';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."course_id" IS '채팅 대상 강좌 ID';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."week_id" IS '채팅 대상 주차 ID';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."student_id" IS '질문 학생 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."assigned_instructor_id" IS '배정된 강사 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."type" IS '채팅방 유형. AI, 강사, 혼합';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."status" IS '채팅방 처리 상태';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."title" IS '채팅방 제목';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."updated_at" IS '레코드 수정 시각';
|
|
||||||
COMMENT ON COLUMN "chat_threads"."last_message_at" IS '마지막 메시지 작성 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "chat_messages" IS '개발 스튜디오 질의응답 메시지.';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."id" IS '채팅 메시지 ID';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."thread_id" IS '소속 채팅방 ID';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."sender_type" IS '메시지 발신자 유형';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."sender_user_id" IS '발신 사용자 ID';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."content" IS '메시지 내용';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."content_format" IS '메시지 내용 형식';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."metadata" IS '메시지 부가 메타데이터 JSON';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."created_at" IS '레코드 생성 시각';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."deleted_at" IS '메시지 삭제 시각';
|
|
||||||
|
|
||||||
COMMENT ON TABLE "ai_chat_runs" IS 'AI 채팅 응답 생성 실행 로그.';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."id" IS 'AI 실행 로그 ID';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."message_id" IS 'AI 응답 메시지 ID';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."model" IS '사용한 AI 모델명';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."prompt_tokens" IS '프롬프트 토큰 수';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."completion_tokens" IS '응답 토큰 수';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."total_tokens" IS '전체 토큰 수';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."latency_ms" IS 'AI 응답 지연 시간 밀리초';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."status" IS 'AI 실행 상태';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."error" IS 'AI 실행 오류 메시지';
|
|
||||||
COMMENT ON COLUMN "ai_chat_runs"."created_at" IS '레코드 생성 시각';
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
ALTER TABLE "student_groups"
|
|
||||||
ADD COLUMN "instructor_platform_id" UUID;
|
|
||||||
|
|
||||||
UPDATE "student_groups" AS g
|
|
||||||
SET "instructor_platform_id" = p."id"
|
|
||||||
FROM "instructor_platforms" AS p
|
|
||||||
WHERE g."platform_name" IS NOT NULL
|
|
||||||
AND g."platform_name" = p."display_name"
|
|
||||||
AND (
|
|
||||||
g."organization_id" = p."organization_id"
|
|
||||||
OR g."organization_name" = p."organization_name"
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX "student_groups_instructor_platform_id_idx"
|
|
||||||
ON "student_groups"("instructor_platform_id");
|
|
||||||
|
|
||||||
ALTER TABLE "student_groups"
|
|
||||||
ADD CONSTRAINT "student_groups_instructor_platform_id_fkey"
|
|
||||||
FOREIGN KEY ("instructor_platform_id")
|
|
||||||
REFERENCES "instructor_platforms"("id")
|
|
||||||
ON DELETE SET NULL
|
|
||||||
ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "student_groups"."platform_name" IS '기존 호환용 플랫폼 표시명. 실제 배정 참조는 instructor_platform_id를 사용한다.';
|
|
||||||
COMMENT ON COLUMN "student_groups"."instructor_platform_id" IS '그룹에 배정된 강사 등록 플랫폼 ID';
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN "topic_video_urls" JSONB;
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."topic_video_urls" IS '학습단계별 동영상 강의 URL 목록';
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
ALTER TABLE "course_week_references"
|
|
||||||
ADD COLUMN IF NOT EXISTS "kind" TEXT NOT NULL DEFAULT 'REFERENCE';
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS "course_week_references_week_id_kind_idx"
|
|
||||||
ON "course_week_references"("week_id", "kind");
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "course_week_references"."kind" IS '주차 파일 자료 유형. REFERENCE는 참고자료, EXAMPLE_CODE는 예제 코드 파일입니다.';
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
CREATE TYPE "ChatMessageStatus" AS ENUM ('ACTIVE', 'CANCELED');
|
|
||||||
|
|
||||||
ALTER TABLE "chat_messages"
|
|
||||||
ADD COLUMN "status" "ChatMessageStatus" NOT NULL DEFAULT 'ACTIVE',
|
|
||||||
ADD COLUMN "canceled_at" TIMESTAMPTZ(3);
|
|
||||||
|
|
||||||
CREATE INDEX "chat_messages_thread_id_status_created_at_idx"
|
|
||||||
ON "chat_messages"("thread_id", "status", "created_at");
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "chat_messages"."status" IS '채팅 메시지 처리 상태. AI 응답 중단 시 CANCELED로 표시해 다음 AI 컨텍스트에서 제외한다.';
|
|
||||||
COMMENT ON COLUMN "chat_messages"."canceled_at" IS 'AI 응답이 사용자에 의해 중단된 시각.';
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
ALTER TABLE "course_week_modules"
|
|
||||||
ADD COLUMN IF NOT EXISTS "source_type" TEXT NOT NULL DEFAULT 'GIT',
|
|
||||||
ADD COLUMN IF NOT EXISTS "prebuilt_artifacts" JSONB NOT NULL DEFAULT '[]';
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."source_type" IS '모듈 소스 제공 방식입니다. 기본값 GIT은 template_repository_url을 사용합니다.';
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."prebuilt_artifacts" IS 'R2 등 외부 저장소에 둔 모듈별 사전 빌드 산출물 목록입니다. build/install tar 아카이브를 기록합니다.';
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
UPDATE "course_environment_profiles"
|
|
||||||
SET "config" = jsonb_set(
|
|
||||||
jsonb_set("config", '{bridge,image}', '"nav2-slam:humble"'::jsonb, true),
|
|
||||||
'{bridge,buildContext}',
|
|
||||||
'"resources/slam-ros-bridge"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE upper("profile_type") = 'SLAM';
|
|
||||||
|
|
||||||
UPDATE "course_environment_profiles"
|
|
||||||
SET "config" = jsonb_set(
|
|
||||||
"config",
|
|
||||||
'{runtime,containers}',
|
|
||||||
COALESCE((
|
|
||||||
SELECT jsonb_agg(container)
|
|
||||||
FROM jsonb_array_elements("config" #> '{runtime,containers}') AS container
|
|
||||||
WHERE container->>'name' <> 'slam-ros-bridge'
|
|
||||||
), '[]'::jsonb),
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE upper("profile_type") = 'SLAM'
|
|
||||||
AND jsonb_typeof("config" #> '{runtime,containers}') = 'array';
|
|
||||||
|
|
||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = jsonb_set(
|
|
||||||
jsonb_set("default_config", '{bridge,image}', '"nav2-slam:humble"'::jsonb, true),
|
|
||||||
'{bridge,buildContext}',
|
|
||||||
'"resources/slam-ros-bridge"'::jsonb,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'SLAM';
|
|
||||||
|
|
||||||
UPDATE "visualization_platforms"
|
|
||||||
SET "default_config" = jsonb_set(
|
|
||||||
"default_config",
|
|
||||||
'{runtime,containers}',
|
|
||||||
COALESCE((
|
|
||||||
SELECT jsonb_agg(container)
|
|
||||||
FROM jsonb_array_elements("default_config" #> '{runtime,containers}') AS container
|
|
||||||
WHERE container->>'name' <> 'slam-ros-bridge'
|
|
||||||
), '[]'::jsonb),
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" = 'SLAM'
|
|
||||||
AND jsonb_typeof("default_config" #> '{runtime,containers}') = 'array';
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
INSERT INTO "users" ("id", "email", "name", "auth_role", "primary_organization_name", "createdAt", "updatedAt")
|
|
||||||
VALUES (gen_random_uuid(), 'system@autonomystudio.local', 'Autonomy Studio', 'SYSTEM', 'Autonomy Studio', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
||||||
ON CONFLICT ("email") DO UPDATE
|
|
||||||
SET "name" = EXCLUDED."name",
|
|
||||||
"auth_role" = EXCLUDED."auth_role",
|
|
||||||
"primary_organization_name" = EXCLUDED."primary_organization_name",
|
|
||||||
"updatedAt" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH teacher AS (
|
|
||||||
SELECT "id" FROM "users" WHERE "email" = 'system@autonomystudio.local'
|
|
||||||
)
|
|
||||||
INSERT INTO "courses" ("slug", "title", "subtitle", "description", "organization_name", "teacher_id", "is_active", "updated_at")
|
|
||||||
SELECT
|
|
||||||
'autonomy-studio-lms-guide',
|
|
||||||
'NAV2 알아보기',
|
|
||||||
'처음 사용하는 수강생을 위한 짧은 체험 강좌',
|
|
||||||
'강좌 배정 전 신규 사용자가 LMS 화면 흐름을 빠르게 익힐 수 있는 공통 체험 강좌입니다. NAV2 개념을 예시로 커리큘럼 확인, 학습단계 열람, 질의응답 사용법만 간단히 안내합니다.',
|
|
||||||
'Autonomy Studio',
|
|
||||||
teacher."id",
|
|
||||||
true,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM teacher
|
|
||||||
ON CONFLICT ("slug") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"subtitle" = EXCLUDED."subtitle",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"organization_name" = EXCLUDED."organization_name",
|
|
||||||
"teacher_id" = EXCLUDED."teacher_id",
|
|
||||||
"is_active" = true,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
WITH course AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'autonomy-studio-lms-guide'
|
|
||||||
)
|
|
||||||
INSERT INTO "course_weeks" (
|
|
||||||
"course_id",
|
|
||||||
"week",
|
|
||||||
"title",
|
|
||||||
"tags",
|
|
||||||
"progress",
|
|
||||||
"state",
|
|
||||||
"duration",
|
|
||||||
"github",
|
|
||||||
"overview",
|
|
||||||
"deliverable",
|
|
||||||
"requires_source_submission",
|
|
||||||
"updated_at"
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
course."id",
|
|
||||||
1,
|
|
||||||
'NAV2 알아보기',
|
|
||||||
ARRAY['NAV2','진행중']::TEXT[],
|
|
||||||
0,
|
|
||||||
'current',
|
|
||||||
'15분',
|
|
||||||
'제출 없음',
|
|
||||||
'NAV2가 어떤 역할을 하는지 간단히 살펴보고, LMS에서 강좌 내용과 학습단계, 질문 기능을 어떻게 사용하는지 확인합니다.',
|
|
||||||
'이 강좌는 체험용이므로 별도 제출은 없습니다. 실제 수업 강좌가 배정되면 해당 강좌가 우선 표시됩니다.',
|
|
||||||
false,
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
FROM course
|
|
||||||
ON CONFLICT ("course_id", "week") DO UPDATE
|
|
||||||
SET "title" = EXCLUDED."title",
|
|
||||||
"tags" = EXCLUDED."tags",
|
|
||||||
"progress" = EXCLUDED."progress",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"duration" = EXCLUDED."duration",
|
|
||||||
"github" = EXCLUDED."github",
|
|
||||||
"overview" = EXCLUDED."overview",
|
|
||||||
"deliverable" = EXCLUDED."deliverable",
|
|
||||||
"requires_source_submission" = false,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
|
|
||||||
DELETE FROM "course_weeks"
|
|
||||||
USING "courses"
|
|
||||||
WHERE "course_weeks"."course_id" = "courses"."id"
|
|
||||||
AND "courses"."slug" = 'autonomy-studio-lms-guide'
|
|
||||||
AND "course_weeks"."week" <> 1;
|
|
||||||
|
|
||||||
DELETE FROM "course_week_practices"
|
|
||||||
USING "course_weeks", "courses"
|
|
||||||
WHERE "course_week_practices"."week_id" = "course_weeks"."id"
|
|
||||||
AND "course_weeks"."course_id" = "courses"."id"
|
|
||||||
AND "courses"."slug" = 'autonomy-studio-lms-guide';
|
|
||||||
|
|
||||||
DELETE FROM "course_week_topics"
|
|
||||||
USING "course_weeks", "courses"
|
|
||||||
WHERE "course_week_topics"."week_id" = "course_weeks"."id"
|
|
||||||
AND "course_weeks"."course_id" = "courses"."id"
|
|
||||||
AND "courses"."slug" = 'autonomy-studio-lms-guide';
|
|
||||||
|
|
||||||
WITH week1 AS (
|
|
||||||
SELECT w."id" AS week_id
|
|
||||||
FROM "course_weeks" w
|
|
||||||
JOIN "courses" c ON c."id" = w."course_id"
|
|
||||||
WHERE c."slug" = 'autonomy-studio-lms-guide' AND w."week" = 1
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_topics" ("week_id", "sort_order", "title", "detail")
|
|
||||||
SELECT week1.week_id, data.sort_order, data.title, data.detail
|
|
||||||
FROM week1
|
|
||||||
CROSS JOIN (
|
|
||||||
VALUES
|
|
||||||
(1, 'NAV2란 무엇인가', 'NAV2는 ROS 2 기반 로봇 내비게이션 스택입니다. 지도, 위치추정, 경로계획, 제어 흐름이 어떻게 이어지는지 큰 그림을 확인합니다.'),
|
|
||||||
(2, 'LMS에서 강좌 읽기', '주차 카드와 학습단계를 열어 강의 내용, 참고 자료, 진행 상태를 확인하는 방법을 익힙니다.'),
|
|
||||||
(3, '질문 기능 사용하기', '학습 중 궁금한 내용을 AI 또는 강사에게 질문하고, 답변을 확인하는 기본 흐름을 살펴봅니다.')
|
|
||||||
) AS data(sort_order, title, detail);
|
|
||||||
|
|
||||||
WITH week1 AS (
|
|
||||||
SELECT w."id" AS week_id
|
|
||||||
FROM "course_weeks" w
|
|
||||||
JOIN "courses" c ON c."id" = w."course_id"
|
|
||||||
WHERE c."slug" = 'autonomy-studio-lms-guide' AND w."week" = 1
|
|
||||||
)
|
|
||||||
INSERT INTO "course_week_practices" ("week_id", "sort_order", "title", "description")
|
|
||||||
SELECT week1.week_id, data.sort_order, data.title, data.description
|
|
||||||
FROM week1
|
|
||||||
CROSS JOIN (
|
|
||||||
VALUES
|
|
||||||
(1, '화면 둘러보기', '커리큘럼 화면에서 주차 카드, 학습단계, 강의 내용을 차례로 확인합니다.'),
|
|
||||||
(2, '질문 남겨보기', '질의응답 화면에서 NAV2 관련 질문을 입력하는 위치를 확인합니다.')
|
|
||||||
) AS data(sort_order, title, description);
|
|
||||||
|
|
||||||
WITH guide AS (
|
|
||||||
SELECT "id" FROM "courses" WHERE "slug" = 'autonomy-studio-lms-guide'
|
|
||||||
),
|
|
||||||
local_platform AS (
|
|
||||||
SELECT "id" FROM "platforms" WHERE "platform_key" = 'futuredrive-local-pc'
|
|
||||||
)
|
|
||||||
INSERT INTO "course_platforms" ("course_id", "platform_id", "status", "action", "state", "sort_order", "updated_at")
|
|
||||||
SELECT guide."id", local_platform."id", '사용 가능', '연결', 'ready', 1, CURRENT_TIMESTAMP
|
|
||||||
FROM guide
|
|
||||||
CROSS JOIN local_platform
|
|
||||||
ON CONFLICT ("course_id", "platform_id") DO UPDATE
|
|
||||||
SET "status" = EXCLUDED."status",
|
|
||||||
"action" = EXCLUDED."action",
|
|
||||||
"state" = EXCLUDED."state",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN IF NOT EXISTS "learning_steps" JSONB;
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "instructor_curriculum_week_overrides"."learning_steps" IS '학습단계별 제목, 설명, 동영상, 핵심개념을 묶은 JSON 목록.';
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE "course_week_modules"
|
|
||||||
ADD COLUMN IF NOT EXISTS "default_parameters" JSONB NOT NULL DEFAULT '[]';
|
|
||||||
|
|
||||||
COMMENT ON COLUMN "course_week_modules"."default_parameters" IS '주차 모듈 실행 시 적용하는 기본 ROS 파라미터 배열.';
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
CREATE TABLE "course_maps" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"key" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"description" TEXT,
|
|
||||||
"platform_key" TEXT NOT NULL DEFAULT 'NAV2',
|
|
||||||
"world_file" TEXT NOT NULL DEFAULT 'empty_world.world',
|
|
||||||
"viewer_model" TEXT NOT NULL DEFAULT 'turtlebot3_world',
|
|
||||||
"layout" JSONB NOT NULL DEFAULT '{"version":1,"objects":[]}',
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
CONSTRAINT "course_maps_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "course_week_maps" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"map_key" TEXT NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
CONSTRAINT "course_week_maps_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "course_maps_course_id_key_key" ON "course_maps"("course_id", "key");
|
|
||||||
CREATE INDEX "course_maps_course_id_platform_key_idx" ON "course_maps"("course_id", "platform_key");
|
|
||||||
CREATE UNIQUE INDEX "course_week_maps_course_id_week_map_key_key" ON "course_week_maps"("course_id", "week", "map_key");
|
|
||||||
CREATE INDEX "course_week_maps_course_id_week_idx" ON "course_week_maps"("course_id", "week");
|
|
||||||
|
|
||||||
ALTER TABLE "course_maps" ADD CONSTRAINT "course_maps_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "course_week_maps" ADD CONSTRAINT "course_week_maps_course_id_fkey" FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
INSERT INTO "course_week_maps" ("id", "course_id", "week", "map_key", "sort_order", "updated_at")
|
|
||||||
SELECT gen_random_uuid(), "course_id", "week", 'nav2-turtlebot3-world', 0, CURRENT_TIMESTAMP
|
|
||||||
FROM "course_week_visualization_platforms"
|
|
||||||
WHERE "platform_key" = 'NAV2' AND "enabled" = true
|
|
||||||
ON CONFLICT ("course_id", "week", "map_key") DO NOTHING;
|
|
||||||
|
|
||||||
INSERT INTO "course_week_maps" ("id", "course_id", "week", "map_key", "sort_order", "updated_at")
|
|
||||||
SELECT gen_random_uuid(), "course_id", "week", 'nav2-turtlebot3-house', 1, CURRENT_TIMESTAMP
|
|
||||||
FROM "course_week_visualization_platforms"
|
|
||||||
WHERE "platform_key" = 'NAV2' AND "enabled" = true
|
|
||||||
ON CONFLICT ("course_id", "week", "map_key") DO NOTHING;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
CREATE TABLE "student_saved_maps" (
|
|
||||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"source_map_key" TEXT NOT NULL,
|
|
||||||
"world_file" TEXT NOT NULL,
|
|
||||||
"viewer_model" TEXT NOT NULL,
|
|
||||||
"yaml_path" TEXT NOT NULL,
|
|
||||||
"pgm_path" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
CONSTRAINT "student_saved_maps_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_saved_maps_user_id_course_id_week_yaml_path_key"
|
|
||||||
ON "student_saved_maps"("user_id", "course_id", "week", "yaml_path");
|
|
||||||
CREATE INDEX "student_saved_maps_user_id_course_id_week_created_at_idx"
|
|
||||||
ON "student_saved_maps"("user_id", "course_id", "week", "created_at");
|
|
||||||
|
|
||||||
ALTER TABLE "student_saved_maps"
|
|
||||||
ADD CONSTRAINT "student_saved_maps_user_id_fkey"
|
|
||||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "student_saved_maps"
|
|
||||||
ADD CONSTRAINT "student_saved_maps_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
INSERT INTO "visualization_platforms" (
|
|
||||||
"platform_key", "type", "display_name", "description", "default_config", "sort_order", "enabled", "created_at", "updated_at"
|
|
||||||
) VALUES
|
|
||||||
(
|
|
||||||
'REAL_SLAM',
|
|
||||||
'REAL_SLAM',
|
|
||||||
'실차 SLAM',
|
|
||||||
'원격 플랫폼의 실제 센서로 지도를 생성합니다.',
|
|
||||||
'{"type":"REAL_SLAM","target":"remote-jetson","runtimeKind":"real","viewer":{"stack":"nav2","mode":"slam"},"bridge":{"name":"nav2-ros-bridge","port":8765,"image":"slam-ros-bridge:latest","env":{"ROBOT_STACK":"nav2"}},"runtimeImages":[{"url":"https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/slam-ros-bridge/latest/slam-ros-bridge-latest.tar.zst","image":"slam-ros-bridge:latest","fileName":"slam-ros-bridge-latest.tar.zst","required":true}]}'::jsonb,
|
|
||||||
30, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'REAL_NAVIGATION',
|
|
||||||
'REAL_NAVIGATION',
|
|
||||||
'실차 자율주행',
|
|
||||||
'실차 SLAM 결과 맵에서 초기 위치와 목표점을 지정해 주행합니다.',
|
|
||||||
'{"type":"REAL_NAVIGATION","target":"remote-jetson","runtimeKind":"real","viewer":{"stack":"nav2","mode":"navigation"},"bridge":{"name":"nav2-ros-bridge","port":8765,"image":"slam-ros-bridge:latest","env":{"ROBOT_STACK":"nav2"}},"runtimeImages":[{"url":"https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/slam-ros-bridge/latest/slam-ros-bridge-latest.tar.zst","image":"slam-ros-bridge:latest","fileName":"slam-ros-bridge-latest.tar.zst","required":true}]}'::jsonb,
|
|
||||||
31, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
ON CONFLICT ("platform_key") DO UPDATE SET
|
|
||||||
"type" = EXCLUDED."type",
|
|
||||||
"display_name" = EXCLUDED."display_name",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"default_config" = EXCLUDED."default_config",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"enabled" = EXCLUDED."enabled",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
UPDATE "visualization_platforms"
|
|
||||||
SET
|
|
||||||
"display_name" = CASE "platform_key"
|
|
||||||
WHEN 'REAL_SLAM' THEN '플랫폼 SLAM'
|
|
||||||
WHEN 'REAL_NAVIGATION' THEN '플랫폼 자율주행'
|
|
||||||
END,
|
|
||||||
"description" = CASE "platform_key"
|
|
||||||
WHEN 'REAL_SLAM' THEN '원격 플랫폼의 센서로 지도를 생성합니다.'
|
|
||||||
WHEN 'REAL_NAVIGATION' THEN '플랫폼 SLAM 결과 맵에서 초기 위치와 목표점을 지정해 주행합니다.'
|
|
||||||
END,
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP
|
|
||||||
WHERE "platform_key" IN ('REAL_SLAM', 'REAL_NAVIGATION');
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "platform_control_sessions"
|
|
||||||
ADD COLUMN "extension_count" INTEGER NOT NULL DEFAULT 0;
|
|
||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
INSERT INTO "visualization_platforms" (
|
|
||||||
"platform_key", "type", "display_name", "description", "default_config", "sort_order", "enabled", "created_at", "updated_at"
|
|
||||||
) VALUES (
|
|
||||||
'REAL_SLAM_3D',
|
|
||||||
'REAL_SLAM_3D',
|
|
||||||
'플랫폼 3D SLAM',
|
|
||||||
'플랫폼의 3D LiDAR 포인트클라우드와 ICP 위치를 읽기 전용으로 표시합니다.',
|
|
||||||
'{"type":"REAL_SLAM_3D","target":"remote-jetson","runtimeKind":"real","viewer":{"stack":"nav2","mode":"slam3d"},"bridge":{"name":"nav2-ros-bridge","port":8765,"image":"slam-ros-bridge:jetson_orin-3d","env":{"ROBOT_STACK":"nav2","READ_ONLY":"true","ODOM_TOPIC":"/odom","ICP_ODOM_TOPIC":"/icp_odom","SCAN_TOPIC":"/scan","POINT_CLOUD_TOPIC":"/cloud_map","FALLBACK_POINT_CLOUD_TOPIC":"/lidar_filtered"}},"runtimeImages":[{"url":"https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/slam-ros-bridge/jetson-orin-3d/slam-ros-bridge-jetson-orin-3d.tar.zst","image":"slam-ros-bridge:jetson_orin-3d","fileName":"slam-ros-bridge-jetson-orin-3d.tar.zst","required":true}]}'::jsonb,
|
|
||||||
32, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
ON CONFLICT ("platform_key") DO UPDATE SET
|
|
||||||
"type" = EXCLUDED."type",
|
|
||||||
"display_name" = EXCLUDED."display_name",
|
|
||||||
"description" = EXCLUDED."description",
|
|
||||||
"default_config" = EXCLUDED."default_config",
|
|
||||||
"sort_order" = EXCLUDED."sort_order",
|
|
||||||
"enabled" = EXCLUDED."enabled",
|
|
||||||
"updated_at" = CURRENT_TIMESTAMP;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "student_saved_maps"
|
|
||||||
ADD COLUMN "database_path" TEXT;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "student_saved_maps"
|
|
||||||
ADD COLUMN "preview_pgm" TEXT;
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
DO $$
|
|
||||||
DECLARE
|
|
||||||
target record;
|
|
||||||
BEGIN
|
|
||||||
FOR target IN
|
|
||||||
SELECT * FROM (VALUES
|
|
||||||
('student_enrollments', 'created_at'),
|
|
||||||
('student_enrollments', 'updated_at'),
|
|
||||||
('student_enrollments', 'removed_at'),
|
|
||||||
('student_groups', 'created_at'),
|
|
||||||
('student_groups', 'updated_at'),
|
|
||||||
('student_group_members', 'created_at')
|
|
||||||
) AS targets(table_name, column_name)
|
|
||||||
LOOP
|
|
||||||
IF EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM information_schema.columns
|
|
||||||
WHERE table_schema = 'public'
|
|
||||||
AND table_name = target.table_name
|
|
||||||
AND column_name = target.column_name
|
|
||||||
AND data_type = 'timestamp without time zone'
|
|
||||||
) THEN
|
|
||||||
EXECUTE format(
|
|
||||||
'ALTER TABLE %I ALTER COLUMN %I TYPE TIMESTAMPTZ(3) USING %I AT TIME ZONE ''UTC''',
|
|
||||||
target.table_name,
|
|
||||||
target.column_name,
|
|
||||||
target.column_name
|
|
||||||
);
|
|
||||||
END IF;
|
|
||||||
END LOOP;
|
|
||||||
END $$;
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
CREATE TABLE "student_custom_modules" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL,
|
|
||||||
"category" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"folder_name" TEXT NOT NULL,
|
|
||||||
"docker_image" TEXT,
|
|
||||||
"package_name" TEXT NOT NULL,
|
|
||||||
"launch_file" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "student_custom_modules_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_custom_modules_user_id_course_id_week_folder_name_key"
|
|
||||||
ON "student_custom_modules"("user_id", "course_id", "week", "folder_name");
|
|
||||||
|
|
||||||
CREATE INDEX "student_custom_modules_user_id_course_id_week_sort_order_idx"
|
|
||||||
ON "student_custom_modules"("user_id", "course_id", "week", "sort_order");
|
|
||||||
|
|
||||||
ALTER TABLE "student_custom_modules"
|
|
||||||
ADD CONSTRAINT "student_custom_modules_user_id_fkey"
|
|
||||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE "student_custom_modules"
|
|
||||||
ADD CONSTRAINT "student_custom_modules_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
UPDATE "student_custom_modules"
|
|
||||||
SET "folder_name" = '[lms-nav2-custom]' || "package_name" || '_v1.0.0_ws'
|
|
||||||
WHERE "folder_name" <> '[lms-nav2-custom]' || "package_name" || '_v1.0.0_ws';
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
ALTER TABLE "student_saved_maps"
|
|
||||||
ADD COLUMN "runtime_kind" TEXT NOT NULL DEFAULT 'SIMULATION';
|
|
||||||
|
|
||||||
UPDATE "student_saved_maps"
|
|
||||||
SET "runtime_kind" = CASE
|
|
||||||
WHEN "database_path" IS NOT NULL THEN 'PLATFORM_3D'
|
|
||||||
WHEN "source_map_key" = 'nav2-platform-slam' THEN 'PLATFORM_2D'
|
|
||||||
ELSE 'SIMULATION'
|
|
||||||
END;
|
|
||||||
|
|
||||||
CREATE INDEX "student_saved_maps_user_id_course_id_week_runtime_kind_idx"
|
|
||||||
ON "student_saved_maps"("user_id", "course_id", "week", "runtime_kind");
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE "student_saved_maps"
|
|
||||||
ADD COLUMN "yaml_url" TEXT,
|
|
||||||
ADD COLUMN "pgm_url" TEXT,
|
|
||||||
ADD COLUMN "database_url" TEXT;
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
ALTER TABLE "student_custom_modules"
|
|
||||||
ADD COLUMN "runtime_target" TEXT NOT NULL DEFAULT 'LOCAL';
|
|
||||||
|
|
||||||
DROP INDEX "student_custom_modules_user_id_course_id_week_folder_name_key";
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_custom_modules_user_id_course_id_week_folder_name_runtime_target_key"
|
|
||||||
ON "student_custom_modules"("user_id", "course_id", "week", "folder_name", "runtime_target");
|
|
||||||
|
|
||||||
CREATE INDEX "student_custom_modules_user_id_course_id_week_runtime_target_sort_order_idx"
|
|
||||||
ON "student_custom_modules"("user_id", "course_id", "week", "runtime_target", "sort_order");
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
ALTER TABLE "instructor_curriculum_week_overrides"
|
|
||||||
ADD COLUMN "is_team_project" BOOLEAN DEFAULT false;
|
|
||||||
|
|
||||||
CREATE TABLE "team_studio_repositories" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"group_id" UUID NOT NULL,
|
|
||||||
"week_id" UUID NOT NULL,
|
|
||||||
"module_id" UUID NOT NULL,
|
|
||||||
"repo_owner" TEXT NOT NULL,
|
|
||||||
"repo_name" TEXT NOT NULL,
|
|
||||||
"repository_url" TEXT NOT NULL,
|
|
||||||
"template_repository_url" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "team_studio_repositories_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "team_studio_repositories_group_id_module_id_key"
|
|
||||||
ON "team_studio_repositories"("group_id", "module_id");
|
|
||||||
CREATE INDEX "team_studio_repositories_week_id_idx"
|
|
||||||
ON "team_studio_repositories"("week_id");
|
|
||||||
CREATE INDEX "team_studio_repositories_repo_owner_repo_name_idx"
|
|
||||||
ON "team_studio_repositories"("repo_owner", "repo_name");
|
|
||||||
|
|
||||||
ALTER TABLE "team_studio_repositories"
|
|
||||||
ADD CONSTRAINT "team_studio_repositories_group_id_fkey"
|
|
||||||
FOREIGN KEY ("group_id") REFERENCES "student_groups"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "team_studio_repositories"
|
|
||||||
ADD CONSTRAINT "team_studio_repositories_module_id_fkey"
|
|
||||||
FOREIGN KEY ("module_id") REFERENCES "course_week_modules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
CREATE TABLE "team_custom_modules" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"group_id" UUID NOT NULL,
|
|
||||||
"creator_id" UUID NOT NULL,
|
|
||||||
"course_id" UUID NOT NULL,
|
|
||||||
"week" INTEGER NOT NULL,
|
|
||||||
"sort_order" INTEGER NOT NULL,
|
|
||||||
"category" TEXT NOT NULL,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"folder_name" TEXT NOT NULL,
|
|
||||||
"docker_image" TEXT,
|
|
||||||
"package_name" TEXT NOT NULL,
|
|
||||||
"launch_file" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
CONSTRAINT "team_custom_modules_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "team_custom_modules_group_id_course_id_week_folder_name_key"
|
|
||||||
ON "team_custom_modules"("group_id", "course_id", "week", "folder_name");
|
|
||||||
CREATE INDEX "team_custom_modules_group_id_course_id_week_sort_order_idx"
|
|
||||||
ON "team_custom_modules"("group_id", "course_id", "week", "sort_order");
|
|
||||||
|
|
||||||
ALTER TABLE "team_custom_modules"
|
|
||||||
ADD CONSTRAINT "team_custom_modules_group_id_fkey"
|
|
||||||
FOREIGN KEY ("group_id") REFERENCES "student_groups"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "team_custom_modules"
|
|
||||||
ADD CONSTRAINT "team_custom_modules_creator_id_fkey"
|
|
||||||
FOREIGN KEY ("creator_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "team_custom_modules"
|
|
||||||
ADD CONSTRAINT "team_custom_modules_course_id_fkey"
|
|
||||||
FOREIGN KEY ("course_id") REFERENCES "courses"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
CREATE TABLE "student_custom_module_repositories" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"user_id" UUID NOT NULL,
|
|
||||||
"custom_module_id" UUID NOT NULL,
|
|
||||||
"repo_owner" TEXT NOT NULL,
|
|
||||||
"repo_name" TEXT NOT NULL,
|
|
||||||
"repository_url" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "student_custom_module_repositories_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE "team_custom_module_repositories" (
|
|
||||||
"id" UUID NOT NULL,
|
|
||||||
"group_id" UUID NOT NULL,
|
|
||||||
"custom_module_id" UUID NOT NULL,
|
|
||||||
"repo_owner" TEXT NOT NULL,
|
|
||||||
"repo_name" TEXT NOT NULL,
|
|
||||||
"repository_url" TEXT NOT NULL,
|
|
||||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
"updated_at" TIMESTAMPTZ(3) NOT NULL,
|
|
||||||
|
|
||||||
CONSTRAINT "team_custom_module_repositories_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "student_custom_module_repositories_custom_module_id_key" ON "student_custom_module_repositories"("custom_module_id");
|
|
||||||
CREATE INDEX "student_custom_module_repositories_user_id_idx" ON "student_custom_module_repositories"("user_id");
|
|
||||||
CREATE INDEX "student_custom_module_repositories_repo_owner_repo_name_idx" ON "student_custom_module_repositories"("repo_owner", "repo_name");
|
|
||||||
CREATE UNIQUE INDEX "team_custom_module_repositories_custom_module_id_key" ON "team_custom_module_repositories"("custom_module_id");
|
|
||||||
CREATE INDEX "team_custom_module_repositories_group_id_idx" ON "team_custom_module_repositories"("group_id");
|
|
||||||
CREATE INDEX "team_custom_module_repositories_repo_owner_repo_name_idx" ON "team_custom_module_repositories"("repo_owner", "repo_name");
|
|
||||||
|
|
||||||
ALTER TABLE "student_custom_module_repositories"
|
|
||||||
ADD CONSTRAINT "student_custom_module_repositories_user_id_fkey"
|
|
||||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "student_custom_module_repositories"
|
|
||||||
ADD CONSTRAINT "student_custom_module_repositories_custom_module_id_fkey"
|
|
||||||
FOREIGN KEY ("custom_module_id") REFERENCES "student_custom_modules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "team_custom_module_repositories"
|
|
||||||
ADD CONSTRAINT "team_custom_module_repositories_group_id_fkey"
|
|
||||||
FOREIGN KEY ("group_id") REFERENCES "student_groups"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
ALTER TABLE "team_custom_module_repositories"
|
|
||||||
ADD CONSTRAINT "team_custom_module_repositories_custom_module_id_fkey"
|
|
||||||
FOREIGN KEY ("custom_module_id") REFERENCES "team_custom_modules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user