add loot interfaces

This commit is contained in:
Evgenii Alekseev 2019-09-08 03:50:55 +03:00
parent 9bd51d9267
commit 2ea8fed183
11 changed files with 264 additions and 19 deletions

View File

@ -13,6 +13,8 @@ from service.api.views.api.loot import LootView
from service.api.views.api.player import PlayerView from service.api.views.api.player import PlayerView
from service.api.views.html.bis import BiSHtmlView from service.api.views.html.bis import BiSHtmlView
from service.api.views.html.index import IndexHtmlView from service.api.views.html.index import IndexHtmlView
from service.api.views.html.loot import LootHtmlView
from service.api.views.html.loot_suggest import LootSuggestHtmlView
from service.api.views.html.player import PlayerHtmlView from service.api.views.html.player import PlayerHtmlView
@ -37,3 +39,9 @@ def setup_routes(app: Application) -> None:
app.router.add_get('/bis', BiSHtmlView) app.router.add_get('/bis', BiSHtmlView)
app.router.add_post('/bis', BiSHtmlView) app.router.add_post('/bis', BiSHtmlView)
app.router.add_get('/loot', LootHtmlView)
app.router.add_post('/loot', LootHtmlView)
app.router.add_get('/suggest', LootSuggestHtmlView)
app.router.add_post('/suggest', LootSuggestHtmlView)

View File

@ -7,10 +7,11 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from aiohttp.web import View from aiohttp.web import View
from typing import List, Optional from typing import List, Optional, Union
from service.models.piece import Piece from service.models.piece import Piece
from service.models.player import PlayerId, PlayerIdWithCounters from service.models.player import PlayerId, PlayerIdWithCounters
from service.models.upgrade import Upgrade
class LootBaseView(View): class LootBaseView(View):
@ -34,7 +35,7 @@ class LootBaseView(View):
return self.loot_remove(player_id, piece) return self.loot_remove(player_id, piece)
return None return None
def loot_put(self, piece: Piece) -> List[PlayerIdWithCounters]: def loot_put(self, piece: Union[Piece, Upgrade]) -> List[PlayerIdWithCounters]:
return self.request.app['loot'].suggest(piece) return self.request.app['loot'].suggest(piece)
def loot_remove(self, player_id: PlayerId, piece: Piece) -> Piece: def loot_remove(self, player_id: PlayerId, piece: Piece) -> Piece:

View File

@ -10,11 +10,10 @@ from aiohttp.web import HTTPFound, Response
from aiohttp_jinja2 import template from aiohttp_jinja2 import template
from typing import Any, Dict, List from typing import Any, Dict, List
from service.models.job import Job
from service.models.piece import Piece from service.models.piece import Piece
from service.models.player import PlayerId from service.models.player import Player, PlayerId
from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json from service.api.utils import wrap_exception, wrap_invalid_param
from service.api.views.common.bis_base import BiSBaseView from service.api.views.common.bis_base import BiSBaseView
from service.api.views.common.player_base import PlayerBaseView from service.api.views.common.player_base import PlayerBaseView
@ -23,8 +22,9 @@ class BiSHtmlView(BiSBaseView, PlayerBaseView):
@template('bis.jinja2') @template('bis.jinja2')
async def get(self) -> Dict[str, Any]: async def get(self) -> Dict[str, Any]:
items: List[Dict[str, str]] = []
error = None error = None
items: List[Dict[str, str]] = []
players: List[Player] = []
try: try:
players = self.player_get(None) players = self.player_get(None)
@ -43,8 +43,9 @@ class BiSHtmlView(BiSBaseView, PlayerBaseView):
error = repr(e) error = repr(e)
return { return {
'items': items,
'pieces': Piece.available(), 'pieces': Piece.available(),
'players': items, 'players': [player.player_id.pretty_name for player in players],
'request_error': error 'request_error': error
} }
@ -57,21 +58,21 @@ class BiSHtmlView(BiSBaseView, PlayerBaseView):
try: try:
method = data.get('method') method = data.get('method')
player_id = PlayerId.from_pretty_name(data.get('player')) player_id = PlayerId.from_pretty_name(data.get('player')) # type: ignore
if method == 'post': if method == 'post':
required = ['action', 'piece'] required = ['action', 'piece']
if any(param not in data for param in required): if any(param not in data for param in required):
return wrap_invalid_param(required, data) return wrap_invalid_param(required, data)
self.bis_post(data.get('action'), player_id, self.bis_post(data.get('action'), player_id, # type: ignore
Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})) Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})) # type: ignore
elif method == 'put': elif method == 'put':
required = ['bis'] required = ['bis']
if any(param not in data for param in required): if any(param not in data for param in required):
return wrap_invalid_param(required, data) return wrap_invalid_param(required, data)
self.bis_put(player_id, data.get('bis')) self.bis_put(player_id, data.get('bis')) # type: ignore
except Exception as e: except Exception as e:
self.request.app.logger.exception('could not manage bis') self.request.app.logger.exception('could not manage bis')

