diff --git a/api/db.js b/api/db.js index 813a5a4..5710ff3 100644 --- a/api/db.js +++ b/api/db.js @@ -358,7 +358,7 @@ export async function get_character_batch_for_stats(batch, sort, order) { try { const char_count = await get_row_count(CHARACTER.THIS); - const chars = await pool.query(`SELECT id, name, faction_id, bep, cep FROM avatar ORDER BY ${to_sql(sort)} ${to_sql(order)} OFFSET $1*1000 LIMIT 1000`, values); + const chars = await pool.query(`SELECT id, name, faction_id, bep, cep FROM avatar ORDER BY ${to_sql(sort)} ${to_sql(order)} OFFSET $1*500 LIMIT 500`, values); return chars.rows; } catch (e) { @@ -380,9 +380,16 @@ export async function get_weaponstats_by_avatar(id) { } export async function get_avatar(id) { - try { - const avatar = await pool.query('SELECT id, name, faction_id, bep, cep, gender_id, head_id FROM avatar WHERE id=$1', [id]) - return avatar.rows[0]; + try { + const avatar = await pool.query( + 'SELECT a.id, a.name, a.faction_id, a.bep, a.cep, a.gender_id, a.head_id,' + + ' o.id AS outfit_id, o.name AS outfit_name' + + ' FROM avatar a' + + ' LEFT JOIN outfitmember om ON om.avatar_id = a.id' + + ' LEFT JOIN outfit o ON o.id = om.outfit_id' + + ' WHERE a.id = $1', + [id]) + return avatar.rows[0]; } catch (e) { if (e.code) e.code = pg_error_inv[e.code] @@ -398,7 +405,8 @@ export async function get_top_kills() { ' INNER JOIN avatar ON killactivity.killer_id = avatar.id' + ' WHERE exp > 0' + ' GROUP BY killactivity.killer_id, avatar.name, avatar.bep, avatar.cep, avatar.faction_id, avatar.gender_id, avatar.head_id' + - ' ORDER BY count(killer_id) DESC') + ' ORDER BY count(killer_id) DESC' + + ' LIMIT 500') return kills.rows; } catch (e) { if (e.code) @@ -438,6 +446,92 @@ export async function get_top_kills_byDate() { } } +export async function get_top_outfits() { + try { + const outfits = await pool.query( + 'WITH OutfitData AS (' + + ' SELECT o.id AS outfit_id,' + + ' o.faction,' + + ' o.name AS outfit_name,' + + ' a.id AS leader_id, a.name AS leader_name,' + + ' COUNT(om.avatar_id)::int AS members,' + + ' (op.points / 100.0)::int AS points' + + ' FROM outfit o' + + ' JOIN avatar a ON a.id = o.owner_id' + + ' LEFT JOIN outfitmember om ON om.outfit_id = o.id' + + ' LEFT JOIN outfitpoint_mv op ON op.outfit_id = o.id' + + ' GROUP BY o.id, o.faction, o.name, a.name, a.id, op.points' + + ') ' + + 'SELECT outfit_id, faction, outfit_name, leader_name, leader_id, members, points ' + + 'FROM OutfitData ' + + 'ORDER BY points DESC') + return outfits.rows; + } catch (e) { + if (e.code) + e.code = pg_error_inv[e.code] + throw e; + } +} + +export async function get_outfit(id) { + try { +const outfit = await pool.query( + 'WITH OutfitData AS (' + + ' SELECT o.id AS outfit_id, o.faction, o.name AS outfit_name, o.created,' + + ' a.id AS leader_id,' + + ' a.name AS leader_name,' + + ' COUNT(om.avatar_id)::int AS members,' + + ' (op.points / 100.0)::int AS points' + + ' FROM outfit o ' + + ' JOIN avatar a ON a.id = o.owner_id ' + + ' LEFT JOIN outfitmember om ON om.outfit_id = o.id ' + + ' LEFT JOIN outfitpoint_mv op ON op.outfit_id = o.id ' + + ' WHERE o.id = $1 ' + + ' GROUP BY o.created, o.id, o.faction, o.name, a.id, a.name, op.points) ' + + 'SELECT outfit_id, faction, outfit_name, leader_id, leader_name, members, points, created ' + + 'FROM OutfitData', + [id]) + return outfit.rows[0]; + } catch (e) { + if (e.code) + e.code = pg_error_inv[e.code]; + throw e; + } +} + +export async function get_outfit_members(id) { + try { +const members = await pool.query( + 'SELECT av.id AS avatar_id, av.bep, av.cep,' + + ' av.name AS avatar_name,' + + ' om.rank AS rank_num,' + + ' CASE om.rank ' + + ' WHEN 0 THEN COALESCE(o.rank0, \'Fodder\') ' + + ' WHEN 1 THEN COALESCE(o.rank1, \'Soldier\') ' + + ' WHEN 2 THEN COALESCE(o.rank2, \'Commando\') ' + + ' WHEN 3 THEN COALESCE(o.rank3, \'Master at Arms\') ' + + ' WHEN 4 THEN COALESCE(o.rank4, \'Tactical Officer\') ' + + ' WHEN 5 THEN COALESCE(o.rank5, \'Strategic Officer\') ' + + ' WHEN 6 THEN COALESCE(o.rank6, \'Chief Officer\') ' + + ' WHEN 7 THEN COALESCE(o.rank7, \'Outfit Leader\') ' + + ' END AS rank_title,' + + ' COALESCE((op.points / 100.0)::int, 0) AS points,' + + ' om.created AS joined' + + ' FROM outfitmember om ' + + ' JOIN avatar av ON av.id = om.avatar_id ' + + ' JOIN outfit o ON o.id = om.outfit_id ' + + ' LEFT JOIN outfitpoint op ON op.avatar_id = om.avatar_id ' + + ' WHERE om.outfit_id = $1 ' + + ' ORDER BY points DESC, avatar_name ASC', + [id]) + return members.rows; + } catch (e) { + if (e.code) + e.code = pg_error_inv[e.code]; + throw e; + } +} + export async function get_characters_by_account(account_id) { try { const characters = await pool.query('SELECT a.*, b.* FROM avatar a LEFT JOIN avatarmodepermission b ON a.id = b.avatar_id WHERE a.account_id = $1 AND a.deleted = false', [account_id]) diff --git a/api/stats.js b/api/stats.js index 594bf6a..f67fc97 100644 --- a/api/stats.js +++ b/api/stats.js @@ -78,6 +78,38 @@ api.get('/top_kills_byDate', async (req, res, next) => { } }); +api.get('/top_outfits', async (req, res, next) => { + try { + const outfits = await db.get_top_outfits(); + res.status(200).json({ outfits: outfits }); + } catch (e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + +api.get('/outfit/:outfit', async (req, res, next) => { + const fit = req.params.outfit + try { + const outfit = await db.get_outfit(fit); + res.status(200).json(outfit); + } catch (e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + +api.get('/outfit/:outfit/members', async (req, res, next) => { + const fit = req.params.outfit + try { + const members = await db.get_outfit_members(fit); + res.status(200).json({ members: members }); + } catch (e) { + console.log(e); + res.status(500).json({ message: 'error' }); + } +}); + api.get('/weaponstats/:avatar', async (req, res, next) => { const avatar = req.params.avatar; @@ -102,7 +134,9 @@ api.get('/avatar/:avatar', async (req, res, next) => { cep: avatarData.cep, faction: avatarData.faction_id, gender: avatarData.gender_id, - head: avatarData.head_id + head: avatarData.head_id, + outfit: avatarData.outfit_name, + outfit_id: avatarData.outfit_id }); } catch (e) { console.log(e); diff --git a/app/App.svelte b/app/App.svelte index 696d445..a266d0b 100644 --- a/app/App.svelte +++ b/app/App.svelte @@ -28,6 +28,7 @@ import AdminPanel from './views/AdminPanel.svelte'; import CharacterList from './views/CharacterList.svelte'; import Leaderboard from './views/Leaderboard.svelte'; import Avatar from './views/Avatar.svelte' +import Outfit from './views/Outfit.svelte' // Defined by webpack let APP_VERSION = __VERSION__; @@ -106,6 +107,7 @@ page("/avatar/:id", setRoute(Avatar)); page("/admin", setRoute(AdminPanel)); page("/profile", setRoute(Profile, true)); page("/user/:id", setRoute(Profile, true)); +page("/outfit/:id", setRoute(Outfit)); if (process.env.NODE_ENV !== 'production') { console.log("Development mode active"); const cc = await import('./views/components.svelte'); diff --git a/app/views/Avatar.svelte b/app/views/Avatar.svelte index 519e57a..fae5f3f 100644 --- a/app/views/Avatar.svelte +++ b/app/views/Avatar.svelte @@ -138,7 +138,11 @@ Character Name: {avatar.name}
- Empire: {getFactionName(avatar.faction)} + Empire: {getFactionName(avatar.faction)}
+ Outfit: + {#if avatar.outfit_id} + {avatar.outfit} + {/if}
{avatar.faction}/ diff --git a/app/views/Leaderboard.svelte b/app/views/Leaderboard.svelte index 0dff3e0..f47f4fb 100644 --- a/app/views/Leaderboard.svelte +++ b/app/views/Leaderboard.svelte @@ -8,12 +8,14 @@ get_BEPleaderboard(); get_CEPleaderboard(); get_kills(); + get_outfits(); get_topDateKills(); }); let bepPlayers = []; let cepPlayers = []; let kills = []; + let outfits = []; let dateKills = []; let alert; @@ -34,7 +36,7 @@ try { const resp = await axios.get("/api/char_stats_cep/0"); const stats = resp.data; - cepPlayers = stats.players; + cepPlayers = stats.players.filter(p => p.cep > 0); // Reset alert message if needed alert.message(""); } catch (e) { @@ -56,6 +58,19 @@ } } + async function get_outfits() { + try { + const resp = await axios.get("/api/top_outfits"); + const stats = resp.data; + outfits = stats.outfits; + // Reset alert message if needed + alert.message(""); + } catch (e) { + console.log(e); + alert.message("Failed to fetch stats from server"); + } + } + async function get_topDateKills() { try { const resp = await axios.get("/api/top_kills_byDate"); @@ -86,6 +101,9 @@ + @@ -166,7 +184,32 @@ -
+
+ + + + + + + + + + {#each outfits as outfit, $index} + + + + + + + + {/each} + +
#NameLeaderMembersPoints
{$index + 1} + {outfit.faction} + {outfit.outfit_name}{outfit.leader_name}{outfit.members}{outfit.points}
+
+ +
Top 50 Characters Most Daily Kills diff --git a/app/views/Outfit.svelte b/app/views/Outfit.svelte new file mode 100644 index 0000000..fc77490 --- /dev/null +++ b/app/views/Outfit.svelte @@ -0,0 +1,117 @@ + + + +Outfit Stats + + +
+ + + + + +
+ + + + + +
+ {outfit.faction}/
+ Outfit Name: {outfit.outfit_name}
+ Created: + {new Date(outfit.created).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })}
+ Leader: {outfit.leader_name}
+ Points: {outfit.points}
+ Members: {outfit.members}
+
+
+
+
+ + + + + + + + + + + + {#each outfitMembers as member, $index} + + + + + + + + + + {/each} + +
NameTitlePointsBRCRJoined
{$index + 1}{member.avatar_name}{stripColorCode(member.rank_title)}{member.points}{calculateBr(member.bep)}{calculateCr(member.cep)} + {new Date(member.joined).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} +