~lars-luthman-deactivatedaccount/ppvalbok/remove-bookings

« back to all changes in this revision

Viewing changes to templates/calculate_distribution.html

  • Committer: Lars Luthman
  • Date: 2009-04-20 14:48:52 UTC
  • Revision ID: lars.luthman@gmail.com-20090420144852-3745qj3nov9ecism
Added more code to maintain voter statistics, added ballot distribution calculation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
{% extends "base.html" %}
 
2
      
 
3
 
 
4
    {% block script %}
 
5
    <script type="text/javascript">
 
6
      //<![CDATA[
 
7
 
 
8
      // This is called when a checkbox has been toggled. If it has been
 
9
      // set to true the associated text input will be disabled, otherwise
 
10
      // it will be enabled.
 
11
      function checkbox_changed(name) {
 
12
        var input = document.getElementById(name);
 
13
        var checkbox = document.getElementById(name + '_lock');
 
14
        if (input && checkbox)
 
15
          input.disabled = checkbox.checked;
 
16
      }
 
17
 
 
18
 
 
19
      // Constructor for calc_entry.
 
20
      function calc_entry(type, id, voters, locked) {
 
21
        this.type = type;
 
22
        this.id = id;
 
23
        this.voters = voters;
 
24
        this.locked = locked;
 
25
      }
 
26
 
 
27
 
 
28
      // Try to parse the value of a text input as an integer, return
 
29
      // NaN if it fails.
 
30
      function parse_int_from_input(input_name) {
 
31
        elt = document.getElementById(input_name);
 
32
        var value = parseInt(elt.value);
 
33
        if (isNaN(value)) {
 
34
          window.alert(elt.value + ' kan inte tolkas som ett tal!');
 
35
        }
 
36
        return value;
 
37
      }
 
38
      
 
39
      // Calculate the ballot distribution.
 
