コード例 #1
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_ga(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.ga)
     self.assertEqual(data['document'], '1234509876543210987654321')
     self.assertEqual(data['IIN'], '636055')
     self.assertEqual(data['address'], '123 MAIN STREET')
     self.assertEqual(data['city'], 'ANYTOWN')
     self.assertEqual(data['class'], 'C')
     self.assertEqual(data['dob'], datetime.date(1957, 7, 1))
     self.assertEqual(data['endorsements'], 'NONE')
     self.assertEqual(data['eyes'], 'BLU')
     self.assertEqual(data['first'], 'JANICE')
     self.assertEqual(data['middle'], 'NONE')
     self.assertEqual(data['last'], 'SAMPLE')
     self.assertIs(data['prefix'], None)
     self.assertIs(data['hair'], None)
     self.assertEqual(data['height'], aamva.Height(64, format='USA'))
     self.assertEqual(data['expiry'], datetime.date(2017, 7, 1))
     self.assertEqual(data['issued'], datetime.date(2012, 7, 1))
     self.assertEqual(data['license_number'], '123456789')
     self.assertEqual(data['restrictions'], 'B')
     self.assertEqual(data['sex'], 'F')
     self.assertEqual(data['state'], 'GA')
     self.assertEqual(data['suffix'], 'NONE')
     self.assertEqual(data['weight'],
                      aamva.Weight(None, weight=120, format='USA'))
     pprint.pprint(data)
コード例 #2
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_in(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.indiana)
     self.assertEqual(data['document'], '07040602300001')
     self.assertEqual(data['IIN'], '636037')
     self.assertEqual(data['address'], '123 SAMPLE DRIVE')
     self.assertEqual(data['city'], 'INDIANAPOLIS')
     self.assertEqual(data['class'], 'X-1X-2')
     self.assertEqual(data['dob'], datetime.date(1989, 7, 4))
     self.assertEqual(data['endorsements'], 'X-1XY')
     self.assertEqual(data['eyes'], 'HAZ')
     self.assertEqual(data['first'],
                      'HEIDIFIRSTNAMEUPTO40CHARACTERSXYWXYWXYWX')
     self.assertEqual(data['middle'],
                      'MIDDLENAMEUPTO40CHARACTERSXYWXYWXYWXYWXY')
     self.assertEqual(data['last'],
                      'SAMPLEFAMILYNAMEUPTO40CHARACTERSXYWXYWXY')
     self.assertIs(data['prefix'], None)
     self.assertEqual(data['hair'], 'BLN')
     self.assertEqual(data['height'], aamva.Height(64, format='USA'))
     self.assertEqual(data['expiry'], datetime.date(2010, 7, 4))
     self.assertEqual(data['issued'], datetime.date(2006, 7, 4))
     self.assertEqual(data['license_number'], '1234-56-7890')
     self.assertEqual(data['restrictions'], 'X-1X-2X-3X-4')
     self.assertEqual(data['sex'], 'F')
     self.assertEqual(data['state'], 'IN')
     self.assertEqual(data['suffix'], 'XYWXY')
     self.assertEqual(data['weight'], aamva.Weight(None, 120, 'USA'))
     pprint.pprint(data)
コード例 #3
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_wa_edl(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.wa_edl)
     self.assertEqual(data['document'], 'OREALDD521DSL1083014J1459')
     self.assertEqual(data['IIN'], '636045')
     self.assertEqual(data['address'], '2600 MARTIN WAY')
     self.assertEqual(data['city'], 'OLYMPIA')
     self.assertEqual(data['class'], 'NONE')
     self.assertEqual(data['dob'], datetime.date(1948, 3, 10))
     self.assertEqual(data['endorsements'], 'NONE')
     self.assertEqual(data['eyes'], 'BLU')
     self.assertEqual(data['first'], 'DABE')
     self.assertEqual(data['middle'], 'DEE')
     self.assertEqual(data['last'], 'O REALTEST')
     self.assertIs(data['prefix'], None)
     self.assertIs(data['hair'], None)
     self.assertEqual(data['height'], aamva.Height(70, format='USA'))
     self.assertEqual(data['expiry'], datetime.date(2013, 3, 10))
     self.assertEqual(data['issued'], datetime.date(2008, 10, 27))
     self.assertEqual(data['license_number'], 'OREALDD521DS')
     self.assertEqual(data['restrictions'], 'NONE')
     self.assertEqual(data['sex'], 'M')
     self.assertEqual(data['state'], 'WA')
     self.assertEqual(data['suffix'], 'V')
     self.assertEqual(data['weight'],
                      aamva.Weight(None, weight=175, format='USA'))
     pprint.pprint(data)
