From afcf1c34855386dbd15349d269540e3d7280f2dc Mon Sep 17 00:00:00 2001
From: Tanner Collin
Date: Wed, 1 Mar 2023 17:58:50 +0000
Subject: [PATCH 1/3] Return first name of recent card scan
---
apiserver/apiserver/api/views.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py
index e7a92ce..47af87c 100644
--- a/apiserver/apiserver/api/views.py
+++ b/apiserver/apiserver/api/views.py
@@ -632,6 +632,7 @@ class DoorViewSet(viewsets.ViewSet, List):
last_scan = dict(
time=time.time(),
member_id=member.id,
+ first_name=member.preferred_name,
)
cache.set('last_scan', last_scan)
From 7112b19cca8955b408e661293a3a76cbf82cfa4c Mon Sep 17 00:00:00 2001
From: Tanner Collin
Date: Wed, 1 Mar 2023 21:40:45 +0000
Subject: [PATCH 2/3] Add models and API route for hosting new members
---
apiserver/apiserver/api/models.py | 13 +++++
apiserver/apiserver/api/utils_stats.py | 1 +
apiserver/apiserver/api/views.py | 66 ++++++++++++++++++++++++++
apiserver/apiserver/urls.py | 1 +
4 files changed, 81 insertions(+)
diff --git a/apiserver/apiserver/api/models.py b/apiserver/apiserver/api/models.py
index 9cd5424..7f168ef 100644
--- a/apiserver/apiserver/api/models.py
+++ b/apiserver/apiserver/api/models.py
@@ -250,6 +250,19 @@ class PinballScore(models.Model):
def __str__(self):
return str(self.started_at)
+class Hosting(models.Model):
+ user = models.ForeignKey(User, related_name='hosting', blank=True, null=True, on_delete=models.SET_NULL)
+
+ started_at = models.DateTimeField(auto_now_add=True)
+ finished_at = models.DateTimeField()
+ hours = models.DecimalField(max_digits=5, decimal_places=2)
+
+ # no history
+
+ MY_FIELDS = ['started_at', 'hours', 'finished_at', 'user']
+ def __str__(self):
+ return str(self.started_at)
+
class HistoryIndex(models.Model):
content_type = models.ForeignKey(ContentType, null=True, on_delete=models.SET_NULL)
object_id = models.PositiveIntegerField()
diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py
index 021a722..f81c811 100644
--- a/apiserver/apiserver/api/utils_stats.py
+++ b/apiserver/apiserver/api/utils_stats.py
@@ -29,6 +29,7 @@ DEFAULTS = {
'link': '',
'autoscan': '',
'last_scan': {},
+ 'closing': {},
}
if secrets.MUMBLE:
diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py
index 47af87c..1a51acd 100644
--- a/apiserver/apiserver/api/views.py
+++ b/apiserver/apiserver/api/views.py
@@ -1543,6 +1543,72 @@ class PinballViewSet(Base):
return Response(scores)
+class HostingViewSet(Base):
+ @action(detail=False, methods=['post'])
+ def offer(self, request):
+ #auth_token = request.META.get('HTTP_AUTHORIZATION', '')
+ #if secrets.PINBALL_API_TOKEN and auth_token != 'Bearer ' + secrets.PINBALL_API_TOKEN:
+ # raise exceptions.PermissionDenied()
+
+ try:
+ member_id = int(request.data['member_id'])
+ except KeyError:
+ raise exceptions.ValidationError(dict(game_id='This field is required.'))
+ except ValueError:
+ raise exceptions.ValidationError(dict(game_id='Invalid number.'))
+
+ try:
+ hours = int(request.data['hours'])
+ except KeyError:
+ raise exceptions.ValidationError(dict(player='This field is required.'))
+ except ValueError:
+ raise exceptions.ValidationError(dict(player='Invalid number.'))
+
+ hosting_member = get_object_or_404(models.Member, id=member_id)
+ hosting_user = hosting_member.user
+
+ logging.info('Hosting offer from %s %s for %s hours', hosting_member.preferred_name, hosting_member.last_name, hours)
+
+ try:
+ current_hosting = models.Hosting.objects.get(user=hosting_user, finished_at__gte=now())
+ logging.info('Current hosting by member: %s', current_hosting)
+ new_end = now() + datetime.timedelta(hours=hours)
+ new_delta = new_end - current_hosting.started_at
+ new_hours = new_delta.seconds / 3600
+
+ logging.info(
+ 'Hosting %s from %s is still going, updating hours from %s to %s.',
+ current_hosting.id,
+ current_hosting.started_at,
+ current_hosting.hours,
+ new_hours
+ )
+
+ current_hosting.finished_at = new_end
+ current_hosting.hours = new_hours
+ current_hosting.save()
+
+ except models.Hosting.DoesNotExist:
+ h = models.Hosting.objects.create(
+ user=hosting_user,
+ hours=hours,
+ finished_at=now() + datetime.timedelta(hours=hours),
+ )
+
+ logging.info('No current hosting for that user, new hosting #%s created.', h.id)
+
+ # update "open until" time
+ hosting = models.Hosting.objects.order_by('-finished_at').first()
+ closing = dict(
+ time=hosting.finished_at.timestamp(),
+ time_str=hosting.finished_at.astimezone(utils.TIMEZONE_CALGARY).strftime('%-I:%M %p'),
+ first_name=hosting.user.member.preferred_name,
+ )
+ cache.set('closing', closing)
+
+ return Response(200)
+
+
class RegistrationView(RegisterView):
serializer_class = serializers.MyRegisterSerializer
diff --git a/apiserver/apiserver/urls.py b/apiserver/apiserver/urls.py
index fc13f9b..2e34671 100644
--- a/apiserver/apiserver/urls.py
+++ b/apiserver/apiserver/urls.py
@@ -20,6 +20,7 @@ router.register(r'courses', views.CourseViewSet, basename='course')
router.register(r'history', views.HistoryViewSet, basename='history')
router.register(r'vetting', views.VettingViewSet, basename='vetting')
router.register(r'pinball', views.PinballViewSet, basename='pinball')
+router.register(r'hosting', views.HostingViewSet, basename='hosting')
router.register(r'sessions', views.SessionViewSet, basename='session')
router.register(r'training', views.TrainingViewSet, basename='training')
router.register(r'interest', views.InterestViewSet, basename='interest')
From f0e012cc0352829f171bcfeef5eb81dacaaaad71 Mon Sep 17 00:00:00 2001
From: Tanner Collin
Date: Thu, 2 Mar 2023 04:36:40 +0000
Subject: [PATCH 3/3] Add hosting status to home stats
---
webclient/src/Home.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/webclient/src/Home.js b/webclient/src/Home.js
index cd1398a..fbd9be4 100644
--- a/webclient/src/Home.js
+++ b/webclient/src/Home.js
@@ -242,6 +242,8 @@ export function Home(props) {
//const doorOpenStat = () => alarmStat() === 'Disarmed' && stats.alarm['data'] > 360 ? ', door open' : '';
const doorOpenStat = () => '';
+ const closedStat = (x) => stats && stats.closing ? moment().unix() > stats.closing['time'] ? 'Closed' : 'Open until ' + stats.closing['time_str'] : 'Unknown';
+
const show_signup = stats?.at_protospace;
return (
@@ -356,6 +358,8 @@ export function Home(props) {
{user && Alarm status: {alarmStat()}{doorOpenStat()}
}
+
+ {user && Hosting status: {closedStat()}
}