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

finish 试卷库

This commit is contained in:
源文雨
2023-04-17 18:38:27 +08:00
parent 9f940ddc02
commit 67b6abf001
7 changed files with 129 additions and 26 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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[]

View File

@@ -16,6 +16,7 @@ export interface Duplication {
export interface FileStatus {
name: string
size: number
rate: number
questions: Question[]
duplications: Duplication[]
}

View File

@@ -1,7 +1,9 @@
<template>
<PageWrapper :title="t('routes.filelist.file') + ': ' + docxNameRef">
<template #headerContent>
<a-button type="primary" @click="downloadDocx"> 下载试卷 ({{ docxSizeRef }}MB) </a-button>
<a-button type="primary" @click="downloadDocx">
下载试卷 ({{ docxSizeRef.toFixed(2) }}MB)
</a-button>
</template>
<div ref="chartRef" :style="{ height, width }"></div>
<div class="docxWrap" :style="{ width }">
@@ -131,7 +133,7 @@
},
},
{
text: '重复率前十',
text: '重复率: ' + ret.rate.toFixed(2) + '%, 前十如下',
left: '40%',
top: '1%',
textStyle: {