DispenseInterface and OpenDispense Implimentation created and tested. Not curruntly...
authorMitchell Pomery <bob_george33@hotmail.com>
Sun, 8 Mar 2015 14:54:43 +0000 (22:54 +0800)
committerMark Tearle <mark@tearle.com>
Sun, 5 Apr 2015 11:41:19 +0000 (19:41 +0800)
VendServer/DispenseInterface.py [new file with mode: 0644]
VendServer/OpenDispense.py [new file with mode: 0644]
VendServer/OpenDispenseMappings.conf [new file with mode: 0644]

diff --git a/VendServer/DispenseInterface.py b/VendServer/DispenseInterface.py
new file mode 100644 (file)
index 0000000..81a3f06
--- /dev/null
@@ -0,0 +1,70 @@
+"""
+Author: Mitchell Pomery (bobgeorge33)
+
+Dispense is an abstract class that allows easy configuration between different systems. Anything that is 
+intended to talk to OpenDispense or similar should comply with this class. Using this class in particular 
+will cause you a lot of problems.
+"""
+
+class DispenseInterface(object):
+
+       """
+       Create a new dispense object.
+       """
+       def __init__(self, username=None, loggedIn=False, disabled=False):
+               pass
+
+       """
+       Create a new dispense interface as the supplied user.
+       """
+       @staticmethod
+       def authUsernamePin(username, pin):
+               pass
+
+       """
+       Create a new dispense interface as the supplied user.
+       """
+       @staticmethod
+       def authMifare(cardId):
+               pass
+
+       """
+       Check if creating the user worked correctly.
+       """
+       def isLoggedIn(self):
+               pass
+
+       """
+       Get the users username.
+       """
+       def getUsername(self, user):
+               pass
+
+       """
+       Get the users current balance.
+       """
+       def getBalance(self, user):
+               pass
+
+       """
+       Get the name and price of an item.
+       itemId is the number entered into the vending machine
+       """
+       @staticmethod
+       def getItemInfo(itemId):
+               pass
+
+       """
+       Check if the user is disabled.
+       """
+       def isDisabled(self):
+               pass
+
+       """
+       Dispense an item for the current user.
+       itemId is the number entered into the vending machine
+       """
+       def dispenseItem(self, itemId):
+               pass
+
+
diff --git a/VendServer/OpenDispense.py b/VendServer/OpenDispense.py
new file mode 100644 (file)
index 0000000..f8cee0d
--- /dev/null
@@ -0,0 +1,126 @@
+from DispenseInterface import DispenseInterface
+import os
+import re
+import pwd
+from subprocess import Popen, PIPE
+from LDAPConnector import get_uid,get_uname, set_card_id
+
+"""
+Author: Mitchell Pomery (bobgeorge33)
+
+System to connect to OpenDispense2.
+Most of this code has been copied out of VendServer.py, then had variables updated so that it runs.
+This is so VendServer can easily operate regardless of the current accounting backend.
+Documentation for this code can be found inder Dispence.DispenceInterface
+"""
+class OpenDispense(DispenseInterface):
+        _username = None
+        _disabled = True
+        _loggedIn = False
+       _userId = None
+
+        def __init__(self, userId=None, username=None, loggedIn=False):
+               self._username = username
+                self._loggedIn = loggedIn
+               self._userId = userId
+               
+               acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
+               # this is fucking appalling
+               flags = acct[acct.find("(")+1:acct.find(")")].strip()
+               if 'disabled' in flags:
+                       self._disabled = True
+               if 'internal' in flags:
+                       self._disabled = True
+                self._disabled = False
+
+       @staticmethod
+       def authUserIdPin(userId, pin):
+               try:
+                       # Get info from 
+                        info = pwd.getpwuid(userId)
+                except KeyError:
+                        logging.info('getting pin for uid %d: user not in password file'%uid)
+                        return None
+
+                if info.pw_dir == None: return False
+                pinfile = os.path.join(info.pw_dir, '.pin')
+                try:
+                        s = os.stat(pinfile)
+                except OSError:
+                        logging.info('getting pin for uid %d: .pin not found in home directory'%uid)
+                        return None
+                if s.st_mode & 077:
+                        logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%uid)
+                        os.chmod(pinfile, 0600)
+                try:
+                        f = file(pinfile)
+                except IOError:
+                        logging.info('getting pin for uid %d: I cannot read pin file'%uid)
+                        return None
+                pinstr = f.readline().strip()
+                f.close()
+                if not re.search('^[0-9]{4}$', pinstr):
+                        logging.info('getting pin for uid %d: %s not a good pin'%(uid,repr(pinstr)))
+                        return None
+               return OpenDispense(userId, info.pw_name, (int(pin)==int(pinstr)))
+
+       @staticmethod
+       def authMifareCard(cardId):
+               return OpenDispense(get_uid(cardId), get_uname(get_uid(cardId)), True)
+
+       def addCard(self, cardId):
+               set_card_id(self._userId, cardId)
+
+        def isLoggedIn(self):
+                return self._loggedIn
+
+        def getUsername(self):
+                return self._username
+
+        def getBalance(self):
+               # Balance checking
+                acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
+                # this is fucking appalling
+                balance = acct[acct.find("$")+1:acct.find("(")].strip()
+                return balance
+
+        def getItemInfo(itemId):
+               itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
+               args = ('dispense', 'iteminfo', itemId)
+                info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
+                m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
+                cents = int(m.group(1))*100 + int(m.group(2))
+               # return (name, price in cents)
+                return (m.group(3), cents)
+
+        def isDisabled(self):
+               return False
+
+        def dispenseItem(self, itemId):
+               itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
+               if itemId == "":
+                       return False
+               else:
+                       print('dispense -u "%s" %s'%(self._username, itemId))
+                       os.system('dispense -u "%s" %s'%(self._username, itemId))
+                       return True
+
+"""
+This class abstracts the idea of item numbers.
+It removes the need for VendServer to know the mappings between inputted numbers
+and the equivalent itemId in OpenDispense.
+"""
+class OpenDispenseMapping():
+
+       @staticmethod
+       def vendingMachineToOpenDispense(itemId):
+               _mappingFile = "OpenDispenseMappings.conf"
+               fh = open(_mappingFile)
+               map = ""
+               for line in fh:
+                       #line = line.strip()
+                       if line.startswith(str(itemId) + " "):
+                               map = line[len(str(itemId)) + 1:].strip()
+                               print(map)
+               return map
+
diff --git a/VendServer/OpenDispenseMappings.conf b/VendServer/OpenDispenseMappings.conf
new file mode 100644 (file)
index 0000000..61485ca
--- /dev/null
@@ -0,0 +1,76 @@
+# This file is used to map things from vending machine inputs into OpenDispense items
+# Lines that start with a # are comments
+# Lines that contain only white space are ignored
+
+# The top three rows only contain 5 slots for items
+
+11 snack:11
+31 snack:31
+51 snack:51
+71 snack:71
+91 snack:91
+
+12 snack:12
+32 snack:32
+52 snack:52
+72 snack:72
+92 snack:92
+
+13 snack:13
+33 snack:33
+53 snack:53
+73 snack:73
+93 snack:93
+
+# This row has 10
+
+04 snack:04
+14 snack:14
+24 snack:24
+34 snack:34
+44 snack:44
+54 snack:54
+64 snack:64
+74 snack:74
+84 snack:84
+94 snack:94
+
+# Row 5 is used only for door
+
+55 door
+
+# The next two rows have 10 items each
+
+06 snack:06
+16 snack:16
+26 snack:26
+36 snack:36
+46 snack:46
+56 snack:56
+66 snack:66
+76 snack:76
+86 snack:86
+96 snack:96
+
+07 snack:07
+17 snack:17
+27 snack:27
+37 snack:37
+47 snack:47
+57 snack:57
+67 snack:67
+77 snack:77
+87 snack:87
+97 snack:97
+
+# This last lot is for coke
+
+08 coke:0
+18 coke:1
+28 coke:2
+38 coke:3
+48 coke:4
+58 coke:5
+68 coke:6
+
+# There is no row 9

UCC git Repository :: git.ucc.asn.au