*! version 2.2.0 29jul2026

Title

surveye -- SurvEye: create an interactive HTML dashboard from a
Survey Solutions or SurveyCTO questionnaire and the corresponding Stata data

Quick start

Load one survey data file and point surveye to the questionnaire.
Three questionnaire files are accepted, detected by content rather than
extension: a Survey Solutions questionnaire preview (HTML); a SurveyCTO
form definition (XML, downloaded in SurveyCTO under Design - form
files), which is read exactly, including groups, repeats, choice lists,
translations, and calculate fields; and a SurveyCTO printable form
(HTML) - the printed Field/Question/Answer table is read directly,
including group sections, nested and repeated subgroups, choice lists, and
relevance conditions. Printables do not encode field types, so choice
fields import as select_one and open or note fields as text; notes
drop automatically when no matching data column exists. When a printable
layout is not recognized, the error points to the XML definition.
SurveyCTO split select_multiple exports (field_1, field_2, ...)
are reassembled automatically.

. use "survey_data.dta", clear

. surveye using "questionnaire.html", saving("dashboard.html") replace open

. surveye using "form_definition.xml", saving("dashboard.html") replace open

The original Survey Solutions flow is unchanged:

. use "survey_data.dta", clear

. surveye using "questionnaire.html", saving("dashboard.html") replace open

Inspect an unfamiliar questionnaire before building:

. surveye describe using "questionnaire.html", detail

Preview the questionnaire with clearly marked simulated data:

. surveye demo using "questionnaire.html", saving("preview.html") n(300) seed(42) replace open

SurvEye 2.0.0 uses the public command name surveye. Update do-files that
use the former suso_dashboard command or the interim surveydash
preview name; no legacy compatibility command is installed.

Syntax

surveye [varlist] [if] [in]
using questionnaire.html
[[aw=wvar] | [fw=wvar] |
[iw=wvar] | [pw=wvar]]
,
saving(filename) [options]

surveye build [varlist] [if] [in]
using questionnaire.html
[[aw=wvar] | [fw=wvar] |
[iw=wvar] | [pw=wvar]]
,
saving(filename) [options]

surveye describe using questionnaire.html
[, detail strict diagnostics(filename) replace]

