diff --git a/src/service/api/routes.py b/src/service/api/routes.py index c0bc1b9..d8f4c3b 100644 --- a/src/service/api/routes.py +++ b/src/service/api/routes.py @@ -13,6 +13,8 @@ from service.api.views.api.loot import LootView from service.api.views.api.player import PlayerView from service.api.views.html.bis import BiSHtmlView 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 @@ -37,3 +39,9 @@ def setup_routes(app: Application) -> None: app.router.add_get('/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) diff --git a/src/service/api/views/common/loot_base.py b/src/service/api/views/common/loot_base.py index 483f20c..acc84cb 100644 --- a/src/service/api/views/common/loot_base.py +++ b/src/service/api/views/common/loot_base.py @@ -7,10 +7,11 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # 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.player import PlayerId, PlayerIdWithCounters +from service.models.upgrade import Upgrade class LootBaseView(View): @@ -34,7 +35,7 @@ class LootBaseView(View): return self.loot_remove(player_id, piece) 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) def loot_remove(self, player_id: PlayerId, piece: Piece) -> Piece: diff --git a/src/service/api/views/html/bis.py b/src/service/api/views/html/bis.py index 6944415..0553274 100644 --- a/src/service/api/views/html/bis.py +++ b/src/service/api/views/html/bis.py @@ -10,11 +10,10 @@ from aiohttp.web import HTTPFound, Response from aiohttp_jinja2 import template from typing import Any, Dict, List -from service.models.job import Job 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.player_base import PlayerBaseView @@ -23,8 +22,9 @@ class BiSHtmlView(BiSBaseView, PlayerBaseView): @template('bis.jinja2') async def get(self) -> Dict[str, Any]: - items: List[Dict[str, str]] = [] error = None + items: List[Dict[str, str]] = [] + players: List[Player] = [] try: players = self.player_get(None) @@ -43,8 +43,9 @@ class BiSHtmlView(BiSBaseView, PlayerBaseView): error = repr(e) return { + 'items': items, 'pieces': Piece.available(), - 'players': items, + 'players': [player.player_id.pretty_name for player in players], 'request_error': error } @@ -57,21 +58,21 @@ class BiSHtmlView(BiSBaseView, PlayerBaseView): try: 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': required = ['action', 'piece'] if any(param not in data for param in required): return wrap_invalid_param(required, data) - self.bis_post(data.get('action'), player_id, - Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})) + self.bis_post(data.get('action'), player_id, # type: ignore + Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)})) # type: ignore elif method == 'put': required = ['bis'] if any(param not in data for param in required): 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: self.request.app.logger.exception('could not manage bis') diff --git a/src/service/api/views/html/loot.py b/src/service/api/views/html/loot.py new file mode 100644 index 0000000..d341c24 --- /dev/null +++ b/src/service/api/views/html/loot.py @@ -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) diff --git a/src/service/api/views/html/loot_suggest.py b/src/service/api/views/html/loot_suggest.py new file mode 100644 index 0000000..7d4cf3a --- /dev/null +++ b/src/service/api/views/html/loot_suggest.py @@ -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 + } \ No newline at end of file diff --git a/src/service/api/views/html/player.py b/src/service/api/views/html/player.py index 8310456..6bc1f25 100644 --- a/src/service/api/views/html/player.py +++ b/src/service/api/views/html/player.py @@ -57,7 +57,7 @@ class PlayerHtmlView(PlayerBaseView): action = data.get('action') priority = data.get('priority', 0) 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: self.request.app.logger.exception('could not manage players') diff --git a/src/service/models/player.py b/src/service/models/player.py index 07f8ec1..6198c5d 100644 --- a/src/service/models/player.py +++ b/src/service/models/player.py @@ -29,8 +29,10 @@ class PlayerId: return '{} ({})'.format(self.nick, self.job.name) @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.*) \((?P[A-Z]+)\)$', value) + if matches is None: + return None return PlayerId(Job[matches.group('job')], matches.group('nick')) def __hash__(self) -> int: diff --git a/templates/bis.jinja2 b/templates/bis.jinja2 index 2ea7dcf..155a86a 100644 --- a/templates/bis.jinja2 +++ b/templates/bis.jinja2 @@ -13,7 +13,7 @@
{% for player in players %} - + {% endfor %} @@ -45,11 +45,11 @@ - {% for player in players %} + {% for item in items %} - {{ player.player|e }} - {{ player.piece|e }} - {{ player.is_tome|e }} + {{ item.player|e }} + {{ item.piece|e }} + {{ item.is_tome|e }} diff --git a/templates/index.jinja2 b/templates/index.jinja2 index a5bce83..a238931 100644 --- a/templates/index.jinja2 +++ b/templates/index.jinja2 @@ -8,6 +8,7 @@

party

bis

loot

+

suggest

diff --git a/templates/loot.jinja2 b/templates/loot.jinja2 new file mode 100644 index 0000000..9025c3a --- /dev/null +++ b/templates/loot.jinja2 @@ -0,0 +1,56 @@ + + + Loot + + {% include "style.jinja2" %} + + +

Loot

+ + {% include "error.jinja2" %} + {% include "search_line.jinja2" %} + + + + + + + +
+ + + + + + + + + + {% for item in items %} + + + + + + + {% endfor %} +
playerpieceis_tome
{{ item.is_tome|e }} +
+ + +
+
+ + {% include "export_to_csv.jinja2" %} + {% include "root.jinja2" %} + {% include "search.jinja2" %} + + diff --git a/templates/loot_suggest.jinja2 b/templates/loot_suggest.jinja2 new file mode 100644 index 0000000..d976dea --- /dev/null +++ b/templates/loot_suggest.jinja2 @@ -0,0 +1,48 @@ + + + Suggest loot + + {% include "style.jinja2" %} + + +

suggest loot

+ + {% include "error.jinja2" %} + {% include "search_line.jinja2" %} + +
+ + + + +
+ + + + + + + + + {% for player in suggest %} + + + + + + {% endfor %} +
playerbis pieces lootedtotal pieces looted
{{ player.loot_count_bis|e }}{{ player.loot_count|e }}
+ + {% include "export_to_csv.jinja2" %} + {% include "root.jinja2" %} + {% include "search.jinja2" %} + +