go_dynamic_questionnaire

package module
v0.0.0-...-f8e4273 Latest Latest
Warning

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

Go to latest
Published: Sep 8, 2025 License: MIT Imports: 5 Imported by: 0

README

Dynamic Questionnaire

The Go Dynamic Questionnaire is a Go library that creates dynamic questionnaires.
Want to create a questionnaire for your Go project that adapts the next questions based on previous answers? Go Dynamic Questionnaire is what you're looking for! You can even provide recommendations or closing remarks based on the responses.

Dynamic Questionnaire requires Go >= 1.23

PkgGoDev CI Go Report Card Release

Installation

go get "github.com/antfroger/go-dynamic-questionnaire"

Basic Usage

Use questionnaire.New("config.yaml") to create and initialize the questionnaire based on your config.

import "github.com/antfroger/go-dynamic-questionnaire"

func main() {
    q, err := questionnaire.New("config.yaml")
    if err != nil {
        // ...
    }

    answers := make(map[string]int)
    for {
        response, err := q.Next(answers)
        if err != nil {
            // ...
        }

        // When the questionnaire is completed, display closing remarks
        if response.Completed {
            displayClosingRemarks(response.ClosingRemarks)
            break
        }

        // Display progress if available
        if response.Progress != nil {
            fmt.Printf("Progress: %d/%d questions answered\n", response.Progress.Current, response.Progress.Total)
        }

        // Ask the questions and get the answers from the user
        for _, question := range response.Questions {
            answer := getUserAnswer(question)
            answers[question.Id] = answer
        }
    }
}

Configuration Format

Create questionnaires using YAML:

questions:
  - id: "satisfaction"
    text: "How satisfied are you with our service?"
    answers:
      - "Very Satisfied"
      - "Satisfied"
      - "Neutral"
      - "Dissatisfied"
      - "Very Dissatisfied"

  - id: "recommendation"
    text: "Would you recommend us to others?"
    depends_on: ["satisfaction"]
    condition: 'answers["satisfaction"] in [1,2]'
    answers:
      - "Definitely"
      - "Probably"
      - "Maybe"

closing_remarks:
  - id: "thank_you"
    text: "Thank you for your feedback!"
  - id: "follow_up"
    text: "We'll reach out to address your concerns."
    condition: 'answers["satisfaction"] >= 4'

Features

Unified API

Single Next() method returns everything you need:

  • Questions to display
  • Closing remarks (when completed)
  • Completion status
  • Progress tracking

in a unified response:

type Response struct {
    Questions      []Question      `json:"questions"`
    ClosingRemarks []ClosingRemark `json:"closing_remarks,omitempty"`
    Completed      bool            `json:"completed"`
    Progress       *Progress       `json:"progress,omitempty"`
}
Closing Remarks

Show personalized messages when questionnaire is completed:

type ClosingRemark struct {
		Id   string `json:"id"`   // Unique identifier for the remark
		Text string `json:"text"` // The message text to display
	}

Configure closing remarks in your YAML:

closing_remarks:
  - id: "general"
    text: "Thank you for completing the questionnaire!"
  - id: "specific"
    text: "Based on your answers, here's our recommendation..."
    condition: 'answers["interest"] == 1'
Progress Tracking

Track user progress through the questionnaire:

type Progress struct {
    Current int `json:"current"`  // Questions answered
    Total   int `json:"total"`    // Total questions in the current path
}
Conditional Logic

Dynamic question flow based on previous answers:

questions:
  - id: "experience"
    text: "Do you have programming experience?"
    answers:
      - "Yes"
      - "No"
  - id: "language"
    text: "Which language do you prefer?"
    condition: 'answers["experience"] == 1'
    answers:
      - "Go"
      - "Python"
      - "JavaScript"

Examples

CLI Application

Interactive command-line questionnaire with progress display:

cd examples/cli
go run main.go tech.yaml

More details in the dedicated README.

REST API Server

Complete web service with questionnaire endpoints:

cd examples/rest-api
go run main.go

API endpoints:

  • GET /questionnaires - List available questionnaires
  • POST /questionnaires/{id} - Get questions with progress and closing remarks

More details in the dedicated README.

Advanced Features

Thread-Safe Design

The library is stateless and thread-safe by design. Each questionnaire instance can be safely used across multiple goroutines.

Expression Engine

Powerful condition expressions using the expr library:

condition: 'answers["age"] >= 18 and answers["country"] == "US"'
condition: 'answers["score"] in 1..5'
condition: 'len(answers) >= 3'
Flexible Input

Load questionnaires from files or byte arrays:

// From file
q, err := questionnaire.New("config.yaml")

// From bytes
yamlData := []byte(`questions: ...`)
q, err := questionnaire.New(yamlData)

Areas for Improvement

Medium Term
  1. Add loaders for different configuration formats
    Introduce a loader interface to handle different config types. These loaders would be responsible for reading from files, parsing YAML/JSON, etc.
    • read the configuration from a JSON file
    • read the configuration from JSON bytes
    • pass pre-configured questions
  2. Question type system (text input, numbers, etc.)
  3. Question validation (required fields, formats)
  4. Performance optimizations for large questionnaires
    • Pre-compute which questions are available
    • Cache condition evaluation results
Long Term
  1. Internationalization support
  2. Advanced branching logic and survey flows
  3. Builder pattern for easier questionnaire construction

Documentation

Overview

Package go_dynamic_questionnaire provides a flexible, condition-based questionnaire system for Go applications.

