Switch to XML messages instead of CSV import for updating controllers
This commit is contained in:
parent
96c34c95f3
commit
d867cacfef
@ -49,7 +49,8 @@ class DoorController():
|
||||
self.doImportRequest({"task": "importDone"})
|
||||
|
||||
def doXMLRequest(self, xml, prefix=b'<?xml version="1.0" encoding="UTF-8"?>'):
|
||||
if not isinstance(xml, str): xml = etree.tostring(xml)
|
||||
if not isinstance(xml, bytes):
|
||||
xml = etree.tostring(xml)
|
||||
r = requests.get(
|
||||
'https://' + self.ip + '/cgi-bin/vertx_xml.cgi',
|
||||
params={'XML': prefix + xml},
|
||||
|
206
new_xml_based.py
Normal file
206
new_xml_based.py
Normal file
@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
import requests
|
||||
from lxml import etree
|
||||
|
||||
from common import doors, getMembershipworksData, memberLevels
|
||||
from hid.DoorController import E, ROOT
|
||||
|
||||
class Member():
|
||||
def __init__(self, forename, surname, membershipWorksID,
|
||||
middleName="", email="", phone="",
|
||||
cardholderID=None, doorAccess=[], onHold=False,
|
||||
credentials=[], levels=[], schedules=[]):
|
||||
self.forename = forename
|
||||
self.surname = surname
|
||||
self.membershipWorksID = membershipWorksID
|
||||
self.middleName = middleName
|
||||
self.email = email
|
||||
self.phone = phone
|
||||
self.cardholderID = cardholderID
|
||||
self.doorAccess = doorAccess
|
||||
self.onHold = onHold
|
||||
self.credentials = credentials
|
||||
self.levels = levels
|
||||
self.schedules = schedules
|
||||
|
||||
def __repr__(self):
|
||||
return f"""Name: {self.forename} | {self.middleName} | {self.surname}
|
||||
MembershipWorks ID: {self.membershipWorksID}
|
||||
Email: {self.email}
|
||||
Phone: {self.phone}
|
||||
Cardholder ID: {self.cardholderID}
|
||||
doorAccess: {self.doorAccess}
|
||||
On Hold? {self.onHold}
|
||||
Credentials: {self.credentials}
|
||||
Levels: {self.levels}
|
||||
Schedules: {self.schedules}
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def from_cardholder(cls, data, cardFormats):
|
||||
# TODO: maybe keep all attribs, and just add them in
|
||||
# from_MembershipWorks?
|
||||
cardFormatsByID = {v: k for k, v in cardFormats.items()}
|
||||
credentials = [
|
||||
(cardFormatsByID[c.attrib['formatID']], c.attrib['cardNumber'])
|
||||
for c in data.findall('{*}Credential')]
|
||||
roles = [r.attrib['scheduleName']
|
||||
for r in data.findall('{*}Role')]
|
||||
levels = data.attrib.get('custom1', "").split('|')
|
||||
return cls(
|
||||
forename=data.get('forename', ""),
|
||||
surname=data.get('surname', ""),
|
||||
membershipWorksID=data.attrib.get('custom2', ""),
|
||||
middleName=data.attrib.get('middleName', ""),
|
||||
email=data.attrib.get('email', ""),
|
||||
phone=data.attrib.get('phone', ""),
|
||||
cardholderID=data.attrib['cardholderID'],
|
||||
credentials=credentials,
|
||||
levels=levels,
|
||||
schedules=roles)
|
||||
|
||||
@classmethod
|
||||
def from_MembershipWorks(cls, data):
|
||||
if data["Access Card Number"] != "":
|
||||
credentials = [(data["Access Card Facility Code"],
|
||||
data["Access Card Number"])]
|
||||
else:
|
||||
credentials = []
|
||||
|
||||
levels = {k: v for k, v in memberLevels.items() if data[k] == k}
|
||||
doorAccess = [door for door, doorData in doors.items()
|
||||
if data["Access " + doorData.access + "?"] == "Y"]
|
||||
|
||||
return cls(data["First Name"],
|
||||
data["Last Name"],
|
||||
membershipWorksID=data["Account ID"],
|
||||
email=data["Email"],
|
||||
phone=data["Phone"],
|
||||
doorAccess=doorAccess,
|
||||
onHold=data["Account on Hold"] != "",
|
||||
credentials=credentials,
|
||||
levels=list(levels.keys()),
|
||||
schedules=list(levels.values()))
|
||||
|
||||
def attribs(self):
|
||||
out = {"forename": self.forename,
|
||||
"surname": self.surname,
|
||||
"middleName": self.middleName,
|
||||
"email": self.email,
|
||||
"phone": self.phone,
|
||||
"custom1": "|".join(self.levels).replace("&", "and"),
|
||||
"custom2": self.membershipWorksID}
|
||||
return out
|
||||
|
||||
def make_roles(self, door, schedulesMap):
|
||||
if door.name not in self.doorAccess or self.onHold:
|
||||
return []
|
||||
|
||||
for schedule in self.schedules:
|
||||
yield E.Role({"roleID": self.cardholderID,
|
||||
"scheduleID": schedulesMap[schedule],
|
||||
"resourceID": "0"})
|
||||
|
||||
def make_credentials(self, cardFormats):
|
||||
for credential in self.credentials:
|
||||
yield E.Credential(
|
||||
{"formatName": credential[0],
|
||||
"cardNumber": credential[1],
|
||||
"formatID": cardFormats[credential[0]],
|
||||
"isCard": "true",
|
||||
"cardholderID": self.cardholderID})
|
||||
|
||||
def get_cardholders(door, cardFormats):
|
||||
cardholders = door.doXMLRequest(
|
||||
ROOT(E.Cardholders({"action": "LR",
|
||||
"responseFormat": "expanded",
|
||||
"recordOffset": "0",
|
||||
"recordCount": "1000"})))
|
||||
|
||||
for cardholder in cardholders[0]:
|
||||
yield Member.from_cardholder(cardholder, cardFormats)
|
||||
|
||||
def get_cardFormats(door):
|
||||
cardFormats = door.doXMLRequest(
|
||||
ROOT(E.CardFormats({"action": "LR",
|
||||
"responseFormat": "expanded"})))
|
||||
|
||||
return {fmt[0].attrib["value"]: fmt.attrib["formatID"]
|
||||
for fmt in cardFormats[0].findall('{*}CardFormat[{*}FixedField]')}
|
||||
|
||||
def get_schedules(door):
|
||||
schedules = door.doXMLRequest(
|
||||
ROOT(E.Schedules({"action": "LR",
|
||||
"recordOffset": "0",
|
||||
"recordCount": "8"})))
|
||||
return {fmt.attrib["scheduleName"]: fmt.attrib["scheduleID"]
|
||||
for fmt in schedules[0]}
|
||||
|
||||
def update_credentials(member, cardFormats):
|
||||
return E.Credentials({"action": "AD"}, *member.make_credentials(cardFormats))
|
||||
|
||||
def update_schedules(member, door, schedulesMap):
|
||||
return E.RoleSet({"action": "UD", "roleSetID": member.cardholderID},
|
||||
E.Roles(*member.make_roles(door, schedulesMap)))
|
||||
|
||||
def update_door(door, members):
|
||||
cardFormats = get_cardFormats(door)
|
||||
cardholders = {ch.membershipWorksID: ch
|
||||
for ch in get_cardholders(door, cardFormats)}
|
||||
schedulesMap = get_schedules(door)
|
||||
|
||||
for member in members:
|
||||
# TODO: can I combine requests?
|
||||
if member.membershipWorksID in cardholders: # cardholder exists, compare contents
|
||||
ch = cardholders.pop(member.membershipWorksID)
|
||||
member.cardholderID = ch.cardholderID
|
||||
if member.attribs() != ch.attribs(): # update cardholder
|
||||
print(f"- Updating profile for {member.forename} {member.surname}")
|
||||
print(f" - Old: {ch.attribs()}")
|
||||
print(f" - New: {member.attribs()}")
|
||||
door.doXMLRequest(ROOT(
|
||||
E.Cardholders(
|
||||
{"action": "UD", "cardholderID": member.cardholderID},
|
||||
E.CardHolder(member.attribs()))))
|
||||
if member.credentials != ch.credentials:
|
||||
print(f"- Updating card for {member.forename} {member.surname}")
|
||||
print(f" - {ch.credentials} -> {member.credentials}")
|
||||
door.doXMLRequest(ROOT(update_credentials(member, cardFormats)))
|
||||
|
||||
# TODO: might not handle people on hold correctly?
|
||||
schedulesForDoor = member.schedules if door.name in member.doorAccess else []
|
||||
if schedulesForDoor != ch.schedules:
|
||||
print("- Updating schedule for" +
|
||||
f" {member.forename} {member.surname}:" +
|
||||
f" {ch.schedules} -> {member.schedules}")
|
||||
door.doXMLRequest(ROOT(update_schedules(member, door, schedulesMap)))
|
||||
else: # do add
|
||||
print(f"- Adding Member:")
|
||||
print(member)
|
||||
resp = door.doXMLRequest(ROOT(
|
||||
E.Cardholders({"action": "AD"},
|
||||
E.Cardholder(member.attribs()))))
|
||||
member.cardholderID = resp.find('{*}Cardholders/{*}Cardholder') \
|
||||
.attrib["cardholderID"]
|
||||
door.doXMLRequest(ROOT(update_credentials(member, cardFormats),
|
||||
update_schedules(member, door, schedulesMap)))
|
||||
|
||||
# TODO: delete cardholders that are no longer members?
|
||||
|
||||
def main():
|
||||
memberData = getMembershipworksData(
|
||||
['members', 'staff', 'misc'],
|
||||
"_id,nam,phn,eml,lvl,lbl,xws,xms,xsc,xas,xfd,xac,xcf")
|
||||
|
||||
members = [Member.from_MembershipWorks(m) for m in memberData]
|
||||
|
||||
for door in doors.values():
|
||||
print(door.name, door.ip)
|
||||
update_door(door, members)
|
||||
|
||||
# m = Member("test", "test", membershipWorksID="5af07954afd691b84c15a24d",
|
||||
# credentials=[("A901146A-241", "20178")],
|
||||
# schedules=["15:00-24:00"],
|
||||
# doorAccess=["Front Door"])
|
||||
# update_door(doors["Front Door"], [m])
|
||||
main()
|
Reference in New Issue
Block a user