40
      function calculate() {
 
41
 
 
42
        var calc_entries = new Array();
 
43
        var early_calc_entries = new Array();
 
44
        var locked_ballots = 0;
 
45
        var unlocked_subregion_voters = 0;
 
46
        var unlocked_station_voters = 0;
 
47
        var unlocked_entries = 0;
 
48
        var unlocked_locals = 0;
 
49
        
 
50
        ballots_available = parse_int_from_input('ballots_available');
 
51
        if (isNaN(ballots_available))
 
52
          return false;
 
53
        var total_ballots = ballots_available;
 
54
        var unit = parse_int_from_input('unit_size');
 
55
        if (isNaN(unit))
 
56
          return false;
 
57
        {% if locals %}
 
58
        var early_percentage = parse_int_from_input('early_percentage');
 
59
        if (isNaN(early_percentage))
 
60
          return false;
 
61
        if (early_percentage < 0 || early_percentage > 100) {
 
62
          window.alert('Procentandelen valsedlar till fortidsrostningen ' +
 
63
                       'maste vara mellan 0 och 100!');
 
64
          return false;
 
65
        }
 
66
        {% endif %}
 
67
 
 
68
        {% if subregions %}
 
69
        // Add the entries for all subregions
 
70
        {% for subregion in subregions %}
 
71
        var eltId = 'region_{{ subregion.shortname }}_value';
 
72
        var inputElt = document.getElementById(eltId);
 
73
        var lockElt = document.getElementById(eltId + '_lock');
 
74
        cy = new calc_entry('region', '{{ subregion.shortname }}',
 
75
                            {{ subregion.total_voters }}, lockElt.checked);
 
76
        if (lockElt.checked) {
 
77
          var ballots = parse_int_from_input(eltId);
 
78
          if (isNaN(ballots))
 
79
            return false;
 
80
          locked_ballots += ballots;
 
81
          cy.ballots = ballots;
 
82
        }
 
83
        else {
 
84
          unlocked_subregion_voters += {{ subregion.total_voters }};
 
85
          ++unlocked_entries;
 
86
          cy.ballots = 0;
 
87
        }
 
88
        calc_entries.push(cy);
 
89
        {% endfor %}
 
90
        {% endif %}
 
91
            
 
92
        {% if locals %}
 
93
        // Add the entries for all early voting stations
 
94
        {% for local in locals %}
 
95
        var eltId = 'local_{{ local.pk }}_value';
 
96
        var inputElt = document.getElementById(eltId);
 
97
        var lockElt = document.getElementById(eltId + '_lock');
 
98
        cy = new calc_entry('local', {{ local.pk }}, 0, lockElt.checked);
 
99
        if (lockElt.checked) {
 
100
          var ballots = parse_int_from_input(eltId);
 
101
          if (isNaN(ballots))
 
102
            return false;
 
103
          locked_ballots += ballots;
 
104
          cy.ballots = ballots;
 
105
        }
 
106
        else {
 
107
          ++unlocked_locals;
 
108
          cy.ballots = 0;
 
109
        }
 
110
        early_calc_entries.push(cy);
 
111
        {% endfor %}
 
112
        {% endif %}
 
113
 
 
114
        {% if stations %}
 
115
        // Add the entries for all normal voting stations.
 
116
        {% for station in stations %}
 
117
        var eltId = 'station_{{ station.pk }}_value';
 
118
        var inputElt = document.getElementById(eltId);
 
119
        var lockElt = document.getElementById(eltId + '_lock');
 
120
        cy = new calc_entry('station', {{ station.pk }},
 
121
                            {{ station.voters }}, lockElt.checked);
 
122
        if (lockElt.checked) {
 
123
          var ballots = parse_int_from_input(eltId);
 
124
          if (isNaN(ballots))
 
125
            return false;
 
126
          locked_ballots += ballots;
 
127
          cy.ballots = ballots;
 
128
        }
 
129
        else {
 
130
          unlocked_station_voters += {{ station.voters }};
 
131
          ++unlocked_entries;
 
132
          cy.ballots = 0;
 
133
        }
 
134
        calc_entries.push(cy);
 
135
        {% endfor %}
 
136
        {% endif %}
 
137
 
 
138
        var unlocked_voters = 
 
139
          unlocked_subregion_voters + unlocked_station_voters;
 
140
        
 
141
        // If all regions and stations are locked, stop now.
 
142
        if (unlocked_entries == 0)
 
143
          return true;
 
144
 
 
145
        // Subtract all locked ballots.
 
146
        total_ballots -= locked_ballots;
 
147
        if (total_ballots < 0) {
 
148
          window.alert('Du har last fler valsedlar an du har tillgangliga!');
 
149
          return false;
 
150
        }
 
151
        
 
152
        // Check that the unit is sane.
 
153
        rest_ballots = total_ballots % unit;
 
154
        total_ballots -= rest_ballots;
 
155
        ballot_units = total_ballots / unit;
 
156
        if (total_ballots <= 0 || ballot_units < unlocked_entries) {
 
157
          window.alert('Du har inte tillrackligt manga valsedlar! ' +
 
158
                       'Minska paketstorleken.');
 
159
          return false;
 
160
        }
 
161
 
 
162
        {% if locals %}
 
163
        // If there are unlocked early voting stations, calculate the number of
 
164
        // ballots they should have.
 
165
        if (unlocked_locals > 0) {
 
166
          var early_units_min = unlocked_locals;
 
167
          var early_units = Math.round(ballot_units * 
 
168
                                       (unlocked_station_voters / 
 
169
                                        unlocked_voters) * 
 
170
                                       early_percentage / 100);
 
171
          early_units = (early_units < early_units_min ? 
 
172
                         early_units_min : early_units);
 
173
          early_units_per_station = Math.floor(early_units / unlocked_locals);
 
174
          ballot_units -= early_units;
 
175
         
 
176
          var last_unlocked_early = 0;
 
177
          for (i = 0; i < early_calc_entries.length; ++i) {
 
178
            if (!early_calc_entries[i].locked) {
 
179
              early_calc_entries[i].ballots += unit * early_units_per_station;
 
180
              early_units -= early_units_per_station;
 
181
              last_unlocked_early = i;
 
182
            }
 
183
          }
 
184
          early_calc_entries[last_unlocked_early].ballots += 
 
185
            early_units * unit;
 
186
        }
 
187
        {% endif %}
 
188
 
 
189
        
 
190
        var log = '';
 
191
        // Give one unit to each entry.
 
192
        voters_per_unit = unlocked_voters / ballot_units;
 
193
        for (i = 0; i < calc_entries.length; ++i) {
 
194
          if (!calc_entries[i].locked) {
 
195
            log += 'Giving 1 unit to ' + calc_entries[i].type +
 
196
                         '_' + calc_entries[i].id + "\n";
 
197
            calc_entries[i].ballots += unit;
 
198
            calc_entries[i].voters -= voters_per_unit;
 
199
          }
 
200
        }
 
201
        ballot_units -= unlocked_entries;
 
202
        
 
203
        // Distribute the rest of the units as fairly as possible.
 
204
        function compare_voters(a, b) { return b.voters - a.voters; }
 
205
        var voter_threshold = voters_per_unit;
 
206
        while (ballot_units > 0) {
 
207
          calc_entries.sort(compare_voters);
 
208
          var old_ballot_units = ballot_units;
 
209
          for (i = 0; i < calc_entries.length && ballot_units > 0; ++i) {
 
210
            if (!calc_entries[i].locked && 
 
211
                calc_entries[i].voters > voter_threshold) {
 
212
              calc_entries[i].ballots += unit;
 
213
              calc_entries[i].voters -= voters_per_unit;
 
214
              --ballot_units;
 
215
            }
 
216
          }
 
217
          if (ballot_units == old_ballot_units)
 
218
            voter_threshold -= voters_per_unit / 4;
 
219
        }
 
220
 
 
221
        // Set the ballot values for all regions and stations.
 
222
        for (i = 0; i < calc_entries.length; ++i) {
 
223
          name = calc_entries[i].type + '_' + calc_entries[i].id + '_value';
 
224
          value_elt = 
 
225
            document.getElementById(calc_entries[i].type + '_' +
 
226
                                    calc_entries[i].id + '_value');
 
227
          value_elt.value = '' + calc_entries[i].ballots;
 
228
        }
 
229
 
 
230
        // Set the ballot values for all early stations
 
231
        for (i = 0; i < early_calc_entries.length; ++i) {
 
232
          name = early_calc_entries[i].type + '_' + 
 
233
            early_calc_entries[i].id + '_value';
 
234
          value_elt = 
 
235
            document.getElementById(early_calc_entries[i].type + '_' +
 
236
                                    early_calc_entries[i].id + '_value');
 
237
          value_elt.value = '' + early_calc_entries[i].ballots;
 
238
        }
 
239
 
 
240
        document.getElementById('input_rest_ballots').value = ''+ rest_ballots;
 
241
 
 
242
        var sum = 0;
 
243
        for (i = 0; i < early_calc_entries.length; ++i)
 
244
          sum += early_calc_entries[i].ballots;
 
245
        for (i = 0; i < calc_entries.length; ++i)
 
246
          sum += calc_entries[i].ballots;
 
247
        
 
248
        if (sum + rest_ballots != ballots_available)
 
249
          window.alert('Antalet fordelade valsedlar (' + sum +
 
250
                       ') plus resten (' + rest_ballots + 
 
251
                       ') ar inte lika med det totala antalet (' +
 
252
                       ballots_available + ')! Lita inte pa berakningen!');
 
253
        
 
254
      }
 
