Skip to content

Commit

Permalink
Rework price setting
Browse files Browse the repository at this point in the history
So we also list test for the unsponsorable stuff. Also fixes an error which is output when price_levels_pence[s.price] is None
  • Loading branch information
hyanwong committed Apr 16, 2024
1 parent fb6a70a commit 462da47
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 40 deletions.
66 changes: 36 additions & 30 deletions controllers/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def SHOW_SPONSOR_SUMS():
cols['grouped_otts'],
cols['sum_paid'],
cols['count'],
groupby=groupby, orderby= cols['sum_paid'] + " DESC")
groupby=groupby, orderby=cols['sum_paid'] + " DESC")
return dict(rows = rows, cols=cols)

@OZfunc.require_https_if_nonlocal()
Expand Down Expand Up @@ -793,7 +793,14 @@ def SET_PRICES():
"""
# Take the initial prices from the previous prices in the DB
bands = {string.ascii_uppercase[i]: row for i, row in enumerate(db().select(db.prices.ALL))}
bands = {
string.ascii_uppercase[i]: row
for i, row in enumerate(db().select(
db.prices.ALL,
orderby="ISNULL(quantile), quantile, ISNULL(price), price"
))
}
max_band=max((b for b in bands if bands[b].price is not None), default=None)
n_leaves = db(db.ordered_leaves).count()

bespoke_spp = [ #these are in order highest to lowest
Expand All @@ -806,8 +813,10 @@ def SET_PRICES():
# Make a form with all the rows in it
other_fields = [f for f in db.prices if f.name not in ('id', 'quantile', 'n_leaves')]
fields = []
for i, (band, row) in enumerate(sorted(bands.items(), reverse=True)):
if i > 0:
for band, row in bands.items():
if band == max_band or row.price is None:
pass # Don't get quantiles for the max band or the unsponsorable
else:
field = db.prices.quantile
# Don't have a max popularity cutoff for the highest price
fields.append(Field("_".join([field.name, band]), type = field.type, default=row[field.name], requires=IS_NOT_EMPTY()))
Expand All @@ -821,7 +830,7 @@ def SET_PRICES():
cutoffs = {}
other_vars = {}
prev = None
for band in sorted(bands.keys()):
for band in sorted(bands):
if prev is None:
# Cheapest category
quantile = float(form.vars["_".join(["quantile", band])])
Expand All @@ -836,18 +845,22 @@ def SET_PRICES():
(db.ordered_leaves.popularity_rank <= cutoffs[prev][1]) &
(db.ordered_leaves.popularity_rank > cutoffs[band][1]))
else:
#this is the last one, which does not have a defined top price
queries[band] = db(
(db.ordered_leaves.popularity_rank <= cutoffs[prev][1]))
if bands[band].price is not None:
#this is the last quantile: no upper cutoff
queries[band] = db(
(db.ordered_leaves.popularity_rank <= cutoffs[prev][1]))
other_vars[band] = {
f.name: form.vars["_".join([f.name, band])]
for f in other_fields
}
prev = band

if prev is not None:
assert bands[prev].price is None # Check the last has no price set
queries[prev] = db(db.banned.ott) # And set that to the banned list

#save the results
db.prices.truncate()
for band in sorted(bands.keys()):
for band in sorted(bands):
num=queries[band].count()
db.prices.insert(
quantile=(float(cutoffs[band][0]) if band in cutoffs else None),
Expand All @@ -857,34 +870,27 @@ def SET_PRICES():

output = []
revenue = 0
#also None means 'call us'
for band in sorted(bands):
num = queries[band].count()
pc = 0 if n_leaves==0 else (100*num/n_leaves)
price = other_vars[band]['price']
cnt=queries[band].update(price=price)
num=queries[band].count()
revenue += num*price/100
output.append(f"{OZfunc.fmt_pounds(pence=price)}: {num:>8} species ({100*num/n_leaves:.2f}%) - {cnt} changed")
price_str = OZfunc.fmt_pounds(pence=price)
# Now set all the prices for this band
count = 0 if num == 0 else queries[band].update(price=price)
revenue += num*(price or 0)/100
output.append(f"{price_str}: {num:>8} species ({pc:.2f}%): {count} changed")
output.append(BR())


response.flash = DIV(
f"SET THE FOLLOWING DEFAULT PRICE STRUCTURE for {n_leaves} species:", BR(),PRE(*output),
f"Total revenue: {OZfunc.fmt_pounds(revenue)}!\nNow overriding the following special exclusions (and setting banned):", BR(),
f"{bespoke_spp}"
)

#override with the bespoke ones
target_band = 0
for band in sorted(bands, reverse=True):
db(db.ordered_leaves.name.belongs(bespoke_spp[target_band])).update(price=other_vars[band]['price'])
target_band+=1
if target_band>=len(bespoke_spp):
break

#make sure the banned ones are NULLified
rows = db().select(db.banned.ott)
for ban in rows:
db(db.ordered_leaves.ott == ban.ott).update(price=None)
#override bespoke species prices: these are ordered by reverse price tag
rev_price_bands = [b for b in sorted(bands, reverse=True) if other_vars[b]['price'] is not None]
for band, spp in zip(rev_price_bands, bespoke_spp):
db(db.ordered_leaves.name.belongs(spp)).update(price=other_vars[band]['price'])

elif form.errors:
response.flash = 'form has errors'
Expand Down Expand Up @@ -920,9 +926,9 @@ def SET_PRICES():
pop_max=db(db.ordered_leaves).select(mx)
return dict(
form=form,
bands={b: bands[b].price for b in sorted(bands.keys())},
bands={b: bands[b].price for b in sorted(bands)},
other_fields=other_fields,
max_band=max(bands.keys()) if len(bands) else None,
max_band=max_band,
pop_min=pop_min[0][mn],
pop_max=pop_max[0][mx],
example_spp=sorted(example_spp, key=lambda tup: tup[1]),
Expand Down
4 changes: 2 additions & 2 deletions models/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,8 +649,8 @@

# this table defines the current pricing cutoff points
db.define_table('prices',
Field('price', type='integer', unique=True, requires=IS_NOT_EMPTY()),
Field('perpetuity_price', type='integer', requires=IS_NOT_EMPTY()),
Field('price', type='integer', unique=True),
Field('perpetuity_price', type='integer'),
# Map the "normal" 4 year price to the in-perpetuity price
Field('quantile', type='double'),
Field('n_leaves', type='integer'),
Expand Down
8 changes: 5 additions & 3 deletions modules/OZfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,8 @@ def otts2ids(ottIntegers):
return {"nodes": {}, "leaves": {}, "names": {}}

def fmt_pounds(pounds=0, pence=0):
p = pence / 100 + pounds
return '£{:.0f}'.format(p) if float(p).is_integer() else '£{:.2f}'.format(p)

try:
p = pence / 100 + pounds
return '£{:.0f}'.format(p) if float(p).is_integer() else '£{:.2f}'.format(p)
except:
return current.T('Undefined price')
2 changes: 1 addition & 1 deletion views/default/list_sponsorable_children.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ <h2>Alphabetical list of species in this group</h2>
{{if sponsorship_enabled:}}
&pound;{{if s.price is None:}}{{T('contact us')}}{{else:}}{{=f"{s.price/100:.2f}"}}{{pass}}{{T('or more to sponsor.')}}
{{else:}}
{{=T(price_levels_pence.get(s.price, "click for sponsorship details"))}}
{{=T(price_levels_pence.get(s.price) or "click for sponsorship details"))}}
{{pass}}</li>
{{pass}}
</ul>
Expand Down
15 changes: 11 additions & 4 deletions views/manage/SET_PRICES.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ <h2>set cutoffs:</h2>
</tr>
{{for b, price in bands.items():}}
<tr>
{{ if b != max_band:}}
<th style="text-align: left">{{=b}} (currently {{=fmt_pounds(pence=price)}})</th><td style="background-color: yellow; border-right: double">{{=form.custom.widget['_'.join(['quantile', b])]}}</td>
{{ else:}}
{{ if b == max_band:}}
<th style="text-align: left">{{=b}} (currently {{=fmt_pounds(pence=price)}})</th><td style="border-right: double">remainder</td>
{{elif price is None:}}
<th style="text-align: left">{{=b}} (currently {{=fmt_pounds(pence=price)}})</th><td style="border-right: double">{{=price}}</td>
{{ else:}}
<th style="text-align: left">{{=b}} (currently {{=fmt_pounds(pence=price)}})</th><td style="background-color: yellow; border-right: double">{{=form.custom.widget['_'.join(['quantile', b])]}}</td>
{{ pass}}
{{ for i, field in enumerate(other_fields):}}
<td style="border: none; {{if i==0:}}padding-left: 4em;{{pass}}">{{=form.custom.widget["_".join([field.name, b])]}}</td>
Expand All @@ -51,6 +53,11 @@ <h2>set cutoffs:</h2>
</tr>
</table>
Click here to set all the prices in the database (may take some time): {{=form.custom.submit}}
{{=form.custom.end}}<h2>Submitted variables</h2>
{{=form.custom.end}}
<br />
{{if request.vars:}}
<details><summary>Click to see submitted variables</summary>
{{=BEAUTIFY(request.vars)}}
{{pass}}
</details>
</div></div>

0 comments on commit 462da47

Please sign in to comment.