refactoring + started working on map list generator

This commit is contained in:
Sendou
2019-05-10 17:35:27 +03:00
parent e7b2aa68be
commit db75c964bc
22 changed files with 393 additions and 79 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ coverage
# misc
xrank_data
maps
.DS_Store
.env
.env.local

View File

@@ -5,6 +5,7 @@ const express = require('express')
const cors = require('cors')
const Placement = require('./models/placement')
const Player = require('./models/player')
const Maplist = require('./models/maplist')
//const User = require('./models/user')
const jwt = require('jsonwebtoken')
const path = require('path')
@@ -78,6 +79,15 @@ const typeDefs = gql`
placements: [Placement!]!
}
type Maplist {
name: String!
sz: [String!]!
tc: [String!]!
rm: [String!]!
cb: [String!]!
}
type Token {
value: String!
}
@@ -97,6 +107,7 @@ const typeDefs = gql`
weaponPlacementStats(weapon: String!): [Int!]!
playerInfo(uid: String!): PlayerWithPlacements!
searchForPlayers(name: String! exact: Boolean): [Placement]!
maplists: [Maplist!]!
}
type Mutation {
@@ -363,6 +374,16 @@ const resolvers = {
uids.push(p.unique_id)
return true
})
},
maplists: (root, args) => {
return Maplist
.find({})
.sort({ order: "asc" })
.catch(e => {
throw new UserInputError(e.message, {
invalidArgs: args,
})
})
}
}
}

11
models/maplist.js Normal file
View File

@@ -0,0 +1,11 @@
const mongoose = require('mongoose')
const maplistSchema = new mongoose.Schema({
name: {type: String, required: true},
sz: {type: [String], required: true},
tc: {type: [String], required: true},
rm: {type: [String], required: true},
cb: {type: [String], required: true}
})
module.exports = mongoose.model('Maplist', maplistSchema)

22
react-ui/src/App.js vendored
View File

