From 67b6abf001aa19f73c6e948ef5b98f395f684bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Mon, 17 Apr 2023 18:38:27 +0800 Subject: [PATCH] =?UTF-8?q?finish=20=E8=AF=95=E5=8D=B7=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/global/file.go | 26 +++---- backend/global/question.go | 38 +++++++++- backend/paper.go | 6 +- backend/question.go | 72 +++++++++++++++++-- frontend/vben/mock/page/file.ts | 6 +- frontend/vben/src/api/page/model/fileModel.ts | 1 + frontend/vben/src/views/page/file/index.vue | 6 +- 7 files changed, 129 insertions(+), 26 deletions(-) diff --git a/backend/global/file.go b/backend/global/file.go index 6d28685..4078202 100644 --- a/backend/global/file.go +++ b/backend/global/file.go @@ -371,8 +371,10 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func } v := make(map[string]uint8, len(words)*2) for _, word := range words { - if word != "" && word != "\n" && word != " " { - v[word]++ + if word != "" && !strings.Contains("\n 。,、的是使,.()()1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", word) { + if v[word] == 0 { // 二值化 + v[word] = 1 + } } } data, err := json.Marshal(v) @@ -448,9 +450,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func } } majorq.Sub = append(majorq.Sub, QuestionJSON{ - Name: queidstr, - Points: 0, //TODO: fill sub points - Rate: r, + Name: queidstr, //TODO: fill sub points }) } filequestions = append(filequestions, majorq) @@ -552,18 +552,20 @@ func (f *FileDatabase) DelFile(lstid, uid int, istemp bool) error { } // GetFile get analyzed file's structure from List(ID) -func (f *FileDatabase) GetFile(lstid, uid int) (*File, int64, error) { +func (f *FileDatabase) GetFile(lstid, uid int) (file *File, sz int64, istemp bool, err error) { f.mu.RLock() defer f.mu.RUnlock() lst, err := sql.Find[List](&f.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid)) if err != nil { - return nil, 0, err + return } if lst.HasntAnalyzed { - return nil, 0, ErrHasntAnalyzed + err = ErrHasntAnalyzed + return } if lst.IsTemp && lst.Uploader != uid { - return nil, 0, ErrNoGetFileStatusPermission + err = ErrNoGetFileStatusPermission + return } ftable := "" if lst.IsTemp { @@ -571,9 +573,9 @@ func (f *FileDatabase) GetFile(lstid, uid int) (*File, int64, error) { } else { ftable = FileTableFile } - file, err := sql.Find[File](&f.db, ftable, "WHERE ListID="+strconv.Itoa(lstid)) + filestruct, err := sql.Find[File](&f.db, ftable, "WHERE ListID="+strconv.Itoa(lstid)) if err != nil { - return nil, 0, err + return } - return &file, lst.Size, nil + return &filestruct, lst.Size, lst.IsTemp, nil } diff --git a/backend/global/question.go b/backend/global/question.go index 9ff592f..625d723 100644 --- a/backend/global/question.go +++ b/backend/global/question.go @@ -17,7 +17,6 @@ import ( type QuestionJSON struct { Name string `json:"name"` // Name is name or Question ID Points int `json:"points,omitempty"` // Points is sum of subs' points or self - Rate float64 `json:"rate,omitempty"` // Rate is the avg(non-leaf) or max(leaf) similarity Sub []QuestionJSON `json:"sub,omitempty"` } @@ -88,6 +87,43 @@ type Question struct { Dup []byte // Dup is json of {queid: rate, ...} } +// GetQuestion by id +func (f *FileDatabase) GetQuestion(id int64, istemp bool) (Question, error) { + f.mu.RLock() + defer f.mu.RUnlock() + qtable := "" + if istemp { + qtable = FileTableTempQuestion + } else { + qtable = FileTableQuestion + } + return sql.Find[Question](&f.db, qtable, "WHERE ID="+strconv.FormatInt(id, 10)) +} + +// GetQuestionHex by hexid +func (f *FileDatabase) GetQuestionHex(hexid string, istemp bool) (q Question, err error) { + idb, err := hex.DecodeString(hexid) + if err != nil { + return + } + return f.GetQuestion(int64(binary.LittleEndian.Uint64(idb)), istemp) +} + +// MaxDuplicateRate parse q.Dup and get the max rate +func (q *Question) MaxDuplicateRate() float64 { + dupmap := make(map[string]float64, 64) + if err := json.Unmarshal(q.Dup, &dupmap); err != nil { + return 0 + } + r := 0.0 + for _, v := range dupmap { + if v > r { + r = v + } + } + return r +} + // GetDuplicateRate calc q & que's dup rate func (q *Question) GetDuplicateRate(que *Question) (float64, error) { v1, v2 := make(map[string]uint8, 64), make(map[string]uint8, 64) diff --git a/backend/paper.go b/backend/paper.go index 6747564..27c59e9 100644 --- a/backend/paper.go +++ b/backend/paper.go @@ -41,6 +41,7 @@ type filelist struct { type filestatus struct { Name string `json:"name"` Size float64 `json:"size"` + Rate float64 `json:"rate"` Questions []question `json:"questions"` Duplications []duplication `json:"duplications"` // Duplications length == 10 } @@ -308,12 +309,12 @@ func init() { writeresult(w, codeError, nil, err.Error(), typeError) return } - file, sz, err := global.FileDB.GetFile(id, *user.ID) + file, sz, istemp, err := global.FileDB.GetFile(id, *user.ID) if err != nil { writeresult(w, codeError, nil, err.Error(), typeError) return } - qs, ds, err := parseFileQuestions(file.Questions) + qs, ds, filerate, err := parseFileQuestions(file.Questions, istemp) if err != nil { writeresult(w, codeError, nil, err.Error(), typeError) return @@ -321,6 +322,7 @@ func init() { writeresult(w, codeSuccess, &filestatus{ Name: file.Class + ".docx", Size: float64(sz) / 1024 / 1024, // MB + Rate: filerate * 100, Questions: qs, Duplications: ds, }, messageOk, typeSuccess) diff --git a/backend/question.go b/backend/question.go index 6ec6328..e5cfe0b 100644 --- a/backend/question.go +++ b/backend/question.go @@ -1,7 +1,10 @@ package backend import ( + "container/heap" "encoding/json" + "math" + "strconv" "github.com/fumiama/paper-manager/backend/global" ) @@ -17,21 +20,78 @@ type duplication struct { Name string `json:"name"` } -func parseFileQuestions(qb []byte) ([]question, []duplication, error) { +type duplications []duplication + +func (d *duplications) Len() int { + return len(*d) +} + +// Less is actually more for a big-top heap +func (d *duplications) Less(i, j int) bool { + return (*d)[i].Percent > (*d)[j].Percent +} + +func (d *duplications) Swap(i, j int) { + (*d)[i], (*d)[j] = (*d)[j], (*d)[i] +} + +func (d *duplications) Push(x any) { + *d = append(*d, x.(duplication)) +} + +func (d *duplications) Pop() any { + if d.Len() == 0 { + return nil + } + i := d.Len() - 1 + x := (*d)[i] + *d = (*d)[:i] + return x +} + +func parseFileQuestions(qb []byte, istemp bool) ([]question, []duplication, float64, error) { ques := make([]global.QuestionJSON, 0, 16) qs := make([]question, 0, 16) - ds := make([]duplication, 0, 16) - err := json.Unmarshal(qb, &qs) + err := json.Unmarshal(qb, &ques) if err != nil { - return nil, nil, err + return nil, nil, 0, err } + dh := make(duplications, 0, 16) + heap.Init(&dh) + sum := 0.0 + cnt := 0 for _, q := range ques { qs = append(qs, question{ Count: len(q.Sub), Point: q.Points, Name: q.Name, }) - // TODO: use heap to get top 10 ds + for i, subq := range q.Sub { + qstruct, err := global.FileDB.GetQuestionHex(subq.Name, istemp) + if err != nil { + continue + } + p := qstruct.MaxDuplicateRate() + heap.Push(&dh, duplication{ + Percent: int(math.Round(p * 100)), + Name: q.Name + "." + strconv.Itoa(i+1), + }) + sum += p + cnt++ + } } - return nil, nil, nil + i := dh.Len() + ds := make([]duplication, 10) + if i > 10 { + i = 10 + } else { + for j := i; j < 10; j++ { + ds[j] = duplication{Name: "N/A"} + } + } + for i--; i >= 0; i-- { + ds[i] = heap.Pop(&dh).(duplication) + } + + return qs, ds, sum / float64(cnt), nil } diff --git a/frontend/vben/mock/page/file.ts b/frontend/vben/mock/page/file.ts index e30117f..4e2e5ba 100644 --- a/frontend/vben/mock/page/file.ts +++ b/frontend/vben/mock/page/file.ts @@ -1,5 +1,5 @@ import { MockMethod } from 'vite-plugin-mock' -import { resultError, resultSuccess, getRequestToken, requestParams } from '../_util' +// import { resultError, resultSuccess, getRequestToken, requestParams } from '../_util' export default [ /*{ @@ -16,7 +16,7 @@ export default [ }) }, },*/ - { + /*{ url: '/api/getFileStatus', timeout: 500, method: 'get', @@ -49,5 +49,5 @@ export default [ ], }) }, - }, + },*/ ] as MockMethod[] diff --git a/frontend/vben/src/api/page/model/fileModel.ts b/frontend/vben/src/api/page/model/fileModel.ts index ec7712b..15847ba 100644 --- a/frontend/vben/src/api/page/model/fileModel.ts +++ b/frontend/vben/src/api/page/model/fileModel.ts @@ -16,6 +16,7 @@ export interface Duplication { export interface FileStatus { name: string size: number + rate: number questions: Question[] duplications: Duplication[] } diff --git a/frontend/vben/src/views/page/file/index.vue b/frontend/vben/src/views/page/file/index.vue index a005bc0..f4ad9da 100644 --- a/frontend/vben/src/views/page/file/index.vue +++ b/frontend/vben/src/views/page/file/index.vue @@ -1,7 +1,9 @@