From a947c99b6b362c206897baf97f60048f9a26d514 Mon Sep 17 00:00:00 2001 From: Stefan Niclas Heun Date: Thu, 21 Nov 2024 09:32:01 +0100 Subject: [PATCH 1/6] added on delete cascade --- server/db/migration/0001_schema.up.sql | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/server/db/migration/0001_schema.up.sql b/server/db/migration/0001_schema.up.sql index b38c6723..923575c2 100644 --- a/server/db/migration/0001_schema.up.sql +++ b/server/db/migration/0001_schema.up.sql @@ -36,7 +36,7 @@ CREATE TABLE course_phase ( is_initial_phase boolean NOT NULL, CONSTRAINT fk_course FOREIGN KEY (course_id) - REFERENCES course(id) + REFERENCES course(id) ON DELETE CASCADE ); CREATE UNIQUE INDEX unique_initial_phase_per_course @@ -49,10 +49,10 @@ CREATE TABLE course_participation ( student_id uuid NOT NULL, CONSTRAINT fk_student FOREIGN KEY (student_id) - REFERENCES student(id), + REFERENCES student(id) ON DELETE CASCADE, CONSTRAINT fk_course FOREIGN KEY (course_id) - REFERENCES course(id), + REFERENCES course(id) ON DELETE CASCADE, CONSTRAINT unique_course_participation UNIQUE (course_id, student_id) ); @@ -65,10 +65,10 @@ CREATE TABLE course_phase_participation ( meta_data jsonb, CONSTRAINT fk_course_participation FOREIGN KEY (course_participation_id) - REFERENCES course_participation(id), + REFERENCES course_participation(id) ON DELETE CASCADE, CONSTRAINT fk_course_phase FOREIGN KEY (course_phase_id) - REFERENCES course_phase(id), + REFERENCES course_phase(id) ON DELETE CASCADE, CONSTRAINT unique_course_phase_participation UNIQUE (course_participation_id, course_phase_id) ); @@ -80,10 +80,10 @@ CREATE TABLE course_phase_graph ( to_course_phase_id uuid NOT NULL, CONSTRAINT fk_from_course_phase FOREIGN KEY (from_course_phase_id) - REFERENCES course_phase(id), + REFERENCES course_phase(id) ON DELETE CASCADE, CONSTRAINT fk_to_course_phase FOREIGN KEY (to_course_phase_id) - REFERENCES course_phase(id), + REFERENCES course_phase(id) ON DELETE CASCADE, CONSTRAINT unique_from_course_phase UNIQUE (from_course_phase_id), CONSTRAINT unique_to_course_phase UNIQUE (to_course_phase_id) ); @@ -101,7 +101,7 @@ CREATE TABLE application_question_text ( order_num int, CONSTRAINT fk_course_phase FOREIGN KEY (course_phase_id) - REFERENCES course_phase(id) + REFERENCES course_phase(id) ON DELETE CASCADE ); CREATE TABLE application_question_multi_select ( @@ -118,7 +118,7 @@ CREATE TABLE application_question_multi_select ( order_num int, CONSTRAINT fk_course_phase FOREIGN KEY (course_phase_id) - REFERENCES course_phase(id) + REFERENCES course_phase(id) ON DELETE CASCADE ); CREATE TABLE application_answer_text ( @@ -128,10 +128,10 @@ CREATE TABLE application_answer_text ( answer text, CONSTRAINT fk_application_question FOREIGN KEY (application_question_id) - REFERENCES application_question_text(id), + REFERENCES application_question_text(id) ON DELETE CASCADE, CONSTRAINT fk_course_phase_participation FOREIGN KEY (course_phase_participation_id) - REFERENCES course_phase_participation(id), + REFERENCES course_phase_participation(id) ON DELETE CASCADE, CONSTRAINT unique_application_answer_text UNIQUE (course_phase_participation_id, application_question_id) @@ -144,10 +144,10 @@ CREATE TABLE application_answer_multi_select ( answer text[], CONSTRAINT fk_application_question FOREIGN KEY (application_question_id) - REFERENCES application_question_multi_select(id), + REFERENCES application_question_multi_select(id) ON DELETE CASCADE, CONSTRAINT fk_course_phase_participation FOREIGN KEY (course_phase_participation_id) - REFERENCES course_phase_participation(id), + REFERENCES course_phase_participation(id) ON DELETE CASCADE, CONSTRAINT unique_application_answer_multi_select UNIQUE (course_phase_participation_id, application_question_id) ); @@ -158,7 +158,7 @@ CREATE TABLE application_assessment ( score int, CONSTRAINT fk_course_phase_participation FOREIGN KEY (course_phase_participation_id) - REFERENCES course_phase_participation(id) + REFERENCES course_phase_participation(id) ON DELETE CASCADE ); COMMIT; \ No newline at end of file From 86c75dd06012c0b071ee82bad440884fb986ca96 Mon Sep 17 00:00:00 2001 From: Stefan Niclas Heun Date: Thu, 21 Nov 2024 10:25:08 +0100 Subject: [PATCH 2/6] added docker-compose with env variables --- docker-compose.yml | 46 +++++++++++++++++++++++++++++++++++++ server/Dockerfile | 30 ++++++++++++++++++++++++ server/docker-compose.yml | 16 ------------- server/main.go | 48 +++++++++++++++++++++++++++++++-------- 4 files changed, 114 insertions(+), 26 deletions(-) create mode 100644 docker-compose.yml create mode 100644 server/Dockerfile delete mode 100644 server/docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..7b3bc494 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +services: + server: + build: + context: ./server + dockerfile: Dockerfile + container_name: prompt-core-server + depends_on: + db: + condition: service_healthy + ports: + - "8080:8080" + environment: + - DB_USER=prompt-postgres + - DB_PASSWORD=prompt-postgres + - DB_HOST=db + - DB_PORT=5432 + - DB_NAME=prompt + - SSL_MODE=disable + - SERVER_ADDRESS=0.0.0.0:8080 + + db: + image: 'postgres:15.2-alpine' + container_name: prompt-core-db + environment: + - POSTGRES_USER=prompt-postgres + - POSTGRES_PASSWORD=prompt-postgres + - POSTGRES_DB=prompt + healthcheck: + test: ["CMD-SHELL", "pg_isready -d prompt -U prompt-postgres"] + interval: 5s + timeout: 5s + retries: 5 + ports: + - "5432:5432" + + keycloak: + image: quay.io/keycloak/keycloak:20.0 + container_name: prompt-keycloak + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + entrypoint: /opt/keycloak/bin/kc.sh start-dev + ports: + - "8081:8080" + + diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 00000000..2d39db37 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,30 @@ +FROM golang:1.23-alpine + + +# Install dependencies +RUN apk add --no-cache curl + +# Install migrate +RUN curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.1/migrate.linux-amd64.tar.gz | tar xvz && \ + mv migrate /usr/local/bin/ + +# Set the Current Working Directory inside the container +WORKDIR /app + +# Copy go mod and sum files +COPY go.mod go.sum ./ + +# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed +RUN go mod download + +# Copy the source from the current directory to the Working Directory inside the container +COPY . . + +# Build the Go app +RUN go build -o main . + +# Expose port 8080 to the outside world +EXPOSE 8080 + +# Command to run the executable +CMD ["./main"] \ No newline at end of file diff --git a/server/docker-compose.yml b/server/docker-compose.yml deleted file mode 100644 index d7a4c744..00000000 --- a/server/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -services: - db: - image: 'postgres:15.2-alpine' - container_name: prompt-core-db - environment: - - POSTGRES_USER=prompt-postgres - - POSTGRES_PASSWORD=prompt-postgres - - POSTGRES_DB=prompt - healthcheck: - test: ["CMD-SHELL", "pg_isready -d prompt -U prompt-postgres"] - interval: 5s - timeout: 5s - retries: 5 - ports: - - "5432:5432" - diff --git a/server/main.go b/server/main.go index 9818f0b6..f6d92c57 100644 --- a/server/main.go +++ b/server/main.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "os/exec" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" @@ -13,22 +14,53 @@ import ( "github.com/niclasheun/prompt2.0/coursePhase/coursePhaseParticipation" db "github.com/niclasheun/prompt2.0/db/sqlc" "github.com/niclasheun/prompt2.0/student" + log "github.com/sirupsen/logrus" ) +func getDatabaseURL() string { + dbUser := getEnv("DB_USER", "prompt-postgres") + dbPassword := getEnv("DB_PASSWORD", "prompt-postgres") + dbHost := getEnv("DB_HOST", "localhost") + dbPort := getEnv("DB_PORT", "5432") + dbName := getEnv("DB_NAME", "prompt") + sslMode := getEnv("SSL_MODE", "disable") + + return fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=%s", dbUser, dbPassword, dbHost, dbPort, dbName, sslMode) +} + +func getEnv(key, defaultValue string) string { + if value, exists := os.LookupEnv(key); exists { + return value + } + return defaultValue +} + +func runMigrations(databaseURL string) { + cmd := exec.Command("migrate", "-path", "./db/migration", "-database", databaseURL, "up") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + log.Fatalf("Failed to run migrations: %v", err) + } +} + func main() { // establish database connection - databaseURL := "postgres://prompt-postgres:prompt-postgres@localhost:5432/prompt?sslmode=disable" + databaseURL := getDatabaseURL() + log.Debug("Connecting to database at:", databaseURL) - conn, err := pgx.Connect(context.Background(), databaseURL) + // run migrations + runMigrations(databaseURL) + // establish db connection + conn, err := pgx.Connect(context.Background(), databaseURL) if err != nil { - fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err) + log.Fatalf("Unable to connect to database: %v\n", err) os.Exit(1) } defer conn.Close(context.Background()) query := db.New(conn) - //service.CreateNewServices(*query, conn) router := gin.Default() @@ -45,10 +77,6 @@ func main() { courseParticipation.InitCourseParticipationModule(api, *query, conn) coursePhaseParticipation.InitCoursePhaseParticipationModule(api, *query, conn) - router.Run("localhost:8080") - - //server := api.NewServer(*query) - //server.MountHandlers() - //routes.CreateRoutes(router) - //server.Start("localhost:8080") + serverAddress := getEnv("SERVER_ADDRESS", "localhost:8080") + router.Run(serverAddress) } From 0178bbc8436140deae95c4a972cf52a7b56a381e Mon Sep 17 00:00:00 2001 From: Stefan Niclas Heun Date: Thu, 21 Nov 2024 12:53:55 +0100 Subject: [PATCH 3/6] adding course phase type --- server/db/migration/0001_schema.up.sql | 11 ++++++++++- server/db/sqlc/course_graph.sql.go | 23 +++++++++++++---------- server/db/sqlc/course_phase.sql.go | 9 ++++++--- server/db/sqlc/models.go | 16 +++++++++++----- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/server/db/migration/0001_schema.up.sql b/server/db/migration/0001_schema.up.sql index 923575c2..7d95f1fb 100644 --- a/server/db/migration/0001_schema.up.sql +++ b/server/db/migration/0001_schema.up.sql @@ -28,15 +28,24 @@ CREATE TABLE course ( meta_data jsonb ); +CREATE TABLE course_phase_type ( + id uuid PRIMARY KEY, + name text UNIQUE NOT NULL +); + CREATE TABLE course_phase ( id uuid PRIMARY KEY, course_id uuid NOT NULL, name text, meta_data jsonb, is_initial_phase boolean NOT NULL, + course_phase_type_id uuid, CONSTRAINT fk_course FOREIGN KEY (course_id) - REFERENCES course(id) ON DELETE CASCADE + REFERENCES course(id) ON DELETE CASCADE, + CONSTRAINT fk_phase_type + FOREIGN KEY (course_phase_type_id) + REFERENCES course_phase_type(id) ON DELETE SET NULL ); CREATE UNIQUE INDEX unique_initial_phase_per_course diff --git a/server/db/sqlc/course_graph.sql.go b/server/db/sqlc/course_graph.sql.go index 02a46abb..818d8b4a 100644 --- a/server/db/sqlc/course_graph.sql.go +++ b/server/db/sqlc/course_graph.sql.go @@ -40,29 +40,30 @@ func (q *Queries) DeleteCourseGraph(ctx context.Context, courseID uuid.UUID) err const getCoursePhaseSequence = `-- name: GetCoursePhaseSequence :many WITH RECURSIVE phase_sequence AS ( - SELECT cp.id, cp.course_id, cp.name, cp.meta_data, cp.is_initial_phase, 1 AS sequence_order + SELECT cp.id, cp.course_id, cp.name, cp.meta_data, cp.is_initial_phase, cp.course_phase_type_id, 1 AS sequence_order FROM course_phase cp WHERE cp.course_id = $1 AND cp.is_initial_phase = true UNION ALL - SELECT cp.id, cp.course_id, cp.name, cp.meta_data, cp.is_initial_phase, ps.sequence_order + 1 AS sequence_order + SELECT cp.id, cp.course_id, cp.name, cp.meta_data, cp.is_initial_phase, cp.course_phase_type_id, ps.sequence_order + 1 AS sequence_order FROM course_phase cp INNER JOIN course_phase_graph g ON g.to_course_phase_id = cp.id INNER JOIN phase_sequence ps ON g.from_course_phase_id = ps.id ) -SELECT id, course_id, name, meta_data, is_initial_phase, sequence_order +SELECT id, course_id, name, meta_data, is_initial_phase, course_phase_type_id, sequence_order FROM phase_sequence ORDER BY sequence_order ` type GetCoursePhaseSequenceRow struct { - ID uuid.UUID `json:"id"` - CourseID uuid.UUID `json:"course_id"` - Name pgtype.Text `json:"name"` - MetaData []byte `json:"meta_data"` - IsInitialPhase bool `json:"is_initial_phase"` - SequenceOrder int32 `json:"sequence_order"` + ID uuid.UUID `json:"id"` + CourseID uuid.UUID `json:"course_id"` + Name pgtype.Text `json:"name"` + MetaData []byte `json:"meta_data"` + IsInitialPhase bool `json:"is_initial_phase"` + CoursePhaseTypeID pgtype.UUID `json:"course_phase_type_id"` + SequenceOrder int32 `json:"sequence_order"` } func (q *Queries) GetCoursePhaseSequence(ctx context.Context, courseID uuid.UUID) ([]GetCoursePhaseSequenceRow, error) { @@ -80,6 +81,7 @@ func (q *Queries) GetCoursePhaseSequence(ctx context.Context, courseID uuid.UUID &i.Name, &i.MetaData, &i.IsInitialPhase, + &i.CoursePhaseTypeID, &i.SequenceOrder, ); err != nil { return nil, err @@ -93,7 +95,7 @@ func (q *Queries) GetCoursePhaseSequence(ctx context.Context, courseID uuid.UUID } const getNotOrderedCoursePhases = `-- name: GetNotOrderedCoursePhases :many -SELECT cp.id, cp.course_id, cp.name, cp.meta_data, cp.is_initial_phase +SELECT cp.id, cp.course_id, cp.name, cp.meta_data, cp.is_initial_phase, cp.course_phase_type_id FROM course_phase cp WHERE cp.course_id = $1 AND cp.is_initial_phase = FALSE @@ -120,6 +122,7 @@ func (q *Queries) GetNotOrderedCoursePhases(ctx context.Context, courseID uuid.U &i.Name, &i.MetaData, &i.IsInitialPhase, + &i.CoursePhaseTypeID, ); err != nil { return nil, err } diff --git a/server/db/sqlc/course_phase.sql.go b/server/db/sqlc/course_phase.sql.go index 6ff0899a..c0666d72 100644 --- a/server/db/sqlc/course_phase.sql.go +++ b/server/db/sqlc/course_phase.sql.go @@ -15,7 +15,7 @@ import ( const createCoursePhase = `-- name: CreateCoursePhase :one INSERT INTO course_phase (id, course_id, name, is_initial_phase, meta_data) VALUES ($1, $2, $3, $4, $5) -RETURNING id, course_id, name, meta_data, is_initial_phase +RETURNING id, course_id, name, meta_data, is_initial_phase, course_phase_type_id ` type CreateCoursePhaseParams struct { @@ -41,12 +41,13 @@ func (q *Queries) CreateCoursePhase(ctx context.Context, arg CreateCoursePhasePa &i.Name, &i.MetaData, &i.IsInitialPhase, + &i.CoursePhaseTypeID, ) return i, err } const getAllCoursePhaseForCourse = `-- name: GetAllCoursePhaseForCourse :many -SELECT id, course_id, name, meta_data, is_initial_phase FROM course_phase +SELECT id, course_id, name, meta_data, is_initial_phase, course_phase_type_id FROM course_phase WHERE course_id = $1 ` @@ -65,6 +66,7 @@ func (q *Queries) GetAllCoursePhaseForCourse(ctx context.Context, courseID uuid. &i.Name, &i.MetaData, &i.IsInitialPhase, + &i.CoursePhaseTypeID, ); err != nil { return nil, err } @@ -77,7 +79,7 @@ func (q *Queries) GetAllCoursePhaseForCourse(ctx context.Context, courseID uuid. } const getCoursePhase = `-- name: GetCoursePhase :one -SELECT id, course_id, name, meta_data, is_initial_phase FROM course_phase +SELECT id, course_id, name, meta_data, is_initial_phase, course_phase_type_id FROM course_phase WHERE id = $1 LIMIT 1 ` @@ -90,6 +92,7 @@ func (q *Queries) GetCoursePhase(ctx context.Context, id uuid.UUID) (CoursePhase &i.Name, &i.MetaData, &i.IsInitialPhase, + &i.CoursePhaseTypeID, ) return i, err } diff --git a/server/db/sqlc/models.go b/server/db/sqlc/models.go index 9d64b4d6..5b051f00 100644 --- a/server/db/sqlc/models.go +++ b/server/db/sqlc/models.go @@ -119,11 +119,12 @@ type CourseParticipation struct { } type CoursePhase struct { - ID uuid.UUID `json:"id"` - CourseID uuid.UUID `json:"course_id"` - Name pgtype.Text `json:"name"` - MetaData []byte `json:"meta_data"` - IsInitialPhase bool `json:"is_initial_phase"` + ID uuid.UUID `json:"id"` + CourseID uuid.UUID `json:"course_id"` + Name pgtype.Text `json:"name"` + MetaData []byte `json:"meta_data"` + IsInitialPhase bool `json:"is_initial_phase"` + CoursePhaseTypeID pgtype.UUID `json:"course_phase_type_id"` } type CoursePhaseGraph struct { @@ -139,6 +140,11 @@ type CoursePhaseParticipation struct { MetaData []byte `json:"meta_data"` } +type CoursePhaseType struct { + ID uuid.UUID `json:"id"` + Name string `json:"name"` +} + type Student struct { ID uuid.UUID `json:"id"` FirstName pgtype.Text `json:"first_name"` From ffdf37747f4b593eed467b5cec00e9eb961abdff Mon Sep 17 00:00:00 2001 From: Stefan Niclas Heun Date: Thu, 21 Nov 2024 15:08:04 +0100 Subject: [PATCH 4/6] adding course phase type --- .../coursePhaseDTO/create_course_phase.go | 18 +++++---- .../coursePhaseDTO/get_course_phase.go | 22 ++++++----- .../get_course_phase_sequence.go | 39 ++++++++++--------- server/db/migration/0001_schema.up.sql | 4 +- server/db/query/course_phase.sql | 4 +- server/db/sqlc/course_graph.sql.go | 2 +- server/db/sqlc/course_phase.sql.go | 16 ++++---- server/db/sqlc/models.go | 2 +- 8 files changed, 58 insertions(+), 49 deletions(-) diff --git a/server/coursePhase/coursePhaseDTO/create_course_phase.go b/server/coursePhase/coursePhaseDTO/create_course_phase.go index 1c332e79..2b2c32a4 100644 --- a/server/coursePhase/coursePhaseDTO/create_course_phase.go +++ b/server/coursePhase/coursePhaseDTO/create_course_phase.go @@ -8,10 +8,11 @@ import ( ) type CreateCoursePhase struct { - CourseID uuid.UUID `json:"course_id"` - Name string `json:"name"` - IsInitialPhase bool `json:"is_initial_phase"` - MetaData meta.MetaData `json:"meta_data"` + CourseID uuid.UUID `json:"course_id"` + Name string `json:"name"` + IsInitialPhase bool `json:"is_initial_phase"` + MetaData meta.MetaData `json:"meta_data"` + CoursePhaseTypeID uuid.UUID `json:"course_phase_type_id"` } func (cp CreateCoursePhase) GetDBModel() (db.CreateCoursePhaseParams, error) { @@ -20,9 +21,10 @@ func (cp CreateCoursePhase) GetDBModel() (db.CreateCoursePhaseParams, error) { return db.CreateCoursePhaseParams{}, err } return db.CreateCoursePhaseParams{ - CourseID: cp.CourseID, - Name: pgtype.Text{String: cp.Name, Valid: true}, - IsInitialPhase: cp.IsInitialPhase, - MetaData: metaData, + CourseID: cp.CourseID, + Name: pgtype.Text{String: cp.Name, Valid: true}, + IsInitialPhase: cp.IsInitialPhase, + MetaData: metaData, + CoursePhaseTypeID: cp.CoursePhaseTypeID, }, nil } diff --git a/server/coursePhase/coursePhaseDTO/get_course_phase.go b/server/coursePhase/coursePhaseDTO/get_course_phase.go index 1081d687..05deed74 100644 --- a/server/coursePhase/coursePhaseDTO/get_course_phase.go +++ b/server/coursePhase/coursePhaseDTO/get_course_phase.go @@ -7,11 +7,12 @@ import ( ) type CoursePhase struct { - ID uuid.UUID `json:"id"` - CourseID uuid.UUID `json:"course_id"` - Name string `json:"name"` - IsInitialPhase bool `json:"is_initial_phase"` - MetaData meta.MetaData `json:"meta_data"` + ID uuid.UUID `json:"id"` + CourseID uuid.UUID `json:"course_id"` + Name string `json:"name"` + IsInitialPhase bool `json:"is_initial_phase"` + MetaData meta.MetaData `json:"meta_data"` + CoursePhaseTypeID uuid.UUID `json:"course_phase_type_id"` } func GetCoursePhaseDTOFromDBModel(model db.CoursePhase) (CoursePhase, error) { @@ -21,10 +22,11 @@ func GetCoursePhaseDTOFromDBModel(model db.CoursePhase) (CoursePhase, error) { } return CoursePhase{ - ID: model.ID, - CourseID: model.CourseID, - Name: model.Name.String, - IsInitialPhase: model.IsInitialPhase, - MetaData: metaData, + ID: model.ID, + CourseID: model.CourseID, + Name: model.Name.String, + IsInitialPhase: model.IsInitialPhase, + MetaData: metaData, + CoursePhaseTypeID: model.CoursePhaseTypeID, }, nil } diff --git a/server/coursePhase/coursePhaseDTO/get_course_phase_sequence.go b/server/coursePhase/coursePhaseDTO/get_course_phase_sequence.go index f5c9d78d..85a8869b 100644 --- a/server/coursePhase/coursePhaseDTO/get_course_phase_sequence.go +++ b/server/coursePhase/coursePhaseDTO/get_course_phase_sequence.go @@ -7,12 +7,13 @@ import ( ) type CoursePhaseSequence struct { - ID uuid.UUID `json:"id"` - CourseID uuid.UUID `json:"course_id"` - Name string `json:"name"` - MetaData meta.MetaData `json:"meta_data"` - IsInitialPhase bool `json:"is_initial_phase"` - SequenceOrder int `json:"sequence_order"` + ID uuid.UUID `json:"id"` + CourseID uuid.UUID `json:"course_id"` + Name string `json:"name"` + MetaData meta.MetaData `json:"meta_data"` + IsInitialPhase bool `json:"is_initial_phase"` + SequenceOrder int `json:"sequence_order"` + CoursePhaseTypeID uuid.UUID `json:"course_phase_type_id"` } func GetCoursePhaseSequenceDTOFromDBModel(model db.GetCoursePhaseSequenceRow) (CoursePhaseSequence, error) { @@ -22,12 +23,13 @@ func GetCoursePhaseSequenceDTOFromDBModel(model db.GetCoursePhaseSequenceRow) (C } return CoursePhaseSequence{ - ID: model.ID, - CourseID: model.CourseID, - Name: model.Name.String, - MetaData: metaData, - IsInitialPhase: model.IsInitialPhase, - SequenceOrder: int(model.SequenceOrder), + ID: model.ID, + CourseID: model.CourseID, + Name: model.Name.String, + MetaData: metaData, + IsInitialPhase: model.IsInitialPhase, + SequenceOrder: int(model.SequenceOrder), + CoursePhaseTypeID: model.CoursePhaseTypeID, }, nil } @@ -43,12 +45,13 @@ func GetCoursePhaseSequenceDTO(orderedPhases []db.GetCoursePhaseSequenceRow, not for _, phase := range notOrderedPhases { coursePhase, err := GetCoursePhaseSequenceDTOFromDBModel(db.GetCoursePhaseSequenceRow{ - ID: phase.ID, - CourseID: phase.CourseID, - Name: phase.Name, - MetaData: phase.MetaData, - IsInitialPhase: phase.IsInitialPhase, - SequenceOrder: -1, + ID: phase.ID, + CourseID: phase.CourseID, + Name: phase.Name, + MetaData: phase.MetaData, + IsInitialPhase: phase.IsInitialPhase, + SequenceOrder: -1, + CoursePhaseTypeID: phase.CoursePhaseTypeID, }) if err != nil { return nil, err diff --git a/server/db/migration/0001_schema.up.sql b/server/db/migration/0001_schema.up.sql index 7d95f1fb..c2c2d545 100644 --- a/server/db/migration/0001_schema.up.sql +++ b/server/db/migration/0001_schema.up.sql @@ -39,13 +39,13 @@ CREATE TABLE course_phase ( name text, meta_data jsonb, is_initial_phase boolean NOT NULL, - course_phase_type_id uuid, + course_phase_type_id uuid NOT NULL, CONSTRAINT fk_course FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE, CONSTRAINT fk_phase_type FOREIGN KEY (course_phase_type_id) - REFERENCES course_phase_type(id) ON DELETE SET NULL + REFERENCES course_phase_type(id) ); CREATE UNIQUE INDEX unique_initial_phase_per_course diff --git a/server/db/query/course_phase.sql b/server/db/query/course_phase.sql index d5436522..b2483adf 100644 --- a/server/db/query/course_phase.sql +++ b/server/db/query/course_phase.sql @@ -7,8 +7,8 @@ SELECT * FROM course_phase WHERE course_id = $1; -- name: CreateCoursePhase :one -INSERT INTO course_phase (id, course_id, name, is_initial_phase, meta_data) -VALUES ($1, $2, $3, $4, $5) +INSERT INTO course_phase (id, course_id, name, is_initial_phase, meta_data, course_phase_type_id) +VALUES ($1, $2, $3, $4, $5, $6) RETURNING *; -- name: UpdateCoursePhase :exec diff --git a/server/db/sqlc/course_graph.sql.go b/server/db/sqlc/course_graph.sql.go index 818d8b4a..93aca87e 100644 --- a/server/db/sqlc/course_graph.sql.go +++ b/server/db/sqlc/course_graph.sql.go @@ -62,7 +62,7 @@ type GetCoursePhaseSequenceRow struct { Name pgtype.Text `json:"name"` MetaData []byte `json:"meta_data"` IsInitialPhase bool `json:"is_initial_phase"` - CoursePhaseTypeID pgtype.UUID `json:"course_phase_type_id"` + CoursePhaseTypeID uuid.UUID `json:"course_phase_type_id"` SequenceOrder int32 `json:"sequence_order"` } diff --git a/server/db/sqlc/course_phase.sql.go b/server/db/sqlc/course_phase.sql.go index c0666d72..437f47d1 100644 --- a/server/db/sqlc/course_phase.sql.go +++ b/server/db/sqlc/course_phase.sql.go @@ -13,17 +13,18 @@ import ( ) const createCoursePhase = `-- name: CreateCoursePhase :one -INSERT INTO course_phase (id, course_id, name, is_initial_phase, meta_data) -VALUES ($1, $2, $3, $4, $5) +INSERT INTO course_phase (id, course_id, name, is_initial_phase, meta_data, course_phase_type_id) +VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, course_id, name, meta_data, is_initial_phase, course_phase_type_id ` type CreateCoursePhaseParams struct { - ID uuid.UUID `json:"id"` - CourseID uuid.UUID `json:"course_id"` - Name pgtype.Text `json:"name"` - IsInitialPhase bool `json:"is_initial_phase"` - MetaData []byte `json:"meta_data"` + ID uuid.UUID `json:"id"` + CourseID uuid.UUID `json:"course_id"` + Name pgtype.Text `json:"name"` + IsInitialPhase bool `json:"is_initial_phase"` + MetaData []byte `json:"meta_data"` + CoursePhaseTypeID uuid.UUID `json:"course_phase_type_id"` } func (q *Queries) CreateCoursePhase(ctx context.Context, arg CreateCoursePhaseParams) (CoursePhase, error) { @@ -33,6 +34,7 @@ func (q *Queries) CreateCoursePhase(ctx context.Context, arg CreateCoursePhasePa arg.Name, arg.IsInitialPhase, arg.MetaData, + arg.CoursePhaseTypeID, ) var i CoursePhase err := row.Scan( diff --git a/server/db/sqlc/models.go b/server/db/sqlc/models.go index 5b051f00..4cb0c074 100644 --- a/server/db/sqlc/models.go +++ b/server/db/sqlc/models.go @@ -124,7 +124,7 @@ type CoursePhase struct { Name pgtype.Text `json:"name"` MetaData []byte `json:"meta_data"` IsInitialPhase bool `json:"is_initial_phase"` - CoursePhaseTypeID pgtype.UUID `json:"course_phase_type_id"` + CoursePhaseTypeID uuid.UUID `json:"course_phase_type_id"` } type CoursePhaseGraph struct { From 3938da2ac1c5f02489af977caf9d5be61a09930e Mon Sep 17 00:00:00 2001 From: Stefan Niclas Heun Date: Thu, 21 Nov 2024 15:30:15 +0100 Subject: [PATCH 5/6] added ects and coursetype to the course --- server/course/courseDTO/create_course.go | 4 +++ server/db/migration/0001_schema.up.sql | 8 +++++ server/db/query/course.sql | 4 +-- server/db/sqlc/course.sql.go | 20 ++++++++--- server/db/sqlc/models.go | 45 ++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/server/course/courseDTO/create_course.go b/server/course/courseDTO/create_course.go index 42834c55..d4327c21 100644 --- a/server/course/courseDTO/create_course.go +++ b/server/course/courseDTO/create_course.go @@ -13,6 +13,8 @@ type CreateCourse struct { EndDate pgtype.Date `json:"end_date"` SemesterTag pgtype.Text `json:"semester_tag"` MetaData meta.MetaData `json:"meta_data"` + CourseType db.CourseType `json:"course_type"` + Ects pgtype.Int4 `json:"ects"` //TODO: CoursePhases []coursePhaseDTO.CoursePhase `json:"course_phases"` } @@ -29,5 +31,7 @@ func (c CreateCourse) GetDBModel() (db.CreateCourseParams, error) { EndDate: c.EndDate, SemesterTag: c.SemesterTag, MetaData: metaData, + CourseType: c.CourseType, + Ects: c.Ects, }, nil } diff --git a/server/db/migration/0001_schema.up.sql b/server/db/migration/0001_schema.up.sql index c2c2d545..364f5c1d 100644 --- a/server/db/migration/0001_schema.up.sql +++ b/server/db/migration/0001_schema.up.sql @@ -7,6 +7,12 @@ CREATE TYPE gender AS ENUM ( 'prefer_not_to_say' ); +CREATE TYPE course_type as ENUM ( + 'lecture', + 'seminar', + 'practical course' +); + -- UK data standard for full name is 70 chars and for mail 255 chars CREATE TABLE student ( id uuid PRIMARY KEY, @@ -25,6 +31,8 @@ CREATE TABLE course ( start_date date, end_date date, semester_tag text, + course_type course_type NOT NULL, + ects int, meta_data jsonb ); diff --git a/server/db/query/course.sql b/server/db/query/course.sql index 076ba69d..24ff8561 100644 --- a/server/db/query/course.sql +++ b/server/db/query/course.sql @@ -6,6 +6,6 @@ WHERE id = $1 LIMIT 1; SELECT * FROM course; -- name: CreateCourse :one -INSERT INTO course (id, name, start_date, end_date, semester_tag, meta_data) -VALUES ($1, $2, $3, $4, $5, $6) +INSERT INTO course (id, name, start_date, end_date, semester_tag, course_type, ects, meta_data) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *; \ No newline at end of file diff --git a/server/db/sqlc/course.sql.go b/server/db/sqlc/course.sql.go index a4d69b50..6db249be 100644 --- a/server/db/sqlc/course.sql.go +++ b/server/db/sqlc/course.sql.go @@ -13,9 +13,9 @@ import ( ) const createCourse = `-- name: CreateCourse :one -INSERT INTO course (id, name, start_date, end_date, semester_tag, meta_data) -VALUES ($1, $2, $3, $4, $5, $6) -RETURNING id, name, start_date, end_date, semester_tag, meta_data +INSERT INTO course (id, name, start_date, end_date, semester_tag, course_type, ects, meta_data) +VALUES ($1, $2, $3, $4, $5, $6, $7, $8) +RETURNING id, name, start_date, end_date, semester_tag, course_type, ects, meta_data ` type CreateCourseParams struct { @@ -24,6 +24,8 @@ type CreateCourseParams struct { StartDate pgtype.Date `json:"start_date"` EndDate pgtype.Date `json:"end_date"` SemesterTag pgtype.Text `json:"semester_tag"` + CourseType CourseType `json:"course_type"` + Ects pgtype.Int4 `json:"ects"` MetaData []byte `json:"meta_data"` } @@ -34,6 +36,8 @@ func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Cou arg.StartDate, arg.EndDate, arg.SemesterTag, + arg.CourseType, + arg.Ects, arg.MetaData, ) var i Course @@ -43,13 +47,15 @@ func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Cou &i.StartDate, &i.EndDate, &i.SemesterTag, + &i.CourseType, + &i.Ects, &i.MetaData, ) return i, err } const getAllCourses = `-- name: GetAllCourses :many -SELECT id, name, start_date, end_date, semester_tag, meta_data FROM course +SELECT id, name, start_date, end_date, semester_tag, course_type, ects, meta_data FROM course ` func (q *Queries) GetAllCourses(ctx context.Context) ([]Course, error) { @@ -67,6 +73,8 @@ func (q *Queries) GetAllCourses(ctx context.Context) ([]Course, error) { &i.StartDate, &i.EndDate, &i.SemesterTag, + &i.CourseType, + &i.Ects, &i.MetaData, ); err != nil { return nil, err @@ -80,7 +88,7 @@ func (q *Queries) GetAllCourses(ctx context.Context) ([]Course, error) { } const getCourse = `-- name: GetCourse :one -SELECT id, name, start_date, end_date, semester_tag, meta_data FROM course +SELECT id, name, start_date, end_date, semester_tag, course_type, ects, meta_data FROM course WHERE id = $1 LIMIT 1 ` @@ -93,6 +101,8 @@ func (q *Queries) GetCourse(ctx context.Context, id uuid.UUID) (Course, error) { &i.StartDate, &i.EndDate, &i.SemesterTag, + &i.CourseType, + &i.Ects, &i.MetaData, ) return i, err diff --git a/server/db/sqlc/models.go b/server/db/sqlc/models.go index 4cb0c074..2eef8b85 100644 --- a/server/db/sqlc/models.go +++ b/server/db/sqlc/models.go @@ -12,6 +12,49 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) +type CourseType string + +const ( + CourseTypeLecture CourseType = "lecture" + CourseTypeSeminar CourseType = "seminar" + CourseTypePracticalcourse CourseType = "practical course" +) + +func (e *CourseType) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = CourseType(s) + case string: + *e = CourseType(s) + default: + return fmt.Errorf("unsupported scan type for CourseType: %T", src) + } + return nil +} + +type NullCourseType struct { + CourseType CourseType `json:"course_type"` + Valid bool `json:"valid"` // Valid is true if CourseType is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullCourseType) Scan(value interface{}) error { + if value == nil { + ns.CourseType, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.CourseType.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullCourseType) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.CourseType), nil +} + type Gender string const ( @@ -109,6 +152,8 @@ type Course struct { StartDate pgtype.Date `json:"start_date"` EndDate pgtype.Date `json:"end_date"` SemesterTag pgtype.Text `json:"semester_tag"` + CourseType CourseType `json:"course_type"` + Ects pgtype.Int4 `json:"ects"` MetaData []byte `json:"meta_data"` } From 3b4e368c081817caa1ac2e549b3c02ce05d57fd0 Mon Sep 17 00:00:00 2001 From: Stefan Niclas Heun Date: Thu, 21 Nov 2024 16:01:32 +0100 Subject: [PATCH 6/6] little fix - i know I still need to write tests for this component --- server/coursePhase/service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/coursePhase/service.go b/server/coursePhase/service.go index 2f6b1d3c..054bde65 100644 --- a/server/coursePhase/service.go +++ b/server/coursePhase/service.go @@ -31,6 +31,7 @@ func UpdateCoursePhase(ctx context.Context, coursePhase coursePhaseDTO.UpdateCou return err } + dbModel.ID = coursePhase.ID return CoursePhaseServiceSingleton.queries.UpdateCoursePhase(ctx, dbModel) }