mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-06-28 23:01:41 +00:00
some html implementation
This commit is contained in:
@ -11,9 +11,13 @@ from aiohttp.web import Application
|
|||||||
from service.api.views.api.bis import BiSView
|
from service.api.views.api.bis import BiSView
|
||||||
from service.api.views.api.loot import LootView
|
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.index import IndexHtmlView
|
||||||
|
from service.api.views.html.player import PlayerHtmlView
|
||||||
|
|
||||||
|
|
||||||
def setup_routes(app: Application) -> None:
|
def setup_routes(app: Application) -> None:
|
||||||
|
# api routes
|
||||||
app.router.add_get('/api/v1/party', PlayerView)
|
app.router.add_get('/api/v1/party', PlayerView)
|
||||||
app.router.add_post('/api/v1/party', PlayerView)
|
app.router.add_post('/api/v1/party', PlayerView)
|
||||||
|
|
||||||
@ -23,4 +27,13 @@ def setup_routes(app: Application) -> None:
|
|||||||
|
|
||||||
app.router.add_get('/api/v1/party/loot', LootView)
|
app.router.add_get('/api/v1/party/loot', LootView)
|
||||||
app.router.add_post('/api/v1/party/loot', LootView)
|
app.router.add_post('/api/v1/party/loot', LootView)
|
||||||
app.router.add_put('/api/v1/party/loot', LootView)
|
app.router.add_put('/api/v1/party/loot', LootView)
|
||||||
|
|
||||||
|
# html routes
|
||||||
|
app.router.add_get('/', IndexHtmlView)
|
||||||
|
|
||||||
|
app.router.add_get('/party', PlayerHtmlView)
|
||||||
|
app.router.add_post('/party', PlayerHtmlView)
|
||||||
|
|
||||||
|
app.router.add_get('/bis', BiSHtmlView)
|
||||||
|
app.router.add_post('/bis', BiSHtmlView)
|
||||||
|
@ -21,7 +21,7 @@ class PlayerView(PlayerBaseView):
|
|||||||
party = self.player_get(self.request.query.getone('nick', None))
|
party = self.player_get(self.request.query.getone('nick', None))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.request.app.logger.exception('could not get loot')
|
self.request.app.logger.exception('could not get party')
|
||||||
return wrap_exception(e, self.request.query)
|
return wrap_exception(e, self.request.query)
|
||||||
|
|
||||||
return wrap_json(party, self.request.query)
|
return wrap_json(party, self.request.query)
|
||||||
|
@ -22,7 +22,7 @@ class PlayerBaseView(View):
|
|||||||
player_id = player.player_id
|
player_id = player.player_id
|
||||||
self.request.app['party'].set_player(player)
|
self.request.app['party'].set_player(player)
|
||||||
|
|
||||||
if link is not None:
|
if link:
|
||||||
parser = AriyalaParser(self.request.app['config'])
|
parser = AriyalaParser(self.request.app['config'])
|
||||||
items = parser.get(link)
|
items = parser.get(link)
|
||||||
for piece in items:
|
for piece in items:
|
||||||
|
80
src/service/api/views/html/bis.py
Normal file
80
src/service/api/views/html/bis.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#
|
||||||
|
# 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.job import Job
|
||||||
|
from service.models.piece import Piece
|
||||||
|
from service.models.player import PlayerId
|
||||||
|
|
||||||
|
from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json
|
||||||
|
from service.api.views.common.bis_base import BiSBaseView
|
||||||
|
from service.api.views.common.player_base import PlayerBaseView
|
||||||
|
|
||||||
|
|
||||||
|
class BiSHtmlView(BiSBaseView, PlayerBaseView):
|
||||||
|
|
||||||
|
@template('bis.jinja2')
|
||||||
|
async def get(self) -> Dict[str, Any]:
|
||||||
|
items: List[Dict[str, str]] = []
|
||||||
|
error = None
|
||||||
|
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
for player in players
|
||||||
|
for piece in player.bis.pieces
|
||||||
|
]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.request.app.logger.exception('could not get bis')
|
||||||
|
error = repr(e)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'pieces': Piece.available(),
|
||||||
|
'players': items,
|
||||||
|
'request_error': error
|
||||||
|
}
|
||||||
|
|
||||||
|
async def post(self) -> Response:
|
||||||
|
data = await self.request.post()
|
||||||
|
|
||||||
|
required = ['method', 'player']
|
||||||
|
if any(param not in data for param in required):
|
||||||
|
return wrap_invalid_param(required, data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
method = data.get('method')
|
||||||
|
player_id = PlayerId.from_pretty_name(data.get('player'))
|
||||||
|
|
||||||
|
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)}))
|
||||||
|
|
||||||
|
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'))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.request.app.logger.exception('could not manage bis')
|
||||||
|
return wrap_exception(e, data)
|
||||||
|
|
||||||
|
return HTTPFound(self.request.url)
|
18
src/service/api/views/html/index.py
Normal file
18
src/service/api/views/html/index.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# 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 View
|
||||||
|
from aiohttp_jinja2 import template
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
|
||||||
|
class IndexHtmlView(View):
|
||||||
|
|
||||||
|
@template('index.jinja2')
|
||||||
|
async def get(self) -> Dict[str, Any]:
|
||||||
|
return {}
|
66
src/service/api/views/html/player.py
Normal file
66
src/service/api/views/html/player.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#
|
||||||
|
# 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.job import Job
|
||||||
|
from service.models.player import PlayerIdWithCounters
|
||||||
|
|
||||||
|
from service.api.utils import wrap_exception, wrap_invalid_param
|
||||||
|
from service.api.views.common.player_base import PlayerBaseView
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerHtmlView(PlayerBaseView):
|
||||||
|
|
||||||
|
@template('party.jinja2')
|
||||||
|
async def get(self) -> Dict[str, Any]:
|
||||||
|
counters: List[PlayerIdWithCounters] = []
|
||||||
|
error = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
party = self.player_get(None)
|
||||||
|
counters = [player.player_id_with_counters(None) for player in party]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.request.app.logger.exception('could not get party')
|
||||||
|
error = repr(e)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'players': [
|
||||||
|
{
|
||||||
|
'job': player.job.name,
|
||||||
|
'nick': player.nick,
|
||||||
|
'loot_count_bis': player.loot_count_bis,
|
||||||
|
'loot_count': player.loot_count,
|
||||||
|
'priority': player.priority
|
||||||
|
}
|
||||||
|
for player in counters
|
||||||
|
],
|
||||||
|
'request_error': error
|
||||||
|
}
|
||||||
|
|
||||||
|
async def post(self) -> Response:
|
||||||
|
data = await self.request.post()
|
||||||
|
|
||||||
|
required = ['action', 'job', 'nick']
|
||||||
|
if any(param not in data for param in required):
|
||||||
|
return wrap_invalid_param(required, data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
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)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.request.app.logger.exception('could not manage players')
|
||||||
|
return wrap_exception(e, data)
|
||||||
|
|
||||||
|
return HTTPFound(self.request.url)
|
@ -6,6 +6,8 @@
|
|||||||
#
|
#
|
||||||
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
|
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
|
||||||
#
|
#
|
||||||
|
import aiohttp_jinja2
|
||||||
|
import jinja2
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
@ -38,6 +40,8 @@ def setup_service(config: Configuration, database: Database, loot: LootSelector,
|
|||||||
# routes
|
# routes
|
||||||
app.logger.info('setup routes')
|
app.logger.info('setup routes')
|
||||||
setup_routes(app)
|
setup_routes(app)
|
||||||
|
if config.has_option('web', 'templates'):
|
||||||
|
aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader(config.get('web', 'templates')))
|
||||||
|
|
||||||
app.logger.info('setup configuration')
|
app.logger.info('setup configuration')
|
||||||
app['config'] = config
|
app['config'] = config
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Mapping, Type, Union
|
from typing import Any, List, Mapping, Type, Union
|
||||||
|
|
||||||
from .upgrade import Upgrade
|
from .upgrade import Upgrade
|
||||||
|
|
||||||
@ -33,11 +33,19 @@ class Piece:
|
|||||||
return Upgrade.GearUpgrade
|
return Upgrade.GearUpgrade
|
||||||
return Upgrade.NoUpgrade
|
return Upgrade.NoUpgrade
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def available() -> List[str]:
|
||||||
|
return [
|
||||||
|
'weapon',
|
||||||
|
'head', 'body', 'hands', 'waist', 'legs', 'feet',
|
||||||
|
'ears', 'neck', 'wrist', 'left_ring', 'right_ring'
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls: Type[Piece], data: Mapping[str, Any]) -> Union[Piece, Upgrade]:
|
def get(cls: Type[Piece], data: Mapping[str, Any]) -> Union[Piece, Upgrade]:
|
||||||
try:
|
try:
|
||||||
piece_type = data['piece']
|
piece_type = data['piece']
|
||||||
is_tome = bool(data['is_tome'])
|
is_tome = data['is_tome'] in ('yes', 'on', '1', 1, True)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise InvalidDataRow(data)
|
raise InvalidDataRow(data)
|
||||||
if piece_type.lower() == 'weapon':
|
if piece_type.lower() == 'weapon':
|
||||||
|
@ -6,8 +6,12 @@
|
|||||||
#
|
#
|
||||||
# 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 __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Type, Union
|
||||||
|
|
||||||
from .bis import BiS
|
from .bis import BiS
|
||||||
from .job import Job
|
from .job import Job
|
||||||
@ -20,12 +24,22 @@ class PlayerId:
|
|||||||
job: Job
|
job: Job
|
||||||
nick: str
|
nick: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pretty_name(self) -> str:
|
||||||
|
return '{} ({})'.format(self.nick, self.job.name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_pretty_name(cls: Type[PlayerId], value: str) -> PlayerId:
|
||||||
|
matches = re.search('^(?P<nick>.*) \((?P<job>[A-Z]+)\)$', value)
|
||||||
|
return PlayerId(Job[matches.group('job')], matches.group('nick'))
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(str(self))
|
return hash(str(self))
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PlayerIdWithCounters(PlayerId):
|
class PlayerIdWithCounters(PlayerId):
|
||||||
|
priority: int
|
||||||
loot_count: int
|
loot_count: int
|
||||||
loot_count_bis: int
|
loot_count_bis: int
|
||||||
loot_count_total: int
|
loot_count_total: int
|
||||||
@ -50,12 +64,15 @@ class Player:
|
|||||||
def player_id(self) -> PlayerId:
|
def player_id(self) -> PlayerId:
|
||||||
return PlayerId(self.job, self.nick)
|
return PlayerId(self.job, self.nick)
|
||||||
|
|
||||||
def player_id_with_counters(self, piece: Union[Piece, Upgrade]) -> PlayerIdWithCounters:
|
def player_id_with_counters(self, piece: Union[Piece, Upgrade, None]) -> PlayerIdWithCounters:
|
||||||
return PlayerIdWithCounters(self.job, self.nick, self.loot_count(piece),
|
return PlayerIdWithCounters(self.job, self.nick, self.priority, self.loot_count(piece),
|
||||||
self.loot_count_bis(piece), self.loot_count_total(piece))
|
self.loot_count_bis(piece), self.loot_count_total(piece))
|
||||||
|
|
||||||
# ordering methods
|
# ordering methods
|
||||||
def is_required(self, piece: Union[Piece, Upgrade]) -> bool:
|
def is_required(self, piece: Union[Piece, Upgrade, None]) -> bool:
|
||||||
|
if piece is None:
|
||||||
|
return False
|
||||||
|
|
||||||
# lets check if it is even in bis
|
# lets check if it is even in bis
|
||||||
if not self.bis.has_piece(piece):
|
if not self.bis.has_piece(piece):
|
||||||
return False
|
return False
|
||||||
@ -68,14 +85,16 @@ class Player:
|
|||||||
return self.bis.upgrades_required[piece] > self.loot_count(piece)
|
return self.bis.upgrades_required[piece] > self.loot_count(piece)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def loot_count(self, piece: Union[Piece, Upgrade]) -> int:
|
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(piece)
|
||||||
|
|
||||||
def loot_count_bis(self, _: Union[Piece, Upgrade]) -> int:
|
def loot_count_bis(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||||
return len([piece for piece in self.loot if self.bis.has_piece(piece)])
|
return len([piece for piece in self.loot if self.bis.has_piece(piece)])
|
||||||
|
|
||||||
def loot_count_total(self, _: Union[Piece, Upgrade]) -> int:
|
def loot_count_total(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||||
return len(self.loot)
|
return len(self.loot)
|
||||||
|
|
||||||
def loot_priority(self, _: Union[Piece, Upgrade]) -> int:
|
def loot_priority(self, _: Union[Piece, Upgrade, None]) -> int:
|
||||||
return self.priority
|
return self.priority
|
||||||
|
67
templates/bis.jinja2
Normal file
67
templates/bis.jinja2
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Best in slot</title>
|
||||||
|
|
||||||
|
{% include "style.jinja2" %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>best in slot</h2>
|
||||||
|
|
||||||
|
{% include "error.jinja2" %}
|
||||||
|
{% include "search_line.jinja2" %}
|
||||||
|
|
||||||
|
<form action="/bis" method="post">
|
||||||
|
<select name="player" id="player" title="player">
|
||||||
|
{% for player in players %}
|
||||||
|
<option>{{ player.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"/>
|
||||||
|
<input name="method" id="method" type="hidden" value="post"/>
|
||||||
|
<button>add</button>
|
||||||
|
</form>
|
||||||
|
<form action="/bis" method="post">
|
||||||
|
<select name="player" id="player" title="player">
|
||||||
|
{% for player in players %}
|
||||||
|
<option>{{ player.player|e }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input name="bis" id="bis" placeholder="player bis link" title="bis" type="text"/>
|
||||||
|
<input name="method" id="method" type="hidden" value="put"/>
|
||||||
|
<button>add</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<table id="result">
|
||||||
|
<tr>
|
||||||
|
<th>player</th>
|
||||||
|
<th>piece</th>
|
||||||
|
<th>is_tome</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for player in players %}
|
||||||
|
<tr>
|
||||||
|
<td class="include_search">{{ player.player|e }}</td>
|
||||||
|
<td class="include_search">{{ player.piece|e }}</td>
|
||||||
|
<td>{{ player.is_tome|e }}</td>
|
||||||
|
<td>
|
||||||
|
<form action="/bis" 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>
|
3
templates/error.jinja2
Normal file
3
templates/error.jinja2
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{% if request_error is defined and request_error is not none %}
|
||||||
|
<p id="error">Error occurs: {{ request_error|e }}</p>
|
||||||
|
{% endif %}
|
35
templates/export_to_csv.jinja2
Normal file
35
templates/export_to_csv.jinja2
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<button onclick="exportTableToCsv('result.csv')">Export to csv</button>
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
function downloadCsv(csv, filename) {
|
||||||
|
var csvFile = new Blob([csv], {"type": "text/csv"});
|
||||||
|
|
||||||
|
var downloadLink = document.createElement("a");
|
||||||
|
downloadLink.download = filename;
|
||||||
|
downloadLink.href = window.URL.createObjectURL(csvFile);
|
||||||
|
downloadLink.style.display = "none";
|
||||||
|
|
||||||
|
document.body.appendChild(downloadLink);
|
||||||
|
downloadLink.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportTableToCsv(filename) {
|
||||||
|
var table = document.getElementById("result");
|
||||||
|
var rows = table.getElementsByTagName("tr");
|
||||||
|
|
||||||
|
var csv = [];
|
||||||
|
for (var i = 0; i < rows.length; i++) {
|
||||||
|
if (rows[i].style.display === "none")
|
||||||
|
continue
|
||||||
|
var cols = rows[i].querySelectorAll("td, th");
|
||||||
|
|
||||||
|
var row = [];
|
||||||
|
for (var j = 0; j < cols.length; j++)
|
||||||
|
row.push(cols[j].innerText);
|
||||||
|
|
||||||
|
csv.push(row.join(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadCsv(csv.join("\n"), filename);
|
||||||
|
}
|
||||||
|
</script>
|
13
templates/index.jinja2
Normal file
13
templates/index.jinja2
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>FFXIV loot helper</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<center>
|
||||||
|
<a href="/party" title="party"><h2>party</h2></a>
|
||||||
|
<a href="/bis" title="bis management"><h2>bis</h2></a>
|
||||||
|
<a href="/loot" title="loot management"><h2>loot</h2></a>
|
||||||
|
</center>
|
||||||
|
</body>
|
||||||
|
</html>
|
53
templates/party.jinja2
Normal file
53
templates/party.jinja2
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Party</title>
|
||||||
|
|
||||||
|
{% include "style.jinja2" %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>party</h2>
|
||||||
|
|
||||||
|
{% include "error.jinja2" %}
|
||||||
|
{% include "search_line.jinja2" %}
|
||||||
|
|
||||||
|
<form action="/party" method="post">
|
||||||
|
<input name="nick" id="nick" placeholder="player nick name" title="nick" type="text"/>
|
||||||
|
<input name="job" id="job" placeholder="player job" title="job" type="text"/>
|
||||||
|
<input name="bis" id="bis" placeholder="player bis link" title="bis" type="text"/>
|
||||||
|
<input name="priority" id="priority" placeholder="player priority" title="priority" type="number" value="0"/>
|
||||||
|
<input name="action" id="action" type="hidden" value="add"/>
|
||||||
|
<button>add</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<table id="result">
|
||||||
|
<tr>
|
||||||
|
<th>nick</th>
|
||||||
|
<th>job</th>
|
||||||
|
<th>bis pieces looted</th>
|
||||||
|
<th>total pieces looted</th>
|
||||||
|
<th>priority</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for player in players %}
|
||||||
|
<tr>
|
||||||
|
<td class="include_search">{{ player.nick|e }}</td>
|
||||||
|
<td class="include_search">{{ player.job|e }}</td>
|
||||||
|
<td>{{ player.loot_count_bis|e }}</td>
|
||||||
|
<td>{{ player.loot_count|e }}</td>
|
||||||
|
<td>{{ player.priority|e }}</td>
|
||||||
|
<td>
|
||||||
|
<form action="/party" 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>
|
1
templates/root.jinja2
Normal file
1
templates/root.jinja2
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p><a href="/" title="root">root</a></p>
|
23
templates/search.jinja2
Normal file
23
templates/search.jinja2
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
function searchTable() {
|
||||||
|
var input = document.getElementById("search");
|
||||||
|
var filter = input.value.toLowerCase();
|
||||||
|
var table = document.getElementById("result");
|
||||||
|
var tr = table.getElementsByTagName("tr");
|
||||||
|
|
||||||
|
// from 1 coz of header
|
||||||
|
for (var i = 1; i < tr.length; i++) {
|
||||||
|
var td = tr[i].getElementsByClassName("include_search");
|
||||||
|
var display = "none";
|
||||||
|
for (var j = 0; j < td.length; j++) {
|
||||||
|
if (td[j].tagName.toLowerCase() === "td") {
|
||||||
|
if (td[j].innerHTML.toLowerCase().indexOf(filter) > -1) {
|
||||||
|
display = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr[i].style.display = display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
3
templates/search_line.jinja2
Normal file
3
templates/search_line.jinja2
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
<input type="text" id="search" onkeyup="searchTable()" placeholder="search for data" title="search"/>
|
||||||
|
</div>
|
6
templates/style.jinja2
Normal file
6
templates/style.jinja2
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<style>
|
||||||
|
table { border-collapse: collapse; }
|
||||||
|
table, th, td { border: 1px solid black; padding: 5px; }
|
||||||
|
input { margin: 5px; }
|
||||||
|
#error { color: #ff0000; }
|
||||||
|
</style>
|
Reference in New Issue
Block a user