Ejemplo n.º 1
0
 def __init__(self, dbHost, dbDatabase, dbUsersTable, dbVisitsTable, dbUser,
              dbPass):
     self.dbConn = None
     self.dbHost = dbHost
     self.dbDatabase = dbDatabase
     self.dbUsersTable = dbUsersTable
     self.dbVisitsTable = dbVisitsTable
     self.dbUser = dbUser
     self.dbPass = dbPass
     self.tools = Utils()
Ejemplo n.º 2
0
    def __init__(self, db):
        super(MainWnd, self).__init__()

        self.db = db
        self.tools = Utils()

        # Init card input so it can be appended to later
        self.cardInput = ""

        # Compile the regex for pulling the card ID from all the data on a card
        self.regex = re.compile("%(.+)..\?;")

        # Declare sleepThread
        self.sleepThread = SleepThread(c.TIME_BETWEEN_CHECKINS,
                                       self.resetCheckinWidget)

        self.initUI()
Ejemplo n.º 3
0
    def __init__(self, db):
        super(MainWnd, self).__init__()

        self.db = db
        self.tools = Utils()

        # Init card input so it can be appended to later
        self.cardInput = ""

        # Compile the regex for pulling the card ID from all the data on a card
        self.regex = re.compile("%(.+)..\?;")

        # Declare sleepThread
        self.sleepThread = SleepThread(c.TIME_BETWEEN_CHECKINS, self.resetCheckinWidget)

        self.initUI()
Ejemplo n.º 4
0
 def __init__(self):
     self.db = None
     self.tools = Utils()
