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()
|
||||
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(
|
||||
str(time.time() - start)[:4]
|
||||
|
|
|
@ -22,6 +22,7 @@ DEFAULTS = {
|
|||
'bay_108_temp': None,
|
||||
'bay_110_temp': None,
|
||||
'minecraft_players': [],
|
||||
'mumble_users': [],
|
||||
'card_scans': 0,
|
||||
'track': {},
|
||||
}
|
||||
|
@ -114,6 +115,21 @@ def check_minecraft_server():
|
|||
|
||||
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():
|
||||
date = today_alberta_tz()
|
||||
cards = models.Card.objects
|
||||
|
|
|
@ -60,6 +60,7 @@ DOOR_API_TOKEN = ''
|
|||
DOOR_CODE = ''
|
||||
WIFI_PASS = ''
|
||||
MINECRAFT = ''
|
||||
MUMBLE = ''
|
||||
|
||||
# Portal Email Credentials
|
||||
# For sending password resets, etc.
|
||||
|
|
|
@ -221,7 +221,15 @@ export function AdminMemberCards(props) {
|
|||
/>
|
||||
</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
|
||||
</Form.Button>
|
||||
{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 './semantic-ui/semantic.min.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 Darkmode from 'darkmode-js';
|
||||
import { isAdmin, requester } from './utils.js';
|
||||
|
|
|
@ -47,6 +47,25 @@ export function Charts(props) {
|
|||
<Container>
|
||||
<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>
|
||||
|
||||
<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 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 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>Member count: {getStat('member_count')} <Link to='/charts'>[more]</Link></p>
|
||||
<p>Green members: {getStat('green_count')}</p>
|
||||
<p>Old members: {getStat('paused_count')}</p>
|
||||
<p>Card scans today: {getZeroStat('card_scans')}</p>
|
||||
|
||||
<p>
|
||||
Minecraft players: {mcPlayers.length} <Popup content={
|
||||
Minecraft players: {mcPlayers.length} {mcPlayers.length > 5 && '🔥'} <Popup content={
|
||||
<React.Fragment>
|
||||
<p>
|
||||
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>
|
||||
</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>
|
||||
Trotec availability: {getTrackStat('TROTECS300')} <Popup content={
|
||||
<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-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