From: Drew Fisher Date: Thu, 4 Feb 2010 03:36:58 +0000 (-0500) Subject: Added Python docstrings for all classes. X-Git-Url: https://git.zarvox.org/?a=commitdiff_plain;h=fafc0f49d11c73376e89ede56d3c5d35ef0d8cd5;p=wp3.git Added Python docstrings for all classes. --- diff --git a/analytics.py b/analytics.py index 48b91f1..6215b62 100644 --- a/analytics.py +++ b/analytics.py @@ -6,6 +6,7 @@ from PyQt4.QtSql import * class Analytics(QWidget): def __init__(self,parent=None, database=None): QWidget.__init__(self, parent) + # These are the room numbers in the respective dorms that people can live in self.roomlists = { "Lechner" : [111, 112, 113, 114, 115, 116, 117, 121, 122, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 205, 206, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 221, 222, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 305, 306, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 321, 322, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 405, 406, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440] , "McFadden" : [151, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 170, 171, 172, 173, 175, 176, 177, 178, 179, 182, 183, 184, 185, 186, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 274, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 374, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 474] , "Clements" : [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 332, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 432] @@ -27,18 +28,22 @@ class Analytics(QWidget): self.setLayout(self.layout) def updateDB(self, newdbname): + """Give the widget a handle to the changed database, so analytics will be run on the right DB""" self.db.close() self.db.setDatabaseName(newdbname) self.db.open() def vlog(self, line): # append to the verbose log only + """Append line to the verbose log only""" self.verbosereport = self.verbosereport + line + "\n" def log(self, line): # append to the verbose and report logs + """Append line to both the report and verbose logs""" self.verbosereport = self.verbosereport + line + "\n" self.report = self.report + line + "\n" def generateReport(self): + """Runs analytics on the current database and displays them in the report and verbose detail boxes.""" self.report = "" self.verbosereport = "" q = QSqlQuery(self.db) @@ -121,5 +126,6 @@ class Analytics(QWidget): self.verbosereportbox.setPlainText(self.verbosereport) def finished(self): + """Slot that indicates this widget is done being used (Cancel has been pressed)""" self.emit(SIGNAL("done()")) diff --git a/chooseaction.py b/chooseaction.py index 274f465..2fd5702 100644 --- a/chooseaction.py +++ b/chooseaction.py @@ -5,6 +5,7 @@ from editperson import EditPerson class ChooseAction (QWidget): def __init__(self, parent=None, db=None): + """Sets up the list of buttons and associated actions""" QWidget.__init__(self,parent) self.db = db self.l1 = QLabel("

Hall Directory

Select a task:

