Add a special page showing the latest laser usage

This commit is contained in:
Tanner Collin 2022-02-08 02:28:10 +00:00
parent 8d8a399f33
commit 680039fa51
5 changed files with 151 additions and 4 deletions

View File

@ -16,10 +16,15 @@ import datetime, time
from . import models, fields, utils, utils_ldap, utils_auth, utils_stats
from .. import settings, secrets
#class UsageSerializer(serializers.ModelSerializer):
# class Meta:
# model = models.UsageTrack
# fields = '__all__'
class UsageSerializer(serializers.ModelSerializer):
first_name = serializers.SerializerMethodField()
class Meta:
model = models.Usage
fields = '__all__'
def get_first_name(self, obj):
return obj.user.member.preferred_name
class TransactionSerializer(serializers.ModelSerializer):
# fields directly from old portal. replace with slugs we want

View File

@ -683,6 +683,33 @@ class StatsViewSet(viewsets.ViewSet, List):
return Response(200)
@action(detail=False, methods=['get'])
def usage_data(self, request):
if 'device' not in request.query_params:
raise exceptions.ValidationError(dict(device='This field is required.'))
if not utils.is_request_from_protospace(request):
raise exceptions.PermissionDenied()
device = request.query_params['device']
last_session = models.Usage.objects.filter(device=device).last()
if not last_session:
raise exceptions.ValidationError(dict(device='Session not found.'))
serializer = serializers.UsageSerializer(last_session)
try:
track = cache.get('track', {})[device]
except KeyError:
track = False
return Response(dict(
track=track,
session=serializer.data
))
@action(detail=False, methods=['post'])
def autoscan(self, request):
if 'autoscan' not in request.data:

View File

@ -21,6 +21,7 @@ import { Courses, CourseDetail } from './Courses.js';
import { ClassFeed, Classes, ClassDetail } from './Classes.js';
import { Members, MemberDetail } from './Members.js';
import { Charts } from './Charts.js';
import { Usage } from './Usage.js';
import { Auth } from './Auth.js';
import { Subscribe } from './PayPal.js';
import { PasswordReset, ConfirmReset } from './PasswordReset.js';
@ -117,6 +118,10 @@ function App() {
<ClassFeed />
</Route>
<Route exact path='/usage/:name'>
<Usage />
</Route>
<Route path='/'>
<Container>
<div className='hero'>

92
webclient/src/Usage.js Normal file
View File

@ -0,0 +1,92 @@
import React, { useRef, useState, useEffect, useReducer } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, useParams, useLocation } from 'react-router-dom';
import moment from 'moment-timezone';
import QRCode from 'react-qr-code';
import { Button, Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Popup, Segment, Table } from 'semantic-ui-react';
import { statusColor, BasicTable, siteUrl, staticUrl, requester, isAdmin } from './utils.js';
const deviceNames = {
'trotec': {title: 'Trotec', device: 'TROTECS300'},
};
export function Usage(props) {
const { name } = useParams();
const title = deviceNames[name].title;
const device = deviceNames[name].device;
const [usage, setUsage] = useState(false);
const [fullElement, setFullElement] = useState(false);
const ref = useRef(null);
const getUsage = () => {
requester('/stats/usage_data/?device='+device, 'GET', '')
.then(res => {
setUsage(res);
})
.catch(err => {
console.log(err);
setUsage(false);
});
};
useEffect(() => {
getUsage();
const interval = setInterval(getUsage, 60000);
return () => clearInterval(interval);
}, []);
const goFullScreen = () => {
if ('wakeLock' in navigator) {
navigator.wakeLock.request('screen');
}
ref.current.requestFullscreen({ navigationUI: 'hide' }).then(() => {
setFullElement(true);
});
};
const inUse = usage && moment().unix() - usage.track.time < 300;
const showUsage = usage && inUse && usage.track.username === usage.session.username;
const now = moment();
return (
<Container>
<div className='usage' ref={ref}>
{!fullElement &&
<p>
<Button onClick={goFullScreen}>Fullscreen</Button>
</p>
}
{showUsage ?
<>
<Header size='medium'>Hello,</Header>
<p className='stat'>
{usage.session.first_name}
</p>
<Header size='medium'>Session Time</Header>
<p className='stat'>
{parseInt(moment.duration(moment(now).diff(usage.session.start_time)).asMinutes())} mins
</p>
<Header size='medium'>Laser Time</Header>
<p className='stat'>
{parseInt(usage.session.num_seconds / 60)} mins
</p>
</>
:
<>
<Header size='large'>{title} Usage</Header>
<p>Waiting for session</p>
</>
}
</div>
</Container>
);
};

View File

@ -133,6 +133,24 @@ body {
margin-top: 1rem;
}
.usage {
height: 100vh;
background-color: black;
color: white;
padding: 0.5em;
font-size: 3em;
}
.usage .ui.header {
color: white;
}
.usage .stat {
font-size: 2em;
margin-bottom: 0.75em;
}
.footer {
margin-top: -20rem;
background: black;