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.name = name
self.access = access 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""" """Send a request to the door control import script"""
r = requests.post( r = requests.post(
'https://' + self.ip + '/cgi-bin/import.cgi', 'https://' + self.ip + '/cgi-bin/import.cgi',
@ -42,10 +42,10 @@ class DoorController():
def doCSVImport(self, csv): def doCSVImport(self, csv):
"""Do the CSV import procedure on a door control""" """Do the CSV import procedure on a door control"""
self.doImportRequest({"task": "importInit"}) self.doImport({"task": "importInit"})
self.doImportRequest({"task": "importCardsPeople", "name": "cardspeopleschedule.csv"}, self.doImport({"task": "importCardsPeople", "name": "cardspeopleschedule.csv"},
{"importCardsPeopleButton": ("cardspeopleschedule.csv", csv, 'text/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"?>'): def doXMLRequest(self, xml, prefix=b'<?xml version="1.0" encoding="UTF-8"?>'):
if not isinstance(xml, bytes): if not isinstance(xml, bytes):
@ -62,7 +62,28 @@ class DoorController():
raise RemoteError(r) raise RemoteError(r)
return resp_xml 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 # clear all people
outString = StringIO() outString = StringIO()
writer = csv.DictWriter(outString, fieldnames) writer = csv.DictWriter(outString, fieldnames)
@ -84,20 +105,15 @@ class DoorController():
# load new schedules # load new schedules
self.doXMLRequest(schedules) self.doXMLRequest(schedules)
def getSchedules(self): def get_cardFormats(self):
# TODO: might be able to do in one request cardFormats = self.doXMLRequest(
schedules = self.doXMLRequest(ROOT( ROOT(E.CardFormats({"action": "LR",
E.Schedules({"action": "LR"}))) "responseFormat": "expanded"})))
etree.dump(schedules)
data = self.doXMLRequest(ROOT( return {fmt[0].attrib["value"]: fmt.attrib["formatID"]
*[E.Schedules({"action": "LR", for fmt in cardFormats[0].findall('{*}CardFormat[{*}FixedField]')}
"scheduleID": schedule.attrib["scheduleID"]})
for schedule in schedules[0]]))
return ROOT(E_corp.Schedules({"action": "AD"},
*[s[0] for s in data]))
def sendCardFormat(self, formatName, templateID, facilityCode): def set_cardFormat(self, formatName, templateID, facilityCode):
# TODO: add ability to delete formats # TODO: add ability to delete formats
# delete example: <hid:CardFormats action="DD" formatID="7-1-244"/> # delete example: <hid:CardFormats action="DD" formatID="7-1-244"/>
@ -108,15 +124,22 @@ class DoorController():
E.FixedField({"value": str(facilityCode)})))) E.FixedField({"value": str(facilityCode)}))))
return self.doXMLRequest(el) return self.doXMLRequest(el)
def lockOrUnlockDoor(self, lock=True): def get_cardholders(self):
el = ROOT( return self.doXMLRequest(
E.Doors({"action": "CM", ROOT(E.Cardholders({"action": "LR",
"command": "lockDoor" if lock else "unlockDoor"})) "responseFormat": "expanded",
return self.doXMLRequest(el) "recordOffset": "0",
"recordCount": "1000"})))[0]
def getStatus(self): def get_lock(self):
el = ROOT( el = ROOT(
E.Doors({"action": "LR", "responseFormat": "status"})) E.Doors({"action": "LR", "responseFormat": "status"}))
xml = self.doXMLRequest(el) xml = self.doXMLRequest(el)
relayState = xml.find('./{*}Doors/{*}Door').attrib['relayState'] relayState = xml.find('./{*}Doors/{*}Door').attrib['relayState']
return "unlocked" if relayState == "set" else "locked" 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())) schedules=list(levels.values()))
def attribs(self): def attribs(self):
out = {"forename": self.forename, return {"forename": self.forename,
"surname": self.surname, "surname": self.surname,
"middleName": self.middleName, "middleName": self.middleName,
"email": self.email, "email": self.email,
"phone": self.phone, "phone": self.phone,
"custom1": "|".join(self.levels).replace("&", "and"), "custom1": "|".join(self.levels).replace("&", "and"),
"custom2": self.membershipWorksID} "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: if door.name not in self.doorAccess or self.onHold:
return [] return []
else:
return self.schedules
for schedule in self.schedules: def make_schedules(self, door, schedulesMap):
yield E.Role({"roleID": self.cardholderID, roles = [
"scheduleID": schedulesMap[schedule], E.Role({"roleID": self.cardholderID,
"resourceID": "0"}) "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): def make_credentials(self, cardFormats):
for credential in self.credentials: credentials = [
yield E.Credential( E.Credential(
{"formatName": credential[0], {"formatName": credential[0],
"cardNumber": credential[1], "cardNumber": credential[1],
"formatID": cardFormats[credential[0]], "formatID": cardFormats[credential[0]],
"isCard": "true", "isCard": "true",
"cardholderID": self.cardholderID}) "cardholderID": self.cardholderID})
for credential in self.credentials]
def get_cardholders(door, cardFormats): return E.Credentials({"action": "AD"}, *credentials)
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): def update_door(door, members):
cardFormats = get_cardFormats(door) cardFormats = door.get_cardFormats()
cardholders = {ch.membershipWorksID: ch cardholders = {member.membershipWorksID: member
for ch in get_cardholders(door, cardFormats)} for member in [Member.from_cardholder(ch, cardFormats)
schedulesMap = get_schedules(door) for ch in door.get_cardholders()]}
schedulesMap = door.get_scheduleMap()
for member in members: for member in members:
# TODO: can I combine requests? # TODO: can I combine requests?
if member.membershipWorksID in cardholders: # cardholder exists, compare contents if member.membershipWorksID in cardholders: # cardholder exists, compare contents
ch = cardholders.pop(member.membershipWorksID) ch = cardholders.pop(member.membershipWorksID)
member.cardholderID = ch.cardholderID 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"- Updating profile for {member.forename} {member.surname}")
print(f" - Old: {ch.attribs()}") print(f" - Old: {ch.attribs()}")
print(f" - New: {member.attribs()}") print(f" - New: {member.attribs()}")
@ -162,19 +140,18 @@ def update_door(door, members):
E.Cardholders( E.Cardholders(
{"action": "UD", "cardholderID": member.cardholderID}, {"action": "UD", "cardholderID": member.cardholderID},
E.CardHolder(member.attribs())))) E.CardHolder(member.attribs()))))
if member.credentials != ch.credentials: if member.credentials != ch.credentials:
print(f"- Updating card for {member.forename} {member.surname}") print(f"- Updating card for {member.forename} {member.surname}")
print(f" - {ch.credentials} -> {member.credentials}") 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? if member.schedulesForDoor(door) != ch.schedules:
schedulesForDoor = member.schedules if door.name in member.doorAccess else []
if schedulesForDoor != ch.schedules:
print("- Updating schedule for" + print("- Updating schedule for" +
f" {member.forename} {member.surname}:" + f" {member.forename} {member.surname}:" +
f" {ch.schedules} -> {member.schedules}") f" {ch.schedules} -> {member.schedulesForDoor(door)}")
door.doXMLRequest(ROOT(update_schedules(member, door, schedulesMap))) door.doXMLRequest(ROOT(member.make_schedules(door, schedulesMap)))
else: # do add else: # cardholder did not exist, so add them
print(f"- Adding Member:") print(f"- Adding Member:")
print(member) print(member)
resp = door.doXMLRequest(ROOT( resp = door.doXMLRequest(ROOT(
@ -182,8 +159,8 @@ def update_door(door, members):
E.Cardholder(member.attribs())))) E.Cardholder(member.attribs()))))
member.cardholderID = resp.find('{*}Cardholders/{*}Cardholder') \ member.cardholderID = resp.find('{*}Cardholders/{*}Cardholder') \
.attrib["cardholderID"] .attrib["cardholderID"]
door.doXMLRequest(ROOT(update_credentials(member, cardFormats), door.doXMLRequest(ROOT(member.make_credentials(cardFormats),
update_schedules(member, door, schedulesMap))) member.update_schedules(door, schedulesMap)))
# TODO: delete cardholders that are no longer members? # TODO: delete cardholders that are no longer members?
@ -198,9 +175,4 @@ def main():
print(door.name, door.ip) print(door.name, door.ip)
update_door(door, members) 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() main()