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:
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
37
backend/question.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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' },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user