diff --git a/main.go b/main.go index 8d76cf1..f7f7392 100644 --- a/main.go +++ b/main.go @@ -64,6 +64,11 @@ func main() { } db.SetLogger(log.New(f, "\n", 0)) + err = mygorm.Init2(db) + if err != nil { + log.Fatal(err) + } + // Initalize QOR Admin adm, err := myqor.Init(db, assetFS, workDir) if err != nil { diff --git a/mygorm/breedingbook.go b/mygorm/breedingbook.go index 52a771e..73d944a 100644 --- a/mygorm/breedingbook.go +++ b/mygorm/breedingbook.go @@ -2,85 +2,179 @@ package mygorm import ( "github.com/jinzhu/gorm" + "time" ) -type TypeEnum struct { - slug string -} +type TypeEnum string + +const ( + TypeUnknown = TypeEnum("") + TypeString = TypeEnum("string") + TypeText = TypeEnum("text") + TypeCheckbox = TypeEnum("checkbox") + TypeInt = TypeEnum("integer") + TypeFloat = TypeEnum("floating point") + TypeDate = TypeEnum("date") + TypeDateTime = TypeEnum("timestamp") + TypeSelectOne = TypeEnum("select one") + TypeSelectMany = TypeEnum("select many") // []string +) -func (te TypeEnum) String() string { - return te.slug +var AllTypeEnums = []TypeEnum{ + TypeUnknown, + TypeString, + TypeText, + TypeCheckbox, + TypeInt, + TypeFloat, + TypeDate, + TypeDateTime, + TypeSelectOne, + TypeSelectMany, } -var ( - TypeUnknown = TypeEnum{""} - TypeString = TypeEnum{"string"} - TypeText = TypeEnum{"text"} - TypeCheckbox = TypeEnum{"checkbox"} - TypeInt = TypeEnum{"integer"} - TypeFloat = TypeEnum{"floating point"} - TypeDate = TypeEnum{"date"} - TypeDateTime = TypeEnum{"timestamp"} - TypeSelectOne = TypeEnum{"select one"} - TypeSelectMany = TypeEnum{"select many"} +type QualityEnum string + +const ( + QualityNeutral = QualityEnum("neutral") + QualityBad = QualityEnum("bad") + QualityPerfect = QualityEnum("perfect") ) -var AllTypeEnums = []string{ - TypeUnknown.slug, - TypeString.slug, - TypeText.slug, - TypeCheckbox.slug, - TypeInt.slug, - TypeFloat.slug, - TypeDate.slug, - TypeDateTime.slug, - TypeSelectOne.slug, - TypeSelectMany.slug, +var AllQualityEnums = []QualityEnum{ + QualityNeutral, + QualityBad, + QualityPerfect, } -func NewTypeEnum(s string) TypeEnum { - switch s { - case TypeString.slug: - return TypeString - case TypeText.slug: - return TypeText - case TypeCheckbox.slug: - return TypeCheckbox - case TypeInt.slug: - return TypeInt - case TypeFloat.slug: - return TypeFloat - case TypeDate.slug: - return TypeDate - case TypeDateTime.slug: - return TypeDateTime - case TypeSelectOne.slug: - return TypeSelectOne - case TypeSelectMany.slug: - return TypeSelectMany - } - return TypeUnknown +type DogTest struct { + gorm.Model + Name string `gorm:"unique; not null"` + SelectOne1 string `gorm:"not null"` + SelectOne2 string `gorm:"not null"` + SelectOne3 string `gorm:"not null"` + CheckBox1 bool `gorm:"not null"` + CheckBox2 bool `gorm:"not null"` + CheckBox3 bool `gorm:"not null"` + String1 string `gorm:"not null"` + String2 string `gorm:"not null"` + String3 string `gorm:"not null"` + String1Quality QualityEnum `gorm:"not null"` + String2Quality QualityEnum `gorm:"not null"` + String3Quality QualityEnum `gorm:"not null"` + Text1 string `gorm:"not null"` + Text2 string `gorm:"not null"` + Text3 string `gorm:"not null"` + Text1Quality QualityEnum `gorm:"not null"` + Text2Quality QualityEnum `gorm:"not null"` + Text3Quality QualityEnum `gorm:"not null"` + Integer1 int64 `gorm:"not null"` + Integer2 int64 `gorm:"not null"` + Integer3 int64 `gorm:"not null"` + Float1 float64 `gorm:"not null"` + Float2 float64 `gorm:"not null"` + Float3 float64 `gorm:"not null"` + Date1 time.Time `gorm:"not null"` + Date2 time.Time `gorm:"not null"` + Date3 time.Time `gorm:"not null"` + Timestamp1 time.Time `gorm:"not null"` + Timestamp2 time.Time `gorm:"not null"` + Timestamp3 time.Time `gorm:"not null"` + SelectMany1 []string `gorm:"not null"` + SelectMany2 []string `gorm:"not null"` + SelectMany3 []string `gorm:"not null"` } type BaseMetaFeature struct { gorm.Model - Name string `gorm:"unique"` - ShortName string `gorm:"unique"` - Type TypeEnum - GroupID uint + Name string `gorm:"unique; not null"` + ShortName string `gorm:"unique; not null"` + Type TypeEnum `gorm:"not null"` + GroupID uint `gorm:"not null"` Group FeatureGroup `gorm:"foreignkey:GroupID;association_autocreate:false;association_autoupdate:false"` } type FeatureGroup struct { gorm.Model - Name string `gorm:"unique"` - ShortName string `gorm:"unique"` - ColorID uint - Color Color `gorm:"foreignkey:ColorID;association_autocreate:false;association_autoupdate:false"` + Name string `gorm:"unique; not null"` + ShortName string `gorm:"unique; not null"` + ColorID uint `gorm:"not null"` + Color Color `gorm:"foreignkey:ColorID;association_autocreate:false;association_autoupdate:false"` } type Color struct { gorm.Model - Name string `gorm:"unique"` - HexValue string `gorm:"unique"` + Name string `gorm:"unique; not null"` + HexValue string `gorm:"unique; not null"` +} + +type SelectOneMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + Order int `gorm:"not null"` + Value string `gorm:"not null"` + Quality QualityEnum `gorm:"not null"` +} + +type CheckBoxMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + QualityChecked QualityEnum `gorm:"not null"` + QualityUnchecked QualityEnum `gorm:"not null"` +} + +type StringMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + MinLength uint `gorm:"not null"` + MaxLength uint `gorm:"not null"` +} + +type TextMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + MinLength uint `gorm:"not null"` + MaxLength uint `gorm:"not null"` +} + +type IntegerMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + NeutralMin int64 `gorm:"not null"` + NeutralMax int64 `gorm:"not null"` + BadMin int64 `gorm:"not null"` + BadMax int64 `gorm:"not null"` + PerfectMin int64 `gorm:"not null"` + PerfectMax int64 `gorm:"not null"` +} + +type FloatMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + NeutralMin float64 `gorm:"not null"` + NeutralMax float64 `gorm:"not null"` + BadMin float64 `gorm:"not null"` + BadMax float64 `gorm:"not null"` + PerfectMin float64 `gorm:"not null"` + PerfectMax float64 `gorm:"not null"` +} + +type DateMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + // still decide +} + +type TimestampMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + // still decide +} + +type SelectManyMetaFeature struct { + BaseID uint `gorm:"not null"` + ColumnNum int `gorm:"not null"` + Order int `gorm:"not null"` + Value string `gorm:"not null"` + Quality QualityEnum `gorm:"not null"` } diff --git a/mygorm/breedingbookfunc.go b/mygorm/breedingbookfunc.go new file mode 100644 index 0000000..b3d9d08 --- /dev/null +++ b/mygorm/breedingbookfunc.go @@ -0,0 +1,15 @@ +package mygorm + +import ( + "fmt" + "github.com/jinzhu/gorm" +) + +func Init2(db *gorm.DB) error { + if err := db.AutoMigrate(&DogTest{}, &BaseMetaFeature{}, &FeatureGroup{}, &Color{}, &SelectOneMetaFeature{}, &CheckBoxMetaFeature{}, + &StringMetaFeature{}, &TextMetaFeature{}, &IntegerMetaFeature{}, &FloatMetaFeature{}, &DateMetaFeature{}, &TimestampMetaFeature{}, &SelectManyMetaFeature{}).Error; err != nil { + return fmt.Errorf("unable to migrate DB to current state: %v", err) + } + + return nil +} diff --git a/mygorm/gorm_test.go b/mygorm/gorm_test.go index e25330e..a04cf03 100644 --- a/mygorm/gorm_test.go +++ b/mygorm/gorm_test.go @@ -63,13 +63,16 @@ func TestDogsAndParents(t *testing.T) { if err := db.Model(p1New).Association("Mother").Error; err != nil { t.Fatalf("Mother association error: %v", err) } + db.Model(p1New).Association("Mother").Find(&(p1New.Mother)) if p1New.Mother.ID != m.ID { t.Errorf("p1New should have got '%s' as mother but instead it is '%s': %s", m.Name, p1New.Mother.Name, spew.Sdump(p1New)) } + if err := db.Model(p1New).Association("Father").Error; err != nil { t.Fatalf("Father association error: %v", err) } + db.Model(p1New).Association("Father").Find(&(p1New.Father)) if p1New.Father.ID != f.ID { t.Errorf("p1New should have got '%s' as father but instead it is '%s': %s", f.Name, p1New.Father.Name, spew.Sdump(p1New)) @@ -83,13 +86,16 @@ func TestDogsAndParents(t *testing.T) { if err := db.Model(p2New).Association("Mother").Error; err != nil { t.Fatalf("Mother association error: %v", err) } + db.Model(p2New).Association("Mother").Find(&(p2New.Mother)) if p2New.Mother.ID != m.ID { t.Errorf("p2New should have got '%s' as mother but instead it is '%s': %s", m.Name, p2New.Mother.Name, spew.Sdump(p2New)) } + if err := db.Model(p2New).Association("Father").Error; err != nil { t.Fatalf("Father association error: %v", err) } + db.Model(p2New).Association("Father").Find(&(p2New.Father)) if p2New.Father.ID != f.ID { t.Errorf("p2New should have got '%s' as father but instead it is '%s': %s", f.Name, p2New.Father.Name, spew.Sdump(p2New)) @@ -158,3 +164,143 @@ func TestColorAndFeatureGroups(t *testing.T) { } } + +func TestBaseMetaFeatureAndFeatureGroups(t *testing.T) { + // create colors + c3 := &Color{Name: "blue", HexValue: "0000ff"} + + // write to DB + if err := db.Create(c3).Error; err != nil { + t.Fatalf("Unable to create Color 3, %v", err) + } + + // create feature groups + fg3 := &FeatureGroup{Name: "pigmente2", ColorID: c3.ID, ShortName: "pigments2"} + fg4 := &FeatureGroup{Name: "teeth", ColorID: c3.ID, ShortName: "teeth"} + + // write to DB + if err := db.Create(fg3).Error; err != nil { + t.Fatalf("Unable to create feature group 1, %v", err) + } + + // write to DB + if err := db.Create(fg4).Error; err != nil { + t.Fatalf("Unable to create feature group 2, %v", err) + } + + // create BaseMetaFeature + f1 := &BaseMetaFeature{Name: "pigmente2", GroupID: fg3.ID, ShortName: "pigments2", Type: TypeText} + f2 := &BaseMetaFeature{Name: "pigmente3", GroupID: fg3.ID, ShortName: "pigments3", Type: TypeString} + f3 := &BaseMetaFeature{Name: "pigmente4", GroupID: fg3.ID, ShortName: "pigments4", Type: TypeCheckbox} + f4 := &BaseMetaFeature{Name: "teeth", GroupID: fg4.ID, ShortName: "teeth", Type: TypeDate} + f5 := &BaseMetaFeature{Name: "teeth2", GroupID: fg4.ID, ShortName: "teeth2", Type: TypeFloat} + + // f1 ------------------------ + + // write to DB + if err := db.Create(f1).Error; err != nil { + t.Fatalf("Unable to create feature 1, %v", err) + } + + // read from DB + f1New := &BaseMetaFeature{} + if err := db.First(f1New, f1.ID).Error; err != nil { + t.Fatalf("Unable to read the feature 1, %v", err) + } + + if f1New.Type != TypeText { + t.Errorf("f1New should have got '%s' as type but instead it is '%s': %s", TypeText, f1New.Type, spew.Sdump(f1New)) + } + + // checking the association + if err := db.Model(f1New).Association("Group").Error; err != nil { + t.Fatalf("Group association error: %v", err) + } + + // population association + db.Model(f1New).Association("Group").Find(&(f1New.Group)) + if f1New.Group.ID != fg3.ID { + t.Errorf("f1New should have got '%s' as group but instead it is '%s': %s", fg3.Name, f1New.Group.Name, spew.Sdump(f1New)) + } + + // f2 ------------------------ + // write to DB + if err := db.Create(f2).Error; err != nil { + t.Fatalf("Unable to create feature 2, %v", err) + } + + // read from DB + f2New := &BaseMetaFeature{} + if err := db.First(f2New, f2.ID).Error; err != nil { + t.Fatalf("Unable to read the feature group 2, %v", err) + } + + // checking the association + if err := db.Model(f2New).Association("Group").Error; err != nil { + t.Fatalf("Group association error: %v", err) + } + + // population association + db.Model(f2New).Association("Group").Find(&(f2New.Group)) + if f2New.Group.ID != fg3.ID { + t.Errorf("f2New should have got '%s' as group but instead it is '%s': %s", fg3.Name, f2New.Group.Name, spew.Sdump(f2New)) + } + + // f3 ------------------------ + if err := db.Create(f3).Error; err != nil { + t.Fatalf("Unable to create feature group 3, %v", err) + } + + f3New := &BaseMetaFeature{} + if err := db.First(f3New, f3.ID).Error; err != nil { + t.Fatalf("Unable to reade the feature group 3, %v", err) + } + + if err := db.Model(f3New).Association("Group").Error; err != nil { + t.Fatalf("Group association error: %v", err) + } + + db.Model(f3New).Association("Group").Find(&(f3New.Group)) + if f3New.Group.ID != fg3.ID { + t.Errorf("f3new should have got '%s' as group but instead it is '%s': %s", fg3.Name, f3New.Group.Name, spew.Sdump(f3New)) + } + + // f4 ------------------------ + if err := db.Create(f4).Error; err != nil { + t.Fatalf("Unable to create feature group 4, %v", err) + } + + f4New := &BaseMetaFeature{} + if err := db.First(f4New, f4.ID).Error; err != nil { + t.Fatalf("Unable to reade the feature group 4, %v", err) + } + + if err := db.Model(f4New).Association("Group").Error; err != nil { + t.Fatalf("Group association err: %v", err) + } + + db.Model(f4New).Association("Group").Find(&(f4New.Group)) + if f4New.Group.ID != fg4.ID { + t.Errorf("f4New should have got '%s' as group but instead it is '%s': %s", fg4.Name, f4New.Group.Name, spew.Sdump(f4New)) + } + + // f5 ------------------------ + if err := db.Create(f5).Error; err != nil { + t.Fatalf("Unable to create feature group 5, %v", err) + } + + f5New := &BaseMetaFeature{} + if err := db.First(f5New, f5.ID).Error; err != nil { + t.Fatalf("Unable to reade the feature group 5, %v", err) + } + + if err := db.Model(f5New).Association("Group").Error; err != nil { + t.Fatalf("Group association err: %v", err) + } + + db.Model(f5New).Association("Group").Find(&(f5New.Group)) + if f5New.Group.ID != fg4.ID { + t.Errorf("f5New should have got '%s' as group but instead it is '%s': %s", fg4.Name, + f5New.Group.Name, spew.Sdump(f5New)) + } +} diff --git a/mygorm/test.db b/mygorm/test.db deleted file mode 100644 index c72cdcd..0000000 Binary files a/mygorm/test.db and /dev/null differ