View File

@ -0,0 +1,68 @@
#
# Copyright (c) 2019 Evgeniy Alekseev.
#
# This file is part of ffxivbis
# (see https://github.com/arcan1s/ffxivbis).
#
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
#
from aiohttp.web import HTTPFound, Response
from aiohttp_jinja2 import template
from typing import Any, Dict, List
from service.models.piece import Piece
from service.models.player import Player, PlayerId
from service.api.utils import wrap_exception, wrap_invalid_param
from service.api.views.common.loot_base import LootBaseView
from service.api.views.common.player_base import PlayerBaseView
class LootHtmlView(LootBaseView, PlayerBaseView):
@template('loot.jinja2')
async def get(self) -> Dict[str, Any]:
error = None
items: List[Dict[str, str]] = []
players: List[Player] = []
try:
players = self.player_get(None)
items = [
{
'player': player.player_id.pretty_name,
'piece': piece.name,
'is_tome': 'yes' if piece.is_tome else 'no' # type: ignore
}
for player in players
for piece in player.loot
]
except Exception as e:
self.request.app.logger.exception('could not get loot')
error = repr(e)
return {
'items': items,
'pieces': Piece.available(),
'players': [player.player_id.pretty_name for player in players],
'request_error': error
}
async def post(self) -> Response:
data = await self.request.post()
required = ['action', 'piece', 'player']
if any(param not in data for param in required):
return wrap_invalid_param(required, data)
try:
player_id = PlayerId.from_pretty_name(data.get('player')) # type: ignore
self.loot_post(data.get('action'), player_id, # type: ignore
Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})) # type: ignore
except Exception as e:
self.request.app.logger.exception('could not manage loot')
return wrap_exception(e, data)
return HTTPFound(self.request.url)

View File

@ -0,0 +1,60 @@
#
# Copyright (c) 2019 Evgeniy Alekseev.
#
# This file is part of ffxivbis
# (see https://github.com/arcan1s/ffxivbis).
#
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
#
from aiohttp.web import Response
from aiohttp_jinja2 import template
from typing import Any, Dict, List, Union
from service.models.piece import Piece
from service.models.player import Player, PlayerIdWithCounters
from service.api.utils import wrap_invalid_param
from service.api.views.common.loot_base import LootBaseView
from service.api.views.common.player_base import PlayerBaseView
class LootSuggestHtmlView(LootBaseView, PlayerBaseView):
@template('loot_suggest.jinja2')
async def get(self) -> Dict[str, Any]:
return {
'pieces': Piece.available(),
'players': [player.player_id.pretty_name for player in self.player_get(None)],
}
@template('loot_suggest.jinja2')
async def post(self) -> Union[Dict[str, Any], Response]:
data = await self.request.post()
error = None
players: List[PlayerIdWithCounters] = []
required = ['piece']
if any(param not in data for param in required):
return wrap_invalid_param(required, data)
try:
piece = Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})
players = self.loot_put(piece)
except Exception as e:
self.request.app.logger.exception('could not manage loot')
error = repr(e)
return {
'pieces': Piece.available(),
'players': [player.player_id.pretty_name for player in self.player_get(None)],
'suggest': [
{
'player': player.pretty_name,
'loot_count_bis': player.loot_count_bis,
'loot_count': player.loot_count,
}
for player in players
],
'request_error': error
}

View File

@ -57,7 +57,7 @@ class PlayerHtmlView(PlayerBaseView):
action = data.get('action') action = data.get('action')
priority = data.get('priority', 0) priority = data.get('priority', 0)
link = data.get('bis', None) link = data.get('bis', None)
self.player_post(action, Job[data['job'].upper()], data['nick'], link, priority) self.player_post(action, Job[data['job'].upper()], data['nick'], link, priority) # type: ignore
except Exception as e: except Exception as e:
self.request.app.logger.exception('could not manage players') self.request.app.logger.exception('could not manage players')

View File

@ -29,8 +29,10 @@ class PlayerId:
return '{} ({})'.format(self.nick, self.job.name) return '{} ({})'.format(self.nick, self.job.name)
@classmethod @classmethod
def from_pretty_name(cls: Type[PlayerId], value: str) -> PlayerId: def from_pretty_name(cls: Type[PlayerId], value: str) -> Optional[PlayerId]:
matches = re.search('^(?P<nick>.*) \((?P<job>[A-Z]+)\)$', value) matches = re.search('^(?P<nick>.*) \((?P<job>[A-Z]+)\)$', value)
if matches is None:
return None
return PlayerId(Job[matches.group('job')], matches.group('nick')) return PlayerId(Job[matches.group('job')], matches.group('nick'))
def __hash__(self) -> int: def __hash__(self) -> int:

