diff --git a/README.md b/README.md index 0193f6c..1cb1332 100644 --- a/README.md +++ b/README.md @@ -105,4 +105,5 @@ Service which allows to manage savage loot distribution easy. Web server related settings. * `host`: address to bind, string, required. - * `port`: port to bind, int, required. \ No newline at end of file + * `port`: port to bind, int, required. + * `templates`: path to directory with jinja templates, string, required. \ No newline at end of file diff --git a/src/service/api/routes.py b/src/service/api/routes.py index d8f4c3b..5b7a8bc 100644 --- a/src/service/api/routes.py +++ b/src/service/api/routes.py @@ -16,6 +16,7 @@ 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.static import StaticHtmlView def setup_routes(app: Application) -> None: @@ -33,6 +34,7 @@ def setup_routes(app: Application) -> None: # html routes app.router.add_get('/', IndexHtmlView) + app.router.add_get('/static/{resource_id}', StaticHtmlView) app.router.add_get('/party', PlayerHtmlView) app.router.add_post('/party', PlayerHtmlView) diff --git a/src/service/api/views/common/player_base.py b/src/service/api/views/common/player_base.py index 56c64ac..08e9a74 100644 --- a/src/service/api/views/common/player_base.py +++ b/src/service/api/views/common/player_base.py @@ -18,7 +18,7 @@ from service.models.player import Player, PlayerId class PlayerBaseView(View): def player_add(self, job: Job, nick: str, link: Optional[str], priority: int) -> PlayerId: - player = Player(job, nick, BiS(), [], link, priority) + player = Player(job, nick, BiS(), [], link, int(priority)) player_id = player.player_id self.request.app['party'].set_player(player) diff --git a/src/service/api/views/html/loot_suggest.py b/src/service/api/views/html/loot_suggest.py index d6a1e94..1d956b1 100644 --- a/src/service/api/views/html/loot_suggest.py +++ b/src/service/api/views/html/loot_suggest.py @@ -31,6 +31,7 @@ class LootSuggestHtmlView(LootBaseView, PlayerBaseView): async def post(self) -> Union[Dict[str, Any], Response]: data = await self.request.post() error = None + item_values: Dict[str, Any] = {} players: List[PlayerIdWithCounters] = [] required = ['piece'] @@ -40,20 +41,22 @@ class LootSuggestHtmlView(LootBaseView, PlayerBaseView): try: piece = Piece.get({'piece': data.get('piece'), 'is_tome': data.get('is_tome', False)}) players = self.loot_put(piece) + item_values = {'piece': piece.name, 'is_tome': piece.is_tome} except Exception as e: self.request.app.logger.exception('could not manage loot') error = repr(e) return { + 'item': item_values, 'pieces': Piece.available() + [upgrade.name for upgrade in Upgrade], + 'request_error': error, 'suggest': [ { 'player': player.pretty_name, - 'loot_count_bis': player.loot_count_bis, + 'loot_count_bis': player.loot_count_total_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 af7f1ab..1e0804d 100644 --- a/src/service/api/views/html/player.py +++ b/src/service/api/views/html/player.py @@ -38,7 +38,7 @@ class PlayerHtmlView(PlayerBaseView): { 'job': player.job.name, 'nick': player.nick, - 'loot_count_bis': player.loot_count_bis, + 'loot_count_bis': player.loot_count_total_bis, 'loot_count': player.loot_count, 'priority': player.priority } diff --git a/src/service/api/views/html/static.py b/src/service/api/views/html/static.py new file mode 100644 index 0000000..ad58c4d --- /dev/null +++ b/src/service/api/views/html/static.py @@ -0,0 +1,22 @@ +# 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 +# +import os + +from aiohttp.web import HTTPNotFound, Response, View + + +class StaticHtmlView(View): + + async def get(self) -> Response: + resource_name = self.request.match_info['resource_id'] + resource_path = os.path.join(self.request.app['templates_root'], 'static', resource_name) + if not os.path.exists(resource_path) or os.path.isdir(resource_path): + return HTTPNotFound() + + with open(resource_path) as resource_file: + return Response(text=resource_file.read(), content_type='text/css') \ No newline at end of file diff --git a/src/service/api/web.py b/src/service/api/web.py index 682577b..6fc5c46 100644 --- a/src/service/api/web.py +++ b/src/service/api/web.py @@ -41,7 +41,9 @@ def setup_service(config: Configuration, database: Database, loot: LootSelector, app.logger.info('setup routes') setup_routes(app) if config.has_option('web', 'templates'): - aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(config.get('web', 'templates'))) + templates_root = app['templates_root'] = config.get('web', 'templates') + app['static_root_url'] = '/static' + aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(templates_root)) app.logger.info('setup configuration') app['config'] = config diff --git a/src/service/models/player.py b/src/service/models/player.py index 6198c5d..d9981e4 100644 --- a/src/service/models/player.py +++ b/src/service/models/player.py @@ -45,6 +45,7 @@ class PlayerIdWithCounters(PlayerId): loot_count: int loot_count_bis: int loot_count_total: int + loot_count_total_bis: int @dataclass @@ -67,8 +68,9 @@ class Player: return PlayerId(self.job, self.nick) def player_id_with_counters(self, piece: Union[Piece, Upgrade, None]) -> PlayerIdWithCounters: - return PlayerIdWithCounters(self.job, self.nick, self.priority, self.loot_count(piece), - self.loot_count_bis(piece), self.loot_count_total(piece)) + return PlayerIdWithCounters(self.job, self.nick, self.priority, + abs(self.loot_count(piece)), abs(self.loot_count_bis(piece)), + abs(self.loot_count_total(piece)), abs(self.loot_count_total_bis(piece))) # ordering methods def is_required(self, piece: Union[Piece, Upgrade, None]) -> bool: @@ -89,14 +91,19 @@ class Player: def loot_count(self, piece: Union[Piece, Upgrade, None]) -> int: if piece is None: - return len(self.loot) - return self.loot.count(piece) + return -self.loot_count_total(piece) + return -self.loot.count(piece) - def loot_count_bis(self, _: Union[Piece, Upgrade, None]) -> int: - return len([piece for piece in self.loot if self.bis.has_piece(piece)]) + def loot_count_bis(self, piece: Union[Piece, Upgrade, None]) -> int: + if piece is None: + return -self.loot_count_total_bis(piece) + return -len([piece for piece in self.loot if self.bis.has_piece(piece)]) def loot_count_total(self, _: Union[Piece, Upgrade, None]) -> int: - return len(self.loot) + return -len(self.loot) + + def loot_count_total_bis(self, _: Union[Piece, Upgrade, None]) -> int: + return len([piece for piece in self.bis.pieces if not piece.is_tome]) def loot_priority(self, _: Union[Piece, Upgrade, None]) -> int: return self.priority diff --git a/templates/bis.jinja2 b/templates/bis.jinja2 index 155a86a..cb700a0 100644 --- a/templates/bis.jinja2 +++ b/templates/bis.jinja2 @@ -1,8 +1,9 @@ + Best in slot - {% include "style.jinja2" %} +