@@ -1,15 +1,16 @@
import React, { useState } from 'react'
import { Container } from 'semantic-ui-react'
import Footer from './components/Footer'
import MainMenu from './components/MainMenu'
import NotFound from './components/NotFound'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import Footer from './components/Misc/Footer'
import MainMenu from './components/Misc/MainMenu'
import NotFound from './components/Misc/NotFound'
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
import WeaponLeaderboardSelector from './components/WeaponLeaderboardSelector'
import XSearch from './components/XSearch'
import InfoPlayer from './components/InfoPlayer'
import InfoWeapon from './components/InfoWeapon'
import WeaponLeaderboardSelector from './components/XLeaderboard/WeaponLeaderboardSelector'
import XSearch from './components/XSearch/XSearch'
import InfoPlayer from './components/XSearch/InfoPlayer'
import InfoWeapon from './components/XSearch/InfoWeapon'
import ScrollToTop from './utils/ScrollToTop'
import MapListGenerator from './components/Tools/MapListGenerator'
const App = () => {
const [menuSelection, setMenuSelection] = useState('home')
@@ -21,7 +22,7 @@ const App = () => {
<div>
<ScrollToTop />
<Switch>
<Route exact path="/" render={() => <div>This is home.</div>} />
<Route exact path="/" render={() => <Redirect to="/xleaderboard"/>} />
<Route path="/xleaderboard" render={() => <WeaponLeaderboardSelector setMenuSelection={setMenuSelection} />} />
<Route exact path="/xsearch" render={() => <XSearch setMenuSelection={setMenuSelection} />} />
<Route exact path="/xsearch/w/:wpn" render={({ match }) =>
@@ -30,6 +31,9 @@ const App = () => {
<Route exact path="/xsearch/p/:uid" render={({ match }) =>
<InfoPlayer uid={match.params.uid} setMenuSelection={setMenuSelection} />
} />
<Route exact path="/maps" render={() =>
<MapListGenerator setMenuSelection={setMenuSelection} />
} />
<Route path="/404" render={() => <NotFound />} />
<Route path="*" render={() => <NotFound />} />
</Switch>

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import { Menu, Segment } from 'semantic-ui-react'
import { withRouter } from 'react-router-dom'
import { memCakes } from '../utils/lists'
import { memCakes } from '../../utils/lists'
const MainMenu = withRouter(({ history, menuSelection, setMenuSelection }) => {
const [memCakePic, setMemCakePic] = useState(memCakes[Math.floor(Math.random()*memCakes.length)])
@@ -12,23 +12,23 @@ const MainMenu = withRouter(({ history, menuSelection, setMenuSelection }) => {
<img src={process.env.PUBLIC_URL + `/memCakes/${memCakePic}`} alt="mem cake logo" onClick={() => setMemCakePic(memCakes[Math.floor(Math.random()*memCakes.length)])}/>
</Menu.Item>
<Menu.Item
name='home'
active={menuSelection === 'home'}
name='maplists'
active={menuSelection === 'maplists'}
onClick={() => {
history.push('/')
setMenuSelection('home')
history.push('/maps')
setMenuSelection('maplists')
}}
/>
<Menu.Item
name='top 500 search'
active={menuSelection === 'search'}
onClick={() => { history.push('/xsearch') }}
/>
<Menu.Item
name='leaderboards'
active={menuSelection === 'leaderboards'}
onClick={() => { history.push('/xleaderboard') }}
/>
<Menu.Item
name='top 500 search'
active={menuSelection === 'search'}
onClick={() => { history.push('/xsearch') }}
/>
</Menu>
</Segment>
)

View File

@@ -1,13 +1,13 @@
import React from 'react'
import { Header } from 'semantic-ui-react'
import bridge from './img/s1Maps/bridge.png'
import depot from './img/s1Maps/depot.png'
import heights from './img/s1Maps/heights.png'
import mahi from './img/s1Maps/mahi.png'
import museum from './img/s1Maps/museum.png'
import rig from './img/s1Maps/rig.png'
import underpass from './img/s1Maps/underpass.png'
import bridge from '../img/s1Maps/bridge.png'
import depot from '../img/s1Maps/depot.png'
import heights from '../img/s1Maps/heights.png'
import mahi from '../img/s1Maps/mahi.png'
import museum from '../img/s1Maps/museum.png'
import rig from '../img/s1Maps/rig.png'
import underpass from '../img/s1Maps/underpass.png'
const NotFound = () => {
const maps = [

View File

@@ -0,0 +1,176 @@
import React, { useEffect, useState } from 'react'
import { TextArea, Loader, Checkbox, Form, Header, List, Image, Divider, Icon, Grid, Input, Button } from 'semantic-ui-react'
import { useQuery } from 'react-apollo-hooks'
import { maplists } from '../../graphql/queries/maplists'
import szIcon from '../img/modeIcons/sz.png'
import tcIcon from '../img/modeIcons/tc.png'
import rmIcon from '../img/modeIcons/rm.png'
import cbIcon from '../img/modeIcons/cb.png'
//<TextArea rows={30} style={{"resize": "none"}} readOnly value="jooooooooooooooo"/>
const MapListGenerator = ({ setMenuSelection }) => {
const { data, error, loading } = useQuery(maplists)
const [ maps, setMaps ] = useState([])
const [ boxValue, setBoxValue ] = useState(0)
const [ mapValues, setMapValues ] = useState([])
const [ amountToGenerate, setAmountToGenerate ] = useState(12)
useEffect(() => {
if (loading) {
return
}
setMenuSelection('maplists')
document.title = 'Maplist Generator - sendou.ink'
setMaps(data.maplists)
setMapValues(data.maplists.reduce((acc, cur) => {
return (
acc.concat({
sz: new Array(cur.sz.length).fill(true),
tc: new Array(cur.tc.length).fill(true),
rm: new Array(cur.rm.length).fill(true),
cb: new Array(cur.cb.length).fill(true),
})
)
}, []))
}, [data, loading, setMenuSelection])
if (loading || maps.length === 0) {
return <div style={{"paddingTop": "25px", "paddingBottom": "20000px"}} ><Loader active inline='centered' /></div>
}
if (error) {
return <div style={{"color": "red"}}>{error.message}</div>
}
const generateMapPoolString = (mapPoolObject, amount) => {
const allMaps = [...mapPoolObject.sz, ...mapPoolObject.tc, ...mapPoolObject.rm, ...mapPoolObject.cb]
}
return (
<div style={{"paddingTop": "5px"}}>
<Divider horizontal>
<Header as='h4'>
<Icon name='map' />
Choose the map pool to use
</Header>
</Divider>
<Form>
{maps.map((m, i) => {
return (
<Form.Field key={m.name}>
<Checkbox
radio
label={m.name}
name='map'
checked={i === boxValue}
onChange={() => setBoxValue(i)}
/>
</Form.Field>
)
})}
</Form>
<div>
<Divider horizontal>
<Header as='h4'>
<Icon name='checkmark box' />
Choose the maps to include
</Header>
</Divider>
<Grid relaxed='very' columns={4}>
<Grid.Column>
<Image src={szIcon} size="tiny" /><br />
<List>
{maps[boxValue].sz.map((m, i) => {
return (
<List.Item key={m}>
<Checkbox
label={m}
checked={mapValues[boxValue].sz[i]}
onChange={() => {
const copy = [...mapValues]
copy[boxValue].sz[i] = !copy[boxValue].sz[i]
setMapValues(copy)
}}
/>
</List.Item>
)
})}
</List>
</Grid.Column>
<Grid.Column>
<Image src={tcIcon} size="tiny" /><br />
<List>
{maps[boxValue].tc.map((m, i) => {
return (
<List.Item key={m}>
<Checkbox
label={m}
checked={mapValues[boxValue].tc[i]}
onChange={() => {
const copy = [...mapValues]
copy[boxValue].tc[i] = !copy[boxValue].tc[i]
setMapValues(copy)
}}
/>
</List.Item>
)
})}
</List>
</Grid.Column>
<Grid.Column>
<Image src={rmIcon} size="tiny" /><br />
<List>
{maps[boxValue].rm.map((m, i) => {
return (
<List.Item key={m}>
<Checkbox
label={m}
checked={mapValues[boxValue].rm[i]}
onChange={() => {
const copy = [...mapValues]
copy[boxValue].rm[i] = !copy[boxValue].rm[i]
setMapValues(copy)
}}
/>
</List.Item>
)
})}
</List>
</Grid.Column>
<Grid.Column>
<Image src={cbIcon} size="tiny" /><br />
<List>
{maps[boxValue].cb.map((m, i) => {
return (
<List.Item key={m}>
<Checkbox
label={m}
checked={mapValues[boxValue].cb[i]}
onChange={() => {
const copy = [...mapValues]
copy[boxValue].cb[i] = !copy[boxValue].cb[i]
setMapValues(copy)
}}
/>
</List.Item>
)
})}
</List>
</Grid.Column>
</Grid>
</div>
<div>
<Input
type='number'
placeholder='Choose amount'
label='Choose the amount of maps to generate'
value={amountToGenerate}
onChange={(e) => setAmountToGenerate(e.target.value)}
error={amountToGenerate < 1 || amountToGenerate > 100}
/>
</div>
</div>
)
}
export default MapListGenerator

View File

@@ -1,7 +1,7 @@
import React from 'react'
import { Popup } from 'semantic-ui-react'
import weaponDict from '../utils/english_internal.json'
import { modes, months, getNumberWithOrdinal } from '../utils/lists'
import weaponDict from '../../utils/english_internal.json'
import { modes, months, getNumberWithOrdinal } from '../../utils/lists'
const FourWeapons = ({ weapons }) => {
return (

View File

@@ -4,7 +4,7 @@ import { useQuery } from 'react-apollo-hooks'
import { Loader } from 'semantic-ui-react'
import { Link } from 'react-router-dom'
import FourWeapons from '../components/FourWeapons'
import FourWeapons from './FourWeapons'
const WeaponLeaderboard = ({ query, queryName, scoreField, weaponsField, setActiveItem }) => {
const result = useQuery(query)

View File

@@ -2,24 +2,24 @@ import React, { useState } from 'react'
import { Menu, Responsive } from 'semantic-ui-react'
import { Route, withRouter } from 'react-router-dom'
import WeaponLeaderboard from './WeaponLeaderboard'
import { topTotalPlayers } from '../graphql/queries/topPlayers'
import { topShooterPlayers } from '../graphql/queries/topShooters'
import { topBlasterPlayers } from '../graphql/queries/topBlasters'
import { topBrellaPlayers } from '../graphql/queries/topBrellas'
import { topChargerPlayers } from '../graphql/queries/topChargers'
import { topDualiesPlayers } from '../graphql/queries/topDualies'
import { topRollerPlayers } from '../graphql/queries/topRollers'
import { topSlosherPlayers } from '../graphql/queries/topSloshers'
import { topSplatlingPlayers } from '../graphql/queries/topSplatlings'
import allIcon from './img/wpnIcons/all.png'
import blasterIcon from './img/wpnIcons/blasters.png'
import brellaIcon from './img/wpnIcons/brellas.png'
import chargerIcon from './img/wpnIcons/chargers.png'
import dualieIcon from './img/wpnIcons/dualies.png'
import rollerIcon from './img/wpnIcons/rollers.png'
import shooterIcon from './img/wpnIcons/shooters.png'
import slosherIcon from './img/wpnIcons/sloshers.png'
import splatlingIcon from './img/wpnIcons/splatlings.png'
import { topTotalPlayers } from '../../graphql/queries/topPlayers'
import { topShooterPlayers } from '../../graphql/queries/topShooters'
import { topBlasterPlayers } from '../../graphql/queries/topBlasters'
import { topBrellaPlayers } from '../../graphql/queries/topBrellas'
import { topChargerPlayers } from '../../graphql/queries/topChargers'
import { topDualiesPlayers } from '../../graphql/queries/topDualies'
import { topRollerPlayers } from '../../graphql/queries/topRollers'
import { topSlosherPlayers } from '../../graphql/queries/topSloshers'
import { topSplatlingPlayers } from '../../graphql/queries/topSplatlings'
import allIcon from '../img/wpnIcons/all.png'
import blasterIcon from '../img/wpnIcons/blasters.png'
import brellaIcon from '../img/wpnIcons/brellas.png'
import chargerIcon from '../img/wpnIcons/chargers.png'
import dualieIcon from '../img/wpnIcons/dualies.png'
import rollerIcon from '../img/wpnIcons/rollers.png'
import shooterIcon from '../img/wpnIcons/shooters.png'
import slosherIcon from '../img/wpnIcons/sloshers.png'
import splatlingIcon from '../img/wpnIcons/splatlings.png'
const WeaponLeaderboardSelector = withRouter(({ history, setMenuSelection }) => {
const [activeItem, setActiveItem] = useState('')

View File

@@ -2,10 +2,10 @@ import React, { useEffect, useState } from 'react'
import { useQuery } from 'react-apollo-hooks'
import { Loader, Header, Image, Icon } from 'semantic-ui-react'
import { playerInfo } from '../graphql/queries/playerInfo'
import TopPlacementTable from '../components/TopPlacementsTable'
import WpnPlayedTable from '../components/WpnPlayedTable'
import MonthsTable from '../components/MonthsTable'
import { playerInfo } from '../../graphql/queries/playerInfo'
import TopPlacementTable from './TopPlacementsTable'
import WpnPlayedTable from './WpnPlayedTable'
import MonthsTable from './MonthsTable'
const InfoPlayer = ({ uid, setMenuSelection }) => {
const { data, error, loading } = useQuery(playerInfo, {variables: { uid: uid }})

View File

@@ -1,15 +1,15 @@
import React, { useState, useEffect } from 'react'
import { useQuery } from 'react-apollo-hooks'
import { topPlayersOfWeapon } from '../graphql/queries/topPlayersOfWeapon'
import weaponDictReversed from '../utils/internal_english.json'
import { topPlayersOfWeapon } from '../../graphql/queries/topPlayersOfWeapon'
import weaponDictReversed from '../../utils/internal_english.json'
import { Loader, Header, Table, Checkbox } from 'semantic-ui-react'
import { withRouter, Link } from 'react-router-dom'
import szIcon from './img/modeIcons/sz.png'
import tcIcon from './img/modeIcons/tc.png'
import rmIcon from './img/modeIcons/rm.png'
import cbIcon from './img/modeIcons/cb.png'
import { months, modes } from '../utils/lists'
import szIcon from '../img/modeIcons/sz.png'
import tcIcon from '../img/modeIcons/tc.png'
import rmIcon from '../img/modeIcons/rm.png'
import cbIcon from '../img/modeIcons/cb.png'
import { months, modes } from '../../utils/lists'
const modeIcons = ["", szIcon, tcIcon, rmIcon, cbIcon]

View File

@@ -1,12 +1,12 @@
import React from 'react'
import { Table, Header, Image } from 'semantic-ui-react'
import szIcon from './img/modeIcons/sz.png'
import tcIcon from './img/modeIcons/tc.png'
import rmIcon from './img/modeIcons/rm.png'
import cbIcon from './img/modeIcons/cb.png'
import { months } from '../utils/lists'
import weaponDict from '../utils/english_internal.json'
import szIcon from '../img/modeIcons/sz.png'
import tcIcon from '../img/modeIcons/tc.png'
import rmIcon from '../img/modeIcons/rm.png'
import cbIcon from '../img/modeIcons/cb.png'
import { months } from '../../utils/lists'
import weaponDict from '../../utils/english_internal.json'
const MonthsTable = ({ placements }) => { //data received is ordered chronologically and sz->tc->rm->cb
const modeIcons = [null, szIcon, tcIcon, rmIcon, cbIcon]

View File

@@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { Button, Form, Checkbox } from 'semantic-ui-react'
import XSearchResults from '../components/XSearchResults'
import XSearchResults from './XSearchResults'
const PlayerSearchForm = (props) => {
const [playerForm, setPlayerForm] = useState('')

View File

@@ -1,12 +1,12 @@
import React from 'react'
import { Header, Image, Table } from 'semantic-ui-react'
import weaponDict from '../utils/english_internal.json'
import szIcon from './img/modeIcons/sz.png'
import tcIcon from './img/modeIcons/tc.png'
import rmIcon from './img/modeIcons/rm.png'
import cbIcon from './img/modeIcons/cb.png'
import { months } from '../utils/lists'
import weaponDict from '../../utils/english_internal.json'
import szIcon from '../img/modeIcons/sz.png'
import tcIcon from '../img/modeIcons/tc.png'
import rmIcon from '../img/modeIcons/rm.png'
import cbIcon from '../img/modeIcons/cb.png'
import { months } from '../../utils/lists'
const TopPlacementTable = ({ top }) => {
const returnRow = (x, placement, mode) => {

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react'
import { Dropdown, Button } from 'semantic-ui-react'
import { withRouter } from 'react-router-dom'
import { weapons } from '../utils/lists'
import weaponDict from '../utils/english_internal.json'
import { weapons } from '../../utils/lists'
import weaponDict from '../../utils/english_internal.json'
const WeaponForm = (props) => {
return (

View File

@@ -1,7 +1,7 @@
import React from 'react'
import { weaponsByCategory } from '../utils/lists'
import { categoryKeys } from '../utils/lists'
import weaponDict from '../utils/english_internal.json'
import { weaponsByCategory } from '../../utils/lists'
import { categoryKeys } from '../../utils/lists'
import weaponDict from '../../utils/english_internal.json'
import { Table, Header, Popup } from 'semantic-ui-react'
const WpnPlayedTable = ({ weapons }) => {

View File

@@ -1,6 +1,6 @@
import React, { useEffect } from 'react'
import { WeaponFormWithButton } from '../components/WeaponForm'
import PlayerSearchForm from '../components/PlayerSearchForm'
import { WeaponFormWithButton } from './WeaponForm'
import PlayerSearchForm from './PlayerSearchForm'
const XSearch = ({ setMenuSelection }) => {
useEffect(() => {

View File

@@ -2,7 +2,7 @@ import React from 'react'
import { withRouter } from 'react-router-dom'
import { useQuery } from 'react-apollo-hooks'
import { Loader, Message, Grid, Button, Header } from 'semantic-ui-react'
import { searchForPlayers } from '../graphql/queries/searchForPlayers'
import { searchForPlayers } from '../../graphql/queries/searchForPlayers'
const XSearchResults = withRouter(({ history, name, exact }) => {
const result = useQuery(searchForPlayers, {variables: { name, exact }})

View File

@@ -0,0 +1,13 @@
import { gql } from 'apollo-boost'
export const maplists = gql`
{
maplists {
name
sz
tc
rm
cb
}
}
`

88
scripts/maps.py Normal file
View File

@@ -0,0 +1,88 @@
import glob
import os
import pymongo
from config import uri
# I know you could do a lot of the stuff below more efficiently but I don't think it matters in this case :)
maps = ["The Reef",
"Musselforge Fitness",
"Starfish Mainstage",
"Humpback Pump Track",
"Inkblot Art Academy",
"Sturgeon Shipyard",
"Moray Towers",
"Port Mackerel",
"Manta Maria",
"Kelp Dome",
"Snapper Canal",
"Blackbelly Skatepark",
"MakoMart",
"Walleye Warehouse",
"Shellendorf Institute",
"Arowana Mall",
"Goby Arena",
"Piranha Pit",
"Camp Triggerfish",
"Wahoo World",
"New Albacore Hotel",
"Ancho-V Games",
"Skipper Pavilion"]
client = pymongo.MongoClient(uri)
db = client.production
script_dir = os.path.dirname(__file__)
file_name = input('Enter the file name without extension: ')
rel_path = f"maps/{file_name}.txt"
abs_file_path = os.path.join(script_dir, rel_path)
with open(abs_file_path) as f:
content = f.read().split("\n")
for index, line in enumerate(content[:]):
if line != "":
content[index] = content[index].strip()
content[index] = ' '.join(content[index].split())
counter = 0
for line in content[1:]: # validate data
if line not in maps and line != "":
raise ValueError(f'{line} is not a valid map name.')
if line == "" and "ranked" in content[0].lower():
if counter != 0 and counter != 8:
raise ValueError(f'For ranked rotations there should be 8 maps per got. Got: {counter}')
counter = 0
if line != "":
counter += 1
map_list_name = content[0]
sz = []
tc = []
rm = []
cb = []
index = 0
modes = [sz, tc, rm, cb]
modes_sorted = [[], [], [], []]
for line in content[2:]:
if line == "":
index += 1
continue
modes[index].append(line)
for mode in modes:
if len(mode) != len(set(mode)):
raise ValueError(f'Duplicate map in mode {mode}.')
for m in maps:
for i in range(0, 4):
if m in modes[i]:
modes_sorted[i].append(m)
map_object = {"name": map_list_name, "sz": modes_sorted[0], "tc": modes_sorted[1], "rm": modes_sorted[2], "cb": modes_sorted[3]}
db.maplists.insert_one(map_object)
print('Success! Entered the following map list to the database:')
print(map_object)