Browse Source

Enable sorting on operation pages.

master
Ben Kurtovic 4 years ago
parent
commit
adf73770fe
6 changed files with 120 additions and 26 deletions
  1. +35
    -11
      calefaction/modules/campaigns/database.py
  2. +13
    -6
      calefaction/modules/campaigns/getters.py
  3. +2
    -1
      calefaction/modules/campaigns/routes.py
  4. +30
    -1
      static/campaigns.css
  5. +2
    -2
      templates/campaigns/operation.mako
  6. +38
    -5
      templates/campaigns/renderers.mako

+ 35
- 11
calefaction/modules/campaigns/database.py View File

@@ -141,12 +141,23 @@ class CampaignDB:
res = self._conn.execute(query, (campaign, operation)).fetchall()
return tuple(res[0])

def get_associated_kills(self, campaign, operation, limit=5, offset=0):
def get_associated_kills(self, campaign, operation, sort="new", limit=-1,
offset=0):
"""Return a list of kills associated with a campaign/operation.

Kills are returned as dictionaries most recent first, up to a limit.
Use -1 for no limit.
Kills are returned as dictionaries, up to a limit. Use -1 for no limit.
The sort should be "new" for most recent first, "old" for most recent
last, or "value" for most valuable first.
"""
sortkeys = {
"new": "ok_killid DESC",
"old": "ok_killid ASC",
"value": "kill_value DESC, ok_killid DESC"
}
if sort in sortkeys:
sortkey = sortkeys[sort]
else:
raise ValueError(sort)
if not isinstance(limit, int):
raise ValueError(limit)
if not isinstance(offset, int):
@@ -160,8 +171,8 @@ class CampaignDB:
FROM oper_kill
JOIN kill ON ok_killid = kill_id
WHERE ok_campaign = ? AND ok_operation = ?
ORDER BY ok_killid DESC LIMIT {} OFFSET {}"""
qform = query.format(limit, offset)
ORDER BY {} LIMIT {} OFFSET {}"""
qform = query.format(sortkey, limit, offset)
res = self._conn.execute(qform, (campaign, operation)).fetchall()

return [{
@@ -205,21 +216,34 @@ class CampaignDB:
for char_id, types in chars.items()
for type_id, (count, value) in types.items()])

def get_associated_items(self, campaign, operation, limit=5, offset=0):
def get_associated_items(self, campaign, operation, sort="value", limit=-1,
offset=0):
"""Return a list of items associated with a campaign/operation.

Items are returned as 2-tuples of (item_type, item_count), most
valuable first, up to a limit. Use -1 for no limit.
Items are returned as 2-tuples of (item_type, item_count), up to a
limit. Use -1 for no limit. The sort should be "value" for most
valuable first (individual item value * quantity), "quantity" for
greatest quantity first, "price" for most valuable items first.
"""
sortkeys = {
"value": "total_value DESC",
"quantity": "total_count DESC",
"price": "(total_value / total_count) DESC"
}
if sort in sortkeys:
sortkey = sortkeys[sort]
else:
raise ValueError(sort)
if not isinstance(limit, int):
raise ValueError(limit)
if not isinstance(offset, int):
raise ValueError(offset)

query = """SELECT oi_type, SUM(oi_count), TOTAL(oi_value) as total_val
query = """SELECT oi_type, SUM(oi_count) AS total_count,
TOTAL(oi_value) as total_value
FROM oper_item
WHERE oi_campaign = ? AND oi_operation = ?
GROUP BY oi_type ORDER BY total_val DESC LIMIT {} OFFSET {}"""
qform = query.format(limit, offset)
GROUP BY oi_type ORDER BY {} LIMIT {} OFFSET {}"""
qform = query.format(sortkey, limit, offset)
res = self._conn.execute(qform, (campaign, operation)).fetchall()
return [(type_id, count or 0, value) for type_id, count, value in res]

+ 13
- 6
calefaction/modules/campaigns/getters.py View File

@@ -55,18 +55,25 @@ def get_overview(cname, opname):
"operation=%s", age, cname, opname)
return g.campaign_db.get_overview(cname, opname)

def get_summary(cname, opname, limit=5):
"""Return a sample fraction of results for the given campaign/operation."""
def get_summary(cname, opname, sortby=None, limit=5):
"""Return some details for the given campaign/operation."""
optype = config["campaigns"][cname]["operations"][opname]["type"]
if optype == "killboard":
kills = g.campaign_db.get_associated_kills(cname, opname, limit=limit)
return kills, "killboard_recent"
sorts = ["new", "old", "value"]
renderer = "killboard_recent"
func = g.campaign_db.get_associated_kills
elif optype == "collection":
items = g.campaign_db.get_associated_items(cname, opname, limit=limit)
return items, "collection_items"
sorts = ["value", "quantity", "price"]
renderer = "collection_items"
func = g.campaign_db.get_associated_items
else:
raise RuntimeError("Unknown operation type: %s" % optype)

