mirror of
https://github.com/fumiama/paper-manager.git
synced 2026-06-25 05:20:16 +08:00
finish 试卷生成
This commit is contained in:
@@ -4,10 +4,16 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/ttl"
|
||||
"github.com/fumiama/go-docx"
|
||||
"github.com/fumiama/paper-manager/backend/global"
|
||||
)
|
||||
|
||||
var genfilecache = ttl.NewCache[int, *docx.Docx](time.Minute * 10)
|
||||
|
||||
func init() {
|
||||
apimap["/api/genFile"] = &apihandler{"POST", func(w http.ResponseWriter, r *http.Request) {
|
||||
user := usertokens.Get(r.Header.Get("Authorization"))
|
||||
@@ -27,6 +33,21 @@ func init() {
|
||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||
return
|
||||
}
|
||||
genfilecache.Set(*user.ID, docf)
|
||||
writeresult(w, codeSuccess, "请在10分钟内下载, 且不要在下载完成前关闭页面, 云端不会保存", messageOk, typeSuccess)
|
||||
}}
|
||||
|
||||
apimap["/api/dlGen"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
|
||||
user := usertokens.Get(r.Header.Get("Authorization"))
|
||||
if user == nil {
|
||||
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
|
||||
return
|
||||
}
|
||||
docf := genfilecache.Get(*user.ID)
|
||||
if docf == nil {
|
||||
writeresult(w, codeError, nil, os.ErrNotExist.Error(), typeError)
|
||||
return
|
||||
}
|
||||
_, _ = io.Copy(w, docf)
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func init() {
|
||||
panic(err)
|
||||
}
|
||||
err = FileDB.db.Create(FileTableQuestion, &Question{},
|
||||
"FOREIGN KEY(FileID) REFERENCES "+FileTableFile+"(ID)",
|
||||
"FOREIGN KEY(ListID) REFERENCES "+FileTableList+"(ID)",
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
"github.com/fumiama/go-docx"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidGenerateConfig = errors.New("invalid generate config")
|
||||
ErrMajorTooLarge = errors.New("major too large")
|
||||
ErrNoSuchMajor = errors.New("no such major")
|
||||
ErrNoEnoughQuestionToMatchRequire = errors.New("no enough question to match require")
|
||||
ErrRateLimitExceeded = errors.New("rate limit exceeded")
|
||||
)
|
||||
@@ -26,21 +28,31 @@ type GenerateConfig struct {
|
||||
}
|
||||
|
||||
// GenerateFile 用一些限定条件生成新试卷, 云端不保存
|
||||
func (f *FileDatabase) GenerateFile(config *GenerateConfig) (*docx.Docx, error) {
|
||||
func (f *FileDatabase) GenerateFile(config *GenerateConfig) (docf *docx.Docx, err error) {
|
||||
if config == nil || config.Distribution == nil || len(config.Distribution) == 0 {
|
||||
return nil, ErrInvalidGenerateConfig
|
||||
}
|
||||
if len(config.Distribution) > 10 {
|
||||
return nil, ErrMajorTooLarge
|
||||
}
|
||||
docf := docx.NewA4()
|
||||
mm := map[string]struct{}{}
|
||||
for _, m := range f.GetMajors() {
|
||||
mm[m] = struct{}{}
|
||||
}
|
||||
for n := range config.Distribution {
|
||||
if _, ok := mm[n]; !ok {
|
||||
return nil, ErrNoSuchMajor
|
||||
}
|
||||
}
|
||||
docf = docx.NewA4()
|
||||
f.mu.RLock()
|
||||
defer f.mu.RUnlock()
|
||||
i := 0
|
||||
for n, c := range config.Distribution {
|
||||
if c == 0 {
|
||||
if c <= 0 {
|
||||
continue
|
||||
}
|
||||
docf.AddParagraph().AddText(string([]rune("一二三四五六七八九十")[c]) + "、" + n).Size("44").Bold()
|
||||
docf.AddParagraph().AddText(string([]rune("一二三四五六七八九十")[i]) + "、" + n).Size("30").Bold()
|
||||
cond := " WHERE"
|
||||
hasfront := false
|
||||
if config.YearStart > 0 {
|
||||
@@ -51,20 +63,32 @@ func (f *FileDatabase) GenerateFile(config *GenerateConfig) (*docx.Docx, error)
|
||||
if hasfront {
|
||||
cond += " AND"
|
||||
}
|
||||
cond += " Year<=" + strconv.Itoa(int(config.YearStart))
|
||||
cond += " Year<=" + strconv.Itoa(int(config.YearEnd))
|
||||
hasfront = true
|
||||
}
|
||||
if hasfront {
|
||||
cond += " AND"
|
||||
if config.TypeMask > 0 {
|
||||
if hasfront {
|
||||
cond += " AND"
|
||||
}
|
||||
typmsk := strconv.Itoa(int(config.TypeMask))
|
||||
cond += " (Type&" + typmsk + ")==" + typmsk
|
||||
hasfront = true
|
||||
}
|
||||
var ques []*Question
|
||||
q := ""
|
||||
if hasfront {
|
||||
q = "SELECT * FROM " + FileTableQuestion +
|
||||
" WHERE Major='" + n + "' AND ListID IN (SELECT DISTINCT ListID FROM " +
|
||||
FileTableFile + cond +
|
||||
") ORDER BY RANDOM() limit " + strconv.Itoa(int(c)) + ";"
|
||||
ques, err = sql.QueryAll[Question](&f.db, q)
|
||||
} else {
|
||||
q = "SELECT * FROM " + FileTableQuestion +
|
||||
" WHERE Major='" + n + "' ORDER BY RANDOM() limit " + strconv.Itoa(int(c)) + ";"
|
||||
ques, err = sql.QueryAll[Question](&f.db, q)
|
||||
}
|
||||
cond += " (Type&" + strconv.Itoa(int(config.TypeMask)) + ")!=0"
|
||||
ques, err := sql.QueryAll[Question](&f.db,
|
||||
"SELECT * FROM "+FileTableQuestion+
|
||||
" WHERE FileID IN (SELECT FileID FROM "+
|
||||
FileTableFile+cond+
|
||||
") ORDER BY RANDOM() limit "+strconv.Itoa(int(c))+";",
|
||||
)
|
||||
if err != nil {
|
||||
logrus.Warnln(err, q)
|
||||
return nil, err
|
||||
}
|
||||
if len(ques) != int(c) {
|
||||
@@ -79,7 +103,7 @@ func (f *FileDatabase) GenerateFile(config *GenerateConfig) (*docx.Docx, error)
|
||||
return nil, ErrRateLimitExceeded
|
||||
}
|
||||
for i, q := range ques {
|
||||
lst, err := sql.Find[List](&f.db, FileTableFile, "WHERE ID="+strconv.Itoa(q.ListID))
|
||||
lst, err := sql.Find[List](&f.db, FileTableList, "WHERE ID="+strconv.Itoa(q.ListID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -98,6 +122,7 @@ func (f *FileDatabase) GenerateFile(config *GenerateConfig) (*docx.Docx, error)
|
||||
docf.AddParagraph().AddText(strconv.Itoa(i+1) + ". (" + lst.Desc + ")")
|
||||
docf.AppendFile(docq)
|
||||
}
|
||||
i++
|
||||
}
|
||||
return docf, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user