Sessions can be sorted by session name (default), speakers or average
score by clicking the appropriate headlines.
+#### 2.1 Scoring method
+
+There are two methods for calculating the average score of a
+proposal:
+
+- Average — This is the standard average of all the scores.
+- Olympic average — This method removes one instance each of the
+ maximum and minimum of the scores before averaging. This helps
+ prevent both favoritism and also sabotage.
+
+The method used by the conference is set by the administrator and
+shown at the top of the voting page.
+
### 3. Deciding and notifying speakers
Once the voting is done, the decisions can be made and the speakers be
voted and the overall average vote. Usually this would be off until
everyone has finished voting.
+There are two methods for calculating the average score of a
+proposal:
+
+- Average — This is the standard average of all the scores.
+- Olympic average — This method removes one instance each of the
+ maximum and minimum of the scores before averaging. This helps
+ prevent both favoritism and also sabotage.
+
### Roles
There are four types of roles that can be configured at the level of
'schedulewidth', 'pixelsperminute', 'notifyregs', 'notifysessionstatus', 'notifyvolunteerstatus',
'testers', 'talkvoters', 'staff', 'volunteers', 'checkinprocessors',
'asktshirt', 'askfood', 'asknick', 'asktwitter', 'askbadgescan', 'askshareemail', 'askphotoconsent',
- 'callforpapersmaxsubmissions', 'skill_levels', 'showvotes', 'callforpaperstags', 'callforpapersrecording', 'sendwelcomemail',
+ 'callforpapersmaxsubmissions', 'skill_levels', 'showvotes', 'scoring_method', 'callforpaperstags', 'callforpapersrecording', 'sendwelcomemail',
'tickets', 'confirmpolicy', 'queuepartitioning', 'invoice_autocancel_hours', 'attendees_before_waitlist',
'transfer_cost', 'initial_common_countries', 'jinjaenabled', 'dynafields', 'scannerfields',
'videoproviders', ]
{'id': 'twitter', 'legend': 'Twitter settings', 'fields': ['twitter_timewindow_start', 'twitter_timewindow_end', 'twitter_postpolicy', ]},
{'id': 'fields', 'legend': 'Registration fields', 'fields': ['asktshirt', 'askfood', 'asknick', 'asktwitter', 'askbadgescan', 'askshareemail', 'askphotoconsent', 'dynafields', 'scannerfields', ]},
{'id': 'steps', 'legend': 'Steps', 'fields': ['registrationopen', 'registrationtimerange', 'allowedit', 'callforpapersopen', 'callforpaperstimerange', 'callforsponsorsopen', 'callforsponsorstimerange', 'scheduleactive', 'tbdinschedule', 'sessionsactive', 'cardsactive', 'checkinactive', 'conferencefeedbackopen', 'feedbackopen']},
- {'id': 'callforpapers', 'legend': 'Call for papers', 'fields': ['callforpapersmaxsubmissions', 'skill_levels', 'callforpaperstags', 'callforpapersrecording', 'showvotes']},
+ {'id': 'callforpapers', 'legend': 'Call for papers', 'fields': ['callforpapersmaxsubmissions', 'skill_levels', 'callforpaperstags', 'callforpapersrecording', 'showvotes', 'scoring_method']},
{'id': 'roles', 'legend': 'Roles', 'fields': ['testers', 'talkvoters', 'staff', 'volunteers', 'checkinprocessors', ]},
{'id': 'display', 'legend': 'Display', 'fields': ['jinjaenabled', 'videoproviders', ]},
{'id': 'legacy', 'legend': 'Legacy', 'fields': ['schedulewidth', 'pixelsperminute']},
--- /dev/null
+# Generated by Django 4.2.16 on 2024-09-11 14:08
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('confreg', '0115_speaker_photo_hashvals'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='conference',
+ name='scoring_method',
+ field=models.IntegerField(choices=[(0, 'Average'), (1, 'Olympic Average')], default=0, verbose_name='Scoring method'),
+ ),
+ ]
(4, "Volunteers and admins can post without approval"),
)
+SCORING_METHOD_CHOICES = (
+ (0, "Average"),
+ (1, "Olympic Average"),
+)
+
# NOTE! The contents of these arrays must also be matched with the
# database table confreg_status_strings. This one is managed by
# manually creating a separate migration in case the contents change.
callforpaperstags = models.BooleanField(blank=False, null=False, default=False, verbose_name='Use tags')
callforpapersrecording = models.BooleanField(blank=False, null=False, default=False, verbose_name='Ask for recording consent')
showvotes = models.BooleanField(blank=False, null=False, default=False, verbose_name="Show votes", help_text="Show other people's votes on the talkvote page")
+ scoring_method = models.IntegerField(blank=False, null=False, default=0, choices=SCORING_METHOD_CHOICES, verbose_name="Scoring method")
sendwelcomemail = models.BooleanField(blank=False, null=False, default=False, verbose_name="Send welcome email", help_text="Send an email to attendees once their registration is completed.")
tickets = models.BooleanField(blank=False, null=False, default=False, verbose_name="Use tickets", help_text="Generate and send tickets to all attendees once their registration is completed.")
from .models import PendingAdditionalOrder
from .models import RegistrationWaitlistEntry, RegistrationWaitlistHistory
from .models import RegistrationTransferPending
-from .models import STATUS_CHOICES
+from .models import STATUS_CHOICES, SCORING_METHOD_CHOICES
from .models import ConferenceNews, ConferenceTweetQueue
from .models import SavedReportDefinition
from .models import ConferenceMessaging
WHERE cs.conferencesession_id=s.id
) speakers ON true
LEFT JOIN LATERAL (
- SELECT avg(vote) FILTER (WHERE vote > 0)::numeric(3,2) AS avg,
- jsonb_object_agg(username, vote) AS votes,
- jsonb_object_agg(username, comment) FILTER (WHERE comment IS NOT NULL AND comment != '') AS comments
- FROM confreg_conferencesessionvote
- INNER JOIN auth_user ON auth_user.id=voter_id
- WHERE session_id=s.id
+ WITH
+ aggs (votes, comments, avg, sum, count, min, max) AS (
+ SELECT
+ jsonb_object_agg(username, vote),
+ jsonb_object_agg(username, comment) FILTER (WHERE comment > ''),
+ AVG(vote) FILTER (WHERE vote > 0),
+ SUM(vote) FILTER (WHERE vote > 0),
+ COUNT(*) FILTER (WHERE vote > 0),
+ MIN(vote) FILTER (WHERE vote > 0),
+ MAX(vote) FILTER (WHERE vote > 0)
+ FROM confreg_conferencesessionvote
+ INNER JOIN auth_user ON auth_user.id=voter_id
+ WHERE session_id=s.id
+ )
+ SELECT
+ CASE (SELECT scoring_method FROM confreg_conference WHERE id = %(confid)s)
+ WHEN 0 /* Average */
+ THEN avg
+
+ WHEN 1 /* Olympic average */
+ THEN CASE WHEN count > 2
+ THEN (sum - max - min) / (count - 2)
+ ELSE avg
+ END
+ END::numeric(3,2) AS avg,
+ votes,
+ comments
+ FROM aggs
) votes ON true
WHERE s.conference_id=%(confid)s AND
(COALESCE(s.track_id,0)=ANY(%(tracks)s)) AND
'urlfilter': urltrackfilter + urlstatusfilter,
'helplink': 'callforpapers',
'options': options,
+ 'scoring_method': SCORING_METHOD_CHOICES[conference.scoring_method][1],
})
{%if not hasvoters%}
<div class="alert alert-warning">There are no talkvoters configured on this conference! List of talks will be empty!</div>
{%endif%}
+
+<p><b>Scoring method:</b> {{ scoring_method }}</p>
+
<table id="votetable" class="table table-bordered table-condensed" style="display:none">
<tr>
<th style="width: 1%">Seq</th>