diff --git a/backend/global/file.go b/backend/global/file.go index 0b8e4b7..6d28685 100644 --- a/backend/global/file.go +++ b/backend/global/file.go @@ -39,8 +39,10 @@ const ( ) var ( - ErrMajorSplitsTooShort = errors.New("major splits too short") - ErrEmptyClass = errors.New("empty class") + ErrMajorSplitsTooShort = errors.New("major splits too short") + ErrEmptyClass = errors.New("empty class") + ErrHasntAnalyzed = errors.New("hasn't analyzed") + ErrNoGetFileStatusPermission = errors.New("no get file status permission") ) func init() { @@ -108,61 +110,61 @@ func (sy StudyYear) String() string { // AddFile from lst and copy it to analyzed path. // The para reg must belong to a valid user -func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func(uint)) (*File, error) { +func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func(uint)) error { user, err := UserDB.GetUserByID(reg.ID) if err != nil { - return nil, err + return err } if !user.IsFileManager() && !istemp { - return nil, ErrInvalidRole + return ErrInvalidRole } progress(1) f.mu.RLock() lst, err := sql.Find[List](&f.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid)) f.mu.RUnlock() if err != nil { - return nil, err + return err } if lst.Path == "" || strings.Contains(lst.Path, "..") { - return nil, os.ErrNotExist + return os.ErrNotExist } tempath := lst.Path docf, err := os.Open(tempath) if err != nil { - return nil, err + return err } defer docf.Close() progress(2) h := md5.New() _, err = io.Copy(h, docf) if err != nil { - return nil, err + return err } var buf [md5.Size]byte id := int64(binary.LittleEndian.Uint64(h.Sum(buf[:0]))) _, err = docf.Seek(0, io.SeekStart) if err != nil { - return nil, err + return err } stat, err := docf.Stat() if err != nil { - return nil, err + return err } sz := stat.Size() progress(3) doc, err := docx.Parse(docf, sz) if err != nil { - return nil, err + return err } progress(5) doc.Document.Body.DropDrawingOf("NilPicture") majorre, err := regexp.Compile(reg.Major) if err != nil { - return nil, err + return err } docs := doc.SplitByParagraph(docx.SplitDocxByPlainTextRegex(majorre)) if len(docs) < 2 { - return nil, ErrMajorSplitsTooShort + return ErrMajorSplitsTooShort } progress(9) // filling File struct @@ -172,27 +174,27 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func } titlere, err := regexp.Compile(reg.Title) if err != nil { - return nil, err + return err } classre, err := regexp.Compile(reg.Class) if err != nil { - return nil, err + return err } opclre, err := regexp.Compile(reg.OpenCl) if err != nil { - return nil, err + return err } datere, err := regexp.Compile(reg.Date) if err != nil { - return nil, err + return err } timere, err := regexp.Compile(reg.Time) if err != nil { - return nil, err + return err } ratere, err := regexp.Compile(reg.Rate) if err != nil { - return nil, err + return err } progress(10) for _, it := range docs[0].Document.Body.Items { @@ -203,7 +205,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func years, semesters, mfs, abs := title[1], title[2], title[3], title[4] y, err := strconv.Atoi(years) if err != nil { - return nil, err + return err } file.Year = StudyYear(y) if len(semesters) > 0 { @@ -256,7 +258,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func } progress(19) if file.Class == "" || strings.Contains(file.Class, "..") || strings.ContainsAny(file.Class, `/\`) { - return nil, ErrEmptyClass + return ErrEmptyClass } filebasepath := "" if istemp { @@ -270,13 +272,13 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func questionpath := filebasepath + "questions/" err = os.MkdirAll(questionpath, 0755) if err != nil { - return nil, err + return err } docs = docs[1:] // parse questions subre, err := regexp.Compile(reg.Sub) if err != nil { - return nil, err + return err } filequestions := make([]QuestionJSON, 0, len(docs)) lst.QuesC = 0 @@ -458,7 +460,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func file.Questions, _ = json.Marshal(filequestions) _, err = docf.Seek(0, io.SeekStart) if err != nil { - return nil, err + return err } lst.Path = filebasepath + file.Class + ".docx" lst.HasntAnalyzed = false @@ -467,12 +469,12 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func ) dstf, err := os.Create(lst.Path) if err != nil { - return nil, err + return err } defer dstf.Close() _, err = io.Copy(dstf, docf) if err != nil { - return nil, err + return err } progress(95) f.mu.Lock() @@ -486,7 +488,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func _ = f.db.Insert(FileTableList, &lst) f.mu.Unlock() progress(100) - return file, err + return err } // DelFile by listid @@ -548,3 +550,30 @@ func (f *FileDatabase) DelFile(lstid, uid int, istemp bool) error { } return os.RemoveAll(parentfolder) } + +// GetFile get analyzed file's structure from List(ID) +func (f *FileDatabase) GetFile(lstid, uid int) (*File, int64, 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 + } + if lst.HasntAnalyzed { + return nil, 0, ErrHasntAnalyzed + } + if lst.IsTemp && lst.Uploader != uid { + return nil, 0, ErrNoGetFileStatusPermission + } + ftable := "" + if lst.IsTemp { + ftable = FileTableTempFile + } else { + ftable = FileTableFile + } + file, err := sql.Find[File](&f.db, ftable, "WHERE ListID="+strconv.Itoa(lstid)) + if err != nil { + return nil, 0, err + } + return &file, lst.Size, nil +} diff --git a/backend/paper.go b/backend/paper.go index 5a5faa9..6747564 100644 --- a/backend/paper.go +++ b/backend/paper.go @@ -38,6 +38,13 @@ type filelist struct { Per uint `json:"percent"` } +type filestatus struct { + Name string `json:"name"` + Size float64 `json:"size"` + Questions []question `json:"questions"` + Duplications []duplication `json:"duplications"` // Duplications length == 10 +} + func init() { apimap["/api/getFileList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") @@ -186,7 +193,7 @@ func init() { return } go func() { - _, err = global.FileDB.AddFile(id, reg, istemp, func(u uint) { analyzeper.Set(id, u) }) + err = global.FileDB.AddFile(id, reg, istemp, func(u uint) { analyzeper.Set(id, u) }) ch <- struct{}{} close(ch) }() @@ -284,6 +291,40 @@ func init() { } writeresult(w, codeError, nil, "parse filepath error", typeError) }} + apimap["/api/getFileStatus"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("Authorization") + user := usertokens.Get(token) + if user == nil { + writeresult(w, codeError, nil, errInvalidToken.Error(), typeError) + return + } + idstr := r.URL.Query().Get("id") + if idstr == "" { + writeresult(w, codeError, nil, "empty id", typeError) + return + } + id, err := strconv.Atoi(idstr) + if err != nil { + writeresult(w, codeError, nil, err.Error(), typeError) + return + } + file, sz, err := global.FileDB.GetFile(id, *user.ID) + if err != nil { + writeresult(w, codeError, nil, err.Error(), typeError) + return + } + qs, ds, err := parseFileQuestions(file.Questions) + if err != nil { + writeresult(w, codeError, nil, err.Error(), typeError) + return + } + writeresult(w, codeSuccess, &filestatus{ + Name: file.Class + ".docx", + Size: float64(sz) / 1024 / 1024, // MB + Questions: qs, + Duplications: ds, + }, messageOk, typeSuccess) + }} } // PaperHandler serves protected contents in global.PaperFolder diff --git a/backend/question.go b/backend/question.go new file mode 100644 index 0000000..6ec6328 --- /dev/null +++ b/backend/question.go @@ -0,0 +1,37 @@ +package backend + +import ( + "encoding/json" + + "github.com/fumiama/paper-manager/backend/global" +) + +type question struct { + Count int `json:"count"` + Point int `json:"point"` + Name string `json:"name"` +} + +type duplication struct { + Percent int `json:"percent"` + Name string `json:"name"` +} + +func parseFileQuestions(qb []byte) ([]question, []duplication, error) { + ques := make([]global.QuestionJSON, 0, 16) + qs := make([]question, 0, 16) + ds := make([]duplication, 0, 16) + err := json.Unmarshal(qb, &qs) + if err != nil { + return nil, nil, err + } + 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 + } + return nil, nil, nil +} diff --git a/frontend/vben/mock/page/file.ts b/frontend/vben/mock/page/file.ts index 0588d9f..e30117f 100644 --- a/frontend/vben/mock/page/file.ts +++ b/frontend/vben/mock/page/file.ts @@ -29,23 +29,23 @@ export default [ name: '100.docx', size: 1.5, questions: [ - { count: 4, point: 10, name: '一、填空题' }, - { count: 10, point: 20, name: '二、不定项选择题' }, - { count: 5, point: 10, name: '三、判断改错题' }, - { count: 5, point: 30, name: '四、简述题' }, - { count: 4, point: 30, name: '五、综合题' }, + { count: 4, point: 10, name: '填空题' }, + { count: 10, point: 20, name: '不定项选择题' }, + { count: 5, point: 10, name: '判断改错题' }, + { count: 5, point: 30, name: '简述题' }, + { count: 4, point: 30, name: '综合题' }, ], duplications: [ - { percent: 10, name: '二.1' }, - { percent: 20, name: '二.2' }, - { percent: 30, name: '二.3' }, - { percent: 40, name: '二.4' }, - { percent: 50, name: '二.5' }, - { percent: 60, name: '二.6' }, - { percent: 70, name: '二.7' }, - { percent: 80, name: '二.8' }, - { percent: 90, name: '二.9' }, - { percent: 100, name: '二.10' }, + { percent: 10, name: '不定项选择题.1' }, + { percent: 20, name: '判断改错题.2' }, + { percent: 30, name: '简述题.3' }, + { percent: 40, name: '综合题.4' }, + { percent: 50, name: '填空题.5' }, + { percent: 60, name: '不定项选择题.6' }, + { percent: 70, name: '判断改错题.7' }, + { percent: 80, name: '简述题.8' }, + { percent: 90, name: '综合题.9' }, + { percent: 100, name: '填空题.10' }, ], }) },