This package allows you to create dynamic questionnaires where the questions shown to users depend on their previous answers. Questions can have conditional logic using expressions, and the system supports progress tracking and closing remarks.

Basic Usage

See README.md for a quick start guide. For more examples and advanced usage, see the examples/ directory.

Thread Safety

All operations are thread-safe. The same questionnaire instance can be used concurrently by multiple goroutines without any synchronization.

Error Handling

The package provides rich error information through custom error types. Validation errors include context about what went wrong and suggestions for fixing the issue.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClosingRemark

type ClosingRemark struct {
	Id   string `json:"id"`   // Unique identifier for the remark
	Text string `json:"text"` // The message text to display
}

ClosingRemark represents a message shown to users when the questionnaire is completed. Multiple remarks can be shown based on the user's answers and conditional logic.

Example usage in JSON:

{
  "id": "thank_you",
  "text": "Thank you for your feedback!"
}

type Progress

type Progress struct {
	Current int `json:"current"` // Number of questions answered so far
	Total   int `json:"total"`   // Total number of questions that could be answered
}

Progress represents the user's progress through the questionnaire. It provides information about how many questions have been processed and how many remain, useful for displaying progress bars or indicators.

The progress calculation is based on available questions at each step, taking into account conditional logic. Questions that cannot be reached due to user answers are not counted in the total.

Note: Progress is nil when the questionnaire is completed.

type Question

type Question struct {
	Id      string   `json:"id"`      // Unique identifier for the question
	Text    string   `json:"text"`    // The question text to display
	Answers []string `json:"answers"` // List of answer choices (1-indexed when referenced)
}

Question represents a question that should be presented to the user. This is the external representation used in API responses.

type Questionnaire

type Questionnaire interface {
	// Next processes the provided answers and returns the next set of questions,
	// progress information, and completion status.
	//
	// Parameters:
	//   answers: A map where keys are question IDs and values are 1-indexed answer choices.
	//            For example, if a question has answers ["Yes", "No", "Maybe"],
	//            a value of 1 means "Yes", 2 means "No", and 3 means "Maybe".
	//
	// Returns:
	//   *Response: Contains the next questions to show, completion status,
	//             progress information, and closing remarks (if completed).
	//   error: Returns validation errors for invalid question IDs, out-of-range answers,
	//          or condition evaluation errors.
	//
	// The method validates all provided answers before processing. If any answer
	// is invalid, the entire operation fails and returns a validation error with
	// details about what went wrong.
	Next(answers map[string]int) (*Response, error)
}

Questionnaire represents a dynamic questionnaire that can process user answers and determine the next questions to show based on conditional logic.

All methods are thread-safe and can be called concurrently from multiple goroutines. The questionnaire instance is stateless - all state is passed through the answers parameter.

Example usage:

q, err := gdq.New("questionnaire.yaml")
if err != nil {
    return err
}

answers := map[string]int{"q1": 2, "q2": 1}
response, err := q.Next(answers)
if err != nil {
    return err
}

if response.Completed {
    // Handle completion and closing remarks
} else {
    // Present next questions to user
}

func New

func New[T config](config T) (Questionnaire, error)

New creates a new Questionnaire instance from either a file path or YAML content.

The function accepts two types of input:

  • string: Path to a YAML configuration file
  • []byte: Raw YAML content as bytes

Parameters:

config: Either a file path (string) or YAML content ([]byte).
        The YAML must contain 'questions' and optionally 'closing_remarks' sections.

Returns:

Questionnaire: A fully configured questionnaire instance ready for use.
               The instance is immutable and thread-safe.
error: Returns configuration errors, file reading errors, YAML parsing errors,
       or validation errors if the questionnaire structure is invalid.

Example usage with file path:

q, err := gdq.New("surveys/customer-feedback.yaml")
if err != nil {
    log.Fatalf("Failed to load questionnaire: %v", err)
}

Example usage with YAML content:

yamlData := []byte(`
questions:
  - id: "satisfaction"
    text: "How satisfied are you with our service?"
    answers: ["Very satisfied", "Satisfied", "Neutral", "Dissatisfied"]
  - id: "recommend"
    text: "Would you recommend us?"
    answers: ["Yes", "No"]
    condition: 'answers["satisfaction"] <= 2'
closing_remarks:
  - id: "thanks"
    text: "Thank you for your feedback!"
`)

q, err := gdq.New(yamlData)
if err != nil {
    return fmt.Errorf("questionnaire creation failed: %w", err)
}

The function validates the questionnaire structure during creation, checking for:

  • Duplicate question IDs
  • Empty question IDs
  • Questions without answer options
  • Invalid YAML syntax

type Response

type Response struct {
	Questions      []Question      `json:"questions"`                 // Next questions to show (empty if completed)
	ClosingRemarks []ClosingRemark `json:"closing_remarks,omitempty"` // Closing remarks (only when completed)
	Completed      bool            `json:"completed"`                 // Whether the questionnaire is finished
	Progress       *Progress       `json:"progress,omitempty"`        // Progress information (nil when completed)
}

Response represents the complete response from processing a questionnaire step. It contains all information needed to either continue the questionnaire or handle completion.

The response structure allows clients to:

  • Display the next questions to users (Questions field)
  • Show progress information (Progress field)
  • Handle questionnaire completion (Completed field)
  • Display closing messages (ClosingRemarks field)

Example JSON output:

{
  "questions": [{"id": "q1", "text": "...", "answers": ["Yes", "No"]}],
  "completed": false,
  "progress": {"current": 2, "total": 5}
}

Jump to

Keyboard shortcuts

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