Merge remote-tracking branch 'origin/member-summary'
This commit is contained in:
commit
429a6c0354
|
@ -14,6 +14,8 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
players = utils_stats.check_minecraft_server()
|
players = utils_stats.check_minecraft_server()
|
||||||
self.stdout.write('Found Minecraft players: ' + str(players))
|
self.stdout.write('Found Minecraft players: ' + str(players))
|
||||||
|
users = utils_stats.check_mumble_server()
|
||||||
|
self.stdout.write('Found Mumble users: ' + str(users))
|
||||||
|
|
||||||
self.stdout.write('Completed tasks in {} s'.format(
|
self.stdout.write('Completed tasks in {} s'.format(
|
||||||
str(time.time() - start)[:4]
|
str(time.time() - start)[:4]
|
||||||
|
|
|
@ -22,6 +22,7 @@ DEFAULTS = {
|
||||||
'bay_108_temp': None,
|
'bay_108_temp': None,
|
||||||
'bay_110_temp': None,
|
'bay_110_temp': None,
|
||||||
'minecraft_players': [],
|
'minecraft_players': [],
|
||||||
|
'mumble_users': [],
|
||||||
'card_scans': 0,
|
'card_scans': 0,
|
||||||
'track': {},
|
'track': {},
|
||||||
}
|
}
|
||||||
|
@ -114,6 +115,21 @@ def check_minecraft_server():
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
def check_mumble_server():
|
||||||
|
if secrets.MUMBLE:
|
||||||
|
url = secrets.MUMBLE
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = requests.get(url, timeout=5)
|
||||||
|
r.raise_for_status()
|
||||||
|
users = r.text.split()
|
||||||
|
cache.set('mumble_users', users)
|
||||||
|
return users
|
||||||
|
except BaseException as e:
|
||||||
|
logger.error('Problem checking Mumble: {} - {}'.format(e.__class__.__name__, str(e)))
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def calc_card_scans():
|
def calc_card_scans():
|
||||||
date = today_alberta_tz()
|
date = today_alberta_tz()
|
||||||
cards = models.Card.objects
|
cards = models.Card.objects
|
||||||
|
|
|
@ -60,6 +60,7 @@ DOOR_API_TOKEN = ''
|
||||||
DOOR_CODE = ''
|
DOOR_CODE = ''
|
||||||
WIFI_PASS = ''
|
WIFI_PASS = ''
|
||||||
MINECRAFT = ''
|
MINECRAFT = ''
|
||||||
|
MUMBLE = ''
|
||||||
|
|
||||||
# Portal Email Credentials
|
# Portal Email Credentials
|
||||||
# For sending password resets, etc.
|
# For sending password resets, etc.
|
||||||
|
|
|
@ -221,7 +221,15 @@ export function AdminMemberCards(props) {
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
|
|
||||||
<Form.Button loading={loading} error={error.non_field_errors}>
|
<Form.Checkbox
|
||||||
|
label='Confirmed that the member has been given a tour and knows the alarm code'
|
||||||
|
required
|
||||||
|
{...makeProps('given_tour')}
|
||||||
|
onChange={handleCheck}
|
||||||
|
checked={input.given_tour}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Form.Button disabled={!input.given_tour} loading={loading} error={error.non_field_errors}>
|
||||||
Submit
|
Submit
|
||||||
</Form.Button>
|
</Form.Button>
|
||||||
{success && <div>Success!</div>}
|
{success && <div>Success!</div>}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useState, useEffect, useReducer, useContext } from 'react';
|
||||||
import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
|
import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom';
|
||||||
import './semantic-ui/semantic.min.css';
|
import './semantic-ui/semantic.min.css';
|
||||||
import './light.css';
|
import './light.css';
|
||||||
|
import './dark.css';
|
||||||
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
|
||||||
import Darkmode from 'darkmode-js';
|
import Darkmode from 'darkmode-js';
|
||||||
import { isAdmin, requester } from './utils.js';
|
import { isAdmin, requester } from './utils.js';
|
||||||
|
|
|
@ -47,6 +47,25 @@ export function Charts(props) {
|
||||||
<Container>
|
<Container>
|
||||||
<Header size='large'>Charts</Header>
|
<Header size='large'>Charts</Header>
|
||||||
|
|
||||||
|
<Header size='medium'>Summary</Header>
|
||||||
|
|
||||||
|
{memberCount && signupCount &&
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
The total member count is {memberCount.slice().reverse()[0].member_count} members,
|
||||||
|
compared to {memberCount.slice().reverse()[30].member_count} members 30 days ago.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The green member count is {memberCount.slice().reverse()[0].green_count} members,
|
||||||
|
compared to {memberCount.slice().reverse()[30].green_count} members 30 days ago.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
There were {signupCount.slice().reverse()[0].signup_count} signups so far this month,
|
||||||
|
and {signupCount.slice().reverse()[1].signup_count} signups last month.
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
<Header size='medium'>Member Counts</Header>
|
<Header size='medium'>Member Counts</Header>
|
||||||
|
|
||||||
<p>Daily since March 2nd, 2020.</p>
|
<p>Daily since March 2nd, 2020.</p>
|
||||||
|
|
|
@ -152,6 +152,7 @@ export function Home(props) {
|
||||||
const getDateStat = (x) => stats && stats[x] ? moment.utc(stats[x]).tz('America/Edmonton').format('ll') : '?';
|
const getDateStat = (x) => stats && stats[x] ? moment.utc(stats[x]).tz('America/Edmonton').format('ll') : '?';
|
||||||
|
|
||||||
const mcPlayers = stats && stats['minecraft_players'] ? stats['minecraft_players'] : [];
|
const mcPlayers = stats && stats['minecraft_players'] ? stats['minecraft_players'] : [];
|
||||||
|
const mumbleUsers = stats && stats['mumble_users'] ? stats['mumble_users'] : [];
|
||||||
|
|
||||||
const getTrackStat = (x) => stats && stats.track && stats.track[x] ? moment().unix() - stats.track[x]['time'] > 60 ? 'Free' : 'In Use' : '?';
|
const getTrackStat = (x) => stats && stats.track && stats.track[x] ? moment().unix() - stats.track[x]['time'] > 60 ? 'Free' : 'In Use' : '?';
|
||||||
const getTrackLast = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]['time']).tz('America/Edmonton').format('llll') : 'Unknown';
|
const getTrackLast = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]['time']).tz('America/Edmonton').format('llll') : 'Unknown';
|
||||||
|
@ -214,11 +215,10 @@ export function Home(props) {
|
||||||
<p>Next monthly clean: {getDateStat('next_clean')}</p>
|
<p>Next monthly clean: {getDateStat('next_clean')}</p>
|
||||||
<p>Member count: {getStat('member_count')} <Link to='/charts'>[more]</Link></p>
|
<p>Member count: {getStat('member_count')} <Link to='/charts'>[more]</Link></p>
|
||||||
<p>Green members: {getStat('green_count')}</p>
|
<p>Green members: {getStat('green_count')}</p>
|
||||||
<p>Old members: {getStat('paused_count')}</p>
|
|
||||||
<p>Card scans today: {getZeroStat('card_scans')}</p>
|
<p>Card scans today: {getZeroStat('card_scans')}</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Minecraft players: {mcPlayers.length} <Popup content={
|
Minecraft players: {mcPlayers.length} {mcPlayers.length > 5 && '🔥'} <Popup content={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<p>
|
<p>
|
||||||
Server IP:<br />
|
Server IP:<br />
|
||||||
|
@ -233,6 +233,21 @@ export function Home(props) {
|
||||||
{' '}<a href='http://games.protospace.ca:8123/?worldname=world&mapname=flat&zoom=3&x=74&y=64&z=354' target='_blank'>[map]</a>
|
{' '}<a href='http://games.protospace.ca:8123/?worldname=world&mapname=flat&zoom=3&x=74&y=64&z=354' target='_blank'>[map]</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Mumble users: {mumbleUsers.length} <Popup content={
|
||||||
|
<React.Fragment>
|
||||||
|
<p>
|
||||||
|
Server IP:<br />
|
||||||
|
mumble.protospace.ca
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Users:<br />
|
||||||
|
{mumbleUsers.length ? mumbleUsers.map(x => <React.Fragment>{x}<br /></React.Fragment>) : 'None'}
|
||||||
|
</p>
|
||||||
|
</React.Fragment>
|
||||||
|
} trigger={<a>[more]</a>} />
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Trotec availability: {getTrackStat('TROTECS300')} <Popup content={
|
Trotec availability: {getTrackStat('TROTECS300')} <Popup content={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|
57
webclient/src/dark.css
Normal file
57
webclient/src/dark.css
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
.darkmode-layer, .darkmode-toggle {
|
||||||
|
z-index: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .ui.image {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
filter: brightness(75%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated i.green.circle.icon {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
color: #21ba4582 !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated i.yellow.circle.icon {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
color: #fbbd0882 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated i.red.circle.icon {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
color: #db282882 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .footer {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .ql-toolbar.ql-snow,
|
||||||
|
.darkmode--activated .ql-container.ql-snow,
|
||||||
|
.darkmode--activated .ui.segment,
|
||||||
|
.darkmode--activated .ui.form .field input,
|
||||||
|
.darkmode--activated .ui.form .field .selection.dropdown,
|
||||||
|
.darkmode--activated .ui.form .field .ui.checkbox label::before,
|
||||||
|
.darkmode--activated .ui.form .field textarea {
|
||||||
|
border: 1px solid rgba(34,36,38,.50) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .ui.basic.table tbody tr {
|
||||||
|
border-bottom: 1px solid rgba(34,36,38,.50) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .ui.button {
|
||||||
|
background: #c9c9c9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .ui.red.button {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
background: #db282882 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkmode--activated .ui.green.button,
|
||||||
|
.darkmode--activated .ui.button.toggle.active {
|
||||||
|
mix-blend-mode: difference;
|
||||||
|
background: #21ba4582 !important;
|
||||||
|
}
|
|
@ -150,23 +150,3 @@ body {
|
||||||
margin-left: -3.5px;
|
margin-left: -3.5px;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.darkmode-layer, .darkmode-toggle {
|
|
||||||
z-index: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmode--activated .ui.image {
|
|
||||||
mix-blend-mode: difference;
|
|
||||||
filter: brightness(50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmode--activated i.green.circle.icon,
|
|
||||||
.darkmode--activated i.yellow.circle.icon,
|
|
||||||
.darkmode--activated i.red.circle.icon {
|
|
||||||
mix-blend-mode: difference;
|
|
||||||
}
|
|
||||||
|
|
||||||
.darkmode--activated .footer {
|
|
||||||
mix-blend-mode: difference;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user