Compare commits
No commits in common. "b056eb04ed63c096561526e1ce8cf6fba3df74be" and "33b01af78aa27fb0a8acc28aa27260dec0b83985" have entirely different histories.
b056eb04ed
...
33b01af78a
@ -12,36 +12,28 @@ class InvalidHexCode(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class Not26Bit(InvalidHexCode):
|
class Not26Bit(InvalidHexCode):
|
||||||
def __init__(self, bits: bitstring.Bits) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__(f"Card number > 26 bits [{bits.hex}]")
|
super().__init__("Card number > 26 bits")
|
||||||
|
|
||||||
|
|
||||||
class InvalidParity(InvalidHexCode):
|
class InvalidParity(InvalidHexCode):
|
||||||
def __init__(self, bits: bitstring.Bits, even_odd: Literal["even", "odd"]) -> None:
|
def __init__(self, even_odd: Literal["even", "odd"]) -> None:
|
||||||
super().__init__(f"Bad {even_odd} parity [{bits.hex}]")
|
super().__init__(f"Bad {even_odd} parity")
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(frozen=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Credential:
|
class Credential:
|
||||||
bits: bitstring.Bits
|
bits: bitstring.Bits
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def even_parity(bits: bitstring.Bits) -> bool:
|
|
||||||
return bool(bits[7:19].count(1) % 2)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def odd_parity(bits: bitstring.Bits) -> bool:
|
|
||||||
return (bits[19:31].count(1) % 2) == 0
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_code(cls, facility: int, card_number: int) -> "Credential":
|
def from_code(cls, facility=int, card_number=int) -> "Credential":
|
||||||
bits = bitstring.pack(
|
bits = bitstring.pack(
|
||||||
"0b000000, 0b0, uint:8=facility, uint:16=card_number, 0b0",
|
"0b000000, 0b0, uint:8=facility, uint:16=card_number, 0b0",
|
||||||
facility=facility,
|
facility=facility,
|
||||||
card_number=card_number,
|
card_number=card_number,
|
||||||
)
|
)
|
||||||
bits[6] = cls.even_parity(bits)
|
bits[6] = bits[7:19].count(1) % 2 # even parity
|
||||||
bits[31] = cls.odd_parity(bits)
|
bits[31] = bits[19:31].count(0) % 2 # odd parity
|
||||||
return cls(bitstring.Bits(bits))
|
return cls(bitstring.Bits(bits))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -49,11 +41,11 @@ class Credential:
|
|||||||
bits = bitstring.Bits(hex=hex_code)
|
bits = bitstring.Bits(hex=hex_code)
|
||||||
|
|
||||||
if bits[:6].any(1):
|
if bits[:6].any(1):
|
||||||
raise Not26Bit(bits)
|
raise Not26Bit
|
||||||
if bits[6] != cls.even_parity(bits):
|
if bits[6] != bits[7:19].count(1) % 2:
|
||||||
raise InvalidParity(bits, "even")
|
raise InvalidParity("even")
|
||||||
if bits[31] != (cls.odd_parity(bits)):
|
if bits[31] != (bits[19:31].count(0) % 2):
|
||||||
raise InvalidParity(bits, "odd")
|
raise InvalidParity("odd")
|
||||||
|
|
||||||
return cls(bits)
|
return cls(bits)
|
||||||
|
|
||||||
|
@ -18,13 +18,3 @@ class CredentialTestCase(TestCase):
|
|||||||
cred = Credential.from_code(facility_code, card_number)
|
cred = Credential.from_code(facility_code, card_number)
|
||||||
hex_cred = Credential.from_hex(cred.hex)
|
hex_cred = Credential.from_hex(cred.hex)
|
||||||
self.assertEqual(cred, hex_cred)
|
self.assertEqual(cred, hex_cred)
|
||||||
|
|
||||||
def test_example_numbers(self):
|
|
||||||
examples = [
|
|
||||||
(100, 12345, "02C86073"),
|
|
||||||
]
|
|
||||||
|
|
||||||
for facility_code, card_number, hex_number in examples:
|
|
||||||
code_cred = Credential.from_code(facility_code, card_number)
|
|
||||||
hex_cred = Credential.from_hex(hex_number)
|
|
||||||
self.assertEqual(code_cred, hex_cred)
|
|
||||||
|
@ -208,14 +208,11 @@ class DoorMember:
|
|||||||
|
|
||||||
|
|
||||||
def update_door(door: Door, dry_run: bool = False):
|
def update_door(door: Door, dry_run: bool = False):
|
||||||
logger.info(f"Updating {door}")
|
|
||||||
logger.debug(f"Fetching members from database for {door}")
|
|
||||||
members = [
|
members = [
|
||||||
DoorMember.from_membershipworks_member(membershipworks_member, door)
|
DoorMember.from_membershipworks_member(membershipworks_member, door)
|
||||||
for membershipworks_member in (Member.objects.with_is_active()).all()
|
for membershipworks_member in (Member.objects.with_is_active()).all()
|
||||||
]
|
]
|
||||||
|
|
||||||
logger.debug(f"Fetching cardholders from {door}")
|
|
||||||
cardholders = {
|
cardholders = {
|
||||||
member.membershipworks_id: member
|
member.membershipworks_id: member
|
||||||
for member in [
|
for member in [
|
||||||
@ -224,13 +221,11 @@ def update_door(door: Door, dry_run: bool = False):
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(f"Fetching credentials from {door}")
|
|
||||||
existing_door_credentials = {
|
existing_door_credentials = {
|
||||||
Credential.from_hex(c.attrib["rawCardNumber"])
|
Credential.from_hex(c.attrib["rawCardNumber"])
|
||||||
for c in door.controller.get_credentials()
|
for c in door.controller.get_credentials()
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(f"Syncing members with {door}")
|
|
||||||
# TODO: can I combine requests?
|
# TODO: can I combine requests?
|
||||||
for member in members:
|
for member in members:
|
||||||
# cardholder did not exist, so add them
|
# cardholder did not exist, so add them
|
||||||
|
Loading…
Reference in New Issue
Block a user