opaque-demo-app/model.go

210 lines
5.8 KiB
Go
Raw Permalink Normal View History

2020-05-19 20:46:41 +00:00
package main
import (
"encoding/json"
"fmt"
"git.openprivacy.ca/openprivacy/log"
"github.com/therecipe/qt/core"
"io/ioutil"
"os"
"reflect"
"regexp"
"strings"
"time"
)
type FormEntry struct {
core.QObject
API, FormID, FormName, User string
FolderName string `json:",omitempty"`
Token string `json:",omitempty"`
RemotePubKey string `json:",omitempty"`
LocalPubKey string `json:",omitempty"`
Error string `json:",omitempty"`
LastDownload time.Time
Submissions int
}
type FormDataModel struct {
core.QAbstractTableModel
_ string `property:"error"`
_ string `property:"cwd"`
_ map[int]*core.QByteArray `property:"roles"`
_ func() `constructor:"init"`
_ func(*FormEntry) `signal:"addFormEntry,auto"`
modelData []FormEntry
_ func(string) `signal:"createLocalFormEntry,auto"`
_ func() `signal:"requestEIR,auto"` // request this.EndInsertRecord() on gui thread
}
func (this *FormDataModel) init() {
mdt := reflect.TypeOf(this.modelData).Elem()
roles := make(map[int]*core.QByteArray)
for i := 0; i < mdt.NumField(); i++ {
roles[int(core.Qt__UserRole) + 1 + i] = core.NewQByteArray2(mdt.Field(i).Name, -1)
}
roles[int(core.Qt__DisplayRole)] = core.NewQByteArray2("display", -1)
this.SetRoles(roles)
this.ConnectData(this.data)
this.ConnectRowCount(this.rowCount)
this.ConnectColumnCount(this.columnCount)
this.ConnectHeaderData(this.headerData)
this.ConnectRoleNames(this.roleNames)
//this.ConnectAddFormEntry(this.addFormEntry)
}
func (this *FormDataModel) roleNames() map[int]*core.QByteArray {
return this.Roles()
}
func (this *FormDataModel) data(index *core.QModelIndex, role int) *core.QVariant {
if !index.IsValid() {
return core.NewQVariant()
}
if index.Row() >= len(this.modelData) {
return core.NewQVariant()
}
log.Infof("getting role %v column %v", role, index.Column())
if role == int(core.Qt__DisplayRole) {
role = index.Column() + int(core.Qt__UserRole) + 1
}
// modelData-element [role]-field value (aka the data ~_~)
mderfv := reflect.ValueOf(this.modelData[index.Row()]).Field(role - int(core.Qt__UserRole) - 1)
typeStr := reflect.TypeOf(this.modelData).Elem().Field(role - int(core.Qt__UserRole) - 1).Type.String()
if typeStr == "string" {
return core.NewQVariant1(mderfv.String())
} else if strings.HasPrefix(typeStr, "int") {
return core.NewQVariant1(mderfv.Int())
} else if strings.HasPrefix(typeStr, "float") {
return core.NewQVariant1(mderfv.Float())
} else if typeStr == "bool" {
return core.NewQVariant1(mderfv.Bool())
}
return core.NewQVariant1("unknown type " + typeStr)
}
func (this *FormDataModel) headerData(section int, orientation core.Qt__Orientation, role int) *core.QVariant {
if role != int(core.Qt__DisplayRole) || orientation == core.Qt__Vertical {
return this.HeaderDataDefault(section, orientation, role)
}
mdt := reflect.TypeOf(this.modelData).Elem()
return core.NewQVariant12(mdt.Field(section).Name)
}
func (this *FormDataModel) rowCount(parent *core.QModelIndex) int {
return len(this.modelData)
}
func (this *FormDataModel) columnCount(parent *core.QModelIndex) int {
return reflect.TypeOf(this.modelData).Elem().NumField()
}
func (this *FormDataModel) addFormEntry(fe *FormEntry) {
this.BeginInsertRows(core.NewQModelIndex(), len(this.modelData), len(this.modelData))
this.modelData = append(this.modelData, *fe)
this.RequestEIR()
}
func (this *FormDataModel) createLocalFormEntry(name string) {
go this.createLocalFormEntry_thread(name)
}
//todo: figure out error handling ie notifying the user throughout
func (this *FormDataModel) createLocalFormEntry_thread(name string) {
dirname := FormEntryDirectoryName(this.Cwd(), "local", name)
fe := &FormEntry{
API: "local",
FormName: name,
User: "n/a",
Token: "n/a",
}
// put in file
err := os.Mkdir(dirname, 0700)
if err != nil {
log.Errorf("could't make directory %s: %v", dirname, err)
return
}
jsonBytes, err := json.Marshal(fe)
if err != nil {
log.Errorf("couldn't marshal formentry: %v", err)
return
}
err = ioutil.WriteFile(dirname + string(os.PathSeparator) + "config.json", jsonBytes, 0600)
if err != nil {
log.Errorf("couldn't write config.json")
return
}
fe.FolderName = dirname
this.addFormEntry(fe)
}
// perform this.EndInsertRows() on the gui thread
func (this *FormDataModel) requestEIR() {
this.EndInsertRows()
}
// creates a folder name using api+formName
// as well as _## suffix so that the return
// value doesn't exist already in cwd
func FormEntryDirectoryName(cwd, api, formName string) string {
// take the hostname component of the api
reApi := regexp.MustCompile(`^(\w+://)?([^/]+)(/.*)?$`) // -> "$2"
// get rid of most characters
reName := regexp.MustCompile(`[^a-zA-Z0-9_.]`) // -> ""
// base = cwd + reName(reApi(api).formName)
basename := cwd + string(os.PathSeparator) + reName.ReplaceAllString(fmt.Sprintf("%s.%s", reApi.ReplaceAllString(api, `$2`), formName), ``)
// i++ until cwd/base_i does not exist
_, e := os.Stat(basename)
i := 1
for ; os.IsExist(e); i++ { _, e = os.Stat(fmt.Sprintf("%s_%d", basename, i)) }
if i > 1 {
basename = fmt.Sprintf("%s_%d", basename, i)
}
return basename
}
func BuildFormModel(fdm *FormDataModel, datadir string) {
fdm.addFormEntry(LoadFormEntry("wip1"))
fdm.addFormEntry(LoadFormEntry("wip2"))
fdm.addFormEntry(LoadFormEntry("wip3"))
2020-05-19 20:46:41 +00:00
}
func LoadFormEntry(path string) *FormEntry {
fe := &FormEntry{
API: "api1",
FormID: "fid1",
FormName: "fn1",
User: "user1",
FolderName: "fn1",
Token: "token1",
RemotePubKey: "pubkey1",
LocalPubKey: "localkey2",
Error: "error1",
LastDownload: time.Now(),
Submissions: 123,
2020-05-19 20:46:41 +00:00
}
return fe
}
func FormEntryError(path, msg string) *FormEntry {
return &FormEntry{
FolderName: path,
Error: msg,
}
}