qst

package
v0.0.0-...-fe173bb Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 7, 2024 License: MIT Imports: 32 Imported by: 0

Documentation

Overview

Package qst implements a four levels deep nested structure with input controls, groups, pages and questionnaire; contains HTML rendering, page navigation, loading/saving from/to JSON file, consistence validation, multi-language support.

Index

Constants

View Source
const (

	// ValSet is returned, if the checkbox was checked
	ValSet = "1"

	// RemainOpen is a value for the HTML option input 'finished'
	RemainOpen = "remain-open"
	// Finished   is a value for the HTML option input 'finished'
	Finished = "qst-finished"
)
View Source
const (
	// HLeft encodes left horizontal alignment
	HLeft = horizontalAlignment(0)
	// HCenter encodes centered horizontal alignment
	HCenter = horizontalAlignment(1)
	// HRight encodes right horizontal alignment
	HRight = horizontalAlignment(2)
)

Variables

View Source
var CompositeFuncs = map[string]CompositeFuncT{
	"PoliticalFoundationsPretext":            cppat.PoliticalFoundationsPretext,
	"PoliticalFoundations":                   cppat.PoliticalFoundations,
	"PoliticalFoundationsStatic":             cppat.PoliticalFoundationsStatic,
	"PoliticalFoundationsComprehensionCheck": cppat.PoliticalFoundationsComprehensionCheck,
	"TimePreferenceSelf":                     cppat.TimePreferenceSelf,
	"TimePreferenceSelfStatic":               cppat.TimePreferenceSelfStatic,
	"TimePreferenceSelfComprehensionCheck":   cppat.TimePreferenceSelfComprehensionCheck,
	"GroupPreferences":                       cppat.GroupPreferences,
	"GroupPreferencesPOP3":                   cppat.GroupPreferencesPOP3,
	"QuestForOrg":                            cpbiii.QuestForOrg,
	"Special202212Q3":                        cpfmt.Special202212Q3,
	"Special202303":                          cpfmt.Special202303,
	"Special202403QS1":                       cpfmt.Special202403QS1,
	"Special202403QS2":                       cpfmt.Special202403QS2,
}

CompositeFuncs is a lookup map

View Source
var PDSAssetClassGlob = assetClass{
	NameUnused: "acx_global",
	Prefix:     "acx",
	Lbl: trl.S{
		"en": "Global",
		"de": "Global",
	},
	Short: trl.S{
		"de": "Global",
		"en": "Global",
	},
	TrancheTypes: []trancheType{
		{
			Prefix: "ac1",
			Lbl: trl.S{
				"en": "Corporate Direct Lending",
				"de": "Corporate Direct Lending",
			},
		},
		{
			Prefix: "ac2",
			Lbl: trl.S{
				"en": "European  Real Estate Debt",
				"de": "European  Real Estate Debt",
			},
		},
		{
			Prefix: "ac3",
			Lbl: trl.S{
				"en": "European Infrastructure Debt",
				"de": "European Infrastructure Debt",
			},
		},
	},
}

PDSAssetClassGlob

View Source
var PDSAssetClasses = []assetClass{
	{
		NameUnused: "ac1_corplending",
		Prefix:     "ac1",
		LblOld: trl.S{
			"en": "Corporate Direct Lending",
			"de": "Corporate Direct Lending",
		},
		Lbl: trl.S{
			"en": "European Corporate Direct Lending",
			"de": "European Corporate Direct Lending",
		},
		Short: trl.S{
			"en": "Corporate<br>Direct Lending",
			"de": "Corporate<br>Direct Lending",
		},
		TrancheTypes: []trancheType{
			{
				NameUnused: "tt1_senior",
				Prefix:     "tt1",
				Lbl: trl.S{
					"en": "Senior",
					"de": "Senior",
				},
			},
			{
				NameUnused: "tt2_unittranche",
				Prefix:     "tt2",
				Lbl: trl.S{
					"en": "Uni&shy;tranche&nbsp;&nbsp;",
					"de": "Uni&shy;tranche&nbsp;&nbsp;",
				},
			},
			{
				NameUnused: "tt3_subordinated",
				Prefix:     "tt3",
				Lbl: trl.S{

					"en": "Subordinated",
					"de": "Subordinated",
				},
			},
		},
	},
	{
		NameUnused: "ac2_realestate",
		Prefix:     "ac2",
		Lbl: trl.S{
			"en": "European  Real Estate Debt",
			"de": "European  Real Estate Debt",
		},
		Short: trl.S{
			"en": "Real Estate<br> Debt",
			"de": "Real Estate<br> Debt",
		},
		TrancheTypes: []trancheType{
			{
				NameUnused: "tt1_wholeloan",
				Prefix:     "tt1",
				Lbl: trl.S{
					"en": "Whole loan",
					"de": "Whole loan",
				},
			},
			{
				NameUnused: "tt2_subordinated",
				Prefix:     "tt2",
				Lbl: trl.S{
					"en": "Subordinated",
					"de": "Subordinated",
				},
			},
		},
	},
	{
		NameUnused: "ac3_infrastruct",
		Prefix:     "ac3",
		Lbl: trl.S{
			"en": "European Infrastructure Debt",
			"de": "European Infrastructure Debt",
		},
		Short: trl.S{
			"en": "Infrastructure <br> Debt",
			"de": "Infrastructure <br> Debt",
		},
		TrancheTypes: []trancheType{
			{
				NameUnused: "tt1_senior",
				Prefix:     "tt1",
				Lbl: trl.S{
					"en": "Senior",
					"de": "Senior",
				},
			},
			{
				NameUnused: "tt2_subordinated",
				Prefix:     "tt2",
				Lbl: trl.S{
					"en": "Subordinated",
					"de": "Subordinated",
				},
			},
		},
	},
}