255
 
 
256
      // ]]>
 
257
    </script>
 
258
 
 
259
 
 
260
    {% endblock %}  
 
261
 
 
262
 
 
263
    {% block content %}
 
264
    <h1>{{ region.name }}</h1>
 
265
    <hr/>
 
266
    <div id="data">
 
267
      <form action="../save_calculation/" method="POST">
 
268
      <div id="regioninfo">
 
269
        {% if region.user %}
 
270
        <strong>Valsedelsdistribut&ouml;r:</strong>
 
271
        <a href="../../user/{{ region.user.username }}/">
 
272
          {{ region.user.username }}
 
273
        </a>
 
274
        {% else %}
 
275
        <i>Det finns ingen ansvarig f&ouml;r den h&auml;r regionen!</i>
 
276
        {% endif %}
 
277
        <br/>
 
278
        
 
279
        <strong>Bokade vallokaler: 
 
280
          {{ region.total_booked_stations }} / 
 
281
          {{ region.total_stations }}</strong>
 
282
      </div>
 
283
 
 
284
      <div id="region_ballot_info">
 
285
 
 
286
        <div>
 
287
          <div>
 
288
            Tilldelade valsedlar:
 
289
          </div>
 
290
          <div>
 
291
            {{ region.ballots }}
 
292
          </div>
 