コード例 #4
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_va(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.va)
     self.assertEqual(data['document'], '061234567')
     self.assertEqual(data['IIN'], '636000')
     self.assertEqual(data['address'], '17 FIRST STREET')
     self.assertEqual(data['city'], 'STAUNTON')
     self.assertEqual(data['class'], 'NONE')
     self.assertEqual(data['dob'], datetime.date(1958, 7, 15))
     self.assertEqual(data['endorsements'], 'S')
     self.assertEqual(data['eyes'], 'BRO')
     self.assertEqual(data['first'], 'JUSTIN')
     self.assertEqual(data['middle'], 'WILLIAM')
     self.assertEqual(data['last'], 'MAURY')
     self.assertIs(data['prefix'], None)
     self.assertIs(data['hair'], None)
     self.assertEqual(data['height'], aamva.Height(75, format='USA'))
     self.assertEqual(data['expiry'], datetime.date(2017, 8, 14))
     self.assertEqual(data['issued'], datetime.date(2009, 8, 14))
     self.assertEqual(data['license_number'], 'T16700185')
     self.assertEqual(data['restrictions'], '158X9')
     self.assertEqual(data['sex'], 'M')
     self.assertEqual(data['state'], 'VA')
     self.assertIs(data['suffix'], None)
     self.assertIs(data['weight'], None)  # VA does not encode weight
     pprint.pprint(data)
コード例 #5
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_wa(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.wa)
     self.assertEqual(data['document'], 'ANASTPM320QDL1102574D1643')
     self.assertEqual(data['IIN'], '636045')
     self.assertEqual(data['address'], '2600 MARTIN WAY E')
     self.assertEqual(data['city'], 'OLYMPIA')
     self.assertEqual(data['class'], 'NONE')
     self.assertEqual(data['dob'], datetime.date(1968, 11, 4))
     self.assertEqual(data['endorsements'], 'NONE')
     self.assertEqual(data['eyes'], 'GRN')
     self.assertEqual(data['first'], 'PRINCESS')
     self.assertEqual(data['middle'], 'MONACO')
     self.assertEqual(data['last'], 'ANASTASIA')
     self.assertIs(data['prefix'], None)
     self.assertIs(data['hair'], None)
     self.assertEqual(data['height'], aamva.Height(63, format='USA'))
     self.assertEqual(data['expiry'], datetime.date(2014, 11, 4))
     self.assertEqual(data['issued'], datetime.date(2010, 9, 14))
     self.assertEqual(data['license_number'], 'ANASTPM320QD')
     self.assertEqual(data['restrictions'], 'NONE')
     self.assertEqual(data['sex'], 'F')
     self.assertEqual(data['state'], 'WA')
     self.assertEqual(data['suffix'], '')
     self.assertEqual(data['weight'],
                      aamva.Weight(None, weight=115, format='USA'))
     pprint.pprint(data)
コード例 #6
0
ファイル: test.py プロジェクト: combs/py-aamva
    def test_fl(self):
        parser = aamva.AAMVA()

        data = parser.decode(Magstripe.fl)
        self.assertEqual(data['first'], 'JOHN')
        self.assertEqual(data['last'], 'DOE')
        self.assertEqual(data['city'], 'DELRAY BEACH')
        self.assertEqual(data['IIN'], '636010')
        self.assertEqual(data['dob'], datetime.date(1987, 1, 1))
        self.assertEqual(data['expiry'], datetime.date(2021, 1, 31))
コード例 #7
0
ファイル: test.py プロジェクト: combs/py-aamva
    def test_tx(self):
        parser = aamva.AAMVA()

        data = parser.decode(Magstripe.tx)
        self.assertEqual(data['first'], 'JOHN')
        self.assertEqual(data['last'], 'DOE')
        self.assertEqual(data['city'], 'AUSTIN')
        self.assertEqual(data['IIN'], '636015')
        self.assertEqual(data['dob'], datetime.date(1981, 1, 1))
        self.assertEqual(data['expiry'], datetime.date(2015, 8, 31))
コード例 #8
0
ファイル: test.py プロジェクト: combs/py-aamva
    def test_fl2(self):
        parser = aamva.AAMVA()

        data = parser.decode(Magstripe.fl2)
        self.assertEqual(data['first'], 'ROMAN')
        self.assertEqual(data['last'], 'JURKOV')
        self.assertEqual(data['state'], 'FL')
        self.assertEqual(data['address'], '4818 N CLASSICAL BLVD')
        self.assertEqual(data['city'], 'DELRAY BEACH')
        self.assertEqual(data['IIN'], '636010')
        self.assertEqual(data['dob'], datetime.date(1987, 1, 1))
        self.assertEqual(data['expiry'], datetime.date(2021, 1, 31))