") @@ -28,19 +29,24 @@ class ChooseAction (QWidget): QtCore.QObject.connect(self.pb5, QtCore.SIGNAL("clicked()"), self.runAnalytics) self.setLayout(self.layout) def addPerson(self): + """Slot triggered to add a person. Runs the NewPersonWizard.""" wiz = NewPersonWizard(self,self.db) wiz.exec_() wiz = None def editPerson(self): + """Emits the signal editPerson(), so parent can swap to the EditPerson widget""" self.emit(QtCore.SIGNAL("editPerson()")) def importPhotos(self): + """Emits the signal mergePhotos(), so parent can swap to the MergePhotos widget""" self.emit(QtCore.SIGNAL("mergePhotos()")) def exportDocument(self): + """Gets a filename to export the current database to, then emits the exportDocument(QString) signal so parent can pass it to the document exporter""" fileName = QFileDialog.getSaveFileName(self, "Save new file as:", ".", "OpenDocument Text Documents (*.odt)") if not fileName.isEmpty(): if not fileName.endsWith(".odt"): # if they leave off the extension, add it fileName = fileName.append(".odt") self.emit(QtCore.SIGNAL("exportDocument(QString)"),fileName) def runAnalytics(self): + """Emits the signal runAnalytics(), so parent can swap to Analytics widget""" self.emit(QtCore.SIGNAL("runAnalytics()")) diff --git a/editperson.py b/editperson.py index 3aaf1a5..044f655 100644 --- a/editperson.py +++ b/editperson.py @@ -4,6 +4,7 @@ from PyQt4.QtSql import * class EditPerson(QWidget): def __init__(self, parent=None, db=None): + """Set up EditPerson with parent widget and database from which to pull data""" QWidget.__init__(self,parent) self.db = db @@ -51,6 +52,7 @@ class EditPerson(QWidget): self.show() def setupModel(self): + """Prepare data model for the table on the left and add header fields""" self.model = QSqlTableModel(self, self.db) self.model.setEditStrategy(QSqlTableModel.OnManualSubmit) self.model.setTable("people") @@ -58,6 +60,7 @@ class EditPerson(QWidget): self.model.setHeaderData(self.model.fieldIndex("forename"), Qt.Horizontal, QVariant("First Name")) self.model.setHeaderData(self.model.fieldIndex("surname"), Qt.Horizontal, QVariant("Last Name")) def setupForm(self): + """Initialize data breakout widgets on right""" self.current_record = QSqlRecord() self.current_row = -1 # Text labels @@ -147,6 +150,7 @@ class EditPerson(QWidget): self.buttonlayout.addWidget(self.pb_save) self.buttonlayout.addWidget(self.pb_saveclose) def setupActions(self): + """Set up signal/slot connections""" # Update filtered table QObject.connect(self.search_bar, SIGNAL("textChanged(QString)"), self.updateTable ) # Selecting a record loads it for editing @@ -171,18 +175,21 @@ class EditPerson(QWidget): QObject.connect(self.dorm, SIGNAL("currentIndexChanged(int)"), self.formChanged ) def formChanged(self): + """Enable Save/Reset buttons when a field has been changed in the editor""" # Do this only if a record has been selected if not self.current_record.isEmpty(): self.pb_reset.setEnabled(True) self.pb_save.setEnabled(True) self.pb_saveclose.setEnabled(True) def selectionChanged(self, selected, deselected): + """Handle changes in table row selection""" items = selected.indexes() if len(items) > 0: self.current_row = items[0].row() # this is the row of the current selected item self.current_record = self.model.record(items[0].row()) self.fillForm() def fillForm(self): + """Fill form with fields from the selected row of the database""" # Fill fields with data from selected record self.netid.setText(self.current_record.field("netid").value().toString()) self.firstname.setText(self.current_record.field("forename").value().toString()) @@ -211,8 +218,10 @@ class EditPerson(QWidget): self.pb_changephoto.setEnabled(True) def reselect(self): + """Refreshes the table view from the data source. Called when databases merged - need to reload rows from database.""" self.updateTable(self.search_bar.text()) def updateTable(self, text): + """Filter table view with text in the search bar. Table view shows only entries that contain each of the space-separated strings in the bar in either the entries forename or surname.""" if text.isEmpty(): self.model.setFilter("") self.model.select() @@ -232,6 +241,7 @@ class EditPerson(QWidget): if self.model.rowCount() == 1: self.tableview.selectRow(0) def resetPressed(self): + """Revert fields in editor to their source from the database. Disable Save/Reset buttons.""" self.current_record = self.model.record(self.current_row) self.fillForm() self.pb_reset.setEnabled(False) @@ -239,6 +249,7 @@ class EditPerson(QWidget): self.pb_saveclose.setEnabled(False) print "Resetting form" def closePressed(self): + """Revert all unsaved changes, clear all entries, and emit done() for parent to swap widgets shown.""" print "Closing" self.model.revertAll() # Wipe contents of all lineedits, destroy current_record and current_index @@ -246,6 +257,7 @@ class EditPerson(QWidget): self.model.select() self.emit(SIGNAL("done()")) def savePressed(self): + """Commit changes to currently-selected record to the database.""" print "Saving" self.current_record.setValue("netid",QVariant(self.netid.text())) self.current_record.setValue("forename",QVariant(self.firstname.text())) @@ -269,9 +281,11 @@ class EditPerson(QWidget): self.pb_save.setEnabled(False) self.pb_saveclose.setEnabled(False) def saveClosePressed(self): + """Act as if Save were pressed, then Close.""" self.savePressed() self.closePressed() def changePhoto(self): + """Allow user to pick a JPEG or PNG file to set as the current record's photo.""" fileName = QFileDialog.getOpenFileName(self, "Select Photo", ".", "Images (*.jpg *.JPG *.png)" ) if not fileName.isEmpty(): print "opening", fileName @@ -290,6 +304,7 @@ class EditPerson(QWidget): self.pb_save.setEnabled(True) self.pb_saveclose.setEnabled(True) def wipeFields(self): + """Clear all editable fields, including the search bar.""" self.search_bar.clear() self.current_record = QSqlRecord() self.current_index = -1 @@ -307,6 +322,7 @@ class EditPerson(QWidget): self.photo.setText("No record selected") def updateDB(self, newdbname): + """Force database reload with new database name.""" print "updateDB called",newdbname self.db.close() self.db.setDatabaseName(newdbname) diff --git a/ldapsearcher.py b/ldapsearcher.py index 9715a6b..bcde63c 100644 --- a/ldapsearcher.py +++ b/ldapsearcher.py @@ -2,6 +2,7 @@ import ldap class LDAPSearcher(): def __init__(self): + """Bind to the Texas A&M LDAP server.""" server = "operator.tamu.edu" who = "" cred = "" @@ -10,6 +11,7 @@ class LDAPSearcher(): #print "bound successfully" def lookup(self,username=""): + """Look up person with netid and return a dictionary of relevant attributes.""" base = "" scope = ldap.SCOPE_SUBTREE filter = "" diff --git a/mainapp.py b/mainapp.py index 0dd18e6..783bac8 100644 --- a/mainapp.py +++ b/mainapp.py @@ -11,6 +11,7 @@ from analytics import Analytics class MainApp (QMainWindow): schema = "CREATE TABLE people ( id INTEGER PRIMARY KEY AUTOINCREMENT, forename TEXT NOT NULL, surname TEXT NOT NULL, netid TEXT NOT NULL, email TEXT, birthday TEXT, phone TEXT, major TEXT, dorm TEXT, room INTEGER, photo BLOB, createtime TEXT NOT NULL, mtime TEXT NOT NULL);" def __init__(self, Parent=None): + """Set up the main view, load the default database and schema, and set the widgets visible.""" QMainWindow.__init__(self, Parent) self.setWindowTitle("Whitepages V3") @@ -40,6 +41,7 @@ class MainApp (QMainWindow): self.show() def createActions(self): + """Create actions and connect signals and slots.""" self.fileNewAction = QAction("&New Database",self) QObject.connect( self.fileNewAction, SIGNAL("triggered()"), self.newFile) self.fileOpenAction = QAction("&Open Database",self) @@ -61,6 +63,7 @@ class MainApp (QMainWindow): def createMenus(self): + """Create the drop-down File menu.""" self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.fileNewAction) self.fileMenu.addAction(self.fileOpenAction) @@ -70,6 +73,7 @@ class MainApp (QMainWindow): def newFile(self): + """Allow user to specify a new SQLite database to work with.""" fileName = QFileDialog.getSaveFileName(self, "Save new file as:", ".", "Databases (*.db)") if not fileName.isEmpty(): # If they cancelled, do nothing if not fileName.endsWith(".db"): # if they leave off the extension, add it @@ -84,6 +88,7 @@ class MainApp (QMainWindow): else: self.statusBar().showMessage("Cancelled creating new database") def openFile(self): + """Load user-specified SQLite database for editing.""" fileName = QFileDialog.getOpenFileName(self, "Open dataset", ".", "Databases (*.db)") if not fileName.isEmpty(): self.db.close() @@ -95,6 +100,7 @@ class MainApp (QMainWindow): else: self.statusBar().showMessage("Canceled loading database") def mergeWizard(self): + """Import user-specified SQLite database into this database, with intelligent merge heuristics. NetID conflicts are resolved by ignoring the to-be-imported record and keeping the one in the currently-open database.""" fileName = QFileDialog.getOpenFileName(self, "Import which dataset?", ".", "Databases (*.db)" ) if not fileName.isEmpty(): tempdb = QSqlDatabase.addDatabase("QSQLITE","tempdb") @@ -149,19 +155,24 @@ class MainApp (QMainWindow): else: self.statusBar().showMessage("Canceled merging databases") def quit(self): + """Exit program.""" # You could do cleanup, like closing/flushing the database, an "Are you sure you want to quit?" # modal dialog, or saving the window layout/state. qApp.quit() def returnToMainMenu(self): + """Switch to ChooseAction widget.""" self.center.setCurrentWidget(self.chooseaction) def editPersonSlot(self): + """Switch to EditPerson widget.""" self.editperson.model.select() # update the table self.center.setCurrentWidget(self.editperson) def mergePhotosSlot(self): + """Switch to MergePhotos widget.""" print "beginning photo merge" self.center.setCurrentWidget(self.mergephotos) #self.mergephotos.beginMerge() def exportDocumentSlot(self, filename): + """Export current database to ODT file .""" writer = ODTWriter(self.db) success = writer.write(filename) if success: @@ -169,6 +180,7 @@ class MainApp (QMainWindow): else: self.statusBar().showMessage(QString("Document export cancelled or failed")) def runAnalyticsSlot(self): + """Switch to Analytics widget.""" print "Running analytics" self.analytics.generateReport() self.center.setCurrentWidget(self.analytics) diff --git a/mergephotos.py b/mergephotos.py index 02a9c46..04b0f47 100644 --- a/mergephotos.py +++ b/mergephotos.py @@ -55,6 +55,7 @@ class MergePhotos(QWidget): self.setLayout(self.layout) def beginMergeNetID(self): + """Import photos from a user-specified folder by filename; namely .JPG.""" foldertext = QFileDialog.getExistingDirectory( None, "Pick folder of photos", ".") if not foldertext.isEmpty(): print foldertext @@ -121,6 +122,7 @@ class MergePhotos(QWidget): self.done() def beginMergeTimestamp(self): + """Import photos from a user-specified folder by timestamp. This relies on the folder containing the same number of pictures as records in the database missing photos, as well as the photos and database records being created in the same order.""" foldertext = QFileDialog.getExistingDirectory( None, "Pick folder of photos", ".") if not foldertext.isEmpty(): print foldertext @@ -188,5 +190,6 @@ class MergePhotos(QWidget): print "incorrect number of photos, can't merge" def done(self): + """Indicate to parent widget to switch to main menu.""" self.emit(SIGNAL("done()") ) diff --git a/newpersonwizard.py b/newpersonwizard.py index 268e945..2a0a66f 100644 --- a/newpersonwizard.py +++ b/newpersonwizard.py @@ -7,6 +7,7 @@ from pagecommit import PageCommit class NewPersonWizard(QWizard): def __init__(self, parent=None,db=None): + """Run the new person wizard.""" QWizard.__init__(self, parent) self.db = db self.addPage(PageNetID(self,self.db)) diff --git a/odtwriter.py b/odtwriter.py index 14de92c..793c1d3 100644 --- a/odtwriter.py +++ b/odtwriter.py @@ -7,6 +7,7 @@ class ODTWriter(): self.db = db def write(self, filename): + """Write out data from current database to in ODT format.""" doc = QTextDocument() cur = QTextCursor(doc) cur.movePosition(QTextCursor.Start) diff --git a/pagecommit.py b/pagecommit.py index 46aadc4..847b47b 100644 --- a/pagecommit.py +++ b/pagecommit.py @@ -4,6 +4,7 @@ from PyQt4.QtSql import * class PageCommit(QWizardPage): def __init__(self, parent=None, db=None): + """Set up widgets and message to have picture taken.""" QWizardPage.__init__(self,parent) self.db = db self.instructions = QLabel("You're done with the computer. Go have your picture taken.\n\nThe SAs/PAs will add the photo to your data.") @@ -15,6 +16,7 @@ class PageCommit(QWizardPage): self.setSubTitle("Photo") def initializePage(self): + """Add data collected from previous pages to database.""" q = QSqlQuery(self.db) q.prepare("INSERT INTO people (netid, forename, surname, email, birthday, phone, major, dorm, room, createtime, mtime )" "VALUES (:netid, :forename, :surname, :email, :birthday, :phone, :major, :dorm, :room, :createtime, :mtime )" ); diff --git a/pagenetid.py b/pagenetid.py index 6ec85a3..6ce0b9f 100644 --- a/pagenetid.py +++ b/pagenetid.py @@ -4,6 +4,7 @@ from PyQt4.QtSql import * class PageNetID(QWizardPage): def __init__(self, parent=None, db=None ): + """Display disclaimer and get NetID from user.""" QWizardPage.__init__(self,parent) self.db = db self.instructions = QLabel("&Enter your NetID:") @@ -22,6 +23,7 @@ class PageNetID(QWizardPage): self.setTitle("Move-in Wizard") self.setSubTitle("Enter your NetID:") def validatePage(self): + """Make sure the NetID doesn't already exist in this database - if it does, we already have this person's data.""" netid = self.field("netid") q = QSqlQuery() q.prepare("SELECT * FROM people where netid = :netid") diff --git a/pagenewuserdata.py b/pagenewuserdata.py index 29e40ac..311ddb8 100644 --- a/pagenewuserdata.py +++ b/pagenewuserdata.py @@ -5,6 +5,7 @@ from ldapsearcher import LDAPSearcher class PageNewUserData(QWizardPage): def __init__(self, parent=None): + """Set up lots of input fields.""" QWizardPage.__init__(self,parent) self.setCommitPage(True) self.setButtonText(QWizard.CommitButton, "Next") @@ -89,6 +90,7 @@ class PageNewUserData(QWizardPage): def initializePage(self): + """Load known information from LDAP, like email, major, name, and NetID.""" a = LDAPSearcher() ldapinfo = a.lookup(str(self.field("netid").toString())) for key,value in ldapinfo.iteritems():