Skip to content
9 changes: 8 additions & 1 deletion cmd/metaforo_api_test/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ func main() {
metaforo.UpdateVoteTime(*voteAccessToken, *voteGroup, *voteId, startTs.Unix(), endTs.Unix())
case "show":
showCommand.Parse(os.Args[2:])
proposal, _ := metaforo.GetProposal(*showThreadId, *showGroup, "", 0)
proposal, err := metaforo.GetProposal(*showThreadId, *showGroup, "", 0)
if err != nil {
if strings.Contains(err.Error(), "thread not found") {
log.Err(err).Msgf("thread not found, threadId: %d", *showThreadId)
} else {
panic(err)
}
}
api.PrintStructAsJson(proposal, "")
default:
fmt.Println("Unknown subcommand:", os.Args[1])
Expand Down
22 changes: 20 additions & 2 deletions internal/api/proposal/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ func SaveProposalRecordToDB(db *gorm.DB, reqData *CreateOrUpdateProposalData, us
proposalRcd.VoteDurationSecond = dbProposalRcd.VoteDurationSecond
proposalRcd.VoteDurationSecond = dbProposalRcd.VoteDurationSecond
proposalRcd.AssociateProposalId = reqData.CreateProjectProposalId
proposalRcd.IsMultipleVote = reqData.IsMultipleVote

// proposalRcd is a new record, use create function here. Also update other data related to proposal
if err = db.Transaction(func(tx *gorm.DB) error {
Expand Down Expand Up @@ -764,10 +765,11 @@ func SaveProposalToMetaforo(db *gorm.DB, dbProposalId uint, voteType int, metafo
metaforoProposalResponse, err = metaforo.GetProposal(metaforoThreadId, metaforoGroupName, metaforoAccessToken, 0)
if err != nil {
log.Error().Msgf("get metaforoProposal %d error: %+v", metaforoThreadId, err)
_ = UpdateDbRecordsFromMetaforoProposalResponse(db, updatedProposalRecord.ID, metaforoProposalResponse, err)
return err
}

err = UpdateDbRecordsFromMetaforoProposalResponse(db, updatedProposalRecord.ID, metaforoProposalResponse)
err = UpdateDbRecordsFromMetaforoProposalResponse(db, updatedProposalRecord.ID, metaforoProposalResponse, nil)
if err != nil {
log.Error().Msgf("update db records from metaforoProposalResponse error: %+v", err)
}
Expand Down Expand Up @@ -977,7 +979,23 @@ func IsUserMetVoteGate(userSeepassData *sdk.SeepassResponse, proposalVoteGate *m
}
}

func UpdateDbRecordsFromMetaforoProposalResponse(db *gorm.DB, dbProposalRcdId uint, metaforoProposal *metaforo.ProposalResponse) error {
func UpdateDbRecordsFromMetaforoProposalResponse(db *gorm.DB, dbProposalRcdId uint, metaforoProposal *metaforo.ProposalResponse, mfError error) error {
// Check whether update form metaforo contains error, if yes, update state to metaforo error and return
if mfError != nil {
log.Error().Msgf("get metaforo proposal error: %+v", mfError)
proposalStateForMetaforoError := model.ProposalStateUncategorizedMetaforoError
if strings.Contains(mfError.Error(), "not found") {
proposalStateForMetaforoError = model.ProposalStateDeletedFromMetaforo
}

if err = db.Model(&model.Proposal{}).Where("id = ?", dbProposalRcdId).Update("state", proposalStateForMetaforoError).Error; err != nil {
log.Error().Msgf("update proposal %d state to deleted_by_metaforo error", dbProposalRcdId)
return err
}

return nil
}

// Save all version proposals' arweave hash
if err = UpdateArweaveHashFromMetaforoProposalResponse(db, dbProposalRcdId, metaforoProposal); err != nil {
log.Error().Msgf("update arweave hash error: %+v", err)
Expand Down
10 changes: 6 additions & 4 deletions internal/api/proposal/req_n_resp.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,18 +371,20 @@ func ConvertProposalToFrontendDetailRecord(db *gorm.DB, proposalId uint, startPo
if proposal.ProposalRecordId != "" {
metaforoProposal, err := metaforo.GetProposal(proposal.GetMetaforoThreadId(), metaforoGroupName, accessToken, startPostId)
if err != nil {
log.Error().Msgf("get metaforo proposal error: %+v", err)
_ = UpdateDbRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal, err)
return nil, err
}

commentCount = metaforoProposal.Thread.PostsCount
votes = metaforoProposal.Thread.Polls

err = UpdateDbRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal)
err = UpdateDbRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal, nil)
if err != nil {
log.Error().Msgf("update proposal %d from metaforo error: %+v", proposalId, err)
return nil, err
}

commentCount = metaforoProposal.Thread.PostsCount
votes = metaforoProposal.Thread.Polls

pollStatusChanged, err := UpdateDbVoteOptionRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal)
if err != nil {
log.Error().Msgf("update propsal vote option records with metaforo response error: %+v", err)
Expand Down
3 changes: 2 additions & 1 deletion internal/api/proposal/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,11 @@ func GetMetaforoProposalByInternalId(db *gorm.DB, proposalIdStr string, metaforo
metaforoProposalResponse, err := metaforo.GetProposal(osProposalRcd.GetMetaforoThreadId(), metaforoGroupName, mfAccessToken, 0)
if err != nil {
log.Error().Msgf("get metaforo proposal error: %+v", err)
_ = UpdateDbRecordsFromMetaforoProposalResponse(db, osProposalRcd.ID, metaforoProposalResponse, err)
return nil, nil, err
}

err = UpdateDbRecordsFromMetaforoProposalResponse(db, osProposalRcd.ID, metaforoProposalResponse)
err = UpdateDbRecordsFromMetaforoProposalResponse(db, osProposalRcd.ID, metaforoProposalResponse, nil)
if err != nil {
log.Error().Msgf("update db records from metaforoProposalResponse error: %+v", err)
}
Expand Down
37 changes: 24 additions & 13 deletions internal/model/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ const (
// ProposalStateVetoed indicates the proposal has been voted by city hall proposal.
// TODO: In this case, there should be some field to reflect this relationship
ProposalStateVetoed

// ProposalStateDeletedFromMetaforo indicates the proposal has been deleted from metaforo
ProposalStateDeletedFromMetaforo

// ProposalStateUncategorizedMetaforoError indicates other errors returned by metaforo
ProposalStateUncategorizedMetaforoError
)

const (
Expand All @@ -49,18 +55,19 @@ const (
)

var ProposalStateIdNameMapping = map[string]ProposalState{
"pending_submit": ProposalStatePendingSubmit,
"draft": ProposalStateDraft,
"withdrawn": ProposalStateWithdrawn,
"rejected": ProposalStateRejected,
"approved": ProposalStateApproved,
"voting": ProposalStateVoting,
"vote_passed": ProposalStateVotePassed,
"vote_failed": ProposalStateVoteFailed,
"pending_execution": ProposalStatePendingExecution,
"executed": ProposalStateExecuted,
"execution_failed": ProposalStateExecutionFailed,
"vetoed": ProposalStateVetoed,
"pending_submit": ProposalStatePendingSubmit,
"draft": ProposalStateDraft,
"withdrawn": ProposalStateWithdrawn,
"rejected": ProposalStateRejected,
"approved": ProposalStateApproved,
"voting": ProposalStateVoting,
"vote_passed": ProposalStateVotePassed,
"vote_failed": ProposalStateVoteFailed,
"pending_execution": ProposalStatePendingExecution,
"executed": ProposalStateExecuted,
"execution_failed": ProposalStateExecutionFailed,
"vetoed": ProposalStateVetoed,
"deleted_from_metaforo": ProposalStateDeletedFromMetaforo,
}

var ProposalStateName = []string{
Expand Down Expand Up @@ -175,6 +182,10 @@ type Proposal struct {

// Whether user vote record has been saved for this proposal
UserVoteRecordSaved bool `gorm:"default:false"`

// IsMultiple saves the proposal is multiple vote or not, the flag will be used for draft proposal
// For proposals already created in Metaforo, the max_vote will be used as proporal multiple vote flag
IsMultipleVote bool `gorm:"default:false"`
}

func (p *Proposal) StateName() string {
Expand All @@ -197,7 +208,7 @@ func (p *Proposal) TaskStartDelay() time.Duration {
// which means the no more actions should be performed to the proposal
// VoteFailed may require additional actions, so it is not in here
func (p *Proposal) IsInFinState() bool {
return p.State == int(ProposalStateExecuted) || p.State == int(ProposalStateVetoed)
return p.State == int(ProposalStateExecuted) || p.State == int(ProposalStateVetoed) || p.State == int(ProposalStateDeletedFromMetaforo)
}

// StateIsUpdatable returns bool value indicates whether this proposal can be updated.
Expand Down
4 changes: 3 additions & 1 deletion internal/task_manager/metaforo_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,16 @@ func RefreshVotingProposalInfoJob(db *gorm.DB, job *model.CronJob, jobParams str
for _, dbRcd := range proposals {
metaforoThreadId := dbRcd.GetMetaforoThreadId()
metaforoProposalData, err := metaforo.GetProposal(metaforoThreadId, params.GroupName, cfg.MetaforoData.AccessToken, 0)

if err != nil {
_ = proposal.UpdateDbRecordsFromMetaforoProposalResponse(db, dbRcd.ID, metaforoProposalData, err)
log.Warn().Msgf("get metaforo proposal error: %+v", err)
continue
}

// Start transaction to update db records
if err = db.Transaction(func(tx *gorm.DB) error {
err = proposal.UpdateDbRecordsFromMetaforoProposalResponse(tx, dbRcd.ID, metaforoProposalData)
err = proposal.UpdateDbRecordsFromMetaforoProposalResponse(tx, dbRcd.ID, metaforoProposalData, nil)
if err != nil {
log.Warn().Msgf("update propsal with metaforo response error: %+v", err)
return err
Expand Down
49 changes: 41 additions & 8 deletions internal_inject/proposal/proposal.service.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ func (s *ProposalService) SaveProposalRecordToDB(db *gorm.DB, reqData *CreateOrU
proposalRcd.VoteDurationSecond = dbProposalRcd.VoteDurationSecond
proposalRcd.VoteDurationSecond = dbProposalRcd.VoteDurationSecond
proposalRcd.AssociateProposalId = reqData.CreateProjectProposalId
proposalRcd.IsMultipleVote = reqData.IsMultipleVote

// proposalRcd is a new record, use create function here. Also update other data related to proposal
if err = db.Transaction(func(tx *gorm.DB) error {
Expand Down Expand Up @@ -333,6 +334,7 @@ func (s *ProposalService) SaveProposalRecordToDB(db *gorm.DB, reqData *CreateOrU
AssociateProposalId: reqData.CreateProjectProposalId,
ProposalTemplateID: &reqData.TemplateId,
ExtraResultCheckRule: pTemplate.ExtraResultCheckRule,
IsMultipleVote: reqData.IsMultipleVote,
}
proposalRecord.PublicitySecond = voteTimeProps.PublicitySecond
proposalRecord.PendingExecutionSecond = voteTimeProps.PendingExecutionSecond
Expand Down Expand Up @@ -687,10 +689,11 @@ func (s *ProposalService) SaveProposalToMetaforo(db *gorm.DB, dbProposalId uint,
metaforoProposalResponse, err = metaforo.GetProposal(metaforoThreadId, metaforoGroupName, metaforoAccessToken, 0)
if err != nil {
log.Error().Msgf("get metaforoProposal %d error: %+v", metaforoThreadId, err)
_ = s.UpdateDbRecordsFromMetaforoProposalResponse(db, updatedProposalRecord.ID, metaforoProposalResponse, err)
return err
}

err = s.UpdateDbRecordsFromMetaforoProposalResponse(db, updatedProposalRecord.ID, metaforoProposalResponse)
err = s.UpdateDbRecordsFromMetaforoProposalResponse(db, updatedProposalRecord.ID, metaforoProposalResponse, nil)
if err != nil {
log.Error().Msgf("update db records from metaforoProposalResponse error: %+v", err)
}
Expand Down Expand Up @@ -900,7 +903,23 @@ func (s *ProposalService) IsUserMetVoteGate(userSeepassData *sdk.SeepassResponse
}
}

func (s *ProposalService) UpdateDbRecordsFromMetaforoProposalResponse(db *gorm.DB, dbProposalRcdId uint, metaforoProposal *metaforo.ProposalResponse) error {
func (s *ProposalService) UpdateDbRecordsFromMetaforoProposalResponse(db *gorm.DB, dbProposalRcdId uint, metaforoProposal *metaforo.ProposalResponse, mfError error) error {
// Check whether update form metaforo contains error, if yes, update state to metaforo error and return
if mfError != nil {
log.Error().Msgf("get metaforo proposal error: %+v", mfError)
proposalStateForMetaforoError := model.ProposalStateUncategorizedMetaforoError
if strings.Contains(mfError.Error(), "not found") {
proposalStateForMetaforoError = model.ProposalStateDeletedFromMetaforo
}

if err := db.Model(&model.Proposal{}).Where("id = ?", dbProposalRcdId).Update("state", proposalStateForMetaforoError).Error; err != nil {
log.Error().Msgf("update proposal %d state to deleted_by_metaforo error", dbProposalRcdId)
return err
}

return nil
}

// Save all version proposals' arweave hash
if err := s.UpdateArweaveHashFromMetaforoProposalResponse(db, dbProposalRcdId, metaforoProposal); err != nil {
log.Error().Msgf("update arweave hash error: %+v", err)
Expand Down Expand Up @@ -2006,18 +2025,20 @@ func (s *ProposalService) ConvertProposalToFrontendDetailRecord(db *gorm.DB, pro
if proposal.ProposalRecordId != "" {
metaforoProposal, err := metaforo.GetProposal(proposal.GetMetaforoThreadId(), metaforoGroupName, accessToken, startPostId)
if err != nil {
log.Error().Msgf("get proposal %d from metaforo error: %+v", proposalId, err)
_ = s.UpdateDbRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal, err)
return nil, err, internal.ERRCODE_GetMetaforoDataError
}

commentCount = metaforoProposal.Thread.PostsCount
votes = metaforoProposal.Thread.Polls

err = s.UpdateDbRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal)
err = s.UpdateDbRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal, nil)
if err != nil {
log.Error().Msgf("update proposal %d from metaforo error: %+v", proposalId, err)
return nil, err, -1
}

commentCount = metaforoProposal.Thread.PostsCount
votes = metaforoProposal.Thread.Polls

pollStatusChanged, err := s.UpdateDbVoteOptionRecordsFromMetaforoProposalResponse(db, proposalId, metaforoProposal)
if err != nil {
log.Error().Msgf("update propsal vote option records with metaforo response error: %+v", err)
Expand Down Expand Up @@ -2160,6 +2181,12 @@ func (s *ProposalService) ConvertProposalToFrontendDetailRecord(db *gorm.DB, pro
log.Error().Msgf("fetch proposal error: %+v", err)
return nil, err, -1
}

proposalMultipleVoteFlag := proposal.IsMultipleVote
if proposal.ProposalRecordId != "" {
proposalMultipleVoteFlag = len(votes) > 0 && votes[0].Max > 1
}

return &FrontendProposalDetailRecord{
ID: proposalId,
Title: proposal.Title,
Expand All @@ -2185,7 +2212,7 @@ func (s *ProposalService) ConvertProposalToFrontendDetailRecord(db *gorm.DB, pro
Votes: votes,
OsVoteOptions: frontendVoteOptions,
VoteType: proposal.VoteType,
IsMultipleVote: len(votes) > 0 && votes[0].Max > 1,
IsMultipleVote: proposalMultipleVoteFlag,
CreateTs: proposal.CreateTs,
IsBasedOnCustomTemplate: proposal.IsBasedOnCustomTemplate,
TemplateName: templateName,
Expand All @@ -2206,10 +2233,11 @@ func (s *ProposalService) GetMetaforoProposalByInternalId(db *gorm.DB, proposalI
metaforoProposalResponse, err := metaforo.GetProposal(osProposalRcd.GetMetaforoThreadId(), metaforoGroupName, mfAccessToken, 0)
if err != nil {
log.Error().Msgf("get metaforo proposal error: %+v", err)
_ = s.UpdateDbRecordsFromMetaforoProposalResponse(db, osProposalRcd.ID, metaforoProposalResponse, err)
return nil, nil, err
}

err = s.UpdateDbRecordsFromMetaforoProposalResponse(db, osProposalRcd.ID, metaforoProposalResponse)
err = s.UpdateDbRecordsFromMetaforoProposalResponse(db, osProposalRcd.ID, metaforoProposalResponse, nil)
if err != nil {
log.Error().Msgf("update db records from metaforoProposalResponse error: %+v", err)
}
Expand Down Expand Up @@ -2608,6 +2636,11 @@ func (s *ProposalService) UpdateProposalStateAndLaunchStateChangeActions(db *gor
log.Error().Msgf("change proposal to executed error")
return 0, err
}
case model.ProposalStateDeletedFromMetaforo:
if err = db.Model(&proposalRecord).Where("id = ?", proposalRecord.ID).Update("state", model.ProposalStateDeletedFromMetaforo).Error; err != nil {
log.Error().Msgf("update proposal %d state to deleted_by_metaforo error", proposalRecord.ID)
return 0, err
}
default:
return 0, fmt.Errorf("changing proposal from state %s to %s is not approved", proposalRecord.StateName(), model.ProposalStateName[newState])
}
Expand Down
Loading