Compare commits
No commits in common. "37b7edb2efa819fe8b093157e9b26f671f390f95" and "8d9b6f75d0b75e9495b3a963f02bd26acd989b26" have entirely different histories.
37b7edb2ef
...
8d9b6f75d0
@ -2,22 +2,6 @@
|
|||||||
*/}}{{ if .Container }}{{ .Container.Id }}{{ else }}Obaly{{ end }}{{ end }}
|
*/}}{{ if .Container }}{{ .Container.Id }}{{ else }}Obaly{{ end }}{{ end }}
|
||||||
{{ define "Content" }}
|
{{ define "Content" }}
|
||||||
|
|
||||||
{{ if .ErrorNoSuchSeries }}
|
|
||||||
<p>Chyba: Řada neexistuje.
|
|
||||||
{{ else if .ErrorContainerAlreadyExists }}
|
|
||||||
<p>Chyba: Obal s tímto ID už existuje.
|
|
||||||
{{ else if .ErrorNoSuchContainer }}
|
|
||||||
<p>Chyba: Obal neexistuje.
|
|
||||||
{{ else if .ErrorCannotChangeSeriesNotEmpty }}
|
|
||||||
<p>Chyba: Řadu u neprázdných obalů nelze měnit.
|
|
||||||
{{ else if .ErrorCannotChangeNumber }}
|
|
||||||
<p>Chyba: Číslo obalu v řadě nelze měnit.
|
|
||||||
{{ else if .ErrorContainerInUse }}
|
|
||||||
<p>Chyba: Obal se používá.
|
|
||||||
{{ else if .Error }}
|
|
||||||
<p>Chyba: {{ .Error }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if .Container }}
|
{{ if .Container }}
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
138
cmd/sklad/db.go
138
cmd/sklad/db.go
@ -14,11 +14,6 @@ import (
|
|||||||
type Series struct {
|
type Series struct {
|
||||||
Prefix string // PK: prefix
|
Prefix string // PK: prefix
|
||||||
Description string // what kind of containers this is for
|
Description string // what kind of containers this is for
|
||||||
Counter uint // last used container number
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Series) Containers() []*Container {
|
|
||||||
return indexMembers[s.Prefix]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ContainerId string
|
type ContainerId string
|
||||||
@ -65,13 +60,15 @@ var (
|
|||||||
dbLog *os.File
|
dbLog *os.File
|
||||||
|
|
||||||
indexSeries = map[string]*Series{}
|
indexSeries = map[string]*Series{}
|
||||||
indexMembers = map[string][]*Container{}
|
|
||||||
indexContainer = map[ContainerId]*Container{}
|
indexContainer = map[ContainerId]*Container{}
|
||||||
indexChildren = map[ContainerId][]*Container{}
|
indexChildren = map[ContainerId][]*Container{}
|
||||||
|
|
||||||
labelFont *bdf.Font
|
labelFont *bdf.Font
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Some functions to add, remove and change things in the database.
|
||||||
|
// Indexes must be kept valid, just like any invariants.
|
||||||
|
|
||||||
func dbSearchSeries(query string) (result []*Series) {
|
func dbSearchSeries(query string) (result []*Series) {
|
||||||
query = strings.ToLower(query)
|
query = strings.ToLower(query)
|
||||||
added := map[string]bool{}
|
added := map[string]bool{}
|
||||||
@ -108,134 +105,6 @@ func dbSearchContainers(query string) (result []*Container) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var errInvalidPrefix = errors.New("invalid prefix")
|
|
||||||
var errSeriesAlreadyExists = errors.New("series already exists")
|
|
||||||
var errCannotChangePrefix = errors.New("cannot change the prefix")
|
|
||||||
var errNoSuchSeries = errors.New("no such series")
|
|
||||||
var errSeriesInUse = errors.New("series is in use")
|
|
||||||
|
|
||||||
// Find and filter out the series in O(n).
|
|
||||||
func filterSeries(slice []*Series, s *Series) (filtered []*Series) {
|
|
||||||
for _, series := range slice {
|
|
||||||
if s != series {
|
|
||||||
filtered = append(filtered, series)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbSeriesCreate(s *Series) error {
|
|
||||||
if s.Prefix == "" {
|
|
||||||
return errInvalidPrefix
|
|
||||||
}
|
|
||||||
if _, ok := indexSeries[s.Prefix]; ok {
|
|
||||||
return errSeriesAlreadyExists
|
|
||||||
}
|
|
||||||
db.Series = append(db.Series, s)
|
|
||||||
indexSeries[s.Prefix] = s
|
|
||||||
return dbCommit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbSeriesUpdate(s *Series, updated Series) error {
|
|
||||||
// It might be easily possible with no members, though this
|
|
||||||
// is not reachable from the UI and can be solved by deletion.
|
|
||||||
if updated.Prefix != s.Prefix {
|
|
||||||
return errCannotChangePrefix
|
|
||||||
}
|
|
||||||
*s = updated
|
|
||||||
return dbCommit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbSeriesRemove(s *Series) error {
|
|
||||||
if len(s.Containers()) > 0 {
|
|
||||||
return errSeriesInUse
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Series = filterSeries(db.Series, s)
|
|
||||||
|
|
||||||
delete(indexSeries, s.Prefix)
|
|
||||||
delete(indexMembers, s.Prefix)
|
|
||||||
return dbCommit()
|
|
||||||
}
|
|
||||||
|
|
||||||
var errContainerAlreadyExists = errors.New("container already exists")
|
|
||||||
var errNoSuchContainer = errors.New("no such container")
|
|
||||||
var errCannotChangeSeriesNotEmpty = errors.New(
|
|
||||||
"cannot change the series of a non-empty container")
|
|
||||||
var errCannotChangeNumber = errors.New("cannot change the number")
|
|
||||||
var errContainerInUse = errors.New("container is in use")
|
|
||||||
|
|
||||||
// Find and filter out the container in O(n).
|
|
||||||
func filterContainer(slice []*Container, c *Container) (filtered []*Container) {
|
|
||||||
for _, container := range slice {
|
|
||||||
if c != container {
|
|
||||||
filtered = append(filtered, container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbContainerCreate(c *Container) error {
|
|
||||||
if series, ok := indexSeries[c.Series]; !ok {
|
|
||||||
return errNoSuchSeries
|
|
||||||
} else if c.Number == 0 {
|
|
||||||
c.Number = series.Counter
|
|
||||||
for {
|
|
||||||
c.Number++
|
|
||||||
if _, ok := indexContainer[c.Id()]; !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
series.Counter = c.Number
|
|
||||||
}
|
|
||||||
if _, ok := indexContainer[c.Id()]; ok {
|
|
||||||
return errContainerAlreadyExists
|
|
||||||
}
|
|
||||||
if c.Parent != "" && indexContainer[c.Parent] == nil {
|
|
||||||
return errNoSuchContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Containers = append(db.Containers, c)
|
|
||||||
|
|
||||||
indexMembers[c.Series] = append(indexMembers[c.Series], c)
|
|
||||||
indexChildren[c.Parent] = append(indexChildren[c.Parent], c)
|
|
||||||
indexContainer[c.Id()] = c
|
|
||||||
return dbCommit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbContainerUpdate(c *Container, updated Container) error {
|
|
||||||
newId := updated.Id()
|
|
||||||
if updated.Series != c.Series && len(c.Children()) > 0 {
|
|
||||||
return errCannotChangeSeriesNotEmpty
|
|
||||||
}
|
|
||||||
if updated.Number != c.Number {
|
|
||||||
return errCannotChangeNumber
|
|
||||||
}
|
|
||||||
if _, ok := indexContainer[newId]; ok && newId != c.Id() {
|
|
||||||
return errContainerAlreadyExists
|
|
||||||
}
|
|
||||||
if updated.Parent != c.Parent {
|
|
||||||
indexChildren[c.Parent] = filterContainer(indexChildren[c.Parent], c)
|
|
||||||
indexChildren[newId] = append(indexChildren[newId], c)
|
|
||||||
}
|
|
||||||
*c = updated
|
|
||||||
return dbCommit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbContainerRemove(c *Container) error {
|
|
||||||
if len(indexChildren[c.Id()]) > 0 {
|
|
||||||
return errContainerInUse
|
|
||||||
}
|
|
||||||
|
|
||||||
db.Containers = filterContainer(db.Containers, c)
|
|
||||||
indexMembers[c.Series] = filterContainer(indexMembers[c.Series], c)
|
|
||||||
indexChildren[c.Parent] = filterContainer(indexChildren[c.Parent], c)
|
|
||||||
|
|
||||||
delete(indexContainer, c.Id())
|
|
||||||
delete(indexChildren, c.Id())
|
|
||||||
return dbCommit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbCommit() error {
|
func dbCommit() error {
|
||||||
// Write a timestamp.
|
// Write a timestamp.
|
||||||
e := json.NewEncoder(dbLog)
|
e := json.NewEncoder(dbLog)
|
||||||
@ -315,7 +184,6 @@ func loadDatabase() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
indexChildren[pv.Parent] = append(indexChildren[pv.Parent], pv)
|
indexChildren[pv.Parent] = append(indexChildren[pv.Parent], pv)
|
||||||
indexMembers[pv.Series] = append(indexMembers[pv.Series], pv)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that no container is a parent of itself on any level.
|
// Validate that no container is a parent of itself on any level.
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
@ -84,40 +83,11 @@ func handleLogout(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleContainerPost(r *http.Request) error {
|
|
||||||
id := ContainerId(r.FormValue("id"))
|
|
||||||
description := r.FormValue("description")
|
|
||||||
series := r.FormValue("series")
|
|
||||||
parent := ContainerId(r.FormValue("parent"))
|
|
||||||
_, remove := r.Form["remove"]
|
|
||||||
|
|
||||||
if container, ok := indexContainer[id]; ok {
|
|
||||||
if remove {
|
|
||||||
return dbContainerRemove(container)
|
|
||||||
} else {
|
|
||||||
c := *container
|
|
||||||
c.Description = description
|
|
||||||
c.Series = series
|
|
||||||
return dbContainerUpdate(container, c)
|
|
||||||
}
|
|
||||||
} else if remove {
|
|
||||||
return errNoSuchContainer
|
|
||||||
} else {
|
|
||||||
return dbContainerCreate(&Container{
|
|
||||||
Series: series,
|
|
||||||
Parent: parent,
|
|
||||||
Description: description,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleContainer(w http.ResponseWriter, r *http.Request) {
|
func handleContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
err = handleContainerPost(r)
|
// TODO
|
||||||
// XXX: This is rather ugly. When removing, we want to keep
|
}
|
||||||
// the context id, in addition to the id being changed.
|
if r.Method != http.MethodGet {
|
||||||
} else if r.Method != http.MethodGet {
|
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -128,77 +98,40 @@ func handleContainer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var container *Container
|
var container *Container
|
||||||
children := indexChildren[""]
|
children := []*Container{}
|
||||||
|
|
||||||
if c, ok := indexContainer[ContainerId(r.FormValue("id"))]; ok {
|
if id := ContainerId(r.FormValue("id")); id == "" {
|
||||||
|
children = indexChildren[""]
|
||||||
|
} else if c, ok := indexContainer[id]; ok {
|
||||||
children = c.Children()
|
children = c.Children()
|
||||||
container = c
|
container = c
|
||||||
}
|
}
|
||||||
|
|
||||||
params := struct {
|
params := struct {
|
||||||
Error error
|
Container *Container
|
||||||
ErrorNoSuchSeries bool
|
Children []*Container
|
||||||
ErrorContainerAlreadyExists bool
|
AllSeries map[string]string
|
||||||
ErrorNoSuchContainer bool
|
|
||||||
ErrorCannotChangeSeriesNotEmpty bool
|
|
||||||
ErrorCannotChangeNumber bool
|
|
||||||
ErrorContainerInUse bool
|
|
||||||
Container *Container
|
|
||||||
Children []*Container
|
|
||||||
AllSeries map[string]string
|
|
||||||
}{
|
}{
|
||||||
Error: err,
|
Container: container,
|
||||||
ErrorNoSuchSeries: err == errNoSuchSeries,
|
Children: children,
|
||||||
ErrorContainerAlreadyExists: err == errContainerAlreadyExists,
|
AllSeries: allSeries,
|
||||||
ErrorNoSuchContainer: err == errNoSuchContainer,
|
|
||||||
ErrorCannotChangeSeriesNotEmpty: err == errCannotChangeSeriesNotEmpty,
|
|
||||||
ErrorCannotChangeNumber: err == errCannotChangeNumber,
|
|
||||||
ErrorContainerInUse: err == errContainerInUse,
|
|
||||||
Container: container,
|
|
||||||
Children: children,
|
|
||||||
AllSeries: allSeries,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executeTemplate("container.tmpl", w, ¶ms)
|
executeTemplate("container.tmpl", w, ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSeriesPost(r *http.Request) error {
|
|
||||||
prefix := r.FormValue("prefix")
|
|
||||||
description := r.FormValue("description")
|
|
||||||
_, remove := r.Form["remove"]
|
|
||||||
|
|
||||||
if series, ok := indexSeries[prefix]; ok {
|
|
||||||
if remove {
|
|
||||||
return dbSeriesRemove(series)
|
|
||||||
} else {
|
|
||||||
s := *series
|
|
||||||
s.Description = description
|
|
||||||
return dbSeriesUpdate(series, s)
|
|
||||||
}
|
|
||||||
} else if remove {
|
|
||||||
return errNoSuchSeries
|
|
||||||
} else {
|
|
||||||
return dbSeriesCreate(&Series{
|
|
||||||
Prefix: prefix,
|
|
||||||
Description: description,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleSeries(w http.ResponseWriter, r *http.Request) {
|
func handleSeries(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
err = handleSeriesPost(r)
|
// TODO
|
||||||
// XXX: This is rather ugly.
|
}
|
||||||
r.Form = url.Values{}
|
if r.Method != http.MethodGet {
|
||||||
} else if r.Method != http.MethodGet {
|
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
allSeries := map[string]*Series{}
|
allSeries := map[string]string{}
|
||||||
for _, s := range indexSeries {
|
for _, s := range indexSeries {
|
||||||
allSeries[s.Prefix] = s
|
allSeries[s.Prefix] = s.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := r.FormValue("prefix")
|
prefix := r.FormValue("prefix")
|
||||||
@ -207,30 +140,16 @@ func handleSeries(w http.ResponseWriter, r *http.Request) {
|
|||||||
if prefix == "" {
|
if prefix == "" {
|
||||||
} else if series, ok := indexSeries[prefix]; ok {
|
} else if series, ok := indexSeries[prefix]; ok {
|
||||||
description = series.Description
|
description = series.Description
|
||||||
} else {
|
|
||||||
err = errNoSuchSeries
|
|
||||||
}
|
}
|
||||||
|
|
||||||
params := struct {
|
params := struct {
|
||||||
Error error
|
Prefix string
|
||||||
ErrorInvalidPrefix bool
|
Description string
|
||||||
ErrorSeriesAlreadyExists bool
|
AllSeries map[string]string
|
||||||
ErrorCannotChangePrefix bool
|
|
||||||
ErrorNoSuchSeries bool
|
|
||||||
ErrorSeriesInUse bool
|
|
||||||
Prefix string
|
|
||||||
Description string
|
|
||||||
AllSeries map[string]*Series
|
|
||||||
}{
|
}{
|
||||||
Error: err,
|
Prefix: prefix,
|
||||||
ErrorInvalidPrefix: err == errInvalidPrefix,
|
Description: description,
|
||||||
ErrorSeriesAlreadyExists: err == errSeriesAlreadyExists,
|
AllSeries: allSeries,
|
||||||
ErrorCannotChangePrefix: err == errCannotChangePrefix,
|
|
||||||
ErrorNoSuchSeries: err == errNoSuchSeries,
|
|
||||||
ErrorSeriesInUse: err == errSeriesInUse,
|
|
||||||
Prefix: prefix,
|
|
||||||
Description: description,
|
|
||||||
AllSeries: allSeries,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executeTemplate("series.tmpl", w, ¶ms)
|
executeTemplate("series.tmpl", w, ¶ms)
|
||||||
|
@ -1,20 +1,6 @@
|
|||||||
{{ define "Title" }}{{ or .Prefix "Řady" }}{{ end }}
|
{{ define "Title" }}{{ or .Prefix "Řady" }}{{ end }}
|
||||||
{{ define "Content" }}
|
{{ define "Content" }}
|
||||||
|
|
||||||
{{ if .ErrorInvalidPrefix }}
|
|
||||||
<p>Chyba: Neplatný prefix.
|
|
||||||
{{ else if .ErrorSeriesAlreadyExists }}
|
|
||||||
<p>Chyba: Řada s tímto prefixem už existuje.
|
|
||||||
{{ else if .ErrorCannotChangePrefix }}
|
|
||||||
<p>Chyba: Prefix nelze měnit.
|
|
||||||
{{ else if .ErrorNoSuchSeries }}
|
|
||||||
<p>Chyba: Řada neexistuje.
|
|
||||||
{{ else if .ErrorSeriesInUse }}
|
|
||||||
<p>Chyba: Řada se používá.
|
|
||||||
{{ else if .Error }}
|
|
||||||
<p>Chyba: {{ .Error }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if .Prefix }}
|
{{ if .Prefix }}
|
||||||
<h2>{{ .Prefix }}</h2>
|
<h2>{{ .Prefix }}</h2>
|
||||||
|
|
||||||
@ -35,24 +21,15 @@
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{ range .AllSeries }}
|
{{ range $prefix, $desc := .AllSeries }}
|
||||||
<section>
|
<section>
|
||||||
<header>
|
<header>
|
||||||
<h3><a href="/series?prefix={{ .Prefix }}">{{ .Prefix }}</a></h3>
|
<h3><a href="/series?prefix={{ $prefix }}">{{ $prefix }}</a></h3>
|
||||||
{{ with $count := len .Containers }}
|
<form method=post action="/series?prefix={{ $prefix }}">
|
||||||
{{ if eq $count 1 }}
|
<input type=text name=description value="{{ $desc }}"
|
||||||
<p>{{ $count }} obal
|
|
||||||
{{ else if and (ge $count 2) (le $count 4) }}
|
|
||||||
<p>{{ $count }} obaly
|
|
||||||
{{ else if gt $count 0 }}
|
|
||||||
<p>{{ $count }} obalů
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
<form method=post action="/series?prefix={{ .Prefix }}">
|
|
||||||
<input type=text name=description value="{{ .Description }}"
|
|
||||||
><input type=submit value="Uložit">
|
><input type=submit value="Uložit">
|
||||||
</form>
|
</form>
|
||||||
<form method=post action="/series?prefix={{ .Prefix }}&remove">
|
<form method=post action="/series?prefix={{ $prefix }}&remove">
|
||||||
<input type=submit value="Odstranit">
|
<input type=submit value="Odstranit">
|
||||||
</form>
|
</form>
|
||||||
</header>
|
</header>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user