best in slot

@@ -24,7 +25,7 @@ - +
- +
@@ -52,8 +53,11 @@ diff --git a/templates/index.jinja2 b/templates/index.jinja2 index a238931..79fc00f 100644 --- a/templates/index.jinja2 +++ b/templates/index.jinja2 @@ -1,14 +1,17 @@ +FFXIV loot helper + +
-

party

-

bis

-

loot

-

suggest

+

party

+

bis

+

loot

+

suggest

diff --git a/templates/loot.jinja2 b/templates/loot.jinja2 index 9025c3a..d5a6374 100644 --- a/templates/loot.jinja2 +++ b/templates/loot.jinja2 @@ -1,8 +1,9 @@ +Loot - {% include "style.jinja2" %} +

Loot

@@ -23,7 +24,7 @@ - +
{{ item.is_tome|e }}
+ + + - +
@@ -41,8 +42,11 @@ diff --git a/templates/loot_suggest.jinja2 b/templates/loot_suggest.jinja2 index 4b4dc63..08b846f 100644 --- a/templates/loot_suggest.jinja2 +++ b/templates/loot_suggest.jinja2 @@ -1,8 +1,9 @@ +Suggest loot - {% include "style.jinja2" %} +

suggest loot

@@ -17,7 +18,7 @@ {% endfor %} - +
{{ item.is_tome|e }}
+ + + - +
@@ -25,6 +26,7 @@ + {% for player in suggest %} @@ -32,6 +34,14 @@ + {% endfor %}
player bis pieces looted total pieces looted
{{ player.loot_count_bis|e }} {{ player.loot_count|e }} +
+ + + + +
+
diff --git a/templates/party.jinja2 b/templates/party.jinja2 index f8b29c4..747470d 100644 --- a/templates/party.jinja2 +++ b/templates/party.jinja2 @@ -1,8 +1,9 @@ + Party - {% include "style.jinja2" %} +

party

