1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-08-02 20:15:26 +02:00

improve level code

This commit is contained in:
Harvey Kandola 2018-01-10 16:07:17 +00:00
parent 049b83e0b9
commit 5f59e95495
25 changed files with 1104 additions and 461 deletions

View file

@ -38,6 +38,9 @@ const (
// SourceTypeDocument indicates activity against a document.
SourceTypeDocument SourceType = 2
// SourceTypePage indicates activity against a document page.
SourceTypePage SourceType = 3
)
const (
@ -70,6 +73,9 @@ const (
// TypeFeedback records user providing document feedback
TypeFeedback Type = 10
// TypeRejected records user rejecting document
TypeRejected Type = 11
)
// DocumentActivity represents an activity taken against a document.

View file

@ -45,6 +45,13 @@ func (d *Document) SetDefaults() {
}
}
// ByTitle sorts a collection of documents by document title.
type ByTitle []Document
func (a ByTitle) Len() int { return len(a) }
func (a ByTitle) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByTitle) Less(i, j int) bool { return strings.ToLower(a[i].Title) < strings.ToLower(a[j].Title) }
// DocumentMeta details who viewed the document.
type DocumentMeta struct {
Viewers []DocumentMetaViewer `json:"viewers"`

View file

@ -72,3 +72,71 @@ func Numberize(pages []Page) {
prevPageLevel = p.Level
}
}
// Levelize ensure page level increments are consistent
// after a page is inserted or removed.
//
// Valid: 1, 2, 3, 4, 4, 4, 2, 1
// Invalid: 1, 2, 4, 4, 2, 1 (note the jump from 2 --> 4)
//
// Rules:
// 1. levels can increase by 1 only (e.g. from 1 to 2 to 3 to 4)
// 2. levels can decrease by any amount (e.g. drop from 4 to 1)
func Levelize(pages []Page) {
var prevLevel uint64
prevLevel = 1
for i := 0; i < len(pages); i++ {
currLevel := pages[i].Level
// handle deprecated level value of 0
if pages[i].Level == 0 {
pages[i].Level = 1
}
// first time thru
if i == 0 {
// first time thru
pages[i].Level = 1
prevLevel = 1
continue
}
if currLevel == prevLevel {
// nothing doing
continue
}
if currLevel > prevLevel+1 {
// bad data detected e.g. prevLevel=1 and pages[i].Level=3
// so we re-level to pages[i].Level=2 and all child pages
// and then increment i to correct point
prevLevel++
pages[i].Level = prevLevel
// safety check before entering loop and renumbering child pages
if i+1 <= len(pages) {
for j := i + 1; j < len(pages); j++ {
if pages[j].Level < prevLevel {
i = j
break
}
if pages[j].Level == currLevel {
pages[j].Level = prevLevel
} else if (pages[j].Level - prevLevel) > 1 {
currLevel = pages[j].Level
prevLevel++
pages[j].Level = prevLevel
}
i = j
}
}
continue
}
prevLevel = currLevel
}
}

View file

@ -111,4 +111,124 @@ func TestNumberize3(t *testing.T) {
}
}
// go test github.com/documize/community/core/model -run TestNumberize
// Tests that numbering does not crash because of bad data
func TestNumberize4(t *testing.T) {
pages := []Page{}
pages = append(pages, Page{Level: 0, Sequence: 1000})
pages = append(pages, Page{Level: 1, Sequence: 2000})
pages = append(pages, Page{Level: 1, Sequence: 3000})
// corruption starts here with Level=3 instead of Level=2
pages = append(pages, Page{Level: 3, Sequence: 4000})
pages = append(pages, Page{Level: 4, Sequence: 4000})
pages = append(pages, Page{Level: 1, Sequence: 5000})
pages = append(pages, Page{Level: 2, Sequence: 6000})
Numberize(pages)
expecting := []string{
"1",
"2",
"3",
"3.1",
"3.1.1",
// data below cannot be processed due to corruption
"", // should be 4
"1", // should be 5
}
for i, p := range pages {
if p.Numbering != expecting[i] {
t.Errorf("(Test 4) Position %d: expecting %s got %s\n", i, expecting[i], p.Numbering)
}
}
}
// Tests that good level data is not messed with
func TestLevelize1(t *testing.T) {
pages := []Page{}
pages = append(pages, Page{Level: 1, Sequence: 1000})
pages = append(pages, Page{Level: 1, Sequence: 2000})
pages = append(pages, Page{Level: 2, Sequence: 3000})
pages = append(pages, Page{Level: 3, Sequence: 4000})
pages = append(pages, Page{Level: 4, Sequence: 5000})
pages = append(pages, Page{Level: 1, Sequence: 6000})
pages = append(pages, Page{Level: 2, Sequence: 7000})
Levelize(pages)
expecting := []uint64{1, 1, 2, 3, 4, 1, 2}
for i, p := range pages {
if p.Level != expecting[i] {
t.Errorf("(TestLevelize1) Position %d: expecting %d got %d (sequence: %f)\n", i+1, expecting[i], p.Level, p.Sequence)
}
}
}
// Tests that bad level data
func TestLevelize2(t *testing.T) {
pages := []Page{}
pages = append(pages, Page{Level: 1, Sequence: 1000})
pages = append(pages, Page{Level: 1, Sequence: 2000})
pages = append(pages, Page{Level: 3, Sequence: 3000})
pages = append(pages, Page{Level: 3, Sequence: 4000})
pages = append(pages, Page{Level: 4, Sequence: 5000})
pages = append(pages, Page{Level: 1, Sequence: 6000})
pages = append(pages, Page{Level: 2, Sequence: 7000})
Levelize(pages)
expecting := []uint64{1, 1, 2, 2, 3, 1, 2}
for i, p := range pages {
if p.Level != expecting[i] {
t.Errorf("(TestLevelize2) Position %d: expecting %d got %d (sequence: %f)\n", i+1, expecting[i], p.Level, p.Sequence)
}
}
}
func TestLevelize3(t *testing.T) {
pages := []Page{}
pages = append(pages, Page{Level: 1, Sequence: 1000})
pages = append(pages, Page{Level: 4, Sequence: 2000})
pages = append(pages, Page{Level: 5, Sequence: 3000})
Levelize(pages)
expecting := []uint64{1, 2, 3}
for i, p := range pages {
if p.Level != expecting[i] {
t.Errorf("(TestLevelize3) Position %d: expecting %d got %d (sequence: %f)\n", i+1, expecting[i], p.Level, p.Sequence)
}
}
}
func TestLevelize4(t *testing.T) {
pages := []Page{}
pages = append(pages, Page{Level: 1, Sequence: 1000})
pages = append(pages, Page{Level: 4, Sequence: 2000})
pages = append(pages, Page{Level: 5, Sequence: 3000})
pages = append(pages, Page{Level: 5, Sequence: 4000})
pages = append(pages, Page{Level: 6, Sequence: 5000})
pages = append(pages, Page{Level: 6, Sequence: 6000})
pages = append(pages, Page{Level: 2, Sequence: 7000})
Levelize(pages)
expecting := []uint64{1, 2, 3, 3, 4, 4, 2}
for i, p := range pages {
if p.Level != expecting[i] {
t.Errorf("(TestLevelize4) Position %d: expecting %d got %d (sequence: %f)\n", i+1, expecting[i], p.Level, p.Sequence)
}
}
}
// go test github.com/documize/community/core/model -run TestNumberiz, 3, 4, 4, 2e