View File

@ -13,7 +13,7 @@
<form action="/bis" method="post"> <form action="/bis" method="post">
<select name="player" id="player" title="player"> <select name="player" id="player" title="player">
{% for player in players %} {% for player in players %}
<option>{{ player.player|e }}</option> <option>{{ player|e }}</option>
{% endfor %} {% endfor %}
</select> </select>
<select name="piece" id="piece" title="piece"> <select name="piece" id="piece" title="piece">
@ -29,7 +29,7 @@
<form action="/bis" method="post"> <form action="/bis" method="post">
<select name="player" id="player" title="player"> <select name="player" id="player" title="player">
{% for player in players %} {% for player in players %}
<option>{{ player.player|e }}</option> <option>{{ player|e }}</option>
{% endfor %} {% endfor %}
</select> </select>
<input name="bis" id="bis" placeholder="player bis link" title="bis" type="text"/> <input name="bis" id="bis" placeholder="player bis link" title="bis" type="text"/>
@ -45,11 +45,11 @@
<th></th> <th></th>
</tr> </tr>
{% for player in players %} {% for item in items %}
<tr> <tr>
<td class="include_search">{{ player.player|e }}</td> <td class="include_search">{{ item.player|e }}</td>
<td class="include_search">{{ player.piece|e }}</td> <td class="include_search">{{ item.piece|e }}</td>
<td>{{ player.is_tome|e }}</td> <td>{{ item.is_tome|e }}</td>
<td> <td>
<form action="/bis" method="post"> <form action="/bis" method="post">
<input name="action" id="action" type="hidden" value="remove"/> <input name="action" id="action" type="hidden" value="remove"/>

View File

@ -8,6 +8,7 @@
<a href="/party" title="party"><h2>party</h2></a> <a href="/party" title="party"><h2>party</h2></a>
<a href="/bis" title="bis management"><h2>bis</h2></a> <a href="/bis" title="bis management"><h2>bis</h2></a>
<a href="/loot" title="loot management"><h2>loot</h2></a> <a href="/loot" title="loot management"><h2>loot</h2></a>
<a href="/suggest" title="suggest loot"><h2>suggest</h2></a>
</center> </center>
</body> </body>
</html> </html>

56
templates/loot.jinja2 Normal file
View File

@ -0,0 +1,56 @@
<html lang="en">
<head>
<title>Loot</title>
{% include "style.jinja2" %}
</head>
<body>
<h2>Loot</h2>
{% include "error.jinja2" %}
{% include "search_line.jinja2" %}
<form action="/loot" method="post">
<select name="player" id="player" title="player">
{% for player in players %}
<option>{{ player|e }}</option>
{% endfor %}
</select>
<select name="piece" id="piece" title="piece">
{% for piece in pieces %}
<option>{{ piece|e }}</option>
{% endfor %}
</select>
<input name="is_tome" id="is_tome" title="is tome" type="checkbox"/>
<input name="action" id="action" type="hidden" value="add"/>
<button>add</button>
</form>
<table id="result">
<tr>
<th>player</th>
<th>piece</th>
<th>is_tome</th>
<th></th>
</tr>
{% for item in items %}
<tr>
<td class="include_search">{{ item.player|e }}</td>
<td class="include_search">{{ item.piece|e }}</td>
<td>{{ item.is_tome|e }}</td>
<td>
<form action="/loot" method="post">
<input name="action" id="action" type="hidden" value="remove"/>
<button>remove</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% include "export_to_csv.jinja2" %}
{% include "root.jinja2" %}
{% include "search.jinja2" %}
</body>
</html>

View File

@ -0,0 +1,48 @@
<html lang="en">
<head>
<title>Suggest loot</title>
{% include "style.jinja2" %}
</head>
<body>
<h2>suggest loot</h2>
{% include "error.jinja2" %}
{% include "search_line.jinja2" %}
<form action="/suggest" method="post">
<select name="player" id="player" title="player">
{% for player in players %}
<option>{{ player|e }}</option>
{% endfor %}
</select>
<select name="piece" id="piece" title="piece">
{% for piece in pieces %}
<option>{{ piece|e }}</option>
{% endfor %}
</select>
<input name="is_tome" id="is_tome" title="is tome" type="checkbox"/>
<button>suggest</button>
</form>
<table id="result">
<tr>
<th>player</th>
<th>bis pieces looted</th>
<th>total pieces looted</th>
</tr>
{% for player in suggest %}
<tr>
<td class="include_search">{{ player.player|e }}</td>
<td>{{ player.loot_count_bis|e }}</td>
<td>{{ player.loot_count|e }}</td>
</tr>
{% endfor %}
</table>
{% include "export_to_csv.jinja2" %}
{% include "root.jinja2" %}
{% include "search.jinja2" %}
</body>
</html>