@@ -20,7 +21,7 @@ - + @@ -42,8 +43,10 @@ diff --git a/templates/static/styles.css b/templates/static/styles.css new file mode 100644 index 0000000..c5be043 --- /dev/null +++ b/templates/static/styles.css @@ -0,0 +1,277 @@ +/* in-text images */ +figure.img { + float: right; + border: 0px solid #333; + padding: 0px; + margin: 5px 0px 5px 10px; +} +figure.img img { + max-width: 100%; + height: auto; +} +figure.img figcaption { + margin: 0px; + font-size: 90%; + font-style: italic; + text-align: center; +} + +h1 .octicon-link, h2 .octicon-link, h3 .octicon-link, h4 .octicon-link, h5 .octicon-link, h6 .octicon-link { + display: none; + color: #222222; + vertical-align: middle; +} + +h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, h4:hover .anchor, h5:hover .anchor, h6:hover .anchor{ + padding-left: 8px; + margin-left: -24px; + text-decoration: none; +} + +h1:hover .anchor .octicon-link, h2:hover .anchor .octicon-link, h3:hover .anchor .octicon-link, h4:hover .anchor .octicon-link, h5:hover .anchor .octicon-link, h6:hover .anchor .octicon-link { + display: inline-block; +} + +body { + padding: 50px; + font: 14px/1.5 "Liberation Sans", Helvetica, Arial, sans-serif; + color: #555555; + background: #eaeaea +} + +h1, h2, h3, h4, h5, h6 { + color: #222222; + margin: 0 0 20px; +} + +p, ul, ol, table, pre, dl { + margin: 0 0 20px; + text-align: justify; +} + +h1, h2, h3 { + line-height: 1.1; +} + +h1 { + font-size: 28px; +} + +h2 { + color: #393939; +} + +h3, h4, h5, h6 { + color: #494949; +} + +a { + color: #3399cc; + font-weight: 350; + text-decoration: none; +} + +a small { + font-size: 11px; + color: #777777; + margin-top: -0.6em; + display: block; +} + +.wrapper { + width: 80%; + margin: 0 auto; +} + +blockquote { + border-left: 1px solid #ffffff; + margin: 0; + padding: 0 0 0 20px; + font-style: italic; +} + +code, pre { + font-family: "Liberation Mono", Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + color: #222222; + font-size: 12px; +} + +pre { + padding: 8px 15px; + border-radius: 5px; + border: 1px solid #e5e5e5; + overflow-x: auto; + overflow-y: auto; +} + +input, select{ + box-sizing: border-box; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, td { + padding: 5px 10px; + border-bottom: 1px solid #ffffff; +} + +td { + text-align: justify; +} + +dt { + color: #444444; + font-weight: 700; +} + +th { + text-align: left; + color: #444444; +} + +img { + max-width: 100%; +} + +header { + width: 20%; + float: left; + position: fixed; +} + +header ul { + list-style: none; + height: 40px; + padding: 0; + background: #eeeeee; + border-radius: 5px; + border: 1px solid #d2d2d2; + box-shadow: inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0; + width: 15%; +} + +header li { + width: 8%; + float: left; + border-right: 1px solid #d2d2d2; + height: 40px; +} + +header ul a { + line-height: 1; + font-size: 11px; + color: #999999; + display: block; + text-align: center; + padding-top: 6px; + height: 40px; +} + +strong { + color: #222222; + font-weight: 700; +} + +header ul li + li { + width: 8%; + border-left: 1px solid #ffffff; +} + +header ul li + li + li { + width: 8%; + border-right: none; +} + +header ul a strong { + font-size: 14px; + display: block; + color: #222222; +} + +section { + width: 70%; + float: right; + padding-bottom: 50px; +} + +small { + font-size: 11px; +} + +hr { + border: 0; + background: #ffffff; + height: 1px; + margin: 0 0 20px; +} + +footer { + width: 20%; + float: left; + position: fixed; + bottom: 50px; +} + +@media print, screen and (max-width: 960px) { + div.wrapper { + width: auto; + margin: 0; + } + header, section, footer { + float: none; + position: static; + width: auto; + } + header { + padding-right: 320px; + } + section { + border: 1px solid #e5e5e5; + border-width: 1px 0; + padding: 20px 0; + margin: 0 0 20px; + } + header a small { + display: inline; + } + header ul { + position: absolute; + right: 50px; + top: 52px; + } +} + +@media print, screen and (max-width: 720px) { + body { + word-wrap: break-word; + } + header { + padding: 0; + } + header ul, header p.view { + position: static; + } + pre, code { + word-wrap: normal; + } +} + +@media print, screen and (max-width: 480px) { + body { + padding: 15px; + } + header ul { + display: none; + } +} + +@media print { + body { + padding: 0.4in; + font-size: 12pt; + color: #444444; + } +} diff --git a/templates/style.jinja2 b/templates/style.jinja2 deleted file mode 100644 index 30da561..0000000 --- a/templates/style.jinja2 +++ /dev/null @@ -1,6 +0,0 @@ -
{{ player.priority|e }}
+ + - +