293
          <div class="help_text">
 
294
            Det h&auml;r &auml;r antalet valsedlar som den h&auml;r
 
295
            regionen har tilldelats fr&aring;n regionen ovanf&ouml;r.
 
296
          </div>
 
297
        </div>
 
298
 
 
299
        <div>
 
300
          <div>
 
301
            Tillg&auml;ngliga valsedlar:
 
302
          </div>
 
303
          <div>
 
304
            <input type="text" id="ballots_available" name="ballots_available"
 
305
              value="{{ region.ballots_available }}"/>
 
306
          </div>
 
307
          <div class="help_text">
 
308
            H&auml;r kan du &auml;ndra antalet valsedlar som faktiskt &auml;r
 
309
            tillg&auml;ngliga ifall det skiljer sig fr&aring;n det tilldelade
 
310
            antalet.
 
311
          </div>
 
312
        </div>
 
313
 
 
314
        <div>
 
315
          <div>
 
316
            Paketstorlek:
 
317
          </div>
 
318
          <div>
 
319
            <input type="text" id="unit_size" name="unit_size"
 
320
              value="{{ region.ballot_unit_size }}"/>
 
321
          </div>
 
322
          <div class="help_text">
 
323
            H&auml;r kan du &auml;ndra antalet valsedlar i ett paket.
 
324
          </div>
 
325
        </div>
 
326
 
 
327
        {% if locals %}
 
328
        <div>
 
329
          <div>
 
330
            F&ouml;rtidsr&ouml;stning (procent):
 
331
          </div>
 
332
          <div>
 
333
            <input type="text" id="early_percentage" 
 
334
             value="{{ region.ballot_early_percentage }}"
 
335
             name="early_percentage" />
 
336
          </div>
 
337
          <div class="help_text">
 
338
            H&auml;r skriver du in andelen av valsedlarna som ska 
 
339
            anv&auml;ndas till f&ouml;rtidsr&ouml;stningslokaler, i procent.
 
340
          </div>
 
341
        </div>
 
342
        {% endif %}
 
343
 
 
344
      </div>
 
345
      
 
346
      <div class="help_text">
 
347
        <p>
 
348
          I textf&auml;lten nedan kommer antalet valsedlar som b&ouml;r 
 
349
          distribueras till varje region och/eller vallokal att fyllas i
 
350
          automatiskt n&auml;r du trycker p&aring; 
 
351
          <strong>Ber&auml;kna</strong>.
 
352
          Om du vill s&auml;tta antalet valsedlar f&ouml;r en viss region
 
353
          eller lokal till ett fixt antal, t.ex. f&ouml;r att du redan har gett
 
354
          valsedlar till den ansvariga personen, s&aring; kan du skriva in det
 
355
          i dess textf&auml;lt och kryssa f&ouml;r <strong>L&aring;st</strong>
 
356
          s&aring; kommer den att undantas fr&aring;n den automatiska 
 
357
          ber&auml;kningen.
 