コード例 #9
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_sc(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.sc)
     self.assertEqual(data['IIN'], '636005')
     self.assertEqual(data['address'], '1500 PARK ST')
     self.assertEqual(data['city'], 'COLUMBIA')
     self.assertEqual(data['class'], 'D')
     self.assertEqual(data['dob'], datetime.date(1978, 9, 28))
     self.assertEqual(data['endorsements'], '')
     self.assertEqual(data['eyes'], '')
     self.assertEqual(data['first'], 'DRIVER')
     self.assertEqual(data['middle'], 'CREDENTIAL')
     self.assertEqual(data['last'], 'SAMPLE')
     self.assertIs(data['prefix'], None)
     self.assertEqual(data['hair'], '')
     #self.assertEqual(data['height'], '600')
     self.assertEqual(data['expiry'], datetime.date(2019, 9, 28))
     self.assertEqual(data['issued'], datetime.date(2009, 10, 26))
     self.assertEqual(data['license_number'], '102245737')
     self.assertEqual(data['restrictions'], '')
     self.assertEqual(data['sex'], 'M')
     self.assertEqual(data['state'], 'SC')
     self.assertIs(data['suffix'], None)
     pprint.pprint(data)
コード例 #10
0
ファイル: browser-worker.py プロジェクト: combs/py-aamva
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)
        self.panel = wx.Panel(self)

        vSizer = wx.BoxSizer(wx.VERTICAL)
        hSizer1 = wx.BoxSizer(wx.HORIZONTAL)

        #Row 1
        nameSt = wx.StaticText(self.panel, wx.ID_ANY, "Name")
        hSizer1.Add(nameSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.NameText = wx.TextCtrl(self.panel,
                                    wx.ID_ANY,
                                    style=wx.TE_READONLY)
        hSizer1.Add(self.NameText, 1,
                    wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, 5)
        middleSt = wx.StaticText(self.panel, wx.ID_ANY, "Middle")
        hSizer1.Add(middleSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.MiddleText = wx.TextCtrl(self.panel,
                                      wx.ID_ANY,
                                      style=wx.TE_READONLY)
        hSizer1.Add(self.MiddleText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer1, 0, wx.EXPAND, 5)

        #Row 2
        hSizer2 = wx.BoxSizer(wx.HORIZONTAL)
        surnameSt = wx.StaticText(self.panel, wx.ID_ANY, "Surname")
        hSizer2.Add(surnameSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.SurnameText = wx.TextCtrl(self.panel,
                                       wx.ID_ANY,
                                       style=wx.TE_READONLY)
        hSizer2.Add(self.SurnameText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        dobSt = wx.StaticText(self.panel, wx.ID_ANY, "DOB")
        hSizer2.Add(dobSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.DOBText = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_READONLY)
        hSizer2.Add(self.DOBText, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer2, 0, wx.EXPAND, 5)

        #Row 3
        hSizer3 = wx.BoxSizer(wx.HORIZONTAL)
        addressSt1 = wx.StaticText(self.panel, wx.ID_ANY, "Address 1")
        hSizer3.Add(addressSt1, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.AddressText = wx.TextCtrl(self.panel,
                                       wx.ID_ANY,
                                       style=wx.TE_READONLY)
        hSizer3.Add(self.AddressText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer3, 0, wx.EXPAND, 5)

        #Row 4
        hSizer4 = wx.BoxSizer(wx.HORIZONTAL)
        addressSt2 = wx.StaticText(self.panel, wx.ID_ANY, "Address 2")
        hSizer4.Add(addressSt2, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.Address2Text = wx.TextCtrl(self.panel,
                                        wx.ID_ANY,
                                        style=wx.TE_READONLY)
        hSizer4.Add(self.Address2Text, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer4, 0, wx.EXPAND, 5)

        #Row 5
        hSizer5 = wx.BoxSizer(wx.HORIZONTAL)
        citySt = wx.StaticText(self.panel, wx.ID_ANY, "City")
        hSizer5.Add(citySt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.CityText = wx.TextCtrl(self.panel,
                                    wx.ID_ANY,
                                    style=wx.TE_READONLY)
        hSizer5.Add(self.CityText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        stateSt = wx.StaticText(self.panel, wx.ID_ANY, "State")
        hSizer5.Add(stateSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.StateText = wx.TextCtrl(self.panel,
                                     wx.ID_ANY,
                                     style=wx.TE_READONLY)
        hSizer5.Add(self.StateText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        ZIPSt = wx.StaticText(self.panel, wx.ID_ANY, "ZIP")
        hSizer5.Add(ZIPSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.ZIPText = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_READONLY)
        hSizer5.Add(self.ZIPText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer5, 0, wx.EXPAND, 5)

        #Row 6
        hSizer6 = wx.BoxSizer(wx.HORIZONTAL)
        IINSt = wx.StaticText(self.panel, wx.ID_ANY, "IIN")
        hSizer6.Add(IINSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.IINText = wx.TextCtrl(self.panel, wx.ID_ANY, style=wx.TE_READONLY)
        hSizer6.Add(self.IINText, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        LicenseNoSt = wx.StaticText(self.panel, wx.ID_ANY, "License #")
        hSizer6.Add(LicenseNoSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.LicenseNoText = wx.TextCtrl(self.panel,
                                         wx.ID_ANY,
                                         style=wx.TE_READONLY)
        hSizer6.Add(self.LicenseNoText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL,
                    5)
        vSizer.Add(hSizer6, 0, wx.EXPAND, 5)

        #Row 7
        hSizer7 = wx.BoxSizer(wx.HORIZONTAL)
        issuedSt = wx.StaticText(self.panel, wx.ID_ANY, "Issued")
        hSizer7.Add(issuedSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.IssuedText = wx.TextCtrl(self.panel,
                                      wx.ID_ANY,
                                      style=wx.TE_READONLY)
        hSizer7.Add(self.IssuedText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        expiresSt = wx.StaticText(self.panel, wx.ID_ANY, "Expires")
        hSizer7.Add(expiresSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.ExpiresText = wx.TextCtrl(self.panel,
                                       wx.ID_ANY,
                                       style=wx.TE_READONLY)
        hSizer7.Add(self.ExpiresText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        countrySt = wx.StaticText(self.panel, wx.ID_ANY, "Country")
        hSizer7.Add(countrySt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.CountryText = wx.TextCtrl(self.panel,
                                       wx.ID_ANY,
                                       style=wx.TE_READONLY)
        hSizer7.Add(self.CountryText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer7, 0, wx.EXPAND, 5)

        #Row 8
        hSizer8 = wx.BoxSizer(wx.HORIZONTAL)
        self.MaleRadio = wx.RadioButton(self.panel, wx.ID_ANY, "M")
        hSizer8.Add(self.MaleRadio, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.FemaleRadio = wx.RadioButton(self.panel, wx.ID_ANY, "F")
        hSizer8.Add(self.FemaleRadio, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        heightSt = wx.StaticText(self.panel, wx.ID_ANY, "Height")
        hSizer8.Add(heightSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.HeightText = wx.TextCtrl(self.panel,
                                      wx.ID_ANY,
                                      style=wx.TE_READONLY)
        hSizer8.Add(self.HeightText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        weightSt = wx.StaticText(self.panel, wx.ID_ANY, "Weight")
        hSizer8.Add(weightSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.WeightText = wx.TextCtrl(self.panel,
                                      wx.ID_ANY,
                                      style=wx.TE_READONLY)
        hSizer8.Add(self.WeightText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        hairSt = wx.StaticText(self.panel, wx.ID_ANY, "Hair")
        hSizer8.Add(hairSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.HairText = wx.TextCtrl(self.panel,
                                    wx.ID_ANY,
                                    style=wx.TE_READONLY)
        hSizer8.Add(self.HairText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer8, 0, wx.EXPAND, 5)

        #Row 9
        hSizer9 = wx.BoxSizer(wx.HORIZONTAL)
        eyesSt = wx.StaticText(self.panel, wx.ID_ANY, "Eyes")
        hSizer9.Add(eyesSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.EyesText = wx.TextCtrl(self.panel,
                                    wx.ID_ANY,
                                    style=wx.TE_READONLY)
        hSizer9.Add(self.EyesText, 1, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        endorsementsSt = wx.StaticText(self.panel, wx.ID_ANY, "Endorsements")
        hSizer9.Add(endorsementsSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.EndorsementsText = wx.TextCtrl(self.panel,
                                            wx.ID_ANY,
                                            style=wx.TE_READONLY)
        hSizer9.Add(self.EndorsementsText, 1,
                    wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        restrictionsSt = wx.StaticText(self.panel, wx.ID_ANY, "Restrictions")
        hSizer9.Add(restrictionsSt, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        self.RestrictionsText = wx.TextCtrl(self.panel,
                                            wx.ID_ANY,
                                            style=wx.TE_READONLY)
        hSizer9.Add(self.RestrictionsText, 1,
                    wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
        vSizer.Add(hSizer9, 0, wx.EXPAND, 5)

        self.clearForm()

        #Bind events
        self.Bind(DATA_WAITING, self.ProcessScan)
        self.Bind(THREAD_ERROR, self.ThreadError)
        self.Bind(wx.EVT_CLOSE, self.Close)

        #Setup parser
        self.parser = aamva.AAMVA()

        #Start serial thread
        self.THREAD_EXIT_SIGNAL = threading.Event()
        self.thread = threading.Thread(target=self._serialWorkerThread,
                                       args=(SERIAL_DEVICE, ))
        self.thread.start()

        self.panel.SetSizer(vSizer)
        self.panel.Layout()
コード例 #11
0
ファイル: test.py プロジェクト: combs/py-aamva
 def test_aamva_v1(self):
     parser = aamva.AAMVA()
     data = parser._decodeBarcode(PDF417.aamva_v1)
     pprint.pprint(data)
コード例 #12
0
class RootWidget(BoxLayout):
    # Use to signal the barcode reader thread that we are exiting
    stop = threading.Event()

    parser = aamva.AAMVA()

    def scanner_thread(self):
        ser = None
        try:
            ser = serial.Serial('/dev/ttyUSB0', timeout=0.5)
        except:
            while True:
                raw_input()
                if self.stop.is_set():
                    return
                self.process_scan(test.PDF417.va)
                time.sleep(10)
                self.process_scan(test.PDF417.va_under21)

        while True:
            charbuffer = ""
            while charbuffer[-2:] != '\r\n':
                charbuffer += ser.read(1)
                if self.stop.is_set():
                    ser.close()
                    return

            # Parse and update display
            self.process_scan(charbuffer)

    def start_scanner_thread(self, *args):
        scan_thread = threading.Thread(target=self.scanner_thread)
        scan_thread.start()

    @mainthread
    def process_scan(self, raw):
        # TODO: Add exception handling aamva.ReaderError
        # Display message if didn't decode correctly
        data = self.parser.decode(raw)
        import pprint
        pprint.pprint(data)

        age = self.calculate_age(data['dob'])
        # TODO: If over 18, show "OVER 18" icon and message
        # (cigarette icon)
        expired = (self.calculate_age(data['expiry']) > 0)
        expired_text = ""
        if expired:
            expired_text = "[color=#ff0000][b]Expired {0}[/b][/color]\n".format(
                str(data['expiry']))
        print data['expiry']

        # TODO: Show name fields

        over21_text = "[color=#ff0000][b]Under 21[/b][/color]"
        over18_text = "[color=#ff0000][b]Under 18[/b][/color]"
        if age > 21:
            self.set_left_color((0, 1, 0, 1))
            over21_text = "Over 21"
        else:
            self.set_left_color((1, 0, 0, 1))
        if age >= 18:
            self.set_right_color((0, 1, 0, 1))
            over18_text = "Over 18"
        else:
            self.set_right_color((1, 0, 0, 1))

        self.vlayout.top_layout.status.text = (
            '{3}[b]Age:[/b] {0}\n\n{1}\n{2}'.format(age, over21_text,
                                                    over18_text, expired_text))

        zipcode = "{0}-{1}".format(data['ZIP'][0:5], data['ZIP'][-4:])
        status = "[b]Name:[/b] {0}, {1} {2}\n[b]DOB: [/b]{3}\n".format(
            data['last'][:20], data['first'][:20], data['middle'][:20],
            data['dob'])
        status += "[b]Address:[/b]\n    {0}\n    {1}, {2} {3}\n".format(
            data['address'], data['city'], data['state'], zipcode)
        status += "[b]Issued:[/b] {0}".format(data['issued'])

        self.vlayout.low_layout.status.text = (status)

        self.canvas.ask_update()

    @mainthread
    def set_left_color(self, color):
        self.vlayout.top_layout.left_status.bcolor = color

    @mainthread
    def set_right_color(self, color):
        self.vlayout.top_layout.right_status.bcolor = color

    @staticmethod
    def calculate_age(dob):
        today = date.today()
        years_difference = today.year - dob.year
        is_before_birthday = (today.month, today.day) < (dob.month, dob.day)
        elapsed_years = years_difference - int(is_before_birthday)
        return elapsed_years