surveye demo using questionnaire.html,
saving(filename)
[n(#) seed(#) maxpanels(#) ci level(#)
replace open presentation_options]

The main command uses the dataset in memory. Stata 16 or newer is required.
Use Stata's native weight syntax after the using filename and before the
comma, for example
[aw=wmedian], [fw=frequency], [iw=importance], or
[pw=pop]. There is no weight() option. The optional build
word is equivalent to the main form. It is useful when the first selected data
variable is named build, describe, or demo. Repeat that
variable after the subcommand; for example,
surveye build build sales using "questionnaire.html", saving("results.html").

Options for the main command

Output

saving(filename) is required and names the HTML dashboard. The
.html extension is added if omitted.

replace permits an existing dashboard, diagnostics file, or other engine
output to be replaced.

open opens the completed dashboard in the system browser. The file is
still saved if Stata cannot open the browser.

diagnostics(filename) writes a technical log for troubleshooting.

Questionnaire selection

varlist selects ordinary Stata variables to chart. If both varlist
and questions() are omitted, the command considers all chartable
questionnaire items present in the data. Variables used only for filters,
highlights, weights, or maps are added to the temporary export automatically
and are not charted unless selected. Variables used only by the optional
profile table or local-currency/USD switch are handled the same way.

questions(question_names) selects logical Survey Solutions question
names. Separate names with spaces or commas and quote the list, for example
questions("services_used certifications_used"). This option is useful
when a multiselect question q is stored as q__1, q__2, and so
on, with no variable named q. Do not use wildcards. varlist and
questions() may be combined; duplicate selections are removed. Names
shown by surveye describe ..., detail are suitable inputs.

exclude(names) omits questionnaire items. It accepts Stata varlists and
logical Survey Solutions question names. A name may not be both selected or
chart-overridden and excluded.

sections(numlist) keeps questionnaire sections by number. Use
surveye describe ..., detail to see the section numbers.

sectionmatch(text) keeps sections whose title contains any term in a
pipe-separated list, such as sectionmatch("profile|employment"). It may
not be combined with sections().

customsections(spec) reorganizes all selected charts. Separate groups
with | and write each group as Title: varlist. Unassigned charts
are placed in Other indicators. Example:
customsections("Firm profile: sector size|Performance: sales employment").
It may not be combined with addtosections().

showempty keeps an explicitly selected questionnaire item even when no
matching nonempty data column is found. It is mainly useful for diagnostics
and templates.

strict stops on undeclared selected variables and promotes questionnaire
parser warnings to errors. Variables deliberately declared in
customvars() are allowed under strict.

maxpanels(#) limits indicator charts while preserving questionnaire
order. The default is 100; maxpanels(0) removes the limit. Allowed
values are 0 through 5,000. Explicit custom variables are processed first so
the cap does not silently place them behind a long questionnaire. The command
stops if customvars() alone contains more variables than the cap.

Variables not in the questionnaire

customvars(varlist) adds constructed indicators, quality-control fields,
administrative classifications, or other variables in memory even when they
do not exist in the questionnaire. It is additive: normal questionnaire
selection still applies, and the custom variables are added to it.

The variable label becomes the chart title by default; the variable name is
used when the label is empty. Generic Survey Solutions labels such as
Calculated variable of type String also fall back to the variable name.
Numeric variables receive distributions.
Variables formatted as %tc, %td, %tw, %tm, %tq,
%th, or %ty receive date charts. Strings receive categorical
charts, and numeric variables with value labels receive categorical charts.
Observed Stata value-label text is retained when the number of levels does not
exceed maxcategories(). Custom variables not otherwise placed appear in
Additional indicators.

Business-calendar %tb values are not currently interpreted as dates
because their calendar rules are dataset-specific. Convert or export them to
one of the supported date representations before including them in
customvars().

addtosections(spec) places variables declared in customvars() in
any selected section. Separate assignments with |. The target before
the colon may be an exact section title (ignoring case), a number, or a new
text title. A number first matches the questionnaire's original section
number; when that number is not selected, it matches the visible dashboard
position (1, 2, 3, and so on). A new text title creates a new section; an
unmatched numeric target is an error. Examples:

addtosections("Firm profile: qc_score risk_band")

addtosections("3: qc_score|Quality checks: risk_band")

Only variables listed in customvars() may be moved. Unmentioned custom
variables remain in Additional indicators. addtosections() may not
be combined with customsections().

Related-variable families

SurvEye automatically combines high-confidence families of related binary
variables into one compact comparison figure. For example,
srib8a, srib8b, and srib8c can be displayed as three labelled
rows in a single card. Automatic grouping is conservative: members must have
a recognizable letter suffix, compatible response categories, and the same
final dashboard section. Survey Solutions multiselect storage columns such as
q__1 and q__2 are never treated as a suffix family.

vargroups(spec) defines groups manually. Separate groups with |
and write each as Title:: varlist. The double colon separates the
displayed title from an ordinary Stata varlist, so ranges and wildcards are
expanded by Stata. Every group requires at least two variables, and a
variable may occur in only one manual group. Members must already be selected
by the main varlist, questions(), or customvars(). Example:

vargroups("Digital channels:: srib8a srib8b srib8c|Business support:: srib9a srib9b srib9c")

Questionnaire text supplies each row label. For a custom variable, its Stata
variable label is used, with the variable name as the fallback. Manual groups
take precedence over automatic grouping and inherit the final section shared
by their members. Use customsections() or addtosections() first
when custom variables need to be placed together.

ungroupvars(varlist) keeps named variables as separate cards while
leaving automatic grouping available elsewhere. Use noautogroups to
turn off all automatically detected families. Explicit vargroups()
remain active with noautogroups. A variable may not appear in both
vargroups() and ungroupvars().

Explicit binary comparisons

compare(varlist) combines up to 12 selected binary variables in one
grouped horizontal-bar comparison. Each bar reports the affirmative response
share, and compareby(varname) supplies the comparison groups. Both
options are required together. The compareby() variable is exported
for the comparison but is not added as an indicator card unless it is also
selected normally. Example:

compare(srib8a srib8b srib8c) compareby(city)

comparetitle(text) replaces the automatically generated comparison
title.

comparelevels(levels) limits and orders the displayed values of
compareby(). Separate exact raw values or displayed value labels with
|; for example,
comparelevels("01_Colombo|02_Kandy|03_Jaffna"). Without this option,
valid observed comparison levels are used in their natural order.

Comparison variables must be selected and must have a recognizable binary
affirmative response. Missing values, special response codes, [if] [in], and
weights follow the same rules as the corresponding binary cards. Confidence
interval whiskers are never drawn on these binary comparison bars, including
when ci is specified.

Controls, messages, and charts

filters(varlist) creates dashboard filters from low-cardinality
variables. If omitted, the engine may suggest up to three conservative
filters. Use this option for required controls, particularly with translated
questionnaires. A requested filter with more than maxcategories()
levels is rejected.

Choices come only from observed valid values; codes named by
missingcodes() are never offered. Numeric choices are compared by
numeric value, multiselect choices match when any selected option is present,
and privacy-reduced text or media questions offer localized
Answered/Missing choices. An explicitly requested categorical
filter with no observed valid values stops with an explanatory error instead
of showing controls that can only produce zero results.

The sticky search/filter panel is collapsed by default so it does not cover the
figures. Select Show filters to reveal its contents and Hide filters
to return to the compact toolbar. Collapsing the panel preserves the search
text and selected choices. The toolbar keeps Reset all, the live interview
count, and—when applicable—the number of active filter choices visible.
Reset all clears both the response filters and the indicator search.

highlights(varlist) creates up to six summary cards.

keymessages(text) adds up to six editorial messages. Separate messages
with |. An optional heading precedes ::; for example,
keymessages("Coverage::Completed interviews|Caution::Weighted estimates").

Automatic chart selection uses horizontal percentage bars for categorical and
multiselect questions, split bars for binary and answered/missing items,
histograms for numeric variables, and time distributions for dates. Donuts
are not selected automatically.

bars(varlist), donuts(varlist), and histograms(varlist)
override compatible automatic chart choices. A variable may appear in only
one override. With a main varlist or questions() selection, an
override must also be selected there or in customvars().

Small integer-valued numeric variables are recognized automatically as
discrete counts when their question text and observed support strongly look
like counts, such as number of workers or household members. Detection is
conservative and uses the actual analysis sample after [if] [in], weights, and
missing-code exclusions. Readable ranges display one bar for every integer,
including zero-frequency gaps. Wider ranges automatically use equal-width,
integer-aligned bins. A numeric x axis supplies regular round-number ticks.
Negative questionnaire response codes are excluded. Every valid measurement
remains in the chart and summary. A dotted mean-plus-three-standard-deviations
guide is drawn only when it falls strictly inside the plotted range. The
numeric Stats tab remains available and updates immediately with filters.

discrete(varlist) forces numeric counts, bounded scores, or numeric
category codes to use this smart integer distribution. It preserves exact
values when readable and bins only a genuinely wide integer range.
continuous(varlist) forces numeric variables to use a continuous
histogram and numeric Stats table, even when all observed values are integers
or a Stata value label is attached. Both options require numeric, non-date
variables that are also selected for the dashboard. Examples:

discrete(employees visits) continuous(revenue productivity_score)

noautodiscrete disables only automatic discrete-count detection.
Explicit discrete(), continuous(), and chart overrides still
apply. histograms() explicitly requests continuous behavior and may be
combined redundantly with continuous(); it conflicts with
discrete(). bars() and donuts() may accompany
discrete() but conflict with continuous().

maxcategories(#) sets the maximum categories for filters and categorical
figures. The default is 12; allowed values are 2 through 200. Compact mode
shows at most seven displayed levels. Less frequent levels are combined as
Other, while percentages continue to use the full valid denominator.
This option does not limit discrete() or continuous numeric distributions.

missingcodes(numlist) explicitly excludes extra numeric nonresponse or
sentinel codes from chart denominators and numeric/date calculations, for
example missingcodes(-999 -998 999). Questionnaire-defined special
responses remain visible and muted in categorical figures. Negative special
codes are automatically excluded from numeric distributions, statistics,
and numeric filters because response codes such as
-4 and -9 are not measurements. Nonnegative substantive
shortcuts and legitimate negative-valued questions remain valid. Use
missingcodes() for additional undeclared sentinels.

Weights

Weights use ordinary Stata command syntax; they are not options. Place one
single-variable weight specification after the using filename and before
the comma:

. surveye sector sales using "questionnaire.html" [aw=wmedian], saving("analytic.html") replace

. surveye sector sales using "questionnaire.html" [fw=frequency], saving("frequency.html") replace

. surveye sector sales using "questionnaire.html" [iw=importance], saving("importance.html") replace

. surveye sector sales using "questionnaire.html" [pw=pop], saving("population.html") replace

The weight must resolve to one numeric variable. Negative values are rejected.
Frequency weights must contain integers. Observations with zero or missing
weights are excluded for all four types, so every retained weight is positive.
The command stops if no observations remain after if, in, and
weight restrictions. A weight-only variable is exported for calculation but
is not charted unless selected. See weight.

Weights are applied to shares, histograms, means, medians, quantiles, standard
deviations, and numeric Stats tables. They do not replace svyset, and
surveye does not estimate design-based standard errors. When ci
is supplied, frequency-weighted Wilson intervals use the weighted count.
Analytic- and probability-weighted intervals use a labelled Kish effective-
sample-size approximation;
they do not account for strata, primary sampling units, finite-population
corrections, or other complex-design features. Importance weights have no
general sampling interpretation, so surveye automatically suppresses a
requested interval for iweight and displays an explanatory note.

Supplying any supported weight automatically adds a Weighted estimates
switch to the dashboard. It is on initially. Turning it off recalculates
charts, comparisons, numeric summaries, and the optional profile table with
one unit per row; turning it on restores the supplied weight. Raw sample
counts remain raw in either mode. Both modes use the same exported analysis
sample, so the switch does not restore observations removed by if,
in, or invalid, zero, or missing weights when the file was built.

Local currency and USD

usdvars(varlist) identifies numeric, non-date monetary variables that
readers may display in local currency or US dollars. It requires
usdrate().

usdrate(#) gives the fixed number of local-currency units per US dollar.
For example, with usdrate(300), a stored local value of 30,000 is shown
as USD 100 when the USD switch is on. The rate must be positive.

currency(code) gives the local-currency code printed with converted
variables, for example currency(LKR), currency(KES), or
currency(PKR). When it is omitted, the label is Local currency.

The dashboard starts in local currency. Its USD switch updates charts,
numeric Stats, and mean, median, or sum cells in the optional
profile table for variables named in usdvars(). It does not change the
embedded source values. SurvEye does not retrieve a current or historical
exchange rate; choose a rate appropriate to the survey reference period and
document it with note() or source().

. surveye sales costs using "questionnaire.html", saving("financials.html") usdvars(sales costs) usdrate(300) currency(LKR) replace open

Side-by-side profile table

tableby(varname) supplies the grouping variable whose displayed levels
become table rows. tablevars(varlist) supplies the indicator columns.
The two options are required together. Variables used only in the table are
exported automatically and do not become chart cards unless selected normally.
The responsive table appears near the top of the dashboard, before the
detailed chart sections.

tablestats(spec) chooses each indicator's statistic. Give one entry per
tablevars() variable, in the same order, separated by |. Allowed
entries are auto, share, share:code, mean,
median, and sum. auto uses the median for numeric
distribution variables and an affirmative share for other compatible
variables. share also uses a recognized affirmative category;
share:code identifies the intended raw response code explicitly.
mean, median, and sum require numeric variables. Omitting
tablestats() uses auto for every column.

tablelabels(labels) overrides the indicator column headings. Supply one
label per tablevars() variable, in the same order, separated by |.
Without this option, SurvEye uses each variable's available label from the
questionnaire or Stata metadata; the variable name is the fallback when that
label is empty.

tabletitle(text) supplies the profile-table title. The default is
Summary table.

tablesubtitle(text) supplies a short explanation immediately below the
table title.

tabletotal(text) changes the label of the all-group reference row, for
example tabletotal("Sri Lanka (all surveyed locations)"). Every profile
table includes that reference; its default label is
All filtered interviews. The table ignores only its own
tableby() dashboard filter,
so every grouping row and the reference remain visible for comparison;
selected grouping rows may be highlighted. Every other active filter is
honored. The reference, table rows, and cells also follow the current weight
and local-currency/USD switches.

tableweightlabel(text) changes the weighted-total column heading, for
example tableweightlabel("Est. firms"). Raw n is always shown.
The weighted total is the sum of the active Stata weights. Interpret it
according to the supplied weight type; only an appropriate expansion weight
should be described as an estimated population count. The column appears
only when a Stata weight was supplied and Weighted estimates is on; it is
hidden in unweighted mode. Its default heading is Weighted total.

Table shares, means, medians, sums, raw counts, and conditional weighted totals
recalculate immediately with the dashboard controls. Use a concise set of
decision-relevant columns so the table remains readable on smaller screens.

. surveye women_led sales banked digital_channel using "questionnaire.html"
[pw=pop], saving("profile.html") filters(stratum owner_type) usdvars(sales)
usdrate(300) currency(LKR) tableby(stratum)
tablevars(women_led sales banked digital_channel)
tablestats("share:1|median|share:1|share:1")
tablelabels("Women-led|Median sales|Banked|Digital channel")
tabletitle("Stratum profile") tablesubtitle("Key indicators side by side")
tabletotal("Sri Lanka (all surveyed locations)") tableweightlabel("Est. firms")
replace open

Confidence intervals and numeric statistics

Confidence intervals are off by default. This keeps compact dashboards easy
to scan and avoids inferential marks that can be mistaken for ranges covering
part of a binary stacked bar.

ci requests pointwise Wilson confidence intervals for ordinary
categorical and multiselect horizontal bars. Binary yes/no cards,
answered/missing completion cards, and donuts never display confidence-
interval text or whiskers, even when ci is supplied.

level(#) sets the requested interval's confidence level. It requires
ci; the default with ci is 95, and the value must be greater than
50 and less than 100.

Unweighted requested intervals use the valid raw count. Frequency-weighted
intervals use the weighted count. Analytic- and probability-weighted intervals use a
Kish effective-sample-size approximation. They are descriptive pointwise
intervals, not design-based survey estimates, and do not adjust for strata,
clusters, finite-population corrections, or other complex-design features.
Importance weights automatically suppress requested intervals because they
have no general sampling interpretation.

Each numeric card has Distribution and Stats tabs. The distribution
includes every valid measurement on a regular numeric axis. A dotted
Mean + 3 SD reference is shown only when the standard deviation is positive
and the reference lies strictly inside the plotted range. The Stats tab
reports valid raw n, missing/excluded n, mean, standard deviation, minimum,
maximum, p25, median, p75, and the mean-plus-three-standard-deviations
reference. It recalculates immediately whenever a dashboard filter changes,
including while the Stats tab is open. Weighted dashboards identify these
statistics as descriptive weighted summaries and report the valid weighted
sum. SurvEye does not classify or label observations as Tukey outliers.

GPS map

latitude(varname) and longitude(varname) request a Leaflet map.
Both variables must be numeric and both options must be supplied.

country(name_or_code) is required for a GPS map. Use a country name,
ISO-2 code, or ISO-3 code, such as country(Kenya) or country(KEN).

boundaries(filename) supplies a ZIP with a polygon shapefile
(.shp, .dbf, and preferably .prj) for detailed boundaries.
Coordinates and boundaries must use WGS84 longitude/latitude. Without this
option, a bundled country outline is used. Country and Admin-2 lines are drawn
as WGS84 Leaflet rings aligned with the points and tiles; antimeridian
geometries are normalized to one longitude branch.

maplevel(country|admin2) selects boundary detail. The default is
country without boundaries() and admin2 with a boundary ZIP.
admin2 requires a compatible ZIP.

maptype(points|cluster|heat) selects the point display. The default is
points: every valid row receives its own circle marker and nearby points
are not collapsed. Exact duplicate coordinates are separated slightly on
screen so every marker remains selectable; popups show the original coordinate.
Each point can receive keyboard focus and opens its localized popup with Enter
or Space.
cluster and heat are explicit aggregated alternatives.

basemap(google_hybrid|google_sat|google_road|osm) chooses the initial
Leaflet base layer. The default is google_hybrid. The options follow
esqc_gps: Google Hybrid uses satellite imagery and labels,
google_sat uses satellite imagery, google_road uses the road map,
and osm uses OpenStreetMap. The map layer control can switch among all
four after the file opens.

Leaflet, survey points, and boundary geometry are embedded in the dashboard.
Base-map tiles are fetched when the map opens, so a map-enabled dashboard needs
internet access to the selected provider. The esqc_gps-compatible Google
choices use keyless compatibility tile endpoints; they are not an authenticated
Google Maps Platform integration, and their availability or access policy can
change. Before distributing a dashboard, verify that the selected provider is
approved for the intended use and follow its current terms and attribution
rules. Tile requests contain map coordinates, not other survey variables.

mapby(varname) colors or groups the map by a variable with at most ten
observed categories after missingcodes() exclusions. A point whose map
group is an excluded code remains visible in the default point color but is
not added to the legend.

maptitle(text) replaces the default map title.

Presentation

uilanguage(auto|english|arabic|urdu) selects the language of interface
controls, summaries, map text, chart descriptions, and empty-state messages. Technical
parser warnings, diagnostics, and Stata console messages remain in English. The
default is auto. The aliases en, ar, and ur are
accepted. Auto gives priority to a questionnaire declaration of Arabic
(ar) or Urdu (ur). Otherwise, predominantly Arabic-script
questionnaire text selects Arabic, with Urdu-specific characters selecting
Urdu; incidental Arabic text in an otherwise non-Arabic instrument does not
change the interface. Other text selects English. Questionnaire wording and user-supplied
titles, notes, sources, and messages are never translated.

direction(auto|ltr|rtl) selects document and component direction. The
default is auto, which follows the resolved interface language: Arabic
and Urdu use rtl, while English uses ltr. An explicit rtl or
ltr overrides layout direction but does not translate text. Thus,
uilanguage(english) direction(auto) stays left-to-right even for an
Arabic questionnaire. The output
sets matching HTML lang and dir attributes for browsers and
assistive technology. RTL mode mirrors navigation, controls, tables,
horizontal chart axes and direct labels, and Leaflet controls; it does not
merely align the surrounding text.

title(text) and subtitle(text) set the dashboard heading. The
questionnaire title is used by default. A pair of asterisks marks emphasis:
title("The shape of *informality*") renders the marked words as a
cyan accent in the masthead, and marked words in subtitle() render in
bold. Single asterisks pass through unchanged, and the marks never reach the
browser tab title.

byline(text) signs the masthead with up to four parts separated by
| in the order label, name, role, and email:
byline("Task Team Leader|A. Rehman|Economist|arehman@worldbank.org").
Any part may be left empty and the email becomes a mailto link. The byline
follows World Bank task-attribution practice and prints with the dashboard.

theme(editorial|worldbank|clean|forest|dark) selects the overall finish.
The default is editorial: a warm paper canvas, subtle cyan/gold glow,
World Bank role colors, serif heading hierarchy, rounded cards, shallow shadows,
and reduced-motion-safe subtle entrance transitions. The preset is entirely
survey-agnostic and does not change questions, calculations, filters, or chart
selection. Explicit worldbank, clean, forest, and dark
retain their earlier appearance unless an individual layer below is overridden.

background(auto|glow|paper|plain) controls the page canvas. auto
leaves the selected legacy theme unchanged. The editorial default is
glow; it places restrained cyan and gold radial light over warm paper.

typography(auto|editorial|modern|system) controls heading hierarchy.
editorial uses a serif display stack for headings and key figures while
retaining a highly legible sans-serif body. No web-font request is made.

corners(auto|rounded|soft|square) and
shadow(auto|soft|lifted|none) control card finishing independently of the
color theme.

motion(none|subtle|reveal) controls entrance motion. Both animated modes
use an intersection observer so sections animate only when first visible and
honor the browser's reduced-motion preference. The editorial default is
subtle; legacy themes default to none.

pagewidth(#) sets the maximum content width in pixels. Use 0 to leave a
legacy theme unchanged, or a value from 960 through 2,000. The editorial
default is 1,280 pixels.

density(compact|comfortable) controls spacing. The default,
compact, uses balanced rows of up to three panels, short charts, focused
labels, and a collapsed map summary. Incomplete rows are repacked as equal
pairs or a full-width single panel, so card edges remain aligned without adding
page height. comfortable uses a roomier two-column grid,
larger figures, and an initially open map. Meanings and denominators do not
change.

logo(filename) embeds a local PNG, JPEG, GIF, or SVG logo.

note(text), source(text), and disclaimer(text) add context.
Document the universe, reference period, weighting, source, and disclosure
limitations. Without disclaimer(), a conservative internal-working and
disclosure-review notice is shown.

Questionnaire inspection

surveye describe reads only the questionnaire and does not change
the dataset in memory. detail displays numbered sections, chartable
variable names, possible filters, and possible GPS fields. strict
promotes parser warnings to errors. This is the fastest way to inspect an
unfamiliar, translated, legacy, or current Survey Solutions HTML file.

Simulated preview

surveye demo builds a dashboard without a dataset. Every preview
is visibly marked SIMULATED -- PREVIEW ONLY. n(#) sets the number
of simulated records (default 240; maximum 100,000), and seed(#) makes
the simulation reproducible. maxpanels(#), ci, and level(#)
work as described above. Presentation options are
uilanguage(), direction(), title(), subtitle(),
logo(), theme(), density(), background(),
typography(), corners(), shadow(), motion(),
pagewidth(), note(), source(), disclaimer(), and
diagnostics().

Data rules and privacy

The command works with one rectangular data file at a time. Survey Solutions
rosters are exported separately; build one dashboard per roster level or merge
a carefully defined roster summary into the parent file. An undefined join
can change denominators.

The dataset in memory is preserved. Selected columns are sent as a temporary
UTF-8 raw-code CSV so questionnaire category text and order remain authoritative.
For customvars(), the wrapper deliberately sends the Stata variable label,
type/format, and applicable observed value-label text.

The HTML embeds selected analysis-level values needed for filtering and charting.
Text, picture, audio, linked text-list, and questionnaire-GPS completion items
are reduced to answered/not-answered flags instead of embedding original
contents. A map embeds valid latitude and longitude so Leaflet can draw and
filter the display: six decimal places for points, four for
cluster, and three for heat. Even reduced precision can identify
respondents or establishments. Protect the HTML like the source data, review
disclosure risk, and do not publish row-level maps without authorization.
cluster and heat reduce precision and aggregate the display, but
they are not a substitute for a formal disclosure-control method. The command
does not upload data.

Reader tools inside the dashboard

Every dashboard built by version 2.2.0 or later includes reader-facing tools
that require no additional options and add no runtime network dependency:

Theme switch. A control in the sticky top bar switches between
the built theme and the dark theme. Charts, the profile table, and map
point colors re-skin immediately, and the choice is remembered across
reopenings when browser storage is available.

Copy view link. The control bar copies a link that reopens the
dashboard with the same filters, weighted/unweighted setting, currency
setting, and search query — convenient when circulating a subgroup view.

Download data (CSV). Exports the currently filtered interviews.
Categorical codes are written with their questionnaire labels, multi-select
answers are joined with semicolons, and the weight column is appended for
weighted dashboards. Only values already embedded in the file are exported,
so the button adds no new disclosure surface; the privacy guidance above
applies unchanged.

Customize any chart. The gear control on each card opens a
small editor: rewrite the chart title, switch between bars and a donut
where the two displays are statistically equivalent (single-select and
yes/no cards), split the chart by any filter variable (Compare by:
categorical indicators become one composition bar per group and numeric
indicators the median per group, honoring weights and the currency
switch), choose an accent color, scale the chart fonts, and show or hide
value labels, and pick a chart size (Compact to Extra tall) - the
chart area also grows automatically when a choice needs room, for example
a donut or a compare-by split on a slim yes/no card. The accent recolors
the primary series of every chart family, including the affirmative side
of yes/no cards and their splits. Changes are presentation-only - no
estimate moves - apply immediately, persist for that dashboard in the
reader's browser, and reset per chart.

Download chart (PNG). Each card downloads a presentation-ready
image at double resolution: the full wrapped question title, a line with the
variable name and the live filtered statistic, the chart as currently
customized, the yes/no headline percentage and legend where applicable, and
a source footer with the dashboard title and date. Category labels are
written out in full: donut downloads carry a complete legend below the
chart, and bar downloads append any label the axis had to shorten. The
image follows the active light or dark theme.

Navigation aids. Expand all and Collapse all in the
section navigation open or close every section; a floating button returns to
the top; a progress bar under the top bar shows reading position; pressing
/ focuses the indicator search and Esc clears it.

Examples

1. Inspect a questionnaire

. surveye describe using "English TRG_2025.html", detail

2. Build a complete compact dashboard

. use "TRG_2025.dta", clear

. surveye using "English TRG_2025.html", saving("TRG_dashboard.html") replace open

3. Select indicators and observations

. surveye legalstatus employment sales if consent==1 using "questionnaire.html", saving("results.html") filters(region sector) highlights(employment sales) title("Enterprise survey results") replace

4. Add a logical expanded multiselect

. surveye sales employment using "questionnaire.html", saving("services.html") questions("services_used") filters(region) replace

5. Add custom data variables with their Stata labels

. label variable qc_score "Enumerator quality score"

. surveye using "questionnaire.html", saving("quality.html") customvars(qc_score risk_band) addtosections("Interview quality: qc_score risk_band") histograms(qc_score) bars(risk_band) replace open

6. Put custom variables in existing and new sections

. surveye sales using "questionnaire.html", saving("focused.html") customvars(qc_score risk_band) addtosections("3: qc_score|Quality checks: risk_band") replace

7. Combine related suffix variables in one figure

. surveye srib8a srib8b srib8c using "questionnaire.html", saving("digital.html") vargroups("Digital channels:: srib8a srib8b srib8c") replace

8. Compare affirmative shares across locations

. surveye srib8a srib8b srib8c using "questionnaire.html", saving("digital_by_city.html") compare(srib8a srib8b srib8c) compareby(city) comparelevels("01_Colombo|02_Kandy|03_Jaffna") comparetitle("Digital access by city") replace

9. Show integer distributions or force a continuous histogram

. surveye employees visits revenue using "questionnaire.html", saving("numeric.html") discrete(employees visits) continuous(revenue) replace

10. Keep figures clean or request confidence intervals

. surveye sector ownership using "questionnaire.html", saving("shares_clean.html") replace

. surveye sector ownership using "questionnaire.html", saving("shares_90.html") ci level(90) replace

11. Apply native Stata weights

. surveye sector sales using "questionnaire.html" [aw=wmedian], saving("analytic.html") replace

. surveye sector sales using "questionnaire.html" [fw=frequency], saving("frequency.html") replace

. surveye sector sales using "questionnaire.html" [iw=importance], saving("importance.html") note("Descriptive importance weights.") replace

. surveye sector sales using "questionnaire.html" [pw=pop], saving("weighted.html") filters(region) note("Intervals use an effective-sample-size approximation.") replace

Each weighted dashboard starts with Weighted estimates on. Readers may
turn it off to recalculate the displayed results without weights.

12. Add a local-currency/USD switch

. surveye sales costs profit using "questionnaire.html", saving("financials.html") usdvars(sales costs profit) usdrate(300) currency(LKR) note("USD values use 300 LKR per USD.") replace open

13. Add a live stratum profile table

. surveye women_led sales banked digital_channel using "questionnaire.html"
[pw=pop], saving("profile.html") filters(stratum owner_type) usdvars(sales)
usdrate(300) currency(LKR) tableby(stratum)
tablevars(women_led sales banked digital_channel)
tablestats("share:1|median|share:1|share:1")
tablelabels("Women-led|Median sales|Banked|Digital channel")
tabletitle("Stratum profile") tablesubtitle("Key indicators side by side")
tabletotal("Sri Lanka (all surveyed locations)") tableweightlabel("Est. firms")
replace open

14. Build an Arabic right-to-left dashboard

. surveye using "questionnaire_ar.html", saving("dashboard_ar.html") uilanguage(ar) direction(auto) replace open

15. Build an Urdu right-to-left dashboard

. surveye using "questionnaire_ur.html", saving("dashboard_ur.html") uilanguage(urdu) direction(rtl) replace open

16. Show every GPS record over Google Hybrid

. surveye sector sales using "questionnaire.html", saving("mapped.html")
latitude(gps_latitude) longitude(gps_longitude) country(KEN)
boundaries("World Bank Official Boundaries - Admin 2.zip") maplevel(admin2)
maptype(points) basemap(google_hybrid) mapby(sector)
maptitle("Interview locations") replace open

17. Use OpenStreetMap or an aggregated point display

. surveye sector using "questionnaire.html", saving("clustered.html") latitude(gps_latitude) longitude(gps_longitude) country(KEN) maptype(cluster) basemap(osm) replace

18. Preview a translated questionnaire

. surveye demo using "siNhl Global_informal2026.html", saving("sinhala_preview.html") n(250) seed(42) theme(clean) replace open

Stored results

On success, surveye stores the following in r() when supplied
by the engine:

r(success) 1

r(message) engine message

r(title) dashboard or questionnaire title

r(N) observations in the dashboard

r(sample_N) observations exported after restrictions

r(k_charted) charted indicators

r(k_skipped) skipped indicators

r(k_sections) dashboard sections

r(k_filters) filters

r(k_questions) parsed questions in describe mode

r(chartvars) charted variable names

r(skippedvars) skipped variable names

r(filters) filter variable names

r(sections) section identifiers or, in describe, numbered titles

r(warnings) engine warning count

r(weighted) 1 if weighted, 0 otherwise

r(has_map) 1 if a map was created

r(map_N) valid mapped observations

r(map_missing) missing or out-of-range coordinate pairs

r(map_outside) valid points outside the selected boundary

r(engine_version) Java engine version

r(output) dashboard filename

r(filename) alias for r(output)

r(questionnaire) questionnaire filename

r(filter_candidates) possible filters from describe

r(gps_candidates) possible GPS fields from describe

r(package_version) Stata package version

Troubleshooting

If the Java engine is missing or Stata reports r(5100), verify the package:

. which surveye

. findfile surveye_2_2_0.jar

. java query

Reinstall the complete package if the wrapper and release-specific JAR do not
resolve together, then type discard or restart Stata. The command prints
the JAR path and preserves Stata's Java diagnostic when the engine cannot load.

If parsing or rendering fails, rerun with diagnostics(filename) and first
try surveye describe ..., detail. A file with no recognizable
Survey Solutions sections or variables is rejected rather than producing an
empty dashboard.

If a map shows points and boundary lines but no background imagery, check the
internet connection, firewall, and access to the selected tile provider. Try
basemap(osm) to distinguish a provider-specific problem. The rest of the
dashboard remains usable when tiles are unavailable.

Author

Attique Ur Rehman, Economist

The World Bank - Development Economics (DEC), Enterprise Surveys

Email: attique@worldbank.org

Web: https://sites.google.com/view/attique-ur-rehman

Acknowledgments

Thanks to Fahad Mirza (World Bank / CERP,
github.com/fahad-mirza) for his insights
and guidance, and for his self-contained Stata tooling (sparkta,
wordcloud2) that helped shape this package's design.

Also see

Online: Survey Solutions API documentation

Help: suso, javacall, import, shell