ultra short abbreviations would be

CDL / RED / ID

so far unused

View Source
var PDSLbls = map[string][]trl.S{
	"teamsize": {
		{
			"en": "<5",
			"de": "<5",
		},
		{
			"en": "5-10",
			"de": "5-10",
		},
		{
			"en": "11-20",
			"de": "11-20",
		},
		{
			"en": ">20",
			"de": ">20",
		},
	},
	"relevance1-5": {
		{
			"en": "not rele&shy;vant<br>(1)",
			"de": "not rele&shy;vant<br>(1)",
		},
		{
			"en": "some&shy;what<br>rele&shy;vant<br>(2)",
			"de": "some&shy;what<br>rele&shy;vant<br>(2)",
		},
		{
			"en": "rele&shy;vant<br>(3)",
			"de": "rele&shy;vant<br>(3)",
		},
		{
			"en": "core prin&shy;ciple<br>(4)",
			"de": "core prin&shy;ciple<br>(4)",
		},
		{
			"en": "potential<br>deal&shy;breaker<br>(5)",
			"de": "potential<br>deal&shy;breaker<br>(5)",
		},
	},

	"improveDecline1-5-prev": {
		{
			"en": "bad&nbsp;&nbsp;&nbsp;&nbsp;",
			"de": "bad&nbsp;&nbsp;&nbsp;&nbsp;",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{

			"en": "satis&shy;factory",
			"de": "satis&shy;factory",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "&nbsp;&nbsp;good",
			"de": "&nbsp;&nbsp;good",
		},
	},
	"improveDecline1-5-prev-spec": {
		{
			"en": "bad&nbsp;&nbsp;&nbsp;&nbsp;",
			"de": "bad&nbsp;&nbsp;&nbsp;&nbsp;",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "not change",
			"de": "not change",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "&nbsp;&nbsp;good",
			"de": "&nbsp;&nbsp;good",
		},
	},
	"improveDecline1-5-next": {
		{
			"en": "will<br>de&shy;cline",
			"de": "will<br>de&shy;cline",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "not change",
			"de": "not change",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "will<br>im&shy;prove",
			"de": "will<br>im&shy;prove",
		},
	},
	"improveDecline1-5-next-2": {
		{
			"en": "<span  style='font-size:90%'> will de&shy;te&shy;ri&shy;o&shy;rate  </span>",
			"de": "<span  style='font-size:90%'> will de&shy;te&shy;ri&shy;o&shy;rate  </span>",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "not change",
			"de": "not change",
		},
		{
			"en": "&nbsp;",
			"de": "&nbsp;",
		},
		{
			"en": "will<br>im&shy;prove",
			"de": "will<br>im&shy;prove",
		},
	},
	"closing-time-weeks": {
		{
			"en": "<<br>4",
			"de": "<<br>4",
		},

		{
			"en": "&nbsp;<br>4&#8209;8",
			"de": "&nbsp;<br>4&#8209;8",
		},
		{
			"en": "weeks<br><br>8&#8209;12",
			"de": "weeks<br><br>8&#8209;12",
		},
		{
			"en": "&nbsp;<br>12&#8209;16",
			"de": "&nbsp;<br>12&#8209;16",
		},
		{
			"en": "><br>16",
			"de": "><br>16",
		},
	},

	"covenants-per-credit": {

		{
			"en": "0&#8209;1",
			"de": "0&#8209;1",
		},
		{
			"en": "2&#8209;3",
			"de": "2&#8209;3",
		},
		{
			"en": "4&#8209;5",
			"de": "4&#8209;5",
		},
		{
			"en": "&nbsp;>5",
			"de": "&nbsp;>5",
		},
	},
}

Functions

func AddStaticDynamicGroups

func AddStaticDynamicGroups(page pageT)

AddStaticDynamicGroups adds five groups the second remains static, the others are randomized based on

func BIIILater

func BIIILater(q *QuestionnaireT, pageIdx int) bool

func BIIIMeasure

func BIIIMeasure(q *QuestionnaireT, pageIdx int) bool

func BIIINow

func BIIINow(q *QuestionnaireT, pageIdx int) bool

func BasePath

func BasePath() string

BasePath gives the 'root' for loading and saving questionnaire JSON files. Duplicate of lgn.basePath - cyclic dependencies

func CleanseUserAgent

func CleanseUserAgent(s string) string

CleanseUserAgent is stricter than EnglishTextAndNumbersOnly

func DeadlineAndPublication

func DeadlineAndPublication(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

DeadlineAndPublication

func DelocalizeNumber

func DelocalizeNumber(s string) string

DelocalizeNumber removes localized number formatting; HTML5 input type number does not de-localize values; 123,456.78 is allowed - and so is 123.456,78; at least we have at most *one* dot and *one* comma; https://www.ryadel.com/en/html-input-type-number-with-localized-decimal-values-jquery/

func EnglishTextAndNumbersOnly

func EnglishTextAndNumbersOnly(s string) string

EnglishTextAndNumbersOnly replaces all other UTF characters by space

func ErrorProxy

func ErrorProxy(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

ErrorProxy - shows errors for inputs named like paramSet

func FederalStateAboveOrBelowMedian

func FederalStateAboveOrBelowMedian(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

FederalStateAboveOrBelowMedian returns "besser" or "schlechter"; depending on the user's federal state education ranking

func GermanOnly

func GermanOnly(q *QuestionnaireT, pageIdx int) bool

func LinkBack

func LinkBack(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

LinkBack

func Mustaz09Underscore

func Mustaz09Underscore(s string) bool

Mustaz09Underscore tests strings for a-z, 0-9, _

func NewInput

func NewInput() inputT

NewInput returns an input filled in with globally enumerated label, decription etc.

func ParseJavaScript

func ParseJavaScript(tName string) (*template.Template, error)

ParseJavaScript loads js file and embeds its contents into a <script> tag; so that parsing the template does not do harmful escaping;

would better belong into package tpl - but circular dependencies;

func PatLogos

func PatLogos(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

PatLogos - only for the img src URLs

func PermaLink(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

PermaLink returns the perma link; only possible if the initial login was accomplished via direct login.

func PersonalLink(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

PersonalLink is only a reminder, does not contain the personal link; see PermaLink

func RenderStaticContent

func RenderStaticContent(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

RenderStaticContent - http request time display of a markdown file

func ResponseStatistics

func ResponseStatistics(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

ResponseStatistics returns the percentage of answers responded to.

func ResponseTextHasEuro

func ResponseTextHasEuro(q *QuestionnaireT, inp *inputT, paramSet string) (string, error)

ResponseTextHasEuro yields texts => want to keep € - want to have €

func UNUSEDpdsPage23

func UNUSEDpdsPage23(q *QuestionnaireT, page *pageT, acIdx int) error

Types

type CompositeFuncT

type CompositeFuncT func(qstif.Q, int, int, bool) (string, []string, error)

CompositeFuncT inputs combine challenging HTML and multiple inputs in complicated ways

Compare dynFuncT, validatorT

A composite func returns dynamic HTML with session values inserted from the questionnaire A composite func also returns the input *names* for json generation of the questionnaire template

Matching is required for

returned input names
input names in HTML form
input names to query session values from *q argument

Parameters

dynamic questionnaire - filled with response values
sequence  idx  -  usually a visible page sequence number
param set idx  -  statically determined - from a slice of param sets
preflight      -  only return the input names for questionnaire generation; dont render HTML output

Returns

rendered HTML of the group
slice of input names
error
type DropdownT struct {
	Name       string
	AutoSubmit bool // onchange this.Form.Submit() is suppressed
	Disabled   bool

	// management of 'style' and 'class' HTML attributes
	Attrs map[template.HTMLAttr]template.HTMLAttr

	LC      string // for rendering the labels inside the template below
	Options []optionT

	NameJavaScriptExpression template.JSStr `json:"-"` // helper
}

DropdownT represents a HTML dropdown control Methods need to return a string, so we can use them in templates

func (d *DropdownT) Add(k string, v trl.S) string

Add adds an option returns selected key

func (d *DropdownT) AddPleaseSelect(v trl.S)

AddPleaseSelect adds a default option at the top

func (d *DropdownT) HasAttr(k string) bool

HasAttr checks whether an attribute key exists

func (d *DropdownT) Len() int
func (d *DropdownT) Less(i, j int) bool
func (d *DropdownT) RemoveAllAttrs() string

RemoveAllAttrs removes all attributes

func (d *DropdownT) RemoveAttrVal(k, v string) string

RemoveAttrVal - removing the value in an attribute

func (d *DropdownT) Render(w io.Writer)

Render to io.Writer

func (d *DropdownT) RenderStr() string

RenderStr to string

func (d *DropdownT) Select(selectKey string) string

Select an option by key

func (d *DropdownT) Selected() string

Selected returns selected key

func (d *DropdownT) SetAttr(k string, vi interface{}) string

SetAttr - setting or appending an attribute

func (d *DropdownT) SetAutoSubmit(b bool) string

SetAutoSubmit - for usage in templates

func (d *DropdownT) SetDisabled(b bool) string

SetDisabled - for usage in templates

func (d *DropdownT) SetName(s string) string

SetName - for usage in templates

func (d *DropdownT) SortOptionsByLabel()

SortOptionsByLabel for quest generation time

func (d *DropdownT) Swap(i, j int)

type ErrorForward

type ErrorForward struct {
	// contains filtered or unexported fields
}

ErrorForward contains a markdown page or page id to jump to

func (ErrorForward) Error

func (ef ErrorForward) Error() string

Error implements the errors.Error interface

func (ErrorForward) MarkDownPath

func (ef ErrorForward) MarkDownPath() string

MarkDownPath returns the path to redirect to;

it should be renamed - since it might also contain absolute URLs

type GridBuilder

type GridBuilder struct {
	MainLabel trl.S // first row - before column headers - as wide as the group

	CellTopLeft trl.S
	// contains filtered or unexported fields
}

GridBuilder to generate a matrix or grid or table of labels and inputs; cells are stored column-wise - enforcing equal spans for label and control; columns together constitute a CSS-grid-column-template rendering is done row-wise;

func NewGridBuilderRadios

func NewGridBuilderRadios(
	columnTemplate []float32,
	hdrLabels []trl.S,
	inputNames []string,
	radioVals []string,
	firstColLabels []trl.S,
) *GridBuilder

NewGridBuilderRadios - see NewGridBuilderRadiosWithValidator

firstColLabels only appear, if columnTemplate has space > 0 for them

func NewGridBuilderRadiosWithValidator

func NewGridBuilderRadiosWithValidator(
	columnTemplate []float32,
	hdrLabels []trl.S,
	inputNames []string,
	radioVals []string,
	firstColLabels []trl.S,
	Validator string,
) *GridBuilder

NewGridBuilderRadiosWithValidator uses GridBuilder.AddCol() and GridBuilder.AddRadioRow() to compile a matrix of cells; basically, each cell can contain input names, input values and input labels

firstColLabels can be nil

func (*GridBuilder) AddCol

func (gb *GridBuilder) AddCol(headerCell trl.S, spanLabel, spanControl float32)

AddCol adds a column; to the grid builder template

func (*GridBuilder) AddRadioRow

func (gb *GridBuilder) AddRadioRow(name string, vals []string, sparseLabels map[int]trl.S)

AddRadioRow adds a row radio inputs - empty columns are filled with empty text; sparse labels allows to set labels for selected columns only; often only the first columns has a label

type P

type P interface {
	// Setters, and some getters for every struct field are required.
	// We could use reflection:
	//
	//     SetField(name string, value interface{})
	//
	// sacrificing type safety.
	//
	// We could use AST code generation to
	// create setters and getters like in Java.
	// => 15 pairs for pageT
	// => 25 pairs for inputT
	SetSection(trl.S)

	WidthMax(string) // setting max width
}

type ParamT

type ParamT struct {
	Name string `json:"name,omitempty"` // i.e. main_refinance_rate_ecb
	Val  string `json:"val,omitempty"`  // i.e. "0,5%"
}

ParamT contains changing parameters to a questionnaire

type Q

type Q interface {

	// Questionnaire level
	UserIDInt() int
	Version() int
	GetLangCode() string

	// AddPage() *pageT
	AddPageIf() P

	ResponseByName(n string) (string, error)
	ErrByName(n string) (string, error)
}

Q decouples

type QuestionnaireT

type QuestionnaireT struct {
	Survey SurveyT `json:"survey,omitempty"`
	UserID string  `json:"user_id,omitempty"` // participant ID, decimal, but string, i.E. 1011

	// Attrs are user specific key-value pairs -
	//    i.e. user country or euro-member
	// 		whereas surveyT.Params are specific to the survey + wave
	// Attrs survey ID and wave ID and lang code come from login;
	// additional Attrs come from cfg.Profiles - mediated by login `p` parameter
	//
	//    key 'survey_variant' loads distinct questionnaire templates
	//
	//  Attrs are dynamically replaced
	//    attr-country
	Attrs map[string]string `json:"user_attrs,omitempty"`

	// if any response key "finished" equals qst.Finished
	// this is set to time.Now() - truncated to second
	// it is the marker for preventing any more edits
	ClosingTime time.Time `json:"closing_time,omitempty"`
	RemoteIP    string    `json:"remote_ip,omitempty"`
	UserAgent   string    `json:"user_agent,omitempty"`
	MD5         string    `json:"md_5,omitempty"`

	LangCodes []string `json:"lang_codes,omitempty"` // default, order and availability - [en, de, ...] or [de, en, ...]
	LangCode  string   `json:"lang_code,omitempty"`  // current lang code - i.e. 'de' - session key lang_code

	CurrPage  int  `json:"curr_page,omitempty"`
	HasErrors bool `json:"has_errors,omitempty"` // If any response is faulty; set by ValidateReponseData

	// ShufflingVariations indicated how many different reshufflings occur;
	// until repetition; primitive permutation mechanism;
	// deterministically reordering / reshuffling a set of groups
	// based on
	//      * user id
	// 		* groupT.RandomizationSeed
	//
	// 	page idx is no longer relevant
	//  groupT.RandomizationGroup is only to distinguish multiple groups per page
	ShufflingVariations  int `json:"shuffling_variations,omitempty"`
	ShufflingRepetitions int `json:"shuffling_repetitions,omitempty"` // if equals 0, then defaults to three; usually you dont have to touch this value

	// PreventSkipForward - skipping back always possible,
	// skipping forward is preventable
	PreventSkipForward bool `json:"allow_skip_forward"`

	// PostponeNavigationButtons - how many seconds delay, before the navigation appears
	PostponeNavigationButtons int `json:"postpone_navigation_buttons,omitempty"`

	// Previously we had SurveyT.Variant; now all questionnaire variations
	// should be captured with a distinct VersionEffective.
	// Notice the difference to tpl.SiteCore();
	// tpl.SiteCore() yields a common *style* identifier
	// for completely different questionnaires
	VersionMax       int    `json:"version_max,omitempty"`    // total number of versions - usually permutations
	AssignVersion    string `json:"assign_version,omitempty"` // default is UserID modulo VerionMax - other value is "round-robin" based on in memory atomic counter
	VersionEffective int    `json:"version_effective"`        // zero based, result of q.Version() - intended for read access - written on first call of q.Version() - can be reset with to -2

	MaxGroups int `json:"max_groups,omitempty"` //  Max number of groups - a helper value - computed during questionnaire creation - previously used for shuffing of groups.

	Pages []*pageT `json:"pages,omitempty"`
}

QuestionnaireT contains pages with groups with inputs

func FromSession

func FromSession(w io.Writer, r *http.Request) (*QuestionnaireT, bool, error)

FromSession loads a graph from session; second return value contains 'is set'.

func Load1

func Load1(fn string) (*QuestionnaireT, error)

Load1 loads a questionnaire from a JSON file. On error returns completely empty Questionnaire

func (*QuestionnaireT) AddFinishButtonNextToLast

func (q *QuestionnaireT) AddFinishButtonNextToLast()

AddFinishButtonNextToLast from page

Adding explicit button to finish page, which is outsite navigation. Button is added to the next-to-last page. Call this method at the end of page insertions.

func (*QuestionnaireT) AddPage

func (q *QuestionnaireT) AddPage() *pageT

AddPage creates a new page and adds this page to the questionnaire's pages

func (*QuestionnaireT) AddPageAfter

func (q *QuestionnaireT) AddPageAfter(idx int) *pageT

AddPageAfter creates a new page and adds this page to the questionnaire's pages

func (*QuestionnaireT) AddPageIf

func (q *QuestionnaireT) AddPageIf() P

AddPageIf like AddPage compare WrapPage

func (*QuestionnaireT) ByName

func (q *QuestionnaireT) ByName(n string) *inputT

ByName retrieves an input element by name. Returns nil if the input element was not found.

func (*QuestionnaireT) Compare

func (q *QuestionnaireT) Compare(v *QuestionnaireT, lenient bool) (bool, error)

Compare compares page completion times and input responses. Compare stops with the first difference and returns an error.

func (*QuestionnaireT) ComputeDynamicContent

func (q *QuestionnaireT) ComputeDynamicContent(idx int) error

ComputeDynamicContent computes elements of type dynamic func

func (*QuestionnaireT) ComputeMaxGroups

func (q *QuestionnaireT) ComputeMaxGroups()

ComputeMaxGroups computes the maximum number of groups and puts them into q.MaxGroups

func (*QuestionnaireT) CurrentPageHTML

func (q *QuestionnaireT) CurrentPageHTML() (string, error)

CurrentPageHTML is a comfort shortcut to PageHTML called in html template

func (*QuestionnaireT) DumpErrors

func (q *QuestionnaireT) DumpErrors()

DumpErrors logs all ErrMsgs from the questionnaire

func (*QuestionnaireT) DynamicPageValues

func (q *QuestionnaireT) DynamicPageValues(onlyThisIndex int) map[string]string

func (*QuestionnaireT) DynamicPages

func (q *QuestionnaireT) DynamicPages(onlyThisIndex int) error

DynamicPages dynamically re-creates groups and inputs based on conditions like q.UserID or values from other pages;

there are three issues integrating this method with the Join() method:

  • the structure depends on user input from other pages - we must call Join() first
  • Join() will see distinct structures for groups/inputs between base (empty) and split (previously entered data) - we coded an exception
  • DynamicPages() will now create re-groups and _empty_ inputs
  • We now pull _previous_ dynamic page values (qSplit.DynamicPageValues) and apply them to the joined questionnaire

We have to do a similar thing in PageHTML() because conditional values in other forms may have changed

func (*QuestionnaireT) DynamicPagesApplyValues

func (q *QuestionnaireT) DynamicPagesApplyValues(kv map[string]string) (hasContent bool)

func (*QuestionnaireT) EditPage

func (q *QuestionnaireT) EditPage(idx int) *pageT

EditPage returns page X compare WrapPage

func (*QuestionnaireT) EnumeratePages

func (q *QuestionnaireT) EnumeratePages()

EnumeratePages allocates a sequence number based on IsInNavigation()

func (*QuestionnaireT) ErrByName

func (q *QuestionnaireT) ErrByName(n string) (string, error)

ErrByName returns the error for an input name; implements qstif.Q

func (*QuestionnaireT) FilePath1

func (q *QuestionnaireT) FilePath1() string

FilePath1 returns the location of the questionnaire file. Similar to lgn.LoginT.QuestPath()

func (*QuestionnaireT) FindNewPage

func (q *QuestionnaireT) FindNewPage(sess *sessx.SessT)

FindNewPage determines the new page

func (*QuestionnaireT) GetLangCode

func (q *QuestionnaireT) GetLangCode() string

GetLangCode -

func (QuestionnaireT) GroupHTMLGridBased

func (q QuestionnaireT) GroupHTMLGridBased(pageIdx, grpIdx int) string

GroupHTMLGridBased renders a group of inputs to grid based HTML

func (QuestionnaireT) GroupHTMLTableBased

func (q QuestionnaireT) GroupHTMLTableBased(pageIdx, grpIdx int) string

GroupHTMLTableBased renders a group of inputs to GroupHTMLTableBased

func (*QuestionnaireT) HasComposit

func (q *QuestionnaireT) HasComposit(pgIdx, grIdx int) ([]string, bool, error)

HasComposit - group contains composit element?

if yes, we retrieve its input names

func (*QuestionnaireT) HasNext

func (q *QuestionnaireT) HasNext() bool

HasNext if a next page exists

func (*QuestionnaireT) HasPrev

func (q *QuestionnaireT) HasPrev() bool

HasPrev if a previous page exists

func (*QuestionnaireT) Hyphenize

func (q *QuestionnaireT) Hyphenize()

Hyphenize replaces "mittelfristig" with "mittel&shy;fristig" for all labels and descriptions

func (QuestionnaireT) InputHTMLGrid

func (q QuestionnaireT) InputHTMLGrid(pageIdx, grpIdx, inpIdx int, langCode string) string

InputHTMLGrid renders an input to HTML

func (*QuestionnaireT) IsInNavigation

func (q *QuestionnaireT) IsInNavigation(pageIdx int) bool

IsInNavigation checks whether pageIdx is suitable as next or previous page and whether it should show up in progress bar

func (*QuestionnaireT) Join

func (q *QuestionnaireT) Join(q2 *QuestionnaireT) error

Join adds user input from q2 onto q

func (*QuestionnaireT) KeysValues

func (q *QuestionnaireT) KeysValues(cleanse bool, getRangeInfo bool) (keys, vals, types, finishes []string)

KeysValues returns all pages finish times; keys and values in defined order. Empty values are also returned. Major purpose is CSV export across several questionnaires. Does deduplicate radio fieldnames, since they have the same value.

func (*QuestionnaireT) LabelCleanse

func (q *QuestionnaireT) LabelCleanse(s string) string

LabelCleanse removes some common HTML stuff; argument q is not yet used

func (*QuestionnaireT) LabelIsOutline

func (q *QuestionnaireT) LabelIsOutline(s string) bool

LabelIsOutline - if s starts with some outline see unit test

func (*QuestionnaireT) LabelsByInputNames

func (q *QuestionnaireT) LabelsByInputNames() (lblsByNames map[string]string, keys, lbls []string)

LabelsByInputNames extracts the label texts for each input; starting from the input to the top; since there is a lot of summarized labeling for multiple inputs, we have no clear relationship; for each input, we traverse up on the page collecting all text until we hit a piece of text which is an outline number.

If no limiting outline number is found, text is concatenated all the way up to the start of the page.

functions cleanseIdentical(...) and cleansePrefixes(...) are used to clear out redundancies; see documentation.

func (*QuestionnaireT) Next

func (q *QuestionnaireT) Next() int

Next returns index of the next page

func (*QuestionnaireT) NextNaviNum

func (q *QuestionnaireT) NextNaviNum() string

NextNaviNum returns navigational number of the next page

func (*QuestionnaireT) PageHTML

func (q *QuestionnaireT) PageHTML(pageIdx int) (string, error)

PageHTML generates HTML for a specific page of the questionnaire

func (*QuestionnaireT) Prev

func (q *QuestionnaireT) Prev() int

Prev returns index of the previous page

func (*QuestionnaireT) PrevNaviNum

func (q *QuestionnaireT) PrevNaviNum() string

PrevNaviNum returns navigational number of the prev page

func (*QuestionnaireT) PrevPage

func (q *QuestionnaireT) PrevPage() (prevPage int)

PrevPage computation; q contains currPage from *last* request; we remember this, because we want to store request values *there*

func (*QuestionnaireT) ProgressBar

func (q *QuestionnaireT) ProgressBar() string

ProgressBar generates a discrete position indicator https://fribly.com/2015/01/01/scalable-responsive-progress-bar/

It should be clickable and jump to the indicated page. This means, we have to submit the form and submit the destination page. Compare MenuMobile()

func (*QuestionnaireT) ProgressBarLine

func (q *QuestionnaireT) ProgressBarLine() string

func (*QuestionnaireT) RandomizeOrder

func (q *QuestionnaireT) RandomizeOrder(pageIdx int) []int

RandomizeOrder creates a shuffled ordering of groups determined by UserID and .RandomizationGroup; groups with RandomizationGroup==0 retain their position on fixed order position; others get a randomized position

func (*QuestionnaireT) RenderJS

func (q *QuestionnaireT) RenderJS(
	w io.Writer,
	fileName string,
	translations map[string]trl.S,
	jsStrings map[string]string,
)

RenderJS prints the contents of a JavaScript template into the HTML response; used for page level JavaScript blocks; used also for inputs of type 'javascript-block'

func (*QuestionnaireT) ResponseByName

func (q *QuestionnaireT) ResponseByName(n string) (string, error)

ResponseByName implements qstif.Q

func (*QuestionnaireT) Save1

func (q *QuestionnaireT) Save1(fn string) error

Save1 a questionnaire to JSON

func (*QuestionnaireT) SetColspans

func (q *QuestionnaireT) SetColspans()

func (*QuestionnaireT) SetLangCode

func (q *QuestionnaireT) SetLangCode(newCode string) error

SetLangCode tries to change the questionnaire langCode if supported by langCodes.

func (*QuestionnaireT) SiteSpecificTrl

func (q *QuestionnaireT) SiteSpecificTrl(key string) trl.S

SiteSpecificTrl returns a survey type or site specific configuration resource string. Compare cfg.ConfigT.SiteSpecificTrl and its usage in templates: {{ cfg.SiteSpecificTrl ...

func (*QuestionnaireT) Split

func (q *QuestionnaireT) Split() (*QuestionnaireT, error)

Split creates a copy of q containing only the user responses and q metadata

func (*QuestionnaireT) Statistics

func (q *QuestionnaireT) Statistics() (int, int, float64)

Statistics returns the percentage of answers responded to. It is helper to ResponseStatistics().

func (*QuestionnaireT) SuppressProgressBar

func (q *QuestionnaireT) SuppressProgressBar() bool

SuppressProgressBar - dont show progress bar in current page; while back/forth buttons may still be rendered convenience func for templates;

func (*QuestionnaireT) TranslationCompleteness

func (q *QuestionnaireT) TranslationCompleteness() error

TranslationCompleteness tests all multilanguage strings for completeness. Use only at JSON creation time, since dynamic elements have only one language.

func (*QuestionnaireT) UserIDInt

func (q *QuestionnaireT) UserIDInt() int

UserIDInt retrieves the userID as int. For dynamic questionaires - use q.Version().

func (*QuestionnaireT) Validate

func (q *QuestionnaireT) Validate() error

Validate performs integrity tests on a static base questionnaire. Validate should be run for every JSON load operation.

Checks performed:

waveId, langCodes valid
input names uniqueness
input type valid
submit button jump page exists
validator func exists

Validate also does some initialization stuff. This is needed only at JSON creation time.

Setting page and group width to 100
Setting values for radiogroups
Setting navigation sequence enumeration values

Compare funcs above, which are only called on JSON creation time and for DynamicPages()

func (*QuestionnaireT) ValidateResponseData

func (q *QuestionnaireT) ValidateResponseData(pageNum int, langCode string) (last error, forward *ErrorForward)

ValidateResponseData applies all input validation rules on the responses. Restricted by page, since validation errors are handled page-wise. Errors are collected for groups of inputs and stored into those group's errorProxy. The last error is returned. If any validation demands a HTTP forwarding, then the second return value contains the URL

func (*QuestionnaireT) Version

func (q *QuestionnaireT) Version() int

Version sets and returns the questionnaire's version; default - version depends on user ID 'round-robin' - version depends on login order 'version-from-login-url' - version depends parameter v=[xx] during login

Version() is idempotent - once it is set to greater zero, this value remains Version() called first when a new QuestionaireT is loaded from a JSON base file. Version() can be recomputed if q.VersionEffective has been set to -1 before

type SurveyT

type SurveyT struct {
	Type string `json:"type,omitempty"` // The type identifier, i.e. "fmt" or "cep"

	Org  trl.S `json:"org,omitempty"`  // organization, i.e. Unicef
	Name trl.S `json:"name,omitempty"` // full name, i.e. programming languages survey

	Year  int        `json:"year,omitempty"`  // The wave year
	Month time.Month `json:"month,omitempty"` // The wave month, 1-based, int

	Deadline time.Time `json:"deadline,omitempty"` // No more responses accepted

	Params []ParamT `json:"params,omitempty"` // I.e. NASDAQ at the time begin of the wave - being used in the wording of the questions
}

SurveyT stores the interval components of a questionnaire wave. For quarterly intervals, it needs to be extended

func NewSurvey

func NewSurvey(tp string) SurveyT

NewSurvey returns a survey based on current time

func (SurveyT) Filename

func (s SurveyT) Filename() string

Filename returns the filename for the base file

func (*SurveyT) HTMLForm

func (s *SurveyT) HTMLForm(questTypes []string, errStr string) string

HTMLForm renders an HTML edit form for survey data

func (SurveyT) MonthOfQuarter

func (s SurveyT) MonthOfQuarter() int

MonthOfQuarter returns 1 for Jan, 2 for Feb, 3 for March; 1 for April

func (*SurveyT) Param

func (s *SurveyT) Param(name string) (string, error)

Param returns the value of a surveys param

func (SurveyT) Quarter

func (s SurveyT) Quarter(offs ...int) string

General rule: Ask for the forecast for the *current* quarter.

Exception: If previous quarter growth numbers were not yet published by Statistisches Bundesamt, then ask for previous quarter.

You find out by calling <https://www.destatis.de/SiteGlobals/Forms/Suche/Termine/DE/Terminsuche_Formular.html?startDate_dt=2021-06-30T22%3A00%3A00Z&cl2Taxonomies_Themen_0=volkswirtschaftliche_gesamtrechnungen_inlandsprodukt> or searching for "volkswirtschaftliche gesamtrechnungen inlandsprodukt" Also <https://www.finanzen.net/konjunktur/bruttoinlandsprodukt>

This exception usually applies in the first month of the current quarter. => set questionnaire survey parameter destatis to -1

Quarter yields quarter plus year; based on the survey month; offset adds/subtracts to/from quarter; overflowing over 4; underflowing under 1

January of 0 - => Q1 0

January  of 2021 -                       => Q1 2021
January  of 2021 -    plus 1 Quarter     => Q2 2021
January  of 2021 -    plus 3 Quarters    => Q4 2021
January  of 2021 -    plus 4 Quarters    => Q1 2022
January  of 2021 -    plus 5 Quarters    => Q2 2022
March    of    0 -                       => Q1 0
March    of 2021 -                       => Q1 2021
March    of 2021 -    plus 1 Quarter     => Q2 2021
March    of 2021 -    plus 3 Quarters    => Q4 2021
March    of 2021 -    plus 4 Quarters    => Q1 2022
April    of 2021 -                       => Q2 2021
April    of 2021 -    plus 1 Quarter     => Q3 2021
April    of 2021 -    plus 3 Quarters    => Q1 2022
April    of 2021 -    plus 4 Quarters    => Q2 2022
October  of 2021 -                       => Q4 2021
October  of 2021 -    plus 1 Quarter     => Q1 2022
October  of 2021 -    plus 4 Quarters    => Q4 2022
October  of 2021 -    plus 1 Quarter     => Q2 2024
December of 2021 -                       => Q4 2021
December of 2021 -    plus 1 Quarter     => Q1 2022
December of 2021 -    plus 4 Quarters    => Q4 2022
December of 2021 -    plus 1 Quarter     => Q2 2024
January  of 2021 -    minus 1 Quarter    => Q4 2020
March    of 2021 -    minus 1 Quarter    => Q4 2020
April    of 2021 -    minus 1 Quarter    => Q1 2021
January  of 2021 -    minus 4 Quarters   => Q1 2020
January  of 2021 -    minus 5 Quarters   => Q4 2019

func (SurveyT) String

func (s SurveyT) String() string

String is the default identifier

func (SurveyT) TemplateLogoText

func (s SurveyT) TemplateLogoText(langCode string) template.HTML

TemplateLogoText for display in HTML

func (SurveyT) WaveID

func (s SurveyT) WaveID() string

WaveID returns the year-month in standard format yyyy-mm

func (SurveyT) WaveIDPretty

func (s SurveyT) WaveIDPretty() string

WaveIDPretty is empty, if we dont have proper year, otherwise like WaveID()

func (SurveyT) YearStr

func (s SurveyT) YearStr(offs ...int) string

YearStr yields the year as string; based on the survey year; offset adds years

type WrappedPageT

type WrappedPageT struct {
	// contains filtered or unexported fields
}

WrappedPageT for creating helper funcs outside the package - *temporarily*. compare EditPage

func WrapPageT

func WrapPageT(pt *pageT) *WrappedPageT

WrapPageT is a wrapper for pageT

func (WrappedPageT) AddBiiiPrio

func (p WrappedPageT) AddBiiiPrio(
	mainLbl trl.S,
	labels []trl.S,
	names []string,
	idxOther map[int]bool,
	showFreeInput int,
) *groupT

AddBiiiPrio question block

func (WrappedPageT) AddBiiiPrio2Cols

func (p WrappedPageT) AddBiiiPrio2Cols(
	mainLbl trl.S,
	labels []trl.S,
	names []string,
	idxOther map[int]bool,
) *groupT

AddBiiiPrio question block

func (WrappedPageT) AddGrid

func (p WrappedPageT) AddGrid(gb *GridBuilder) *groupT

AddGrid statically adds inputs to the page; these inputs are based on the preconfigured GridBuilder's cells

func (WrappedPageT) AddGroup

func (p WrappedPageT) AddGroup() *groupT

AddGroup creates a new group and adds this group to the pages's groups

func (WrappedPageT) ConsolidateRadioErrors

func (page WrappedPageT) ConsolidateRadioErrors(grpOrder []int)

ConsolidateRadioErrors removes repeating error messages from radio inputs

func (WrappedPageT) RedirectFuncExec

func (p WrappedPageT) RedirectFuncExec(q *QuestionnaireT, w http.ResponseWriter, r *http.Request) error

RedirectFuncExec replaces the ErrorForward design

func (WrappedPageT) SetSection

func (p WrappedPageT) SetSection(s trl.S)

SetSection corresponds to the struct field Section. Setters, and some getters for every struct field required

func (WrappedPageT) WidthDefault

func (p WrappedPageT) WidthDefault()

WidthDefault is called for every page - setting auto margins

func (WrappedPageT) WidthMax

func (p WrappedPageT) WidthMax(s string)

WidthMax limits width in desktop view; horizontal centering by default via WidthDefault(); for instance to 30rem; mobile view: no limitation compare groupT.WidthMax

Directories

Path Synopsis
internal
x

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL