Move fundraiser calculations to model
This changes simplifies the template and adds correct pluralisation of hours/hour remaining.
This commit is contained in:
		
							parent
							
								
									28f3b8de08
								
							
						
					
					
						commit
						79361cdf97
					
				
					 3 changed files with 58 additions and 73 deletions
				
			
		|  | @ -1,3 +1,5 @@ | ||||||
|  | import datetime | ||||||
|  | import math | ||||||
| import random | import random | ||||||
| 
 | 
 | ||||||
| from django.db import models | from django.db import models | ||||||
|  | @ -10,6 +12,9 @@ class FundraisingGoal(models.Model): | ||||||
|     fundraiser_goal_amount = models.DecimalField(max_digits=10, decimal_places=2) |     fundraiser_goal_amount = models.DecimalField(max_digits=10, decimal_places=2) | ||||||
|     fundraiser_so_far_amount = models.DecimalField(max_digits=10, decimal_places=2) |     fundraiser_so_far_amount = models.DecimalField(max_digits=10, decimal_places=2) | ||||||
|     fundraiser_donation_count = models.IntegerField() |     fundraiser_donation_count = models.IntegerField() | ||||||
|  |     # The number of new Sustainers that can be double-matched this fundraiser. | ||||||
|  |     # (No, this name makes no sense. We're repurposing an existing model field | ||||||
|  |     # for this new reason.) | ||||||
|     fundraiser_donation_count_disclose_threshold = models.IntegerField() |     fundraiser_donation_count_disclose_threshold = models.IntegerField() | ||||||
|     fundraiser_endtime = models.DateTimeField(null=True) |     fundraiser_endtime = models.DateTimeField(null=True) | ||||||
| 
 | 
 | ||||||
|  | @ -34,6 +39,20 @@ class FundraisingGoal(models.Model): | ||||||
|         else: |         else: | ||||||
|             return random.sample(providers, k) |             return random.sample(providers, k) | ||||||
| 
 | 
 | ||||||
|  |     def days_remaining(self): | ||||||
|  |         now = datetime.datetime.now() | ||||||
|  |         return (self.fundraiser_endtime - now).days | ||||||
|  | 
 | ||||||
|  |     def hours_remaining(self): | ||||||
|  |         now = datetime.datetime.now() | ||||||
|  |         return int(math.ceil((self.fundraiser_endtime - now).seconds / 3600)) | ||||||
|  | 
 | ||||||
|  |     def match_remaining(self): | ||||||
|  |         return self.fundraiser_goal_amount - self.fundraiser_so_far_amount | ||||||
|  | 
 | ||||||
|  |     def match_exceeded_by(self): | ||||||
|  |         return self.fundraiser_so_far_amount - self.fundraiser_goal_amount | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class GoalProvider(models.Model): | class GoalProvider(models.Model): | ||||||
|     fundraising_goal = models.ForeignKey( |     fundraising_goal = models.ForeignKey( | ||||||
|  |  | ||||||
|  | @ -1,81 +1,50 @@ | ||||||
| {% load humanize %} | {% load humanize %} | ||||||
| {% load subtract %} | {% load subtract %} | ||||||
| 
 | 
 | ||||||
| {% comment %} | {% if sitefundgoal.days_remaining >= -7 %}{# i.e. 7 days over completion #} | ||||||
| # FUNDRAISER VARIABLES AND CONSTANTS GUIDE |   <div class="fundraiser-top-text ph3 pt2 pb3 mb2 mb3-ns"> | ||||||
| 
 |     <div class="mw8 center ph2 ph4-ns"> | ||||||
| ## From Local Context |  | ||||||
| 
 |  | ||||||
| * datetime_now: Current DateTime in UTC |  | ||||||
| * sitefundgoal: The current FundraisingGoal. Attributes: |  | ||||||
|   * fundraiser_goal_amount: The amount being matched |  | ||||||
|   * fundraiser_so_far_amount: The amount contributed so far |  | ||||||
|   * fundraiser_donation_count: The number of people who have contributed so far |  | ||||||
|   * fundraiser_donation_count_disclose_threshold: The number of new Sustainers that can be double-matched this fundraiser. |  | ||||||
|       (No, this name makes no sense. We're repurposing an existing model field for this new reason.) |  | ||||||
| * sitefundgoal_endtime: DateTime when sitefundgoal ends. |  | ||||||
| 
 |  | ||||||
| ## Local convenience variables |  | ||||||
| 
 |  | ||||||
| * sitefundgoal_timeleft: TimeDelta for how much time remains in the current fundraiser |  | ||||||
| * this_match_goal: The amount being matched |  | ||||||
| * this_match_so_far: The amount contributed so far |  | ||||||
| * this_match_remaining: this_match_goal - this_match_so_far |  | ||||||
| * this_match_exceeded: this_match_so_far - this_match_goal |  | ||||||
| 
 |  | ||||||
| {% endcomment %} |  | ||||||
| {% with this_match_goal=sitefundgoal.fundraiser_goal_amount this_match_so_far=sitefundgoal.fundraiser_so_far_amount %} |  | ||||||
| {% with this_match_remaining=this_match_goal|subtract:this_match_so_far sitefundgoal_timeleft=sitefundgoal.fundraiser_endtime|subtract:datetime_now this_match_exceeded=this_match_so_far|subtract:this_match_goal %} |  | ||||||
|  {% if sitefundgoal_timeleft.total_seconds >= -604800 %} |  | ||||||
|     <div class="fundraiser-top-text ph3 pt2 pb3 mb2 mb3-ns"> |  | ||||||
|       <div class="mw8 center ph2 ph4-ns"> |  | ||||||
|       <div class="mt2 mb3 tc"> |       <div class="mt2 mb3 tc"> | ||||||
|       {% if datetime_now < sitefundgoal.fundraiser_endtime %} |         {% if sitefundgoal.days_remaining >= 0 %} | ||||||
|         {% if this_match_remaining <= 0 %} |           {% if fundgoal.match_remaining <= 0 %} | ||||||
|           Thanks to so many donors, we earned our full match! |             Thanks to so many donors, we earned our full match! | ||||||
|           Help us go further to stand up for software freedom — <a href="/sustainer">sign up now</a>! |             Help us go further to stand up for software freedom — <a href="/sustainer">sign up now</a>! | ||||||
|         {% else %} |  | ||||||
|           {% if sitefundgoal_timeleft.days == 0 %} |  | ||||||
|             For the next {% widthratio sitefundgoal_timeleft.total_seconds 3600 1 %} hours <strong>only</strong>, the |  | ||||||
|           {% elif sitefundgoal_timeleft.days == 1 %} |  | ||||||
|             Through tomorrow only, the |  | ||||||
|           {% elif sitefundgoal_timeleft.days < 14 %} |  | ||||||
|             For only {{ sitefundgoal_timeleft.days }} more days, the |  | ||||||
|           {% else %} |           {% else %} | ||||||
|             Until January 15, the |             {% if sitefundgoal.days_remaining == 0 %} | ||||||
|  |               For the next {{ sitefundgoal.hours_remaining }} hour{{ sitefundgoal.hours_remaining|pluralize }} <strong>only</strong>, the | ||||||
|  |             {% elif sitefundgoal.days_remaining == 1 %} | ||||||
|  |               Through tomorrow only, the | ||||||
|  |             {% elif sitefundgoal.days_remaining < 14 %} | ||||||
|  |               For only {{ sitefundgoal.days_remaining }} more days, the | ||||||
|  |             {% else %} | ||||||
|  |               Until January 15, the | ||||||
|  |             {% endif %} | ||||||
|  |             next ${{ sitefundgoal.match_remaining|floatformat:0|intcomma }} of <a href="/sustainer/">support we receive</a> will be matched! | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         next ${{ this_match_remaining|floatformat:0|intcomma }} of <a href="/sustainer/">support we receive</a> will be matched! |         {% else %} | ||||||
| 
 |           Thank you so much to all our donors who participated in our donation match challenge!  Here are the results: | ||||||
|         {% endif %} |         {% endif %} | ||||||
|        {% else %} |  | ||||||
| 	  Thank you so much to all our donors who participated in our donation match challenge!  Here are the results: |  | ||||||
|        {% endif %} |  | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
| {% if 1 %} |       <a href="/sustainer/" style="text-decoration: none !important"> | ||||||
| <a href="/sustainer/" style="text-decoration: none !important"> |         <div id="siteprogressbar" class="flex items-stretch w-100"> | ||||||
| <div id="siteprogressbar" class="flex items-stretch w-100"> |           {% if sitefundgoal.match_remaining <= 0 %} | ||||||
|   {% if this_match_remaining <= 0 %} |             <div class="progress matched ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.fundraiser_so_far_amount }}px"> | ||||||
|     <div class="progress matched pv1 b flex items-center" style="flex-basis: {{ this_match_so_far }}px"> |               <span id="site-fundraiser-match-count" class="soFarText tc w-100 ph1">${{ sitefundgoal.fundraiser_goal_amount|floatformat:0|intcomma }} fully matched!</span> | ||||||
|       <span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ this_match_goal|floatformat:0|intcomma }} fully matched!</span> |             </div> | ||||||
|  |             <div class="progress exceeded pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.match_exceeded_by }}px"> | ||||||
|  |               <span id="site-fundraiser-match-count" class="soFarText tc w-100 exceeded ph1">${{sitefundgoal.match_exceeded_by|floatformat:0|intcomma }} additional<br> raised!<br></span> | ||||||
|  |             </div> | ||||||
|  |           {% else %} | ||||||
|  |             <div class="progress ph1 pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.fundraiser_so_far_amount }}px"> | ||||||
|  |               <span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ sitefundgoal.fundraiser_so_far_amount|floatformat:0|intcomma }} matched!</span> | ||||||
|  |             </div> | ||||||
|  |             <div class="final-goal pv1 b flex items-center" style="flex-basis: {{ sitefundgoal.match_remaining }}px"> | ||||||
|  |               <span id="site-fundraiser-final-goal" class="goalText tc w-100 ph1">${{ sitefundgoal.match_remaining|floatformat:0|intcomma }} to go!</span> | ||||||
|  |             </div> | ||||||
|  |           {% endif %} | ||||||
|  |         </div> | ||||||
|  |       </a> | ||||||
|     </div> |     </div> | ||||||
|     <div class="progress exceeded pv1 b flex items-center" style="flex-basis: {{ this_match_exceeded }}px"> |   </div> | ||||||
|       <span id="site-fundraiser-match-count" class="soFarText tc w-100 exceeded">${{this_match_exceeded|floatformat:0|intcomma }} additional<br> raised!<br></span> |  | ||||||
|     </div> |  | ||||||
|   {% else %} |  | ||||||
|     <div class="progress pv1 b flex items-center" style="flex-basis: {{ this_match_so_far }}px"> |  | ||||||
|       <span id="site-fundraiser-match-count" class="soFarText tc w-100">${{ this_match_so_far|floatformat:0|intcomma }} matched!</span> |  | ||||||
|     </div> |  | ||||||
|     <div class="final-goal pv1 b flex items-center" style="flex-basis: {{ this_match_remaining }}px"> |  | ||||||
|       <span id="site-fundraiser-final-goal" class="goalText tc w-100">${{ this_match_remaining|floatformat:0|intcomma }} to go!</span> |  | ||||||
|     </div> |  | ||||||
|     {% endif %} |  | ||||||
| </div> |  | ||||||
| </a> |  | ||||||
| {% endif %} | {% endif %} | ||||||
| 
 |  | ||||||
| </div> |  | ||||||
| </div> |  | ||||||
| {% endif %} |  | ||||||
| {% endwith %} |  | ||||||
| {% endwith %} |  | ||||||
|  |  | ||||||
|  | @ -1,5 +1,3 @@ | ||||||
| from datetime import datetime as DateTime |  | ||||||
| 
 |  | ||||||
| from .fundgoal.models import FundraisingGoal | from .fundgoal.models import FundraisingGoal | ||||||
| 
 | 
 | ||||||
| SITE_FUNDGOAL = 'cy2023-end-year-match' | SITE_FUNDGOAL = 'cy2023-end-year-match' | ||||||
|  | @ -13,7 +11,6 @@ def fundgoal_lookup(fundraiser_sought): | ||||||
| 
 | 
 | ||||||
| def sitefundraiser(request): | def sitefundraiser(request): | ||||||
|     return { |     return { | ||||||
|         'datetime_now': DateTime.now(), |  | ||||||
|         'sitefundgoal': fundgoal_lookup(SITE_FUNDGOAL), |         'sitefundgoal': fundgoal_lookup(SITE_FUNDGOAL), | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue