Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

匿名回答の実装 #1158

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion model/current.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package model

import (
"github.com/go-gormigrate/gormigrate/v2"
"github.com/traPtitech/anke-to/model/migration"
)

// Migrations is all db migrations
func Migrations() []*gormigrate.Migration {
return []*gormigrate.Migration{}
return []*gormigrate.Migration{
migration.V1(), // Questionnariesにis_anonymousカラムを追加
}
}

func AllTables() []interface{} {
Expand Down
105 changes: 105 additions & 0 deletions model/migration/v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package migration

import (
"time"

"github.com/go-gormigrate/gormigrate/v2"
"gopkg.in/guregu/null.v4"
"gorm.io/gorm"
)

func V1() *gormigrate.Migration {
return &gormigrate.Migration{
ID: "1",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(
&v1Questionnaires{},
); err != nil {
return err
}
return nil
},
}
}

type v1Questionnaires struct {
ID int `json:"questionnaireID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
Title string `json:"title" gorm:"type:char(50);size:50;not null"`
Description string `json:"description" gorm:"type:text;not null"`
ResTimeLimit null.Time `json:"res_time_limit,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL;"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL;"`
ResSharedTo string `json:"res_shared_to" gorm:"type:char(30);size:30;not null;default:administrators"`
IsAnonymous bool `json:"is_anonymous" gorm:"type:boolean;not null;default:false"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
ModifiedAt time.Time `json:"modified_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
Administrators []v1Administrators `json:"-" gorm:"foreignKey:QuestionnaireID"`
Targets []v1Targets `json:"-" gorm:"foreignKey:QuestionnaireID"`
Questions []v1Questions `json:"-" gorm:"foreignKey:QuestionnaireID"`
Respondents []v1Respondents `json:"-" gorm:"foreignKey:QuestionnaireID"`
}

type v1Administrators struct {
QuestionnaireID int `gorm:"type:int(11);not null;primaryKey"`
UserTraqid string `gorm:"type:varchar(32);size:32;not null;primaryKey"`
}

type v1Targets struct {
QuestionnaireID int `gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
UserTraqid string `gorm:"type:varchar(32);size:32;not null;primaryKey"`
}

type v1Questions struct {
ID int `json:"id" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
QuestionnaireID int `json:"questionnaireID" gorm:"type:int(11);not null"`
PageNum int `json:"page_num" gorm:"type:int(11);not null"`
QuestionNum int `json:"question_num" gorm:"type:int(11);not null"`
Type string `json:"type" gorm:"type:char(20);size:20;not null"`
Body string `json:"body" gorm:"type:text;default:NULL"`
IsRequired bool `json:"is_required" gorm:"type:tinyint(4);size:4;not null;default:0"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
Options []v1Options `json:"-" gorm:"foreignKey:QuestionID"`
Responses []v1Responses `json:"-" gorm:"foreignKey:QuestionID"`
ScaleLabels []v1ScaleLabels `json:"-" gorm:"foreignKey:QuestionID"`
Validations []v1Validations `json:"-" gorm:"foreignKey:QuestionID"`
}

type v1Respondents struct {
ResponseID int `json:"responseID" gorm:"column:response_id;type:int(11) AUTO_INCREMENT;not null;primaryKey"`
QuestionnaireID int `json:"questionnaireID" gorm:"type:int(11);not null"`
UserTraqid string `json:"user_traq_id,omitempty" gorm:"type:varchar(32);size:32;default:NULL"`
ModifiedAt time.Time `json:"modified_at,omitempty" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
SubmittedAt null.Time `json:"submitted_at,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL"`
DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL"`
Responses []v1Responses `json:"-" gorm:"foreignKey:ResponseID;references:ResponseID"`
}

type v1Options struct {
ID int `gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
QuestionID int `gorm:"type:int(11);not null"`
OptionNum int `gorm:"type:int(11);not null"`
Body string `gorm:"type:text;default:NULL;"`
}

type v1Responses struct {
ResponseID int `json:"-" gorm:"type:int(11);not null"`
QuestionID int `json:"-" gorm:"type:int(11);not null"`
Body null.String `json:"response" gorm:"type:text;default:NULL"`
ModifiedAt time.Time `json:"-" gorm:"type:timestamp;not null;dafault:CURRENT_TIMESTAMP"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL"`
}

