Refactor new XML updater, move methods into correct classes

This commit is contained in:
Adam Goldsmith 2019-11-07 15:22:50 -05:00
parent bb18f34b2e
commit 667260831c
2 changed files with 83 additions and 88 deletions

View File

@ -26,7 +26,7 @@ class DoorController():
self.name = name
self.access = access
def doImportRequest(self, params=None, files=None):
def doImport(self, params=None, files=None):
"""Send a request to the door control import script"""
r = requests.post(
'https://' + self.ip + '/cgi-bin/import.cgi',
@ -42,10 +42,10 @@ class DoorController():
def doCSVImport(self, csv):
"""Do the CSV import procedure on a door control"""
self.doImportRequest({"task": "importInit"})
self.doImportRequest({"task": "importCardsPeople", "name": "cardspeopleschedule.csv"},
self.doImport({"task": "importInit"})
self.doImport({"task": "importCardsPeople", "name": "cardspeopleschedule.csv"},
{"importCardsPeopleButton": ("cardspeopleschedule.csv", csv, 'text/csv')})
self.doImportRequest({"task": "importDone"})
self.doImport({"task": "importDone"})
def doXMLRequest(self, xml, prefix=b'<?xml version="1.0" encoding="UTF-8"?>'):
if not isinstance(xml, bytes):
@ -62,7 +62,28 @@ class DoorController():
raise RemoteError(r)
return resp_xml
def sendSchedules(self, schedules):
def get_scheduleMap(self):
schedules = self.doXMLRequest(
ROOT(E.Schedules({"action": "LR",
"recordOffset": "0",
"recordCount": "8"})))
return {fmt.attrib["scheduleName"]: fmt.attrib["scheduleID"]
for fmt in schedules[0]}
def get_schedules(self):
# TODO: might be able to do in one request
schedules = self.doXMLRequest(ROOT(
E.Schedules({"action": "LR"})))
etree.dump(schedules)
data = self.doXMLRequest(ROOT(
*[E.Schedules({"action": "LR",
"scheduleID": schedule.attrib["scheduleID"]})
for schedule in schedules[0]]))
return ROOT(E_corp.Schedules({"action": "AD"},
*[s[0] for s in data]))
def set_schedules(self, schedules):
# clear all people
outString = StringIO()
writer = csv.DictWriter(outString, fieldnames)
@ -84,20 +105,15 @@ class DoorController():
# load new schedules
self.doXMLRequest(schedules)
def getSchedules(self):
# TODO: might be able to do in one request
schedules = self.doXMLRequest(ROOT(
E.Schedules({"action": "LR"})))
etree.dump(schedules)
def get_cardFormats(self):
cardFormats = self.doXMLRequest(
ROOT(E.CardFormats({"action": "LR",
"responseFormat": "expanded"})))
data = self.doXMLRequest(ROOT(
*[E.Schedules({"action": "LR",
"scheduleID": schedule.attrib["scheduleID"]})
for schedule in schedules[0]]))
return ROOT(E_corp.Schedules({"action": "AD"},
*[s[0] for s in data]))
return {fmt[0].attrib["value"]: fmt.attrib["formatID"]
for fmt in cardFormats[0].findall('{*}CardFormat[{*}FixedField]')}
def sendCardFormat(self, formatName, templateID, facilityCode):
def set_cardFormat(self, formatName, templateID, facilityCode):
# TODO: add ability to delete formats
# delete example: <hid:CardFormats action="DD" formatID="7-1-244"/>
@ -108,15 +124,22 @@ class DoorController():
E.FixedField({"value": str(facilityCode)}))))
return self.doXMLRequest(el)
def lockOrUnlockDoor(self, lock=True):
el = ROOT(
E.Doors({"action": "CM",
"command": "lockDoor" if lock else "unlockDoor"}))
return self.doXMLRequest(el)
def get_cardholders(self):
return self.doXMLRequest(
ROOT(E.Cardholders({"action": "LR",
"responseFormat": "expanded",
"recordOffset": "0",
"recordCount": "1000"})))[0]
def getStatus(self):
def get_lock(self):
el = ROOT(
E.Doors({"action": "LR", "responseFormat": "status"}))
xml = self.doXMLRequest(el)
relayState = xml.find('./{*}Doors/{*}Door').attrib['relayState']
return "unlocked" if relayState == "set" else "locked"
def set_lock(self, lock=True):
el = ROOT(
E.Doors({"action": "CM",
"command": "lockDoor" if lock else "unlockDoor"}))
return self.doXMLRequest(el)

View File

@ -83,78 +83,56 @@ Schedules: {self.schedules}
schedules=list(levels.values()))
def attribs(self):
out = {"forename": self.forename,
return {"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):
def schedulesForDoor(self, door):
if door.name not in self.doorAccess or self.onHold:
return []
else:
return self.schedules
for schedule in self.schedules:
yield E.Role({"roleID": self.cardholderID,
def make_schedules(self, door, schedulesMap):
roles = [
E.Role({"roleID": self.cardholderID,
"scheduleID": schedulesMap[schedule],
"resourceID": "0"})
for schedule in self.schedulesForDoor(door)]
return E.RoleSet({"action": "UD", "roleSetID": self.cardholderID},
E.Roles(*roles))
def make_credentials(self, cardFormats):
for credential in self.credentials:
yield E.Credential(
credentials = [
E.Credential(
{"formatName": credential[0],
"cardNumber": credential[1],
"formatID": cardFormats[credential[0]],
"isCard": "true",
"cardholderID": self.cardholderID})
for credential in self.credentials]
def get_cardholders(door, cardFormats):
cardholders = door.doXMLRequest(
ROOT(E.Cardholders({"action": "LR",
"responseFormat": "expanded",
"recordOffset": "0",
"recordCount": "1000"})))
return E.Credentials({"action": "AD"}, *credentials)
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)
cardFormats = door.get_cardFormats()
cardholders = {member.membershipWorksID: member
for member in [Member.from_cardholder(ch, cardFormats)
for ch in door.get_cardholders()]}
schedulesMap = door.get_scheduleMap()
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
if member.attribs() != ch.attribs(): # update cardholder attributes
print(f"- Updating profile for {member.forename} {member.surname}")
print(f" - Old: {ch.attribs()}")
print(f" - New: {member.attribs()}")
@ -162,19 +140,18 @@ def update_door(door, members):
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)))
door.doXMLRequest(ROOT(member.make_credentials(cardFormats)))
# TODO: might not handle people on hold correctly?
schedulesForDoor = member.schedules if door.name in member.doorAccess else []
if schedulesForDoor != ch.schedules:
if member.schedulesForDoor(door) != 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
f" {ch.schedules} -> {member.schedulesForDoor(door)}")
door.doXMLRequest(ROOT(member.make_schedules(door, schedulesMap)))
else: # cardholder did not exist, so add them
print(f"- Adding Member:")
print(member)
resp = door.doXMLRequest(ROOT(
@ -182,8 +159,8 @@ def update_door(door, members):
E.Cardholder(member.attribs()))))
member.cardholderID = resp.find('{*}Cardholders/{*}Cardholder') \
.attrib["cardholderID"]
door.doXMLRequest(ROOT(update_credentials(member, cardFormats),
update_schedules(member, door, schedulesMap)))
door.doXMLRequest(ROOT(member.make_credentials(cardFormats),
member.update_schedules(door, schedulesMap)))
# TODO: delete cardholders that are no longer members?
@ -198,9 +175,4 @@ def main():
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()