358
        </p>
 
359
        <p>
 
360
          N&auml;r du &auml;r n&ouml;jd med f&ouml;rdelningen s&aring; ska
 
361
          du trycka p&aring; <strong>Spara</strong>, s&aring; sparas alla
 
362
          v&auml;rden i databasen. Om den h&auml;r regionen har n&aring;gra
 
363
          delregioner s&aring; kommer dessutom det ber&auml;knade antalet
 
364
          valsedlar f&ouml;r dem att dyka upp i f&auml;ltet 
 
365
          <strong>Tilldelade valsedlar</strong> p&aring; deras 
 
366
          ber&auml;kningssidor.
 
367
        </p>
 
368
 
 
369
        <p>
 
370
          Paketstorleken ovan anv&auml;nds i den automatiska ber&auml;kningen.
 
371
          Alla stationer eller delregioner kommer att tilldelas minst
 
372
          ett paket, och alla tilldelningar &auml;r alltid j&auml;mnt 
 
373
          delbara med paketstorleken.
 
374
        </p>
 
375
      </div>
 
376
      
 
377
      {% if subregions %}
 
378
      <h3>Delregioner</h3>
 
379
      <div id="subregions" class="calc_result">
 
380
        <div class="calc_result_header">
 
381
          <div>Namn</div>
 
382
          <div>Antal valsedlar</div>
 
383
          <div>L&aring;st</div>
 
384
        </div>
 
385
        {% for subregion in subregions %}
 
386
        <div class="calc_result_entry">
 
387
          <div class="subregion">
 
388
            <a href="../../{{ subregion.shortname }}/calculate/">
 
389
              {{ subregion.name }}</a>
 
390
            [{{ subregion.total_booked_stations }} /
 
391
            {{ subregion.total_stations }}]
 
392
            <div class="voter_info">
 
393
              {{ subregion.total_voters }} 
 
394
              r&ouml;stber&auml;ttigade
 
395
            </div>
 
396
          </div>
 
397
          <div>
 
398
            <input type="text" id="region_{{ subregion.shortname }}_value"
 
399
              name="region_{{ subregion.shortname }}_value" 
 
400
              class="region_value" value="{{ subregion.ballots }}"
 
401
              {% if subregion.ballots_locked %}disabled="disabled"{% endif %}/>
 
402
          </div>
 
403
          <div>
 
404
            <input type="checkbox" 
 
405
             id="region_{{ subregion.shortname }}_value_lock"
 
406
             name="region_{{ subregion.shortname }}_value_lock"
 
407
             onchange="checkbox_changed('region_{{ subregion.shortname }}_value'); return false;"
 
408
            {% if subregion.ballots_locked %}
 
409
             checked="checked"
 
410
            {% endif %}
 
411
            />
 
412
          </div>
 
413
        </div>
 
414
        {% endfor %}
 
415
      </div>
 
416
      {% endif %}
 
417
 
 
418
      {% if locals %}
 
419
      <h3>F&ouml;rtidsr&ouml;stningslokaler</h3>
 
420
      <div id="locallist" class="calc_result">
 
421
        <div class="calc_result_header">
 
422
          <div>Namn</div>
 
423
          <div>Antal valsedlar</div>
 
424
          <div>L&aring;st</div>
 
425
        </div>
 
426
        {% for local in locals %}
 
427
        <div class="calc_result_entry">
 
428
          <div>
 
429
            {% if local.user %}
 
430
            <img src="{{MEDIA_URL}}images/green_check.png" 
 
431
              alt="Vallokalen &auml;r bokad" 
 
432
              title="Vallokalen &auml;r bokad">
 
433
            {% else %}
 
434
            <img src="{{MEDIA_URL}}images/red_cross.png"
 
435
              alt="Vallokalen &auml;r inte bokad"
 
436
              title="Vallokalen &auml;r inte bokad">
 
437
            {% endif %}
 
438
            <a href="../{{ local.pk }}/">
 