type v1ScaleLabels struct {
QuestionID int `json:"questionID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
ScaleLabelRight string `json:"scale_label_right" gorm:"type:text;default:NULL;"`
ScaleLabelLeft string `json:"scale_label_left" gorm:"type:text;default:NULL;"`
ScaleMin int `json:"scale_min" gorm:"type:int(11);default:NULL;"`
ScaleMax int `json:"scale_max" gorm:"type:int(11);default:NULL;"`
}

type v1Validations struct {
QuestionID int `json:"questionID" gorm:"type:int(11);not null;primaryKey"`
RegexPattern string `json:"regex_pattern" gorm:"type:text;default:NULL"`
MinBound string `json:"min_bound" gorm:"type:text;default:NULL"`
MaxBound string `json:"max_bound" gorm:"type:text;default:NULL"`
}
5 changes: 3 additions & 2 deletions model/questionnaires.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (

// IQuestionnaire QuestionnaireのRepository
type IQuestionnaire interface {
InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error)
UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error
InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isAnonymous bool) (int, error)
UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isAnonymous bool, questionnaireID int) error
DeleteQuestionnaire(ctx context.Context, questionnaireID int) error
GetQuestionnaires(ctx context.Context, userID string, sort string, search string, pageNum int, nontargeted bool) ([]QuestionnaireInfo, int, error)
GetAdminQuestionnaires(ctx context.Context, userID string) ([]Questionnaires, error)
Expand All @@ -21,4 +21,5 @@ type IQuestionnaire interface {
GetQuestionnaireLimitByResponseID(ctx context.Context, responseID int) (null.Time, error)
GetResponseReadPrivilegeInfoByResponseID(ctx context.Context, userID string, responseID int) (*ResponseReadPrivilegeInfo, error)
GetResponseReadPrivilegeInfoByQuestionnaireID(ctx context.Context, userID string, questionnaireID int) (*ResponseReadPrivilegeInfo, error)
GetResponseAnonymousByQuestionnaireID(ctx context.Context, questionnaireID int) (bool, error)
}
59 changes: 44 additions & 15 deletions model/questionnaires_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ func NewQuestionnaire() *Questionnaire {
return new(Questionnaire)
}

//Questionnaires questionnairesテーブルの構造体
// Questionnaires questionnairesテーブルの構造体
type Questionnaires struct {
ID int `json:"questionnaireID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"`
Title string `json:"title" gorm:"type:char(50);size:50;not null"`
Description string `json:"description" gorm:"type:text;not null"`
ResTimeLimit null.Time `json:"res_time_limit,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL;"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL;"`
ResSharedTo string `json:"res_shared_to" gorm:"type:char(30);size:30;not null;default:administrators"`
IsAnonymous bool `json:"is_anonymous" gorm:"type:boolean;not null;default:false"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
ModifiedAt time.Time `json:"modified_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"`
Administrators []Administrators `json:"-" gorm:"foreignKey:QuestionnaireID"`
Expand All @@ -44,28 +45,28 @@ func (questionnaire *Questionnaires) BeforeCreate(tx *gorm.DB) error {
return nil
}

//BeforeUpdate Update時に自動でmodified_atを現在時刻に
// BeforeUpdate Update時に自動でmodified_atを現在時刻に
func (questionnaire *Questionnaires) BeforeUpdate(tx *gorm.DB) error {
questionnaire.ModifiedAt = time.Now()

return nil
}

//QuestionnaireInfo Questionnaireにtargetかの情報追加
// QuestionnaireInfo Questionnaireにtargetかの情報追加
type QuestionnaireInfo struct {
Questionnaires
IsTargeted bool `json:"is_targeted" gorm:"type:boolean"`
}

//QuestionnaireDetail Questionnaireの詳細
// QuestionnaireDetail Questionnaireの詳細
type QuestionnaireDetail struct {
Targets []string
Respondents []string
Administrators []string
Questionnaires
}

//TargettedQuestionnaire targetになっているアンケートの情報
// TargettedQuestionnaire targetになっているアンケートの情報
type TargettedQuestionnaire struct {
Questionnaires
RespondedAt null.Time `json:"responded_at"`
Expand All @@ -78,8 +79,8 @@ type ResponseReadPrivilegeInfo struct {
IsRespondent bool
}

//InsertQuestionnaire アンケートの追加
func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error) {
// InsertQuestionnaire アンケートの追加
func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isAnonymous bool) (int, error) {
db, err := getTx(ctx)
if err != nil {
return 0, fmt.Errorf("failed to get tx: %w", err)
Expand All @@ -91,13 +92,15 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des
Title: title,
Description: description,
ResSharedTo: resSharedTo,
IsAnonymous: isAnonymous,
}
} else {
questionnaire = Questionnaires{
Title: title,
Description: description,
ResTimeLimit: resTimeLimit,
ResSharedTo: resSharedTo,
IsAnonymous: isAnonymous,
}
}

Expand All @@ -109,8 +112,8 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des
return questionnaire.ID, nil
}

//UpdateQuestionnaire アンケートの更新
func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error {
// UpdateQuestionnaire アンケートの更新
func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isAnonymous bool, questionnaireID int) error {
db, err := getTx(ctx)
if err != nil {
return fmt.Errorf("failed to get tx: %w", err)
Expand All @@ -123,13 +126,15 @@ func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, des
Description: description,
ResTimeLimit: resTimeLimit,
ResSharedTo: resSharedTo,
IsAnonymous: isAnonymous,
}
} else {
questionnaire = map[string]interface{}{
"title": title,
"description": description,
"res_time_limit": gorm.Expr("NULL"),
"res_shared_to": resSharedTo,
"is_anonymous": isAnonymous,
}
}

Expand All @@ -148,7 +153,7 @@ func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, des
return nil
}

//DeleteQuestionnaire アンケートの削除
// DeleteQuestionnaire アンケートの削除
func (*Questionnaire) DeleteQuestionnaire(ctx context.Context, questionnaireID int) error {
db, err := getTx(ctx)
if err != nil {
Expand All @@ -167,8 +172,10 @@ func (*Questionnaire) DeleteQuestionnaire(ctx context.Context, questionnaireID i
return nil
}

/*GetQuestionnaires アンケートの一覧
2つ目の戻り値はページ数の最大値*/
/*
GetQuestionnaires アンケートの一覧
2つ目の戻り値はページ数の最大値
*/
func (*Questionnaire) GetQuestionnaires(ctx context.Context, userID string, sort string, search string, pageNum int, nontargeted bool) ([]QuestionnaireInfo, int, error) {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
Expand Down Expand Up @@ -263,7 +270,7 @@ func (*Questionnaire) GetAdminQuestionnaires(ctx context.Context, userID string)
return questionnaires, nil
}

//GetQuestionnaireInfo アンケートの詳細な情報取得
// GetQuestionnaireInfo アンケートの詳細な情報取得
func (*Questionnaire) GetQuestionnaireInfo(ctx context.Context, questionnaireID int) (*Questionnaires, []string, []string, []string, error) {
db, err := getTx(ctx)
if err != nil {
Expand Down Expand Up @@ -315,7 +322,7 @@ func (*Questionnaire) GetQuestionnaireInfo(ctx context.Context, questionnaireID
return &questionnaire, targets, administrators, respondents, nil
}

//GetTargettedQuestionnaires targetになっているアンケートの取得
// GetTargettedQuestionnaires targetになっているアンケートの取得
func (*Questionnaire) GetTargettedQuestionnaires(ctx context.Context, userID string, answered string, sort string) ([]TargettedQuestionnaire, error) {
db, err := getTx(ctx)
if err != nil {
Expand Down Expand Up @@ -359,7 +366,7 @@ func (*Questionnaire) GetTargettedQuestionnaires(ctx context.Context, userID str
return questionnaires, nil
}

//GetQuestionnaireLimit アンケートの回答期限の取得
// GetQuestionnaireLimit アンケートの回答期限の取得
func (*Questionnaire) GetQuestionnaireLimit(ctx context.Context, questionnaireID int) (null.Time, error) {
db, err := getTx(ctx)
if err != nil {
Expand Down Expand Up @@ -431,6 +438,28 @@ func (*Questionnaire) GetResponseReadPrivilegeInfoByResponseID(ctx context.Conte
return &responseReadPrivilegeInfo, nil
}

func (*Questionnaire) GetResponseAnonymousByQuestionnaireID(ctx context.Context, questionnaireID int) (bool, error) {
db, err := getTx(ctx)
if err != nil {
return true, fmt.Errorf("failed to get tx: %w", err)
}

var is_anonymous bool
err = db.
Table("questionnaires").
Where("questionnaires.id = ?", questionnaireID).
Select("questionnaires.is_anonymous").
Take(&is_anonymous).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return true, ErrRecordNotFound
}
if err != nil {
return true, fmt.Errorf("failed to get response read privilege info: %w", err)
}

return is_anonymous, nil
}

func (*Questionnaire) GetResponseReadPrivilegeInfoByQuestionnaireID(ctx context.Context, userID string, questionnaireID int) (*ResponseReadPrivilegeInfo, error) {
db, err := getTx(ctx)
if err != nil {
Expand Down
Loading
Loading