Ejemplo n.º 5
0
class TextUI:
    def __init__(self):
        self.db = None
        self.tools = Utils()

    def start(self):
        #===========================================================================
        # Main function - start connection to db then open main menu
        #===========================================================================
        try:
            while 1:
                # Get DB info
                self.getDbInfo()

                # Create the DB object
                self.db = DB(self.dbHost, self.dbName, self.dbUsersTable,
                             self.dbVisitsTable, self.dbUser, self.dbPass)

                # Connect to the database
                connectStatus = self.connectToDatabase()

                # If we failed to connect to the database offer to re-enter db info
                if connectStatus != c.SUCCESS:
                    reenter = input(
                        "Failed to connect to database. Re-enter database info? (Y,n) "
                    )
                    if reenter.lower() == "n":
                        print("Bye.")
                        sys.exit(0)
                else:
                    break

            # Start the main menu loop
            self.displayMenu()

        except KeyboardInterrupt:
            pass
        finally:
            print("Cleaning up and exiting...")
            if self.db is not None:
                self.db.close()

    def displayMenu(self):
        #===========================================================================
        # Display main cli and take appropriate actions
        #===========================================================================
        #print("\nType \"back\" at any time to go up a menu level.")

        while 1:
            # Display main menu
            print("\n\t1.) Check-in\n\t2.) Show Visits\n\t3.) Exit")
            try:
                option = input("\n>> ")

                if option == "1":
                    self.checkIn()
                elif option == "2":
                    self.showVisits()
                elif option == "3":
                    sys.exit(0)
                #elif option == "back" or option == "exit":
                #    exit = input("Exit? (y,N) ")
                #    if exit.lower() == "y":
                #        sys.exit(0)
                else:
                    self.invalidInput()

            except ValueError:
                self.invalidInput()

    def connectToDatabase(self):
        #===========================================================================
        # Open connection to database and return status
        #===========================================================================
        # Use stdout.write to prevent newline
        sys.stdout.write("Connecting to database...")

        # Connect to the DB!
        status = self.db.connect()

        if status == c.SUCCESS:
            print("done.")
            return status
        elif status == c.BAD_PASSWD:
            print("\nError connecting to database: Bad username or password.")
            return status
        else:
            print("\nUnknown Error connecting to database.")
            return c.FAILURE

    def checkIn(self):
        #===========================================================================
        # Log card data to db
        #===========================================================================
        # Get and validate the visit value for this check-in
        # Limited to 500 visits to prevent bad typos
        """while 1:
            visitValue = self.tools.sanitizeInput(input("\nVisit Value (" + str(c.DEFAULT_VISITS) + "): "))

            # Validate visit input
            if visitValue == "":
                visitValue = str(c.DEFAULT_VISITS)
                break
            elif (visitValue.isdigit() and int(visitValue) <= 500) or visitValue == "back":
                break
            else:
                print("Invalid input. Try again.")"""

        while 1:
            CUID = self.tools.getCardSwipe()
            # If the user requested to exit the loop, break
            if CUID == c.BACK:
                break
            elif CUID == c.ERROR_READING_CARD:
                print("Error reading card. Swipe card again.")
                continue

            # Sanitize CUID
            CUID = self.tools.sanitizeInput(CUID)
            # CUID will be empty if it failed sanitization. Skip checkIn if that is the case
            if CUID == "":
                continue

            # Do the checkIn
            checkInResult = self.db.checkIn(CUID)

            if checkInResult["checkInStatus"] == c.SQL_ERROR:
                self.showDatabaseError(checkInResult["sqlError"])
            elif checkInResult["checkInStatus"] == c.BAD_CHECKIN_TIME:
                print("Error: You may only check-in once per hour.")
            elif checkInResult["checkInStatus"] == c.FUTURE_CHECKIN_TIME:
                print(
                    "Error: Previous check-in time was in the future. Check your local system time."
                )
            elif checkInResult["checkInStatus"] == c.CUID_NOT_IN_DB:
                # Ask if user wants to add the card
                addCard = input(
                    "Error: Card not found in database. Add it now? (Y,n) ")

                if addCard == "n":
                    continue

            # Get the userID for the new card
                firstName = self.tools.sanitizeInput(input("First Name: "))
                lastName = self.tools.sanitizeInput(input("Last Name: "))
                email = self.tools.sanitizeInput(input("Clemson Username: "******"addCardStatus"] == c.SUCCESS:
                    self.showCheckinConfirmation(email)
                elif addCardResult["addCardStatus"] == c.SQL_ERROR:
                    self.showDatabaseError(addCardResult["sqlError"])
            elif checkInResult["checkInStatus"] == c.SUCCESS:
                self.showCheckinConfirmation(checkInResult["userID"])
            else:
                print("Unknown error checking in.")

    def showVisits(self):
        #===========================================================================
        # Poll db for visit data based on userID
        #===========================================================================
        userID = self.tools.sanitizeInput(input("\nUser ID (blank for all): "))
        showVisitsResult = self.db.showVisits(userID)

        if showVisitsResult["showVisitsStatus"] == c.SQL_ERROR:
            self.showDatabaseError(showVisitsResult["sqlError"])
        elif showVisitsResult["showVisitsStatus"] == c.NO_RESULTS:
            print("\nThere were no results to that query.")
        elif showVisitsResult["showVisitsStatus"] == c.SUCCESS:
            # If showing all users, display a pretty table
            if userID == "":
                print(
                    "\n+--------------------+\n| User ID | Visits |\n+--------------------+"
                )

                for i in range(len(showVisitsResult["visitsTuple"])):
                    print("|%10s | %6s |" %
                          (showVisitsResult["visitsTuple"][i][0],
                           showVisitsResult["visitsTuple"][i][1]))

                print("+--------------------+")

            # Show a single user's visits
            else:
                print("\n%s has %s visits." %
                      (userID, str(showVisitsResult["visitsTuple"][0][0])))

    def getDbInfo(self):
        #===========================================================================
        # Request dbInfo from user - suggest default info from constants
        #===========================================================================
        self.dbName = input("Database name: (" + c.DEFAULT_DATABASE + ") ")
        if self.dbName == "":
            self.dbName = c.DEFAULT_DATABASE

        self.dbHost = input("Database host: (" + c.DEFAULT_HOST + ") ")
        if self.dbHost == "":
            self.dbHost = c.DEFAULT_HOST

        self.dbUsersTable = input("Database User table: (" + c.TABLE_USERS +
                                  ") ")
        if self.dbUsersTable == "":
            self.dbUsersTable = c.TABLE_USERS

        self.dbVisitsTable = input("Database Visits table: (" +
                                   c.TABLE_VISITS + ") ")
        if self.dbVisitsTable == "":
            self.dbVisitsTable = c.TABLE_VISITS

        self.dbUser = input("Database Username: (" + c.DEFAULT_USER + ") ")
        if self.dbUser == "":
            self.dbUser = c.DEFAULT_USER

        while 1:
            self.dbPass = getpass.getpass("Database Password: "******"":
                print("Database password cannot be blank.")
            else:
                break

    def showCheckinConfirmation(self, userID):
        #===========================================================================
        # show confirmation of successful check in
        #===========================================================================
        print("\n%s is checked in" % (userID))

    def showDatabaseError(self, error):
        #===========================================================================
        # Warn of database error - and provide information
        #===========================================================================
        print("\nWARNING! Database error:\n%s" % error.pgerror)

    def invalidInput(self):
        #===========================================================================
        # Complain about invalid input
        #===========================================================================
        print("Invalid option. Try again.")
Ejemplo n.º 6
0
class MainWnd(QMainWindow):
    def __init__(self, db):
        super(MainWnd, self).__init__()

        self.db = db
        self.tools = Utils()

        # Init card input so it can be appended to later
        self.cardInput = ""

        # Compile the regex for pulling the card ID from all the data on a card
        self.regex = re.compile("%(.+)..\?;")

        # Declare sleepThread
        self.sleepThread = SleepThread(c.TIME_BETWEEN_CHECKINS, self.resetCheckinWidget)

        self.initUI()

    def initUI(self):
        # ===========================================================================
        # init main ui - allow for attendance logging and checking
        # ===========================================================================
        # Center the window
        # setGeometry args are x, y, width, height
        self.setGeometry(0, 0, 550, 100)
        geo = self.frameGeometry()
        centerPt = QDesktopWidget().availableGeometry().center()
        geo.moveCenter(centerPt)
        self.move(geo.topLeft())

        # Title, icon, and statusbar
        self.setWindowTitle(c.GROUP_INITIALS + " Attendance")
        self.setWindowIcon(QIcon(os.path.abspath("images/login_logo.png")))
        self.statusBar().showMessage(
            "Connected to server  |  " + c.GROUP_NAME + " Attendance Tracker Version " + str(c.VERSION)
        )
        # Init all the central widgets
        self.initMainMenuWidget()
        self.initCheckinWidget()
        self.initShowVisitsWidget()

        # Init the central stacked widget and set it as the central widget
        # This allows us to change the central widget easily
        self.centralWidget = QStackedWidget()
        self.setCentralWidget(self.centralWidget)

        # Add the widgets to the main central stacked widget
        self.centralWidget.addWidget(self.mainMenuWidget)
        self.centralWidget.addWidget(self.checkinWidget)
        self.centralWidget.addWidget(self.visitsWidget)

        # Show the main menu first
        self.showMainMenuWidget()

    def keyPressEvent(self, event):
        # ===========================================================================
        # Collect key data until regex matches - then reset and collect again
        # ===========================================================================
        # Only look for card swipes if the checkin widget is currently shown
        if self.centralWidget.currentWidget() == self.checkinWidget:
            try:
                # Try to match the input to the card ID regex
                r = self.regex.search(self.cardInput)
                CUID = r.groups()[0]

                # A match was made so reset cardInput for the next card
                self.cardInput = ""

                # Set the card ID and start the checkin thread
                # CUID is going into an SQL query; don't forget to sanitize the input
                if not (self.checkinThread.isRunning() and self.sleepThread.isRunning()):
                    # self.checkinThread.setCUID(Utils.sanitizeInput(str(CUID)))
                    self.checkinThread.setCUID(CUID)
                    self.checkinThread.start()

            except AttributeError:
                # If a match was not made append the current text to card input
                self.cardInput += event.text()

    def closeEvent(self, closeEvent):
        # ===========================================================================
        # Close database connection prior to closing application
        # ===========================================================================
        print("Cleaning up and exiting...")
        if self.db is not None:
            self.db.close()
        closeEvent.accept()

    def initMainMenuWidget(self):
        # ===========================================================================
        # Initialize Main menu Widget
        # ===========================================================================
        self.mainMenuWidget = QWidget()

        checkinButton = QImageButton(
            "Check-in", os.path.abspath("images/magnetic_card.png"), self.showCheckinWidget, 100, self
        )
        showVisitsButton = QImageButton(
            "Show Visits", os.path.abspath("images/trophy.png"), self.showVisitsWidget, 100, self
        )

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(checkinButton)
        hbox.addSpacing(45)
        hbox.addWidget(showVisitsButton)
        hbox.addStretch(1)

        self.mainMenuWidget.setLayout(hbox)

    def initCheckinWidget(self):
        # ===========================================================================
        # Initialize Check In widget
        # ===========================================================================
        self.checkinWidget = QWidget()

        # Init widgets
        self.cardPix = QPixmap(os.path.abspath("images/magnetic_card.png"))
        self.greenPix = QPixmap(os.path.abspath("images/green_check_mark.png"))
        self.redPix = QPixmap(os.path.abspath("images/red_x_mark.png"))
        self.checkinImg = QLabel(self)
        self.checkinLabel = QLabel("Waiting for card swipe...")
        self.checkinBackBtn = QPushButton("Back", self)

        # Size the images properly
        self.cardPix = self.cardPix.scaledToHeight(175, Qt.SmoothTransformation)
        self.greenPix = self.greenPix.scaledToHeight(175, Qt.SmoothTransformation)
        self.redPix = self.redPix.scaledToHeight(175, Qt.SmoothTransformation)

        # Add the card image to image widget
        self.checkinImg.setPixmap(self.cardPix)

        # Set the font for the checkin label
        font = QFont("Sans Serif", 16, QFont.Bold)
        self.checkinLabel.setFont(font)

        # Add signals to buttons
        self.checkinBackBtn.clicked.connect(self.closeCheckinScreen)

        # Center the image
        imgHbox = QHBoxLayout()
        imgHbox.addStretch(1)
        imgHbox.addWidget(self.checkinImg)
        imgHbox.addStretch(1)

        # Add widgets to vbox layout for vertical centering
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(imgHbox)
        vbox.addWidget(self.checkinLabel)
        vbox.addWidget(self.checkinBackBtn)
        vbox.addStretch(1)

        # Add grid to the hbox layout for horizontal centering
        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addLayout(vbox)
        hbox.addStretch(1)

        # Add the completeted layout to the overall check-in widget
        self.checkinWidget.setLayout(hbox)

    def initShowVisitsWidget(self):
        # ===========================================================================
        # Initialize visits widget
        # ===========================================================================
        self.visitsWidget = QWidget()

        self.checkinThread = None

        # Init widgets
        self.visitsTitle = QLabel("Current visits Standings")
        self.visitsTextArea = QTextEdit()
        self.visitsBackBtn = QPushButton("Back", self)

        # Set the font for the checkin label
        self.visitsTitle.setFont(QFont("Sans Serif", 12, QFont.Bold))

        # Add signals to buttons
        self.visitsBackBtn.clicked.connect(self.closeShowVisitsScreen)

        # Create the layout for the visits scroll area
        self.visitsTextArea.setFont(QFont("Monospace", 8, QFont.Normal))

        # Add widgets to vbox layout for vertical centering
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addWidget(self.visitsTitle, alignment=Qt.AlignCenter)
        vbox.addWidget(self.visitsTextArea)
        vbox.addWidget(self.visitsBackBtn)
        vbox.addStretch(1)

        self.visitsWidget.setLayout(vbox)

    def showMainMenuWidget(self):
        # ===========================================================================
        # Show main menu widget
        # ===========================================================================
        self.centralWidget.setCurrentWidget(self.mainMenuWidget)

    def showCheckinWidget(self):
        # ===========================================================================
        # Show Checkin Widget - used to request point value that is now depreciated
        # ===========================================================================
        self.centralWidget.setCurrentWidget(self.checkinWidget)

        """# Get the visit value
        while 1:
            visitValue, ok = QInputDialog.getText(self, "Visit Value", "Visit Value:", text=str(c.DEFAULT_VISITS))

            if ok:
                if str(visitValue).isdigit():
                    break
                else:
                    QMessageBox.critical(self, "Input Error", "Invalid input", QMessageBox.Ok, QMessageBox.Ok)
            else:
                self.closeCheckinScreen()
                return"""

        # Init the checkin thread
        # visitValue will be used in SQL queries. Sanitize it.
        self.checkinThread = CheckinThread(self.db, self.postCardSwipe)

    def showVisitsWidget(self):
        # ===========================================================================
        # Show visits for certain CUID
        # ===========================================================================
        self.visitsTextArea.clear()
        self.centralWidget.setCurrentWidget(self.visitsWidget)

        # Get the user ID to show visits for or an empty string for all user ID's
        CUID, ok = QInputDialog.getText(self, "CUID", "CUID (blank for all CUID's):")

        if not ok:
            # The show visits thread was not declared yet so just skip the closeShowvisitsScreen function
            self.showMainMenuWidget()

        # Init the show visits thread
        # userID will be used in SQL queries. Sanitize it.
        self.showVisitsThread = ShowVisitsThread(self.db, self.tools.sanitizeInput(str(CUID)), self.setVisits)
        self.showVisitsThread.start()

    def closeCheckinScreen(self):
        # ===========================================================================
        # Close checkinThread and return to Main Menu
        # ===========================================================================
        if self.checkinThread is not None:
            self.checkinThread.terminate()

        self.showMainMenuWidget()

    def closeShowVisitsScreen(self):
        # =======================================================================
        # Close visits thread and return to main menu
        # =======================================================================
        if self.checkinThread is not None:
            self.showVisitsThread.terminate()
        self.showMainMenuWidget()

    def postCardSwipe(self, checkinStatus, userID, CUID, sqlError):
        # ===========================================================================
        # Display results after a card is read - If new card request name
        # ===========================================================================
        if checkinStatus == c.SUCCESS:
            self.checkinImg.setPixmap(self.greenPix)
            self.checkinLabel.setText(str(userID))
        elif checkinStatus == c.SQL_ERROR:
            QMessageBox.critical(
                self, "Database Error", "WARNING! Database error: " + sqlError.pgerror, QMessageBox.Ok, QMessageBox.Ok
            )
            # Don't bother to change UI elements or start the sleep thread, just wait for the next card
            return
        else:
            self.checkinImg.setPixmap(self.redPix)
            if checkinStatus == c.ERROR_READING_CARD:
                self.checkinLabel.setText("Error reading card. Swipe again.")
            elif checkinStatus == c.BAD_CHECKIN_TIME:
                self.checkinLabel.setText("Yoau may only check-in once per hour.")
            elif checkinStatus == c.FUTURE_CHECKIN_TIME:
                self.checkinLabel.setText("Previous check-in time was in the future. Check your local system time.")
            elif checkinStatus == c.CUID_NOT_IN_DB:
                # If the card is not in the DB ask to add it
                reply = QMessageBox.question(
                    self,
                    "CUID Not in Database",
                    "This CUID was not found in the database. Add it now?",
                    QMessageBox.Yes | QMessageBox.No,
                    QMessageBox.No,
                )

                if reply == QMessageBox.Yes:
                    # If adding new card, get the userID associated with the card
                    userID, ok = QInputDialog.getText(self, "Add New Card", "User ID:")
                    # Sanitize the userID input and call the add card thread

                    if ok and userID != "":
                        self.addCardThread = AddCardThread(
                            self.db, CUID, self.tools.sanitizeInput(userID), self.postCardSwipe
                        )
                        self.addCardThread.start()

                # Don't bother to change UI elements or start the sleep thread, just wait for the next card
                return
            else:
                self.checkinLabel.setText("An unknown error occurred.")
                QMessageBox.critical(self, "Unknown Error", "An unknown error occurred", QMessageBox.Ok, QMessageBox.Ok)

        # Force a repaint of the UI
        self.checkinImg.update()
        self.checkinLabel.update()

        # Sleep for a few seconds before resetting the UI for the next card
        # The number of seconds is defined in the constants file
        # This must be on a separate thread since blocking the UI thread is a big no-no
        self.sleepThread.start()

    def resetCheckinWidget(self):
        # ===========================================================================
        # Reset the UI for a new card swipe
        # ===========================================================================
        self.checkinImg.setPixmap(self.cardPix)
        self.checkinLabel.setText("Waiting for card swipe...")
        self.checkinImg.update()
        self.checkinLabel.update()

    def setVisits(self, showVisitsStatus, visitsTuple, sqlError):
        # ===========================================================================
        # Depreciated - removing visits setting
        # ===========================================================================
        if showVisitsStatus == c.NO_RESULTS:
            QMessageBox.critical(
                self,
                "Empty Query",
                "The specified user ID was not found in the database",
                QMessageBox.Ok,
                QMessageBox.Ok,
            )
            return
        for i in range(len(visitsTuple)):
            userID = str(visitsTuple[i][0])
            visits = str(visitsTuple[i][1])
            self.visitsTextArea.append(userID + "\t" + visits)

        # Move the scrollbar to the top
        scrollbar = self.visitsTextArea.verticalScrollBar()
        scrollbar.setValue(scrollbar.minimum())
 def __init__(self):
     self.db = None
     self.tools = Utils()
class TextUI:
    def __init__(self):
        self.db = None
        self.tools = Utils()
        

    def start(self):
    #===========================================================================
    # Main function - start connection to db then open main menu
    #===========================================================================
        try:
            while 1:
                # Get DB info
                self.getDbInfo()

                # Create the DB object
                self.db = DB(self.dbHost, self.dbName, self.dbUsersTable, self.dbVisitsTable, self.dbUser, self.dbPass)
                
                # Connect to the database
                connectStatus = self.connectToDatabase()

                # If we failed to connect to the database offer to re-enter db info
                if connectStatus != c.SUCCESS:
                    reenter = input("Failed to connect to database. Re-enter database info? (Y,n) ")
                    if reenter.lower() == "n":
                        print("Bye.")
                        sys.exit(0)
                else:
                    break

            # Start the main menu loop
            self.displayMenu()

        except KeyboardInterrupt:
            pass
        finally:
            print("Cleaning up and exiting...")
            if self.db is not None:
                self.db.close()


    def displayMenu(self):
    #===========================================================================
    # Display main cli and take appropriate actions
    #===========================================================================
        #print("\nType \"back\" at any time to go up a menu level.")

        while 1:
            # Display main menu
            print("\n\t1.) Check-in\n\t2.) Show Visits\n\t3.) Exit")
            try:
                option = input("\n>> ")

                if option == "1":
                    self.checkIn()
                elif option == "2":
                    self.showVisits()
                elif option == "3":
                    sys.exit(0)
                #elif option == "back" or option == "exit":
                #    exit = input("Exit? (y,N) ")
                #    if exit.lower() == "y":
                #        sys.exit(0)
                else:
                    self.invalidInput()

            except ValueError:
                self.invalidInput()

    def connectToDatabase(self):
    #===========================================================================
    # Open connection to database and return status
    #===========================================================================
        # Use stdout.write to prevent newline
        sys.stdout.write("Connecting to database...")

        # Connect to the DB!
        status = self.db.connect()

        if status == c.SUCCESS:
            print("done.")
            return status
        elif status == c.BAD_PASSWD:
            print("\nError connecting to database: Bad username or password.")
            return status
        else:
            print("\nUnknown Error connecting to database.")
            return c.FAILURE


    def checkIn(self):
    #===========================================================================
    # Log card data to db
    #===========================================================================
        # Get and validate the visit value for this check-in
        # Limited to 500 visits to prevent bad typos
        """while 1:
            visitValue = self.tools.sanitizeInput(input("\nVisit Value (" + str(c.DEFAULT_VISITS) + "): "))

            # Validate visit input
            if visitValue == "":
                visitValue = str(c.DEFAULT_VISITS)
                break
            elif (visitValue.isdigit() and int(visitValue) <= 500) or visitValue == "back":
                break
            else:
                print("Invalid input. Try again.")"""

        while 1:
            CUID = self.tools.getCardSwipe()
            # If the user requested to exit the loop, break
            if CUID == c.BACK:
                break
            elif CUID == c.ERROR_READING_CARD:
                print("Error reading card. Swipe card again.")
                continue

            # Sanitize CUID
            CUID = self.tools.sanitizeInput(CUID)
            # CUID will be empty if it failed sanitization. Skip checkIn if that is the case
            if CUID == "":
                continue

            # Do the checkIn
            checkInResult = self.db.checkIn(CUID)

            if checkInResult["checkInStatus"] == c.SQL_ERROR:
                self.showDatabaseError(checkInResult["sqlError"])
            elif checkInResult["checkInStatus"] == c.BAD_CHECKIN_TIME:
                print("Error: You may only check-in once per hour.")
            elif checkInResult["checkInStatus"] == c.FUTURE_CHECKIN_TIME:
                print("Error: Previous check-in time was in the future. Check your local system time.")
            elif checkInResult["checkInStatus"] == c.CUID_NOT_IN_DB:
                # Ask if user wants to add the card
                addCard = input("Error: Card not found in database. Add it now? (Y,n) ")
            
                if addCard == "n":
                    continue
            
            # Get the userID for the new card
                firstName = self.tools.sanitizeInput(input("First Name: "))
                lastName = self.tools.sanitizeInput(input("Last Name: "))
                email = self.tools.sanitizeInput(input("Clemson Username: "******"addCardStatus"] == c.SUCCESS:
                    self.showCheckinConfirmation(email)
                elif addCardResult["addCardStatus"] == c.SQL_ERROR:
                    self.showDatabaseError(addCardResult["sqlError"])
            elif checkInResult["checkInStatus"] == c.SUCCESS:
                self.showCheckinConfirmation(checkInResult["userID"])
            else:
                print("Unknown error checking in.")
                

    def showVisits(self):
    #===========================================================================
    # Poll db for visit data based on userID
    #===========================================================================
        userID = self.tools.sanitizeInput(input("\nUser ID (blank for all): "))
        showVisitsResult = self.db.showVisits(userID)

        if showVisitsResult["showVisitsStatus"] == c.SQL_ERROR:
            self.showDatabaseError(showVisitsResult["sqlError"])
        elif showVisitsResult["showVisitsStatus"] == c.NO_RESULTS:
            print("\nThere were no results to that query.")
        elif showVisitsResult["showVisitsStatus"] == c.SUCCESS:
            # If showing all users, display a pretty table
            if userID == "":
                print("\n+--------------------+\n| User ID | Visits |\n+--------------------+")

                for i in range(len(showVisitsResult["visitsTuple"])):
                    print("|%10s | %6s |" % (showVisitsResult["visitsTuple"][i][0], showVisitsResult["visitsTuple"][i][1]))
                
                print("+--------------------+")
         
            # Show a single user's visits
            else:
                print("\n%s has %s visits." % (userID, str(showVisitsResult["visitsTuple"][0][0])))


    def getDbInfo(self):
    #===========================================================================
    # Request dbInfo from user - suggest default info from constants
    #===========================================================================
        self.dbName = input("Database name: (" + c.DEFAULT_DATABASE + ") ")
        if self.dbName == "":
            self.dbName = c.DEFAULT_DATABASE
            
        self.dbHost = input("Database host: (" + c.DEFAULT_HOST + ") ")
        if self.dbHost == "":
            self.dbHost = c.DEFAULT_HOST

        self.dbUsersTable = input("Database User table: (" + c.TABLE_USERS + ") ")
        if self.dbUsersTable == "":
            self.dbUsersTable = c.TABLE_USERS
            
        self.dbVisitsTable = input("Database Visits table: (" + c.TABLE_VISITS + ") ")
        if self.dbVisitsTable == "":
            self.dbVisitsTable = c.TABLE_VISITS

        self.dbUser = input("Database Username: (" + c.DEFAULT_USER + ") ")
        if self.dbUser == "":
            self.dbUser = c.DEFAULT_USER

        while 1:
            self.dbPass = getpass.getpass("Database Password: "******"":
                print("Database password cannot be blank.")
            else:
                break


    def showCheckinConfirmation(self, userID):
    #===========================================================================
    # show confirmation of successful check in
    #===========================================================================
        print("\n%s is checked in" % (userID))


    def showDatabaseError(self, error):
    #===========================================================================
    # Warn of database error - and provide information
    #===========================================================================
        print("\nWARNING! Database error:\n%s" % error.pgerror)


    def invalidInput(self):
    #===========================================================================
    # Complain about invalid input
    #===========================================================================
        print("Invalid option. Try again.")
Ejemplo n.º 9
0
class DB:
    def __init__(self, dbHost, dbDatabase, dbUsersTable, dbVisitsTable, dbUser,
                 dbPass):
        self.dbConn = None
        self.dbHost = dbHost
        self.dbDatabase = dbDatabase
        self.dbUsersTable = dbUsersTable
        self.dbVisitsTable = dbVisitsTable
        self.dbUser = dbUser
        self.dbPass = dbPass
        self.tools = Utils()

    def connect(self):
        #===========================================================================
        # Connect to db with given info - need to fix error system
        #===========================================================================
        # If a password was not given, ask for it
        if self.dbPass == "":
            self.dbPass = getDbPass()

        try:  # Connect to the database server
            self.dbConn = psycopg2.connect(database=self.dbDatabase,
                                           user=self.dbUser,
                                           password=self.dbPass,
                                           host=self.dbHost)
            return c.SUCCESS
        except psycopg2.Error as e:
            #if "user denied" in e.args[1]:  # Bad password error
            print("\n", e)
            #if "password authentication" in e:
            #   return c.BAD_PASSWD
            #else:  # Other error
            #return c.FAILURE

    def close(self):
        #===========================================================================
        # Close out db connection
        #===========================================================================
        if self.dbConn is not None:
            self.dbConn.close()

    def addCard(self, cuid, firstName, lastName, email):
        #===========================================================================
        # add a CUID and userID to the database
        #===========================================================================
        # Init some stuff that could cause problems if not initialized
        sqlError = None
        # Get a cursor to the DB
        cursor = self.dbConn.cursor()

        cuid = self.tools.sanitizeInput(cuid)
        firstName = self.tools.sanitizeInput(firstName)
        lastName = self.tools.sanitizeInput(lastName)
        email = self.tools.sanitizeInput(email)

        try:
            cursor.execute("""BEGIN TRANSACTION;""")
            # Add the new record into the DB
            cursor.execute(
                """INSERT INTO %s (%s, %s, %s, %s, %s) values (\'%s\', \'%s\', \'%s\', \'%s\', \'%s\');"""
                % (self.dbUsersTable, c.CUID_COLUMN_USER,
                   c.FIRST_NAME_COLUMN_USER, c.LAST_NAME_COLUMN_USER,
                   c.EMAIL_COLUMN_USER, c.VISIT_NUM_COLUMN_USER, cuid,
                   firstName, lastName, email, c.DEFAULT_VISITS))
            cursor.execute("""END TRANSACTION;""")
        finally:
            cursor.close()

        checkInResult = self.checkIn(cuid)

        return {
            "addCardStatus": checkInResult["checkInStatus"],
            "Name": firstName,
            "CUID": cuid,
            "sqlError": sqlError
        }

    def checkIn(self, CUID):
        #===========================================================================
        # Check in to db with CUID already in db
        #===========================================================================
        # Init some stuff that could cause problems if not initialized
        status = c.FAILURE
        userID = None
        sqlError = None
        visitNum = None

        # Get a cursor to the DB
        if self.dbConn is not None:
            cursor = self.dbConn.cursor()
        else:
            print("dbConn is None")

        try:
            cursor.execute("""BEGIN TRANSACTION;""")
            # Get the last check-in time
            cursor.execute(
                """SELECT last_checkIn FROM %s WHERE CUID=\'%s\';""" %
                (self.dbUsersTable, CUID))

            # Ensure that the card is in the database
            if cursor.rowcount == 0:
                status = c.CUID_NOT_IN_DB
                # Raise a generic exception to break out of the try block
                raise Exception
            else:
                result = cursor.fetchone()

                # Verify the check-in times
            if c.ALLOW_CHECKIN_WITHIN_HOUR:
                status = c.SUCCESS
            else:
                status = self.checkCheckInTime(result[0])

            if status == c.SUCCESS:
                # Update the database with the new visits
                cursor.execute(
                    """UPDATE %s SET %s = \'%s\' WHERE %s = \'%s\';""" %
                    (self.dbUsersTable, c.LAST_CHECKIN_COLUMN_USER,
                     datetime.now(), c.CUID_COLUMN_USER, CUID))
                cursor.execute(
                    """UPDATE %s SET %s = %s + 1 WHERE %s = \'%s\';""" %
                    (self.dbUsersTable, c.VISIT_NUM_COLUMN_USER,
                     c.VISIT_NUM_COLUMN_USER, c.CUID_COLUMN_USER, CUID))

                cursor.execute(
                    """SELECT %s FROM %s WHERE CUID=\'%s\';""" %
                    (c.VISIT_NUM_COLUMN_USER, self.dbUsersTable, CUID))

                visitNum = cursor.fetchone()[0]
                cursor.execute(
                    """INSERT INTO %s (%s, %s, %s) values (\'%s\', \'%s\', \'%s\');"""
                    % (self.dbVisitsTable, c.CUID_COLUMN_VISIT,
                       c.TIMEIN_COLUMN_VISIT, c.VISIT_NUM_COLUMN_VISIT, CUID,
                       datetime.now(), visitNum))

                # Grab the user ID that just checked-in to print confirmation
                cursor.execute("""SELECT %s FROM %s WHERE CUID=\'%s\';""" %
                               (c.EMAIL_COLUMN_USER, self.dbUsersTable, CUID))

                userID = cursor.fetchone()[0]

            cursor.execute("""END TRANSACTION;""")
        except psycopg2.Error as e:
            status = c.SQL_ERROR
            sqlError = e
        except Exception as e:
            print(e)
            pass
        finally:
            cursor.close()

        return {
            "checkInStatus": status,
            "userID": userID,
            "CUID": CUID,
            "sqlError": sqlError
        }

    def checkCheckInTime(self, lastCheckIn):
        #===========================================================================
        # Verifies that we are not checking into the past or the future
        #===========================================================================
        # Get the current date/time
        curDate = datetime.now()

        # The last_checkIn column was added after the DB was initially populated meaning it could be a NoneType
        # Only check the dates if this is not the case
        if lastCheckIn and datetime.date(curDate) == datetime.date(
                lastCheckIn):
            tmzAdjust = 0

            # Check that the current system time is at least one hour greater than the last check-in time
            if (datetime.time(curDate).hour + tmzAdjust
                    == datetime.time(lastCheckIn).hour
                    or (datetime.time(curDate).hour + tmzAdjust
                        == datetime.time(lastCheckIn).hour + 1
                        and datetime.time(curDate).minute <
                        datetime.time(lastCheckIn).minute)):
                return c.BAD_CHECKIN_TIME
            # If the current system time is before the check-in time, do not allow check-in
            elif datetime.time(curDate).hour + tmzAdjust < datetime.time(
                    lastCheckIn).hour:
                return c.FUTURE_CHECKIN_TIME
        # If the current system date is before the check-in date, do not allow check-in
        elif lastCheckIn and datetime.date(curDate) < datetime.date(
                lastCheckIn):
            return c.FUTURE_CHECKIN_TIME
        else:
            return c.SUCCESS

    def showVisits(self, userID=""):
        #===========================================================================
        # Check visits associated with userID and return value
        #===========================================================================
        # Init result and sqlError
        result = None
        sqlError = None

        # Get a cursor to the DB
        cursor = self.dbConn.cursor()

        try:
            # Either get all user ID's and visits from DB or just one user ID
            if userID == "":
                cursor.execute(
                    """SELECT userID, visits FROM %s ORDER BY visits DESC;""" %
                    (self.dbUsersTable))
            else:
                cursor.execute(
                    """SELECT userID, visits FROM %s WHERE userID=\'%s\';""" %
                    (self.dbUsersTable, userID))

            # Show error if no results (user ID is not in database)
            if cursor.rowcount == 0:
                status = c.NO_RESULTS
            else:
                result = cursor.fetchall()
                status = c.SUCCESS

        except psycopg2.Error as e:
            status = c.SQL_ERROR
            sqlError = e
        finally:
            cursor.close()
            return {
                "showVisitsStatus": status,
                "visitsTuple": result,
                "sqlError": sqlError
            }
Ejemplo n.º 10
0
class MainWnd(QMainWindow):
    def __init__(self, db):
        super(MainWnd, self).__init__()

        self.db = db
        self.tools = Utils()

        # Init card input so it can be appended to later
        self.cardInput = ""

        # Compile the regex for pulling the card ID from all the data on a card
        self.regex = re.compile("%(.+)..\?;")

        # Declare sleepThread
        self.sleepThread = SleepThread(c.TIME_BETWEEN_CHECKINS,
                                       self.resetCheckinWidget)

        self.initUI()

    def initUI(self):
        #===========================================================================
        # init main ui - allow for attendance logging and checking
        #===========================================================================
        # Center the window
        # setGeometry args are x, y, width, height
        self.setGeometry(0, 0, 550, 100)
        geo = self.frameGeometry()
        centerPt = QDesktopWidget().availableGeometry().center()
        geo.moveCenter(centerPt)
        self.move(geo.topLeft())

        # Title, icon, and statusbar
        self.setWindowTitle(c.GROUP_INITIALS + " Attendance")
        self.setWindowIcon(QIcon(os.path.abspath("images/login_logo.png")))
        self.statusBar().showMessage("Connected to server  |  " +
                                     c.GROUP_NAME +
                                     " Attendance Tracker Version " +
                                     str(c.VERSION))
        # Init all the central widgets
        self.initMainMenuWidget()
        self.initCheckinWidget()
        self.initShowVisitsWidget()

        # Init the central stacked widget and set it as the central widget
        # This allows us to change the central widget easily
        self.centralWidget = QStackedWidget()
        self.setCentralWidget(self.centralWidget)

        # Add the widgets to the main central stacked widget
        self.centralWidget.addWidget(self.mainMenuWidget)
        self.centralWidget.addWidget(self.checkinWidget)
        self.centralWidget.addWidget(self.visitsWidget)

        # Show the main menu first
        self.showMainMenuWidget()

    def keyPressEvent(self, event):
        #===========================================================================
        # Collect key data until regex matches - then reset and collect again
        #===========================================================================
        # Only look for card swipes if the checkin widget is currently shown
        if self.centralWidget.currentWidget() == self.checkinWidget:
            try:
                # Try to match the input to the card ID regex
                r = self.regex.search(self.cardInput)
                CUID = r.groups()[0]

                # A match was made so reset cardInput for the next card
                self.cardInput = ""

                # Set the card ID and start the checkin thread
                # CUID is going into an SQL query; don't forget to sanitize the input
                if not (self.checkinThread.isRunning()
                        and self.sleepThread.isRunning()):
                    #self.checkinThread.setCUID(Utils.sanitizeInput(str(CUID)))
                    self.checkinThread.setCUID(CUID)
                    self.checkinThread.start()

            except AttributeError:
                # If a match was not made append the current text to card input
                self.cardInput += event.text()

    def closeEvent(self, closeEvent):
        #===========================================================================
        # Close database connection prior to closing application
        #===========================================================================
        print("Cleaning up and exiting...")
        if self.db is not None:
            self.db.close()
        closeEvent.accept()

    def initMainMenuWidget(self):
        #===========================================================================
        # Initialize Main menu Widget
        #===========================================================================
        self.mainMenuWidget = QWidget()

        checkinButton = QImageButton(
            "Check-in", os.path.abspath('images/magnetic_card.png'),
            self.showCheckinWidget, 100, self)
        showVisitsButton = QImageButton("Show Visits",
                                        os.path.abspath('images/trophy.png'),
                                        self.showVisitsWidget, 100, self)

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(checkinButton)
        hbox.addSpacing(45)
        hbox.addWidget(showVisitsButton)
        hbox.addStretch(1)

        self.mainMenuWidget.setLayout(hbox)

    def initCheckinWidget(self):
        #===========================================================================
        # Initialize Check In widget
        #===========================================================================
        self.checkinWidget = QWidget()

        # Init widgets
        self.cardPix = QPixmap(os.path.abspath("images/magnetic_card.png"))
        self.greenPix = QPixmap(os.path.abspath("images/green_check_mark.png"))
        self.redPix = QPixmap(os.path.abspath("images/red_x_mark.png"))
        self.checkinImg = QLabel(self)
        self.checkinLabel = QLabel("Waiting for card swipe...")
        self.checkinBackBtn = QPushButton("Back", self)

        # Size the images properly
        self.cardPix = self.cardPix.scaledToHeight(175,
                                                   Qt.SmoothTransformation)
        self.greenPix = self.greenPix.scaledToHeight(175,
                                                     Qt.SmoothTransformation)
        self.redPix = self.redPix.scaledToHeight(175, Qt.SmoothTransformation)

        # Add the card image to image widget
        self.checkinImg.setPixmap(self.cardPix)

        # Set the font for the checkin label
        font = QFont("Sans Serif", 16, QFont.Bold)
        self.checkinLabel.setFont(font)

        # Add signals to buttons
        self.checkinBackBtn.clicked.connect(self.closeCheckinScreen)

        # Center the image
        imgHbox = QHBoxLayout()
        imgHbox.addStretch(1)
        imgHbox.addWidget(self.checkinImg)
        imgHbox.addStretch(1)

        # Add widgets to vbox layout for vertical centering
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(imgHbox)
        vbox.addWidget(self.checkinLabel)
        vbox.addWidget(self.checkinBackBtn)
        vbox.addStretch(1)

        # Add grid to the hbox layout for horizontal centering
        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addLayout(vbox)
        hbox.addStretch(1)

        # Add the completeted layout to the overall check-in widget
        self.checkinWidget.setLayout(hbox)

    def initShowVisitsWidget(self):
        #===========================================================================
        # Initialize visits widget
        #===========================================================================
        self.visitsWidget = QWidget()

        self.checkinThread = None

        # Init widgets
        self.visitsTitle = QLabel("Current visits Standings")
        self.visitsTextArea = QTextEdit()
        self.visitsBackBtn = QPushButton("Back", self)

        # Set the font for the checkin label
        self.visitsTitle.setFont(QFont("Sans Serif", 12, QFont.Bold))

        # Add signals to buttons
        self.visitsBackBtn.clicked.connect(self.closeShowVisitsScreen)

        # Create the layout for the visits scroll area
        self.visitsTextArea.setFont(QFont("Monospace", 8, QFont.Normal))

        # Add widgets to vbox layout for vertical centering
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addWidget(self.visitsTitle, alignment=Qt.AlignCenter)
        vbox.addWidget(self.visitsTextArea)
        vbox.addWidget(self.visitsBackBtn)
        vbox.addStretch(1)

        self.visitsWidget.setLayout(vbox)

    def showMainMenuWidget(self):
        #===========================================================================
        # Show main menu widget
        #===========================================================================
        self.centralWidget.setCurrentWidget(self.mainMenuWidget)

    def showCheckinWidget(self):
        #===========================================================================
        # Show Checkin Widget - used to request point value that is now depreciated
        #===========================================================================
        self.centralWidget.setCurrentWidget(self.checkinWidget)
        """# Get the visit value
        while 1:
            visitValue, ok = QInputDialog.getText(self, "Visit Value", "Visit Value:", text=str(c.DEFAULT_VISITS))

            if ok:
                if str(visitValue).isdigit():
                    break
                else:
                    QMessageBox.critical(self, "Input Error", "Invalid input", QMessageBox.Ok, QMessageBox.Ok)
            else:
                self.closeCheckinScreen()
                return"""

        # Init the checkin thread
        # visitValue will be used in SQL queries. Sanitize it.
        self.checkinThread = CheckinThread(self.db, self.postCardSwipe)

    def showVisitsWidget(self):
        #===========================================================================
        # Show visits for certain CUID
        #===========================================================================
        self.visitsTextArea.clear()
        self.centralWidget.setCurrentWidget(self.visitsWidget)

        # Get the user ID to show visits for or an empty string for all user ID's
        CUID, ok = QInputDialog.getText(self, "CUID",
                                        "CUID (blank for all CUID\'s):")

        if not ok:
            # The show visits thread was not declared yet so just skip the closeShowvisitsScreen function
            self.showMainMenuWidget()

        # Init the show visits thread
        # userID will be used in SQL queries. Sanitize it.
        self.showVisitsThread = ShowVisitsThread(
            self.db, self.tools.sanitizeInput(str(CUID)), self.setVisits)
        self.showVisitsThread.start()

    def closeCheckinScreen(self):
        #===========================================================================
        # Close checkinThread and return to Main Menu
        #===========================================================================
        if self.checkinThread is not None:
            self.checkinThread.terminate()

        self.showMainMenuWidget()

    def closeShowVisitsScreen(self):
        #=======================================================================
        # Close visits thread and return to main menu
        #=======================================================================
        if self.checkinThread is not None:
            self.showVisitsThread.terminate()
        self.showMainMenuWidget()

    def postCardSwipe(self, checkinStatus, userID, CUID, sqlError):
        #===========================================================================
        # Display results after a card is read - If new card request name
        #===========================================================================
        if checkinStatus == c.SUCCESS:
            self.checkinImg.setPixmap(self.greenPix)
            self.checkinLabel.setText(str(userID))
        elif checkinStatus == c.SQL_ERROR:
            QMessageBox.critical(
                self, "Database Error",
                "WARNING! Database error: " + sqlError.pgerror, QMessageBox.Ok,
                QMessageBox.Ok)
            # Don't bother to change UI elements or start the sleep thread, just wait for the next card
            return
        else:
            self.checkinImg.setPixmap(self.redPix)
            if checkinStatus == c.ERROR_READING_CARD:
                self.checkinLabel.setText("Error reading card. Swipe again.")
            elif checkinStatus == c.BAD_CHECKIN_TIME:
                self.checkinLabel.setText(
                    "Yoau may only check-in once per hour.")
            elif checkinStatus == c.FUTURE_CHECKIN_TIME:
                self.checkinLabel.setText(
                    "Previous check-in time was in the future. Check your local system time."
                )
            elif checkinStatus == c.CUID_NOT_IN_DB:
                # If the card is not in the DB ask to add it
                reply = QMessageBox.question(
                    self, "CUID Not in Database",
                    "This CUID was not found in the database. Add it now?",
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

                if reply == QMessageBox.Yes:
                    # If adding new card, get the userID associated with the card
                    userID, ok = QInputDialog.getText(self, "Add New Card",
                                                      "User ID:")
                    # Sanitize the userID input and call the add card thread

                    if ok and userID != "":
                        self.addCardThread = AddCardThread(
                            self.db, CUID, self.tools.sanitizeInput(userID),
                            self.postCardSwipe)
                        self.addCardThread.start()

                # Don't bother to change UI elements or start the sleep thread, just wait for the next card
                return
            else:
                self.checkinLabel.setText("An unknown error occurred.")
                QMessageBox.critical(self, "Unknown Error",
                                     "An unknown error occurred",
                                     QMessageBox.Ok, QMessageBox.Ok)

        # Force a repaint of the UI
        self.checkinImg.update()
        self.checkinLabel.update()

        # Sleep for a few seconds before resetting the UI for the next card
        # The number of seconds is defined in the constants file
        # This must be on a separate thread since blocking the UI thread is a big no-no
        self.sleepThread.start()

    def resetCheckinWidget(self):
        #===========================================================================
        # Reset the UI for a new card swipe
        #===========================================================================
        self.checkinImg.setPixmap(self.cardPix)
        self.checkinLabel.setText("Waiting for card swipe...")
        self.checkinImg.update()
        self.checkinLabel.update()

    def setVisits(self, showVisitsStatus, visitsTuple, sqlError):
        #===========================================================================
        # Depreciated - removing visits setting
        #===========================================================================
        if showVisitsStatus == c.NO_RESULTS:
            QMessageBox.critical(
                self, "Empty Query",
                "The specified user ID was not found in the database",
                QMessageBox.Ok, QMessageBox.Ok)
            return
        for i in range(len(visitsTuple)):
            userID = str(visitsTuple[i][0])
            visits = str(visitsTuple[i][1])
            self.visitsTextArea.append(userID + "\t" + visits)

        # Move the scrollbar to the top
        scrollbar = self.visitsTextArea.verticalScrollBar()
        scrollbar.setValue(scrollbar.minimum())