Add a special page showing the latest laser usage
This commit is contained in:
parent
8d8a399f33
commit
680039fa51
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
92
webclient/src/Usage.js
Normal 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>
|
||||
);
|
||||
};
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user