doorcontrol: Add annotations that decode the hex card number

This commit is contained in:
Adam Goldsmith 2023-04-03 23:15:12 -04:00
parent 67f13ce580
commit 906f662419
2 changed files with 52 additions and 2 deletions

View File

@ -30,9 +30,10 @@ class HIDEventAdmin(admin.ModelAdmin):
"event_type", "event_type",
IsRedFilter, IsRedFilter,
] ]
readonly_fields = ["decoded_card_number"]
def get_queryset(self, request): def get_queryset(self, request):
return super().get_queryset(request).with_is_red() return super().get_queryset(request).with_is_red().with_decoded_card_number()
@admin.display(boolean=True) @admin.display(boolean=True)
def is_red(self, obj): def is_red(self, obj):

View File

@ -1,5 +1,6 @@
from django.db import models from django.db import models
from django.db.models import ExpressionWrapper, Q from django.db.models import ExpressionWrapper, F, Func, Q
from django.db.models.functions import Mod
class HIDEventQuerySet(models.QuerySet): class HIDEventQuerySet(models.QuerySet):
@ -28,6 +29,42 @@ class HIDEventQuerySet(models.QuerySet):
) )
) )
def with_decoded_card_number(self):
# TODO: CONV and BIT_COUNT are MySQL/MariaDB specific
class Conv(Func):
function = "CONV"
arity = 3
# This is technically not true, but fine for my purposes
output_field = models.IntegerField()
class BitCount(Func):
function = "BIT_COUNT"
arity = 1
return (
self.alias(card_number=Conv(F("raw_card_number"), 16, 10))
.alias(more_than_26_bits=F("card_number").bitrightshift(26))
.annotate(card_is_26_bit=Q(more_than_26_bits=0))
.alias(
parity_a=Mod(
BitCount(F("card_number").bitrightshift(1).bitand(0xFFF)), 2
),
parity_b=Mod(
BitCount(F("card_number").bitrightshift(13).bitand(0xFFF)), 2
),
)
.annotate(
card_is_valid_26_bit=~Q(parity_a=F("card_number").bitand(1))
& Q(parity_b=F("card_number").bitrightshift(25).bitand(1))
)
.annotate(
card_number_26_bit=F("card_number").bitrightshift(1).bitand(0xFFFF),
card_facility_code_26_bit=F("card_number")
.bitrightshift(17)
.bitand(0xFF),
)
)
class HIDEvent(models.Model): class HIDEvent(models.Model):
objects = HIDEventQuerySet.as_manager() objects = HIDEventQuerySet.as_manager()
@ -121,6 +158,18 @@ class HIDEvent(models.Model):
def __str__(self): def __str__(self):
return f"{self.door_name} {self.timestamp} - {self.description}" return f"{self.door_name} {self.timestamp} - {self.description}"
def decoded_card_number(self) -> str:
"""Requires annotations from `with_decoded_card_number`"""
if self.raw_card_number is None:
return None
elif self.card_is_26_bit:
if self.card_is_valid_26_bit:
return f"{self.card_facility_code_26_bit} - {self.card_number_26_bit}"
else:
return "Invalid"
else:
return "Not 26 bit card"
class Meta: class Meta:
constraints = [ constraints = [
models.UniqueConstraint( models.UniqueConstraint(