From 0c9e483348f8db53dec33a9d4fe839a109431ba2 Mon Sep 17 00:00:00 2001 From: hoangphuc552001 Date: Sun, 28 May 2023 13:40:36 +0700 Subject: [PATCH 1/2] test --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9fe5c51..8f33415 100644 --- a/README.md +++ b/README.md @@ -205,3 +205,5 @@ Don't confuse the project level `/src` directory with the `/src` directory Go us ## Notes A more opinionated project template with sample/reusable configs, scripts and code is a WIP. + +a \ No newline at end of file From 8c6b7bd351ed3c9e8ae048d4de67adfad535d543 Mon Sep 17 00:00:00 2001 From: hoangphuc552001 Date: Mon, 5 Jun 2023 00:27:18 +0700 Subject: [PATCH 2/2] covert,query sql to gorm --- README.md | 4 +- go.mod | 20 +++++- internal/sqlclient/sqlclient.go | 93 ++++++++++++++++++++++++++ main.go | 96 +++++++++++++++++++++++++++ pkg/repository/course.go | 51 +++++++++++++++ pkg/repository/manager.go | 25 ------- pkg/repository/professor.go | 50 ++++++++++++++ pkg/repository/student.go | 112 ++++++++++++++++++++++++++++++++ pkg/types/class.go | 12 ++++ pkg/types/course.go | 6 ++ pkg/types/enroll.go | 9 +++ pkg/types/professor.go | 7 ++ pkg/types/room.go | 8 +++ pkg/types/student.go | 13 ++-- 14 files changed, 470 insertions(+), 36 deletions(-) create mode 100644 internal/sqlclient/sqlclient.go create mode 100644 main.go create mode 100644 pkg/repository/course.go delete mode 100644 pkg/repository/manager.go create mode 100644 pkg/repository/professor.go create mode 100644 pkg/repository/student.go create mode 100644 pkg/types/class.go create mode 100644 pkg/types/course.go create mode 100644 pkg/types/enroll.go create mode 100644 pkg/types/professor.go create mode 100644 pkg/types/room.go diff --git a/README.md b/README.md index 8f33415..1355f93 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,4 @@ Don't confuse the project level `/src` directory with the `/src` directory Go us ## Notes -A more opinionated project template with sample/reusable configs, scripts and code is a WIP. - -a \ No newline at end of file +A more opinionated project template with sample/reusable configs, scripts and code is a WIP. \ No newline at end of file diff --git a/go.mod b/go.mod index bc7f763..4dea0dd 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,24 @@ require ( gorm.io/gorm v1.25.1 ) +require ( + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml v1.9.5 // indirect + github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/cast v1.5.0 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) + require ( github.com/bytedance/sonic v1.8.0 // indirect + github.com/caarlos0/env v3.5.0+incompatible github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -26,9 +42,10 @@ require ( github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.17 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/spf13/viper v1.13.0 github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect @@ -38,4 +55,5 @@ require ( golang.org/x/text v0.7.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + ) diff --git a/internal/sqlclient/sqlclient.go b/internal/sqlclient/sqlclient.go new file mode 100644 index 0000000..5011ba0 --- /dev/null +++ b/internal/sqlclient/sqlclient.go @@ -0,0 +1,93 @@ +package sqlclient + +import ( + "errors" + "fmt" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "log" +) + +const ( + MYSQL = "mysql" +) + +type ISqlClientConn interface { + GetDB() *gorm.DB + GetDriver() string +} + +type SqlConfig struct { + Driver string + Host string + Port int + Database string + Username string + Password string + Timeout int + DialTimeout int + ReadTimeout int + WriteTimeout int + PoolSize int + MaxIdleConns int + MaxOpenConns int +} + +type SqlClientConn struct { + SqlConfig + DB *gorm.DB +} + +func NewSqlClient(config SqlConfig) ISqlClientConn { + client := &SqlClientConn{} + client.SqlConfig = config + if err := client.Connect(); err != nil { + log.Fatal(err) + return nil + } + //ping to check connection in gorm + sqlDB, err := client.DB.DB() + if err != nil { + log.Fatal(err) + return nil + } + if err := sqlDB.Ping(); err != nil { + log.Fatal(err) + return nil + } + + return client +} + +func (c *SqlClientConn) Connect() error { + switch c.Driver { + case MYSQL: + //username:password@protocol(address)/dbname?param=value + connectionString := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true&timeout=%ds", c.Username, c.Password, c.Host, c.Port, c.Database, c.Timeout) + db, err := gorm.Open(mysql.Open(connectionString), &gorm.Config{}) + if err != nil { + log.Fatal(err) + return err + } + sqlDB, err := db.DB() + if err != nil { + log.Fatal(err) + return err + } + sqlDB.SetMaxIdleConns(c.MaxIdleConns) + sqlDB.SetMaxOpenConns(c.MaxOpenConns) + c.DB = db + return nil + default: + log.Fatal("driver is missing") + return errors.New("driver is missing") + } +} + +func (c *SqlClientConn) GetDB() *gorm.DB { + return c.DB +} + +func (c *SqlClientConn) GetDriver() string { + return c.Driver +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..fd3da79 --- /dev/null +++ b/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "errors" + "github.com/EngineerProOrg/BE-K01/internal/sqlclient" + "github.com/EngineerProOrg/BE-K01/pkg/repository" + "github.com/caarlos0/env" + "github.com/spf13/viper" + "gorm.io/gorm" + "log" + "time" +) + +type Config struct { + Dir string `env:"CONFIG_DIR" envDefault:"configs/config.json"` + DB bool +} + +var config Config +var sqlClient sqlclient.ISqlClientConn + +func init() { + loc, err := time.LoadLocation("Asia/Ho_Chi_Minh") + if err != nil { + log.Fatal(err) + } + time.Local = loc + + if err := env.Parse(&config); err != nil { + log.Fatal(err) + } + viper.SetConfigFile(config.Dir) + if err := viper.ReadInConfig(); err != nil { + log.Println(err.Error()) + panic(err) + } + + cfg := Config{ + Dir: config.Dir, + DB: viper.GetBool(`main.db`), + } + + if cfg.DB { + sqlClientConfig := sqlclient.SqlConfig{ + Driver: "mysql", + Host: viper.GetString(`db.host`), + Database: viper.GetString(`db.database`), + Username: viper.GetString(`db.username`), + Password: viper.GetString(`db.password`), + Port: viper.GetInt(`db.port`), + DialTimeout: 20, + ReadTimeout: 30, + WriteTimeout: 30, + Timeout: 30, + PoolSize: 10, + MaxIdleConns: 10, + MaxOpenConns: 10, + } + sqlClient = sqlclient.NewSqlClient(sqlClientConfig) + if sqlClient == nil { + panic(errors.New("sqlClient is nil")) + } else { + CreateOrUpdateGradeView(sqlClient.GetDB()) + } + } + +} + +func CreateOrUpdateGradeView(db *gorm.DB) error { + query := ` + CREATE OR REPLACE VIEW Grade AS + SELECT e.class_id, e.stud_id, + CASE e.grade + WHEN 'A' THEN 10 + WHEN 'B' THEN 8 + WHEN 'C' THEN 6 + WHEN 'D' THEN 4 + WHEN 'E' THEN 2 + WHEN 'F' THEN 0 + ELSE 0 + END + AS Grade + FROM enrolls e + ` + err := db.Exec(query).Error + return err +} + +func main() { + stuRepo := repository.NewStudentRepository(sqlClient.GetDB()) + courses, err := stuRepo.GetAvgGradeOfCourse() + if err != nil { + log.Fatal(err) + } + log.Println(courses) +} diff --git a/pkg/repository/course.go b/pkg/repository/course.go new file mode 100644 index 0000000..f7dced7 --- /dev/null +++ b/pkg/repository/course.go @@ -0,0 +1,51 @@ +package repository + +import ( + "github.com/EngineerProOrg/BE-K01/pkg/types" + "gorm.io/gorm" +) + +type CourseRepository interface { + GetCourseWithProfessorTeaching() ([]string, error) + GetCourseWithStudentStudying() ([]string, error) +} + +func NewCourseRepository(db *gorm.DB) CourseRepository { + return &CourseRepo{ + db: db, + } +} + +type CourseRepo struct { + db *gorm.DB +} + +func (c CourseRepo) GetCourseWithStudentStudying() ([]string, error) { + var courses []string + c.db.Model(&types.Course{}). + Select("DISTINCT(course_name)"). + Joins("JOIN Class ON Course.course_id = Class.course_id"). + Joins("JOIN Enroll ON Class.class_id = Enroll.class_id"). + Scan(&courses) + if err := c.db.Error; err != nil { + return nil, err + } + return courses, nil +} + +type a struct { + s int +} + +func (c CourseRepo) GetCourseWithProfessorTeaching() ([]string, error) { + var courses []string + c.db.Model(&types.Course{}). + Select("DISTINCT(course_name)"). + Joins("JOIN classes ON courses.course_id = classes.course_id"). + Scan(&courses) + + if err := c.db.Error; err != nil { + return nil, err + } + return courses, nil +} diff --git a/pkg/repository/manager.go b/pkg/repository/manager.go deleted file mode 100644 index f6cbce8..0000000 --- a/pkg/repository/manager.go +++ /dev/null @@ -1,25 +0,0 @@ -package repository - -import ( - "github.com/EngineerProOrg/BE-K01/pkg/types" - "gorm.io/gorm" -) - -type StudentRepository interface { - GetStudentByIdx(id int64) -} - -type repo struct { - db *gorm.DB -} - -func NewStudentRepository(db *gorm.DB) StudentRepository { - return &repo{ - db: db, - } -} - -func (r *repo) GetStudentByIdx(id int64) { - std := &types.Student{} - r.db.First(std, id) -} diff --git a/pkg/repository/professor.go b/pkg/repository/professor.go new file mode 100644 index 0000000..6a1c28d --- /dev/null +++ b/pkg/repository/professor.go @@ -0,0 +1,50 @@ +package repository + +import ( + "github.com/EngineerProOrg/BE-K01/pkg/types" + "gorm.io/gorm" +) + +type ProfessorRepository interface { + GetProfessorStudentClassNames() ([]QueryResult, error) +} + +type ProfessorRepoDb struct { + db *gorm.DB +} + +type ProfessorStudentClass struct { + ProfName string `gorm:"column:professors Name"` + StudentName string `gorm:"column:Student Name"` + ClassName string `gorm:"column:classes Name"` +} + +type QueryResult struct { + ProfessorsName string `gorm:"column:Professor Name"` + StudentName string `gorm:"column:Student Name"` + ClassName string `gorm:"column:Class Name"` +} + +func (p ProfessorRepoDb) GetProfessorStudentClassNames() ([]QueryResult, error) { + var result []QueryResult + + p.db.Model(&types.Professor{}). + Select("CONCAT(professors.prof_fname, ' ', professors.prof_lname) AS 'Professor Name'", + "CONCAT(students.stud_fname, ' ', students.stud_lname) AS 'Student Name'", + "classes.class_name AS 'Class Name'"). + Joins("JOIN classes ON professors.prof_id = classes.prof_id"). + Joins("JOIN enrolls ON classes.class_id = enrolls.class_id"). + Joins("JOIN students ON enrolls.stud_id = students.stud_id"). + Scan(&result) + + if err := p.db.Error; err != nil { + return nil, err + } + return result, nil +} + +func NewProfessorRepository(db *gorm.DB) ProfessorRepository { + return &ProfessorRepoDb{ + db: db, + } +} diff --git a/pkg/repository/student.go b/pkg/repository/student.go new file mode 100644 index 0000000..9aeec37 --- /dev/null +++ b/pkg/repository/student.go @@ -0,0 +1,112 @@ +package repository + +import ( + "gorm.io/gorm" +) + +type ClassGrade struct { + ClassName string `gorm:"column:Class Name"` + AvgGrade float64 `gorm:"column:Average Grade"` + Grade string `gorm:"column:Grade"` +} + +type StudentRepository interface { + GetAvgGradeOfStudent() ([]StudentGrade, error) + GetAvgGradeOfClass() ([]ClassGrade, error) + GetAvgGradeOfCourse() ([]CourseGrade, error) +} + +type StudentGrade struct { + StudentName string `gorm:"column:Student Name"` + AvgGrade float64 `gorm:"column:Average Grade"` + Grade string `gorm:"column:Grade"` +} + +func NewStudentRepository(db *gorm.DB) StudentRepository { + return &StudentRepo{ + db: db, + } +} + +type StudentRepo struct { + db *gorm.DB +} + +type CourseGrade struct { + CourseName string `gorm:"column:Course Name"` + AvgGrade float64 `gorm:"column:Average Grade"` + Grade string `gorm:"column:Grade"` +} + +func (s StudentRepo) GetAvgGradeOfCourse() ([]CourseGrade, error) { + var results []CourseGrade + + query := ` + SELECT c.course_name AS 'Course Name', + AVG(G.Grade) AS 'Average Grade', + CASE + WHEN AVG(G.Grade) < 5 THEN 'Weak' + WHEN AVG(G.Grade) >= 5 AND AVG(G.Grade) < 8 THEN 'Average' + WHEN AVG(G.Grade) >= 8 THEN 'Good' + END AS 'Grade' + FROM courses c + JOIN classes C2 ON c.course_id = C2.course_id + JOIN Grade G ON C2.class_id = G.class_id + GROUP BY c.course_id, c.course_name + ` + + err := s.db.Raw(query).Scan(&results).Error + if err != nil { + return nil, err + } + + return results, nil +} + +func (s StudentRepo) GetAvgGradeOfClass() ([]ClassGrade, error) { + var results []ClassGrade + + query := ` + select c.class_name AS 'Class Name', + AVG(G.Grade) AS 'Average Grade', + CASE + WHEN AVG(G.Grade) < 5 THEN 'Weak' + WHEN AVG(G.Grade) >= 5 AND AVG(G.Grade) < 8 THEN 'Average' + WHEN AVG(G.Grade) >= 8 THEN 'Good' + END AS 'Grade' +from classes c + join Grade G on c.class_id = G.class_id +group by c.class_id, c.class_name + ` + + err := s.db.Raw(query).Scan(&results).Error + if err != nil { + return nil, err + } + + return results, nil +} + +func (s StudentRepo) GetAvgGradeOfStudent() ([]StudentGrade, error) { + var results []StudentGrade + + query := ` + SELECT CONCAT(s.stud_fname, ' ', s.stud_lname) AS 'Student Name', + AVG(G.Grade) AS 'Average Grade', + CASE + WHEN AVG(G.Grade) < 5 THEN 'Weak' + WHEN AVG(G.Grade) >= 5 AND AVG(G.Grade) < 8 THEN 'Average' + WHEN AVG(G.Grade) >= 8 THEN 'Good' + END AS 'Grade' + FROM students s + JOIN Grade G ON s.stud_id = G.stud_id + GROUP BY s.stud_id + ` + + err := s.db.Raw(query).Scan(&results).Error + if err != nil { + return nil, err + } + + return results, nil +} diff --git a/pkg/types/class.go b/pkg/types/class.go new file mode 100644 index 0000000..8309f00 --- /dev/null +++ b/pkg/types/class.go @@ -0,0 +1,12 @@ +package types + +type Class struct { + ClassID int `gorm:"primaryKey;autoIncrement"` + ClassName string `gorm:"column:class_name"` + ProfID int `gorm:"column:prof_id"` + CourseID int `gorm:"column:course_id"` + RoomID int `gorm:"column:room_id"` + Professor Professor `gorm:"foreignKey:ProfID"` + Course Course `gorm:"foreignKey:CourseID"` + Room Room `gorm:"foreignKey:RoomID"` +} diff --git a/pkg/types/course.go b/pkg/types/course.go new file mode 100644 index 0000000..38d8677 --- /dev/null +++ b/pkg/types/course.go @@ -0,0 +1,6 @@ +package types + +type Course struct { + CourseID int `gorm:"primaryKey;autoIncrement"` + CourseName string `gorm:"column:course_name"` +} diff --git a/pkg/types/enroll.go b/pkg/types/enroll.go new file mode 100644 index 0000000..f872883 --- /dev/null +++ b/pkg/types/enroll.go @@ -0,0 +1,9 @@ +package types + +type Enroll struct { + StudID int `gorm:"primaryKey"` + ClassID int `gorm:"primaryKey"` + Grade string `gorm:"column:grade;index"` + Student Student `gorm:"foreignKey:StudID"` + Class Class `gorm:"foreignKey:ClassID"` +} diff --git a/pkg/types/professor.go b/pkg/types/professor.go new file mode 100644 index 0000000..6092a19 --- /dev/null +++ b/pkg/types/professor.go @@ -0,0 +1,7 @@ +package types + +type Professor struct { + ProfID int `gorm:"primaryKey;autoIncrement"` + ProfLName string `gorm:"column:prof_lname"` + ProfFName string `gorm:"column:prof_fname"` +} diff --git a/pkg/types/room.go b/pkg/types/room.go new file mode 100644 index 0000000..ba68418 --- /dev/null +++ b/pkg/types/room.go @@ -0,0 +1,8 @@ +package types + +type Room struct { + RoomID int `gorm:"primaryKey;autoIncrement"` + RoomLoc string `gorm:"column:room_loc"` + RoomCap string `gorm:"column:room_cap"` + ClassID int `gorm:"column:class_id"` +} diff --git a/pkg/types/student.go b/pkg/types/student.go index 9670e98..8b78ff3 100644 --- a/pkg/types/student.go +++ b/pkg/types/student.go @@ -1,11 +1,10 @@ package types -import ( - "gorm.io/gorm" -) - type Student struct { - gorm.Model - FirstName string `gorm:"first_name"` - LastName string `gorm:"last_name"` + StudID int `gorm:"primaryKey;autoIncrement"` + StudFName string `gorm:"column:stud_fname"` + StudLName string `gorm:"column:stud_lname"` + StudStreet string `gorm:"column:stud_street"` + StudCity string `gorm:"column:stud_city"` + StudZip string `gorm:"column:stud_zip"` }