Refactored polls module into layered architecture with typed domain objects, isolated SSE handling, and comprehensive tests.
CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 4s
CI / Tests (Python 3.12) (push) Successful in 2m54s
CI / Tests (Python 3.13) (push) Successful in 2m54s
CI / Tests (Python 3.14) (push) Successful in 2m52s
CI / Type Checking (push) Successful in 9s
CI / Spelling (push) Successful in 5s
CI / Formatting (push) Successful in 5s
CI / Linting (push) Successful in 4s
CI / Tests (Python 3.12) (push) Successful in 2m54s
CI / Tests (Python 3.13) (push) Successful in 2m54s
CI / Tests (Python 3.14) (push) Successful in 2m52s
CI / Type Checking (push) Successful in 9s
CI / Spelling (push) Successful in 5s
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
{% for opt in form.options %}
|
||||
<div class="input-group mb-2 option-row">
|
||||
<span class="input-group-text option-number">{{ loop.index }}</span>
|
||||
<input type="text" class="form-control option-input" name="option_{{ loop.index }}" required maxlength="{{ constraints.max_option_length }}" placeholder="Option {{ loop.index }}" value="{{ opt }}">
|
||||
<input type="text" class="form-control option-input" name="options" required maxlength="{{ constraints.max_option_length }}" placeholder="Option {{ loop.index }}" value="{{ opt }}">
|
||||
{% if loop.index > 2 %}
|
||||
<button type="button" class="btn btn-outline-danger remove-option">Remove</button>
|
||||
{% endif %}
|
||||
@@ -52,20 +52,22 @@
|
||||
|
||||
<div class="mb-3 ms-4 {{ '' if form.allow_multiple else 'd-none' }} d-flex align-items-center gap-2 flex-wrap" id="multi-select-options">
|
||||
<span>Voters must select between</span>
|
||||
<input type="number" class="form-control form-control-sm" style="width: 5rem" id="min_selections" name="min_selections" min="1" placeholder="1" value="{{ form.min_selections }}">
|
||||
<input type="number" class="form-control form-control-sm" style="width: 5rem" id="min_selections" name="min_selections" min="1" placeholder="1" value="{{ form.min_selections }}" {% if not form.allow_multiple %}disabled{% endif %}>
|
||||
<span>and</span>
|
||||
<input type="number" class="form-control form-control-sm" style="width: 5rem" id="max_selections" name="max_selections" min="1" value="{{ form.max_selections }}">
|
||||
<input type="number" class="form-control form-control-sm" style="width: 5rem" id="max_selections" name="max_selections" min="1" value="{{ form.max_selections }}" {% if not form.allow_multiple %}disabled{% endif %}>
|
||||
<span>options.</span>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="options" id="options-hidden">
|
||||
<button type="submit" class="btn btn-primary">Create Poll</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>window.CREATE_CONFIG = {
|
||||
maxOptions: {{ constraints.max_options | tojson }},
|
||||
maxOptionLength: {{ constraints.max_option_length | tojson }}
|
||||
};</script>
|
||||
<script src="{{ create_js_url }}?v={{ owlbot_version }}"></script>
|
||||
<script id="create-config" type="application/json">
|
||||
{{ {
|
||||
"minOptions": constraints.min_options,
|
||||
"maxOptions": constraints.max_options,
|
||||
"maxOptionLength": constraints.max_option_length
|
||||
} | tojson }}
|
||||
</script>
|
||||
<script type="module" src="{{ create_js_url }}?v={{ owlbot_version }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,15 +9,40 @@
|
||||
<p class="text-body-secondary mb-1">Time remaining: <strong id="timer">--:--</strong></p>
|
||||
{% if poll.hidden %}
|
||||
{% if viewer_is_moderator %}
|
||||
<p class="text-body-secondary mb-0">Results are hidden until this poll ends. As a moderator, you can still see the real-time results.</p>
|
||||
<p class="text-body-secondary mb-{{ 1 if poll.allow_multiple else 0 }}">Results are hidden until this poll ends. As a moderator, you can still see the real-time results.</p>
|
||||
{% else %}
|
||||
<p class="text-body-secondary mb-0">Results are hidden until this poll ends.</p>
|
||||
<p class="text-body-secondary mb-{{ 1 if poll.allow_multiple else 0 }}">Results are hidden until this poll ends.</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="text-body-secondary mb-0">Results are updated in real time.</p>
|
||||
<p class="text-body-secondary mb-{{ 1 if poll.allow_multiple else 0 }}">Results are updated in real time.</p>
|
||||
{% endif %}
|
||||
{% if poll.allow_multiple %}
|
||||
{% set min_count = poll.min_selections or 1 %}
|
||||
{% set max_count = poll.max_selections %}
|
||||
{% set min_label = min_count ~ " option" ~ ("s" if min_count != 1 else "") %}
|
||||
<p class="text-body-secondary mb-0">
|
||||
{% if max_count is none %}
|
||||
{% if min_count == 1 %}
|
||||
Select one or more options.
|
||||
{% else %}
|
||||
Select at least {{ min_label }}.
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set max_label = max_count ~ " option" ~ ("s" if max_count != 1 else "") %}
|
||||
{% if min_count == max_count %}
|
||||
Select exactly {{ min_label }}.
|
||||
{% else %}
|
||||
Select {{ min_count }} to {{ max_label }}.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-danger" role="alert">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="{{ vote_url }}" id="vote-form">
|
||||
<div class="d-flex flex-column gap-2 mb-4">
|
||||
{% for option in poll.options %}
|
||||
@@ -26,11 +51,11 @@
|
||||
<label>
|
||||
{% if poll.allow_multiple %}
|
||||
<input type="checkbox" name="option" value="{{ loop.index0 }}" class="form-check-input me-2"
|
||||
{% if loop.index0 in current_votes %}checked{% endif %}
|
||||
{% if loop.index0 in current_selections %}checked{% endif %}
|
||||
{% if has_voted or (not viewer_is_authenticated and poll.requires_auth) %}disabled{% endif %}>
|
||||
{% else %}
|
||||
<input type="radio" name="option" value="{{ loop.index0 }}" class="form-check-input me-2" required
|
||||
{% if loop.index0 in current_votes %}checked{% endif %}
|
||||
{% if loop.index0 in current_selections %}checked{% endif %}
|
||||
{% if has_voted or (not viewer_is_authenticated and poll.requires_auth) %}disabled{% endif %}>
|
||||
{% endif %}
|
||||
{{ option }}
|
||||
@@ -66,18 +91,14 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
window.POLL_CONFIG = {
|
||||
hidden: {{ poll.hidden | tojson }},
|
||||
timeRemaining: {{ poll.time_remaining | tojson }},
|
||||
allowMultiple: {{ poll.allow_multiple | tojson }},
|
||||
minSelections: {{ poll.min_selections | tojson }},
|
||||
maxSelections: {{ poll.max_selections | tojson }}
|
||||
};
|
||||
window.VIEWER_INFO = {
|
||||
isModerator: {{ viewer_is_moderator | tojson }},
|
||||
eventsUrl: {{ events_url | tojson }}
|
||||
};
|
||||
<script id="poll-config" type="application/json">
|
||||
{{ {
|
||||
"hidden": poll.hidden,
|
||||
"timeRemaining": poll.time_remaining,
|
||||
"isModerator": viewer_is_moderator,
|
||||
"eventsUrl": events_url,
|
||||
"optionCount": poll.options | length
|
||||
} | tojson }}
|
||||
</script>
|
||||
<script src="{{ polls_js_url }}?v={{ owlbot_version }}"></script>
|
||||
<script type="module" src="{{ polls_js_url }}?v={{ owlbot_version }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user