diff --git a/apiserver/apiserver/api/models.py b/apiserver/apiserver/api/models.py
index a6ddcda..dd01cab 100644
--- a/apiserver/apiserver/api/models.py
+++ b/apiserver/apiserver/api/models.py
@@ -105,9 +105,10 @@ class Card(models.Model):
card_number = models.CharField(unique=True, max_length=16, blank=True, null=True)
notes = models.TextField(blank=True, null=True)
last_seen_at = models.DateField(blank=True, null=True)
+ last_seen = models.DateTimeField(blank=True, null=True)
active_status = models.CharField(max_length=32, blank=True, null=True)
- history = HistoricalRecords(excluded_fields=['last_seen_at'])
+ history = HistoricalRecords(excluded_fields=['last_seen_at', 'last_seen'])
class Course(models.Model):
name = models.TextField(blank=True, null=True)
diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py
index ce74f76..81af3cd 100644
--- a/apiserver/apiserver/api/serializers.py
+++ b/apiserver/apiserver/api/serializers.py
@@ -73,7 +73,6 @@ class TransactionSerializer(serializers.ModelSerializer):
fields = '__all__'
read_only_fields = [
'id',
- 'last_seen_at',
'user',
'recorder',
'paypal_txn_id',
@@ -354,7 +353,7 @@ class AdminSearchSerializer(serializers.Serializer):
queryset = obj.user.cards
else:
queryset = models.Card.objects.filter(member_id=obj.id)
- queryset = queryset.order_by('-last_seen_at')
+ queryset = queryset.order_by('-last_seen')
serializer = CardSerializer(data=queryset, many=True)
serializer.is_valid()
return serializer.data
@@ -406,7 +405,7 @@ class CardSerializer(serializers.ModelSerializer):
fields = '__all__'
read_only_fields = [
'id',
- 'last_seen_at',
+ 'last_seen',
'user',
]
diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py
index 0a6b938..54fc990 100644
--- a/apiserver/apiserver/api/utils_stats.py
+++ b/apiserver/apiserver/api/utils_stats.py
@@ -9,8 +9,9 @@ from django.utils.timezone import now, pytz
from apiserver.api import models
from apiserver import secrets
+tz = pytz.timezone('America/Edmonton')
def today_alberta_tz():
- return datetime.now(pytz.timezone('America/Edmonton')).date()
+ return datetime.now(tz).date()
DEFAULTS = {
'last_card_change': time.time(),
@@ -140,13 +141,16 @@ def check_mumble_server():
def calc_card_scans():
date = today_alberta_tz()
+ dt = datetime.combine(date, datetime.min.time())
+ midnight = tz.localize(dt)
+
cards = models.Card.objects
- count = cards.filter(last_seen_at=date).count()
+ count = cards.filter(last_seen__gte=midnight).count()
cache.set('card_scans', count)
models.StatsSpaceActivity.objects.update_or_create(
- date=today_alberta_tz(),
+ date=date,
defaults=dict(card_scans=count),
)
diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py
index 9c1d9f7..13c0d79 100644
--- a/apiserver/apiserver/api/views.py
+++ b/apiserver/apiserver/api/views.py
@@ -122,7 +122,7 @@ class SearchViewSet(Base, Retrieve):
elif self.action == 'create' and sort == 'last_scanned':
if self.request.user.member.allow_last_scanned:
queryset = queryset.filter(allow_last_scanned=True)
- queryset = queryset.order_by('-user__cards__last_seen_at')
+ queryset = queryset.order_by('-user__cards__last_seen')
else:
queryset = []
elif self.action == 'create' and sort == 'best_looking':
@@ -465,7 +465,7 @@ class DoorViewSet(viewsets.ViewSet, List):
@action(detail=True, methods=['post'])
def seen(self, request, pk=None):
card = get_object_or_404(models.Card, card_number=pk)
- card.last_seen_at = utils.today_alberta_tz()
+ card.last_seen = now()
card.save()
try:
diff --git a/apiserver/convert_card_seen.py b/apiserver/convert_card_seen.py
new file mode 100755
index 0000000..8977d0f
--- /dev/null
+++ b/apiserver/convert_card_seen.py
@@ -0,0 +1,24 @@
+import django, sys, os
+os.environ['DJANGO_SETTINGS_MODULE'] = 'apiserver.settings'
+django.setup()
+
+from datetime import datetime
+import json
+import pytz
+
+from apiserver.api import models, utils
+
+tz = pytz.timezone('America/Edmonton')
+
+cards = models.Card.objects.order_by('last_seen_at')
+
+for card in cards:
+ seen = card.last_seen_at
+ if seen:
+ t = datetime.combine(seen, datetime.min.time())
+ card.last_seen = tz.localize(t)
+ card.save()
+
+ print('card', card.card_number, 'date', seen, '-->', card.last_seen)
+
+print('Done.')
diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js
index 0981ba2..231066a 100644
--- a/webclient/src/AdminMembers.js
+++ b/webclient/src/AdminMembers.js
@@ -103,8 +103,16 @@ function AdminCardDetail(props) {
- Notes: {input.notes || 'None'},
- Last Seen: {input.last_seen_at || 'Unknown'}
+ Notes: {input.notes || 'None'}
+ Last Seen:{' '}
+ {input.last_seen ?
+ input.last_seen > '2021-11-14T02:01:35.415685Z' ?
+ moment.utc(input.last_seen).tz('America/Edmonton').format('lll')
+ :
+ moment.utc(input.last_seen).tz('America/Edmonton').format('ll')
+ :
+ 'Unknown'
+ }
:
diff --git a/webclient/src/Cards.js b/webclient/src/Cards.js
index e73b3e2..10eb768 100644
--- a/webclient/src/Cards.js
+++ b/webclient/src/Cards.js
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, useParams } from 'react-router-dom';
import './light.css';
+import moment from 'moment-timezone';
import { Button, Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react';
import { BasicTable, requester, staticUrl } from './utils.js';
import { NotFound, PleaseLogin } from './Misc.js';
@@ -45,7 +46,16 @@ export function Cards(props) {