{"id":17227409,"url":"https://github.com/plutov/formulosity","last_synced_at":"2025-04-13T06:28:52.225Z","repository":{"id":251929291,"uuid":"695111006","full_name":"plutov/formulosity","owner":"plutov","description":"Self-hosted Surveys as Code platform.","archived":false,"fork":false,"pushed_at":"2025-03-17T21:07:35.000Z","size":1038,"stargazers_count":206,"open_issues_count":8,"forks_count":20,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-04T05:45:28.661Z","etag":null,"topics":["go","hacktoberfest","nextjs","self-hosted","survey","tailwind"],"latest_commit_sha":null,"homepage":"https://formulosity.vercel.app","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plutov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-22T11:32:18.000Z","updated_at":"2025-03-30T20:21:56.000Z","dependencies_parsed_at":"2024-08-22T20:22:09.005Z","dependency_job_id":"d65f2eca-7acf-44a7-bd07-2a8d8d7365d7","html_url":"https://github.com/plutov/formulosity","commit_stats":null,"previous_names":["plutov/formulosity"],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plutov%2Fformulosity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plutov%2Fformulosity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plutov%2Fformulosity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plutov%2Fformulosity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plutov","download_url":"https://codeload.github.com/plutov/formulosity/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248674291,"owners_count":21143679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","hacktoberfest","nextjs","self-hosted","survey","tailwind"],"created_at":"2024-10-15T04:19:12.426Z","updated_at":"2025-04-13T06:28:52.190Z","avatar_url":"https://github.com/plutov.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"https://github.com/plutov/formulosity/blob/main/ui/public/logo_wide.png\" height=\"100px\"\u003e\n\n## Formulosity - self-hosted Surveys as Code platform.\n\nThis approach offers a number of advantages, including:\n\n- **Version control**: Survey files can be stored in a Github repository, which makes it easy to track changes and collaborate with others.\n- **Reproducibility**: Survey files can be easily shared and reproduced, making it easy to reuse surveys or create variations of existing surveys.\n- **Automation**: Survey files can be used to automate the creation and deployment of surveys, which can save time and effort.\n\n**Formulosity** uses human-readable declarative language [YAML](https://en.wikipedia.org/wiki/YAML).\n\n\u003cimg src=\"https://github.com/plutov/formulosity/blob/main/ui/public/questions.png\" height=\"250px\"\u003e\n\n## Features\n\n- [x] API first\n- [x] Survey UI: for end users (respondents)\n- [x] Console UI: manage surveys\n- [x] YAML survey configuration\n- [x] Basic question types\n- [x] Default theme\n- [x] Custom themes support\n- [x] Personalized options: intro, outro, etc.\n- [x] Cookie/IP duplicate response protection\n- [x] Admin user authentication\n- [x] Different database options: SQLite and Postgres\n- [x] Continue where you left off\n- [x] Advanced validation rules\n- [x] Export responses in UI or via API\n- [ ] Advanced question types\n- [ ] Pipe answers into the following questions\n\n## Survey Structure\n\nEach directory in `SURVEYS_DIR` is a survey. You can configure the source of your surveys by setting different `SURVEYS_DIR` env var.\n\n```bash\nsurveys/\n├── survey1/\n│   ├── metadata.yaml\n│   ├── questions.yaml\n│   ├── security.yaml\n│   ├── variables.yaml\n│   └── ...\n└── survey2/\n    ├── metadata.yaml\n    ├── questions.yaml\n    └── ...\n```\n\nTo get started, check out the `./api/surveys` folder with multiple examples.\n\n## Survey Files\n\n### metadata.yaml\n\nThis file is required! The file consists of a YAML object with specific properties describing the survey.\n\n- **title**: This is the main title displayed to users at the beginning of the survey.\n- **theme**: This specifies the visual theme applied to the survey. Currently supported themes are: default.\n- **intro**: This text appears as an introduction before the first question.\n- **outro**: This text appears as a conclusion after the last question.\n\n```yaml\ntitle: Survey Title\ntheme: default # or custom\nintro: |\n  This is the introduction to the survey.\n  It can be multiple lines long.\noutro: |\n  Thank you for taking the survey.\n  Your feedback is important to us.\n```\n\n### questions.yaml\n\nThis file is required! The file consists of a list of questions, each defined as a YAML object with specific properties.\n\n- **type**: This specifies the type of question being asked.\n- **id**: This provides a unique identifier for the question. It is useful for referencing specific questions in branching logic or data analysis. IDs must be unique across all questions in the survey.\n- **label**: This is the text displayed to the user as the question itself.\n- **description**: This provides additional information about the question for the user, such as clarification or instructions.\n- **options**: This list defines the available answer choices for question types like `single-choice`, `multiple-choice` and `ranking`.\n- **optionsFromVariable**: This property references a variable defined in a separate variables.yaml file. The variable should contain a list of options to be used for the question. This allows for reusability and centralized management of option lists.\n- **validation**: This property is used to define validation rules for specific question types.\n\n```yaml\nquestions:\n  - type: single-choice\n    id: question1 # optional ID, must be unique across all questions\n    label: What is the capital of Germany?\n    description: You can select multiple options\n    optionsFromVariable: german-city-options # defined in variables.yaml\n    options:\n      - Berlin\n      - Munich\n      - Paris\n      - London\n      - Hamburg\n      - Cologne\n    validation:\n      min: 1\n      max: 3\n```\n\n### security.yaml\n\nThis file is optional. The file consists of a YAML object with specific properties for survey security settings.\n\n- **duplicateProtection**: This property defines how the platform handles duplicate responses from the same user.\n\n```yaml\nduplicateProtection: cookie # cookie | ip\n```\n\n### variables.yaml\n\nThis file is optional. The file consists of a list of variables, each defined as a YAML object with specific properties.\n\n- **id**: This unique identifier references the variable within questions. IDs must be unique across all variables defined in the file.\n- **type**: This specifies the type of data stored in the variable. Currently supported types are: list.\n\n```yaml\nvariables:\n  - id: german-city-options # must be unique\n    type: list\n    options:\n      - Berlin\n      - Munich\n      - Hamburg\n      - Cologne\n```\n\n## Question Types\n\n### Short Text\n\nPrompts users for a brief written answer.\n\n```yaml\n- type: short-text\n  label: What is the capital of Germany?\n  # set min/max characters\n  validation:\n    min: 10\n    max: 100\n```\n\n### Long Text\n\nPrompts users for a detailed written answer.\n\n```yaml\n- type: long-text\n  label: What is the capital of Germany?\n  # set min/max characters\n  validation:\n    min: 10\n    max: 100\n```\n\n### Single Choice\n\nPresents a question with only one correct answer from a list of options.\n\n```yaml\n- type: single-choice\n  label: What is the capital of Germany?\n  options:\n    - Berlin\n    - Munich\n    - Paris\n    - London\n    - Hamburg\n    - Cologne\n```\n\n### Multiple Choice\n\nPresents a question where users can select multiple answers (with limitations). You can customize the minimum and maximum allowed selections in the validation section.\n\n```yaml\n- type: multiple-choice\n  label: Which of the following are cities in Germany?\n  description: You can select multiple options\n  validation:\n    min: 1\n    max: 3\n  options:\n    - Berlin\n    - Munich\n    - Paris\n    - London\n    - Hamburg\n    - Cologne\n```\n\n### Date\n\nAsks users to enter a specific date.\n\n```yaml\n- type: date\n  label: When was the Berlin Wall built?\n```\n\n### Rating\n\nPresents a scale for users to rate something on a predefined range.\n\n```yaml\n- type: rating\n  label: How much do you like Berlin?\n  min: 1\n  max: 5\n```\n\n### Ranking\n\nAsks users to rank options based on a given criteria.\n\n```yaml\n- type: ranking\n  label: Rank the following cities by population\n  optionsFromVariable: german-city-options\n```\n\n### Yes/No\n\nPresents a question where users can only answer \"yes\" or \"no\".\n\n```yaml\n- type: yes-no\n  label: Is Berlin the capital of Germany?\n```\n\n### Email\n\nPrompts user to enter their email\n\n```yaml\n- type: email\n  label: Please enter your email.\n```\n\n### File\n\nPrompts user to upload their file based on a given formats and maximum upload size.\n\n```yaml\n- type: file\n  label: Upload a Berlin Image\n  validation:\n    formats:\n      - .jpg\n      - .png\n    max_size_bytes: 5*1024*1024 # 5 MB\n```\n\n## Responses\n\nResponses can be shown in the UI and exported as a JSON. Alternatively you can use REST API to get survey resposnes:\n\n```bash\ncurl -XGET \\\nhttp://localhost:9900/app/surveys/{SURVEY_ID}/sessions?limit=100\u0026offset=0\u0026sort_by=created_at\u0026order=desc\n```\n\nWhere `{SURVEY_ID}` id the UUID of a given survey.\n\n## Screenshots\n\n\u003cp align=\"center\" width=\"100%\"\u003e\n\t\u003cimg src=\"https://github.com/plutov/formulosity/blob/main/screenshots/app.png\" hspace=\"10\" height=\"200px\"\u003e\n\t\u003cimg src=\"https://github.com/plutov/formulosity/blob/main/screenshots/survey.png\" hspace=\"10\" height=\"200px\"\u003e\n\u003c/p\u003e\n\n## Installation \u0026 Deployment\n\n```\ndocker-compose up -d --build\n```\n\nAnd you should be able to access the UI on [localhost:3000](http://localhost:3000) (default basic auth: `user:pass`).\n\nYou can deploy individual services to any cloud provider or self host them.\n\n- Go backend. It's packaged as a Docker container and can be deployed anywhere.\n- Next.js frontend. It's also packaged as a Docker container, but also can be deployed to Vercel for example.\n- [Optional] Postgres database. You can use managed Postgres services or deploy it yourself.\n\n### Environment Variables\n\nAPI:\n\n- `DATABASE_TYPE` - `sqlite` or `postgres`\n- `DATABASE_URL` - Postgres or SQLite connection string\n- `SURVEYS_DIR` - Directory with surveys, e.g. `/root/surveys`. It's suggested to use mounted volume for this directory.\n- `UPLOADS_DIR` - Directory for uploading files from the survey forms.\n\nUI:\n\n- `CONSOLE_API_ADDR` - Public address of the Go backend. Need to be accessible from the browser.\n- `CONSOLE_API_ADDR_INTERNAL` - Internal address of the Go backend, e.g. `http://api:8080` (could be the same as `CONSOLE_API_ADDR`).\n- `IRON_SESSION_SECRET` - Secret for session encryption\n- `HTTP_BASIC_AUTH` - Format: `user:pass` for basic auth (optional)\n\n## Tech Stack\n\n- Backend: Go, (Postgres or SQLite)\n- UI: Next.js, Tailwind CSS\n\n## Create new SQLite/Postgres migration\n\nMake sure to install [go-migrate](https://github.com/golang-migrate/migrate) first.\n\n```\ncd api\nmigrate create -dir migrations/postgres -ext sql -seq name\nmigrate create -dir migrations/sqlite -ext sql -seq name\n```\n\n## Run Go tests\n\n```\ncd api\nmake test\n```\n\n## Contributing Guidelines\n\nPull requests, bug reports, and all other forms of contribution are welcomed and highly encouraged!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplutov%2Fformulosity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplutov%2Fformulosity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplutov%2Fformulosity/lists"}