View file

@ -16,23 +16,26 @@ import (
"time"
"github.com/documize/community/model"
"github.com/documize/community/model/workflow"
)
// Page represents a section within a document.
type Page struct {
model.BaseEntity
OrgID string `json:"orgId"`
DocumentID string `json:"documentId"`
UserID string `json:"userId"`
ContentType string `json:"contentType"`
PageType string `json:"pageType"`
BlockID string `json:"blockId"`
Level uint64 `json:"level"`
Sequence float64 `json:"sequence"`
Numbering string `json:"numbering"`
Title string `json:"title"`
Body string `json:"body"`
Revisions uint64 `json:"revisions"`
OrgID string `json:"orgId"`
DocumentID string `json:"documentId"`
UserID string `json:"userId"`
ContentType string `json:"contentType"`
PageType string `json:"pageType"`
BlockID string `json:"blockId"`
Level uint64 `json:"level"`
Sequence float64 `json:"sequence"`
Numbering string `json:"numbering"`
Title string `json:"title"`
Body string `json:"body"`
Revisions uint64 `json:"revisions"`
Status workflow.ChangeStatus `json:"status"`
RelativeID string `json:"relativeId"` // links page to pending page edits
}
// SetDefaults ensures no blank values.
@ -41,6 +44,10 @@ func (p *Page) SetDefaults() {
p.ContentType = "wysiwyg"
}
if p.Level == 0 {
p.Level = 1
}
p.Title = strings.TrimSpace(p.Title)
}
@ -114,3 +121,18 @@ type LevelRequest struct {
PageID string `json:"pageId"`
Level int `json:"level"`
}
// BulkRequest details page, it's meta, pending page changes.
// Used to bulk load data by GUI so as to reduce network requests.
type BulkRequest struct {
Page Page `json:"page"`
Meta Meta `json:"meta"`
Pending []PendingPage `json:"pending"`
}
// PendingPage details page that is yet to be published
type PendingPage struct {
Page Page `json:"page"`
Meta Meta `json:"meta"`
Owner string `json:"owner"`
}

View file

@ -15,29 +15,51 @@ package workflow
type Protection int
const (
// NoProtection means no protection so data item changes are permitted
NoProtection Protection = 0
// ProtectionNone means no protection so data item changes are permitted
ProtectionNone Protection = 0
// Lock means no data itme changes
Lock Protection = 1
// ProtectionLock means no data itme changes
ProtectionLock Protection = 1
// Review means changes must be reviewed and approved
Review Protection = 2
// ProtectionReview means changes must be reviewed and approved
ProtectionReview Protection = 2
)
// Approval tells us how some data item change is to be approved
type Approval int
const (
// NoApproval means no approval necessary
NoApproval Approval = 0
// ApprovalNone means no approval necessary
ApprovalNone Approval = 0
// Anybody can approve data item change
Anybody Approval = 1
// ApprovalAnybody can approve data item change
ApprovalAnybody Approval = 1
// Majority must approve data item change
Majority Approval = 2
// ApprovalMajority must approve data item change
ApprovalMajority Approval = 2
// Unanimous approval must be given for data item change
Unanimous Approval = 3
// ApprovalUnanimous approval must be given for data item change
ApprovalUnanimous Approval = 3
)
// ChangeStatus tells us the state of a data item
type ChangeStatus int
const (
// ChangePublished means data item is visible all
ChangePublished ChangeStatus = 0
// ChangePending means data item is still being edited and not yet requesting review
ChangePending ChangeStatus = 1
// ChangeUnderReview means data item is being reviewed
// Next step would be to mark data item as either
// Published or Rejected
ChangeUnderReview ChangeStatus = 2
// ChangeRejected means data item was not approved for publication
ChangeRejected ChangeStatus = 3
// ChangePendingNew means a new section to a document is pending review
ChangePendingNew ChangeStatus = 4
)