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 @@
{{ item.is_tome|e }} |
|
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
+
+
+
+
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 @@
-
+
@@ -41,8 +42,11 @@
{{ item.is_tome|e }} |
|
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 %}
-
+
@@ -25,6 +26,7 @@
player |
bis pieces looted |
total pieces looted |
+ |
{% for player in suggest %}
@@ -32,6 +34,14 @@
{{ player.player|e }} |
{{ player.loot_count_bis|e }} |
{{ player.loot_count|e }} |
+
+
+ |
{% endfor %}
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 @@
{{ player.priority|e }} |
|
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 @@
-