439
              {% if station.name %}{{ station.name }}{% else %}<i>Namn saknas</i>{% endif %}</a>
 
440
          </div>
 
441
          <div>
 
442
            <input type="text" id="local_{{ local.pk }}_value"
 
443
              name="local_{{ local.pk }}_value" 
 
444
              class="local_value" value="{{ local.ballots }}"
 
445
              {% if local.ballots_locked %}disabled="disabled"{% endif %}/>
 
446
          </div>
 
447
          <div>
 
448
            <input type="checkbox"
 
449
              id="local_{{ local.pk }}_value_lock"
 
450
              name="local_{{ local.pk }}_value_lock"
 
451
              onchange="checkbox_changed('local_{{ local.pk }}_value'); return false;"
 
452
            {% if local.ballots_locked %}
 
453
            checked="checked"
 
454
            {% endif %}
 
455
            />
 
456
          </div>
 
457
        </div>
 
458
        {% endfor %}
 
459
      </div>
 
460
      {% endif %}
 
461
      
 
462
      {% if stations %}
 
463
      <h3>Vallokaler</h3>
 
464
      <div id="stationlist" class="calc_result">
 
465
        <div class="calc_result_header">
 
466
          <div>Namn</div>
 
467
          <div>Antal valsedlar</div>
 
468
          <div>L&aring;st</div>
 
469
        </div>
 
470
        {% for station in stations %}
 
471
        <div class="calc_result_entry">
 
472
          <div>
 
473
            <div>
 
474
              {% if station.user %}
 
475
              <img src="{{MEDIA_URL}}images/green_check.png" 
 
476
                alt="Vallokalen &auml;r bokad" 
 
477
                title="Vallokalen &auml;r bokad">
 
478
              {% else %}
 
479
              <img src="{{MEDIA_URL}}images/red_cross.png"
 
480
                alt="Vallokalen &auml;r inte bokad"
 
481
                title="Vallokalen &auml;r inte bokad">
 
482
              {% endif %}
 
483
              <a href="../{{ station.pk }}/">
 
484
                {{ station.name }}</a>
 
485
            </div>
 
486
            <div class="voter_info">
 
487
              {{ station.voters }} 
 
488
              r&ouml;stber&auml;ttigade
 
489
            </div>
 
490
          </div>
 
491
          <div>
 
492
            <input type="text" id="station_{{ station.pk }}_value"
 
493
              name="station_{{ station.pk }}_value" 
 
494
              class="station_value" value="{{ station.ballots }}"
 
495
              {% if station.ballots_locked %}disabled="disabled"{% endif %}/>
 
496
          </div>
 
497
          <div>
 
498
            <input type="checkbox"
 
499
             id="station_{{ station.pk }}_value_lock"
 
500
             name="station_{{ station.pk }}_value_lock"
 
501
             onchange="checkbox_changed('station_{{ station.pk }}_value'); return false;"
 
502
            {% if station.ballots_locked %}
 
503
            checked="checked"
 
504
            {% endif %}
 
505
            />
 
506
          </div>
 
507
        </div>
 
508
        {% endfor %}
 
509
 
 
510
      </div>
 
511
      {% endif %}
 
512
 
 
513
      <div class="calc_result">
 
514
        <div class="calc_result_entry">
 
515
          <div>
 
516
            <strong>Kvarvarande valsedlar:</strong>
 
517
          </div>
 
518
          <div>
 
519
            <input type="text" 
 
520
              id="input_rest_ballots" disabled="disabled" />
 
521
          </div>
 
522
        </div>
 
523
      </div>
 
524
      
 
525
      <p>
 
526
        <input type="button" value="Ber&auml;kna" onclick="calculate()" />
 
527
      </p>
 
528
      <p>
 
529
        <input type="submit" name="submit" value="Spara"/>
 
530
      </p>
 
531
      
 
532
    </form>
 
533
    </div>
 
534
    {% endblock %}
 
535
    
 
 
b'\\ No newline at end of file'