if sortby not in sorts:
sortby = sorts[0]
data = func(cname, opname, sort=sortby, limit=limit)
return data, renderer

def get_unit(operation, num, primary=True):
"""Return the correct form of the unit tracked by the given operation."""
if not primary:


+ 2
- 1
calefaction/modules/campaigns/routes.py View File

@@ -52,10 +52,11 @@ def operation(cname, opname):
if opname not in campaign["operations"]:
abort(404)
operation = campaign["operations"][opname]
sortby = request.args.get("sort")
enabled = cname in config["enabled"] and opname in campaign["enabled"]
return render_template("campaigns/operation.mako",
cname=cname, campaign=campaign, opname=opname,
operation=operation, enabled=enabled)
operation=operation, sortby=sortby, enabled=enabled)

@blueprint.rroute("/settings/campaign", methods=["POST"])
def set_campaign():


+ 30
- 1
static/campaigns.css View File

@@ -218,7 +218,36 @@ h2 .disabled-info {
.operation.detail .detail-list li:not(:last-child)::after {
margin-left: 0.25em;
content: "/";
color: #AAA;
color: #777;
}

.change-sort {
display: inline-block;
margin: 0;
padding: 0;
}

.change-sort li {
display: inline-block;
}

.change-sort .cur {
font-weight: bold;
}

.change-sort::before {
content: "[";
color: #777;
}

.change-sort::after {
content: "]";
color: #777;
}

.change-sort li:not(:last-child)::after {
content: " |";
color: #777;
}

.last-updated {


+ 2
- 2
templates/campaigns/operation.mako View File

@@ -27,7 +27,7 @@
<%
mod = g.config.modules.campaigns
primary, secondary = mod.get_overview(cname, opname)
summary, renderer = mod.get_summary(cname, opname, limit=-1)
summary, renderer = mod.get_summary(cname, opname, sortby=sortby, limit=-1)
klass = "big" if primary < 1000 else "medium" if primary < 1000000 else "small"
punit = mod.get_unit(operation, primary)
sunit = mod.get_unit(operation, secondary, primary=False)
@@ -48,7 +48,7 @@
</div>
% if summary:
<div class="summary">
${render_summary(renderer, summary, detail=True)}
${render_summary(renderer, summary, detail=True, sortby=sortby)}
</div>
% endif
</div>


+ 38
- 5
templates/campaigns/renderers.mako View File

@@ -95,9 +95,32 @@
</td>
</tr>
</%def>
<%def name="_killboard_recent(summary, detail)">
<%def name="_build_sort_changer(keys, descriptors, sortby)">
% if keys:
<ul class="change-sort">
% for key in keys:
% if key == sortby:
<li class="cur">${descriptors[key]}</li>
% else:
<li><a href="${url_for('.operation', cname=cname, opname=opname, sort=key)}">${descriptors[key]}</a></li>
% endif
% endfor
</ul>
% endif
</%def>
<%def name="_killboard_recent(summary, detail, sortby)">
% if detail:
<%
descriptors = {
"new": "most recent first",
"old": "most recent last",
"value": "most valuable first"
}
if sortby not in descriptors:
sortby = "new"
%>
<h3 class="head">Kills:</h3>
${_build_sort_changer(["new", "old", "value"], descriptors, sortby)}
% else:
<div class="head">Most recent kills:</div>
% endif
@@ -113,9 +136,19 @@
</table>
</div>
</%def>
<%def name="_collection_items(summary, detail)">
<%def name="_collection_items(summary, detail, sortby)">
% if detail:
<%
descriptors = {
"value": "most valuable first",
"quantity": "greatest quantity first",
"price": "most expensive first"
}
if sortby not in descriptors:
sortby = "value"
%>
<h3 class="head">Items:</h3>
${_build_sort_changer(["value", "quantity", "price"], descriptors, sortby)}
% else:
<div class="head">Top items:</div>
% endif
@@ -128,10 +161,10 @@
</div>
</%def>

<%def name="render_summary(renderer, summary, detail=False)"><%
<%def name="render_summary(renderer, summary, detail=False, sortby=None)"><%
if renderer == "killboard_recent":
return _killboard_recent(summary, detail)
return _killboard_recent(summary, detail, sortby)
if renderer == "collection_items":
return _collection_items(summary, detail)
return _collection_items(summary, detail, sortby)
raise RuntimeError("Unknown renderer: %s" % renderer)
%></%def>

Loading…
Cancel
Save