1
0
mirror of https://github.com/fumiama/paper-manager.git synced 2026-06-29 23:30:43 +08:00

implement get file status

This commit is contained in:
源文雨
2023-04-16 23:31:50 +08:00
parent 2286438097
commit 9f940ddc02
4 changed files with 151 additions and 44 deletions

View File

@@ -41,6 +41,8 @@ const (
var ( var (
ErrMajorSplitsTooShort = errors.New("major splits too short") ErrMajorSplitsTooShort = errors.New("major splits too short")
ErrEmptyClass = errors.New("empty class") ErrEmptyClass = errors.New("empty class")
ErrHasntAnalyzed = errors.New("hasn't analyzed")
ErrNoGetFileStatusPermission = errors.New("no get file status permission")
) )
func init() { func init() {
@@ -108,61 +110,61 @@ func (sy StudyYear) String() string {
// AddFile from lst and copy it to analyzed path. // AddFile from lst and copy it to analyzed path.
// The para reg must belong to a valid user // 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) user, err := UserDB.GetUserByID(reg.ID)
if err != nil { if err != nil {
return nil, err return err
} }
if !user.IsFileManager() && !istemp { if !user.IsFileManager() && !istemp {
return nil, ErrInvalidRole return ErrInvalidRole
} }
progress(1) progress(1)
f.mu.RLock() f.mu.RLock()
lst, err := sql.Find[List](&f.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid)) lst, err := sql.Find[List](&f.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid))
f.mu.RUnlock() f.mu.RUnlock()
if err != nil { if err != nil {
return nil, err return err
} }
if lst.Path == "" || strings.Contains(lst.Path, "..") { if lst.Path == "" || strings.Contains(lst.Path, "..") {
return nil, os.ErrNotExist return os.ErrNotExist
} }
tempath := lst.Path tempath := lst.Path
docf, err := os.Open(tempath) docf, err := os.Open(tempath)
if err != nil { if err != nil {
return nil, err return err
} }
defer docf.Close() defer docf.Close()
progress(2) progress(2)
h := md5.New() h := md5.New()
_, err = io.Copy(h, docf) _, err = io.Copy(h, docf)
if err != nil { if err != nil {
return nil, err return err
} }
var buf [md5.Size]byte var buf [md5.Size]byte
id := int64(binary.LittleEndian.Uint64(h.Sum(buf[:0]))) id := int64(binary.LittleEndian.Uint64(h.Sum(buf[:0])))
_, err = docf.Seek(0, io.SeekStart) _, err = docf.Seek(0, io.SeekStart)
if err != nil { if err != nil {
return nil, err return err
} }
stat, err := docf.Stat() stat, err := docf.Stat()
if err != nil { if err != nil {
return nil, err return err
} }
sz := stat.Size() sz := stat.Size()
progress(3) progress(3)
doc, err := docx.Parse(docf, sz) doc, err := docx.Parse(docf, sz)
if err != nil { if err != nil {
return nil, err return err
} }
progress(5) progress(5)
doc.Document.Body.DropDrawingOf("NilPicture") doc.Document.Body.DropDrawingOf("NilPicture")
majorre, err := regexp.Compile(reg.Major) majorre, err := regexp.Compile(reg.Major)
if err != nil { if err != nil {
return nil, err return err
} }
docs := doc.SplitByParagraph(docx.SplitDocxByPlainTextRegex(majorre)) docs := doc.SplitByParagraph(docx.SplitDocxByPlainTextRegex(majorre))
if len(docs) < 2 { if len(docs) < 2 {
return nil, ErrMajorSplitsTooShort return ErrMajorSplitsTooShort
} }
progress(9) progress(9)
// filling File struct // 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) titlere, err := regexp.Compile(reg.Title)
if err != nil { if err != nil {
return nil, err return err
} }
classre, err := regexp.Compile(reg.Class) classre, err := regexp.Compile(reg.Class)
if err != nil { if err != nil {
return nil, err return err
} }
opclre, err := regexp.Compile(reg.OpenCl) opclre, err := regexp.Compile(reg.OpenCl)
if err != nil { if err != nil {
return nil, err return err
} }
datere, err := regexp.Compile(reg.Date) datere, err := regexp.Compile(reg.Date)
if err != nil { if err != nil {
return nil, err return err
} }
timere, err := regexp.Compile(reg.Time) timere, err := regexp.Compile(reg.Time)
if err != nil { if err != nil {
return nil, err return err
} }
ratere, err := regexp.Compile(reg.Rate) ratere, err := regexp.Compile(reg.Rate)
if err != nil { if err != nil {
return nil, err return err
} }
progress(10) progress(10)
for _, it := range docs[0].Document.Body.Items { 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] years, semesters, mfs, abs := title[1], title[2], title[3], title[4]
y, err := strconv.Atoi(years) y, err := strconv.Atoi(years)
if err != nil { if err != nil {
return nil, err return err
} }
file.Year = StudyYear(y) file.Year = StudyYear(y)
if len(semesters) > 0 { if len(semesters) > 0 {
@@ -256,7 +258,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
progress(19) progress(19)
if file.Class == "" || strings.Contains(file.Class, "..") || strings.ContainsAny(file.Class, `/\`) { if file.Class == "" || strings.Contains(file.Class, "..") || strings.ContainsAny(file.Class, `/\`) {
return nil, ErrEmptyClass return ErrEmptyClass
} }
filebasepath := "" filebasepath := ""
if istemp { if istemp {
@@ -270,13 +272,13 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
questionpath := filebasepath + "questions/" questionpath := filebasepath + "questions/"
err = os.MkdirAll(questionpath, 0755) err = os.MkdirAll(questionpath, 0755)
if err != nil { if err != nil {
return nil, err return err
} }
docs = docs[1:] docs = docs[1:]
// parse questions // parse questions
subre, err := regexp.Compile(reg.Sub) subre, err := regexp.Compile(reg.Sub)
if err != nil { if err != nil {
return nil, err return err
} }
filequestions := make([]QuestionJSON, 0, len(docs)) filequestions := make([]QuestionJSON, 0, len(docs))
lst.QuesC = 0 lst.QuesC = 0
@@ -458,7 +460,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
file.Questions, _ = json.Marshal(filequestions) file.Questions, _ = json.Marshal(filequestions)
_, err = docf.Seek(0, io.SeekStart) _, err = docf.Seek(0, io.SeekStart)
if err != nil { if err != nil {
return nil, err return err
} }
lst.Path = filebasepath + file.Class + ".docx" lst.Path = filebasepath + file.Class + ".docx"
lst.HasntAnalyzed = false 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) dstf, err := os.Create(lst.Path)
if err != nil { if err != nil {
return nil, err return err
} }
defer dstf.Close() defer dstf.Close()
_, err = io.Copy(dstf, docf) _, err = io.Copy(dstf, docf)
if err != nil { if err != nil {
return nil, err return err
} }
progress(95) progress(95)
f.mu.Lock() 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.db.Insert(FileTableList, &lst)
f.mu.Unlock() f.mu.Unlock()
progress(100) progress(100)
return file, err return err
} }
// DelFile by listid // DelFile by listid
@@ -548,3 +550,30 @@ func (f *FileDatabase) DelFile(lstid, uid int, istemp bool) error {
} }
return os.RemoveAll(parentfolder) 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
}

View File

@@ -38,6 +38,13 @@ type filelist struct {
Per uint `json:"percent"` 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() { func init() {
apimap["/api/getFileList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) { apimap["/api/getFileList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization") token := r.Header.Get("Authorization")
@@ -186,7 +193,7 @@ func init() {
return return
} }
go func() { 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{}{} ch <- struct{}{}
close(ch) close(ch)
}() }()
@@ -284,6 +291,40 @@ func init() {
} }
writeresult(w, codeError, nil, "parse filepath error", typeError) 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 // PaperHandler serves protected contents in global.PaperFolder

37
backend/question.go Normal file
View File

@@ -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
}

View File

@@ -29,23 +29,23 @@ export default [
name: '100.docx', name: '100.docx',
size: 1.5, size: 1.5,
questions: [ questions: [
{ count: 4, point: 10, name: '一、填空题' }, { count: 4, point: 10, name: '填空题' },
{ count: 10, point: 20, name: '二、不定项选择题' }, { count: 10, point: 20, name: '不定项选择题' },
{ count: 5, point: 10, name: '三、判断改错题' }, { count: 5, point: 10, name: '判断改错题' },
{ count: 5, point: 30, name: '四、简述题' }, { count: 5, point: 30, name: '简述题' },
{ count: 4, point: 30, name: '五、综合题' }, { count: 4, point: 30, name: '综合题' },
], ],
duplications: [ duplications: [
{ percent: 10, name: '.1' }, { percent: 10, name: '不定项选择题.1' },
{ percent: 20, name: '.2' }, { percent: 20, name: '判断改错题.2' },
{ percent: 30, name: '.3' }, { percent: 30, name: '简述题.3' },
{ percent: 40, name: '.4' }, { percent: 40, name: '综合题.4' },
{ percent: 50, name: '.5' }, { percent: 50, name: '填空题.5' },
{ percent: 60, name: '.6' }, { percent: 60, name: '不定项选择题.6' },
{ percent: 70, name: '.7' }, { percent: 70, name: '判断改错题.7' },
{ percent: 80, name: '.8' }, { percent: 80, name: '简述题.8' },
{ percent: 90, name: '.9' }, { percent: 90, name: '综合题.9' },
{ percent: 100, name: '.10' }, { percent: 100, name: '填空题.10' },
], ],
}) })
}, },