diff --git a/egon.pro b/egon.pro new file mode 100644 index 0000000..ccad91c --- /dev/null +++ b/egon.pro @@ -0,0 +1,2 @@ +SOURCES += egon.py +TRANSLATIONS += egon_nb.ts diff --git a/egon.py b/egon.py new file mode 100755 index 0000000..22d0877 --- /dev/null +++ b/egon.py @@ -0,0 +1,2183 @@ +#!/usr/bin/env python +#coding: utf-8 + +# Copyright 2008, Tiril Anette Langfeldt Rødland +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# You should have received a copy of the GNU General Public License along with this program. If not, see . + +import os +import platform +import sys +import string +import datetime +import time +import random +from PyQt4.QtCore import * +from PyQt4.QtGui import * +from pysqlite2 import dbapi2 as sqlite +from qrc_resources import * + + +__version__ = "1.1.0" + + +main = None +semesters = [] +semester = None +courses = [] +coursesString = [] +books = [] +booksString = [] +colors = [] +termList = None + +### The main window +class MainWindow(QMainWindow): + + # Initialize the main window + def __init__(self, parent=None): + super(MainWindow, self).__init__(parent) + + # The program name + self.title = self.trUtf8("Egon") + + # The days + self.days = [self.trUtf8("Monday"), self.trUtf8("Tuesday"), self.trUtf8("Wednesday"), self.trUtf8("Thursday"), self.trUtf8("Friday")] + + # The tabs + self.assignment = AssignmentTab() + self.reading = ReadingTab() + self.schedule = ScheduleTab() + + # Set the tabs in the middle + self.tabs = QTabWidget() + self.tabs.addTab(self.assignment, self.trUtf8("&Assignment")) + self.tabs.addTab(self.reading, self.trUtf8("&Reading List")) + self.tabs.addTab(self.schedule, self.trUtf8("&Schedule")) + self.tabs.setTabShape(QTabWidget.Rounded) + self.tabs.setTabPosition(QTabWidget.North) + self.setCentralWidget(self.tabs) + + # The calendar + self.calendarFrame = QFrame() + self.calendarFrame.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) + self.calendarDockWidget = QDockWidget(self.trUtf8("Calendar"), self) + self.calendarDockWidget.setVisible(False) + self.calendarDockWidget.setObjectName("CalendarDockWidget") + self.calendarDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea|Qt.RightDockWidgetArea|Qt.BottomDockWidgetArea) + self.calendar = QCalendarWidget() + self.calendar.setFirstDayOfWeek(Qt.Monday) + self.calendarLayout = QVBoxLayout() + self.calendarLayout.addWidget(self.calendar) + self.calendarFrame.setLayout(self.calendarLayout) + self.calendarFrame.hide() + self.calendarDockWidget.setWidget(self.calendarFrame) + + # The semesters + global semester, semesters, termList + termList = QStringList() + termList.append(self.trUtf8("Spring")) + termList.append(self.trUtf8("Autumn")) + semesters = getSemestersFromDB() + semester = self.getLatestSemester(semesters) + if semester: + self.load(semester) + + # Actions + fileNewAction = self.createAction(self.trUtf8("&New"), self.fileNew, QKeySequence.New, "filenew", self.trUtf8("Create a new semester plan")) + fileOpenAction = self.createAction(self.trUtf8("&Open"), self.fileOpen, QKeySequence.Open, "fileopen", self.trUtf8("Open an existing semester plan")) + fileQuitAction = self.createAction(self.trUtf8("&Quit"), self.close, "Ctrl+Q", "filequit", self.trUtf8("Quit program")) + editAddCourse = self.createAction(self.trUtf8("Add &course"), self.addCourse, "Ctrl+C", None, self.trUtf8("Add a new course")) + editAddBook = self.createAction(self.trUtf8("Add &book"), self.addBook, "Ctrl+B", None, self.trUtf8("Add a new book")) + editAddAssignment = self.createAction(self.trUtf8("Add &assignment"), self.addAssignment, "Ctrl+A", None, self.trUtf8("Add a new assignment")) + editAddReading = self.createAction(self.trUtf8("Add &reading"), self.addReading, "Ctrl+R", None, self.trUtf8("Add a new reading")) + editAddLesson = self.createAction(self.trUtf8("Add &lesson"), self.addLesson, "Ctrl+L", None, self.trUtf8("Add a new lesson")) + editShowCalendar = self.createAction(self.trUtf8("Cal&endar"), self.showCalendar, "Ctrl+E", None, self.trUtf8("Show the calendar")) + helpAboutScheduler = self.createAction(self.trUtf8("&About Egon")) + + # Menus + self.fileMenu = self.menuBar().addMenu(self.trUtf8("&File")) + self.editMenu = self.menuBar().addMenu(self.trUtf8("&Edit")) + self.helpMenu = self.menuBar().addMenu(self.trUtf8("&Help")) + + # Add actions to the menus + self.addActions(self.fileMenu, (fileNewAction, fileOpenAction, None, fileQuitAction)) + self.addActions(self.editMenu, (editAddCourse, editAddBook, None, editAddAssignment, None, editAddReading, None, editAddLesson, None, editShowCalendar)) + self.addActions(self.helpMenu, (helpAboutScheduler, None)) + + # Connect statements + self.connect(editAddCourse, SIGNAL("triggered()"), self.addCourse) + self.connect(editAddBook, SIGNAL("triggered()"), self.addBook) + self.connect(editAddAssignment, SIGNAL("triggered()"), self.addAssignment) + self.connect(editAddReading, SIGNAL("triggered()"), self.addReading) + self.connect(editAddLesson, SIGNAL("triggered()"), self.addLesson) + self.connect(editShowCalendar, SIGNAL("pressed()"), self.showCalendar) + self.connect(helpAboutScheduler, SIGNAL("triggered()"), self.helpAbout) + self.connect(self.assignment.addAssignmentButton, SIGNAL("pressed()"), self.addAssignment) + self.connect(self.assignment.deleteAssignmentButton, SIGNAL("pressed()"), self.deleteAssignment) + self.connect(self.assignment.statusAssignmentBox, SIGNAL("currentIndexChanged(QString)"), self.statusAssignment) + self.connect(self.reading.addReadingButton, SIGNAL("pressed()"), self.addReading) + self.connect(self.reading.deleteReadingButton, SIGNAL("pressed()"), self.deleteReading) + self.connect(self.reading.readingDoneButton, SIGNAL("pressed()"), self.doneReading) + self.connect(self.schedule.addScheduleButton, SIGNAL("pressed()"), self.addLesson) + self.connect(self.schedule.deleteScheduleButton, SIGNAL("pressed()"), self.deleteLesson) + + # The toolbars + fileToolbar = self.addToolBar(self.trUtf8("File")) + fileToolbar.setObjectName("FileToolBar") + self.addActions(fileToolbar, (fileNewAction, fileOpenAction, None, fileQuitAction)) + editToolbar = self.addToolBar(self.trUtf8("Edit")) + editToolbar.setObjectName("EditToolBar") + self.addActions(editToolbar, (editAddCourse, editAddBook, None, editShowCalendar)) + + # Set the title + self.setMainWindowTitle() + + # The courses + courses = getCourses() + makeCoursesString() + + # This window + global main + main = self + + # Semester + + ## Open the New dialog + def fileNew(self): + self.nsdlg = NewSemesterDlg() + self.nsdlg.show() + + ## Open the Open dialog + def fileOpen(self): + self.osdlg = OpenSemesterDlg() + self.osdlg.show() + + ## Return the latest semester + def getLatestSemester(self, semesters): + if len(semesters) == 0: + return None + global termList + max = semesters[0] + for s in semesters: + if s.getYear() > max.getYear(): + max = s + if s.getYear() == max.getYear(): + if s.getTerm() == self.trUtf8("Autumn"): + max = s + return max + + # Return the list of terms + def getTermList(self): + global termList + return termList + + # Assignment + + ## Open the Add Assignment dialog + def addAssignment(self): + self.adlg = AssignmentDlg() + self.adlg.show() + + ## Delete the assignment from the database and the table + def deleteAssignment(self): + course, number = self.getCourseAndNumber() + table, row = self.getAssignmentTableAndRow() + global semester + removeAssignmentFromDB(course, number) + table.removeRow(row) + table.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + + ## Update the assignment's completion level to the specified level + def statusAssignment(self, completionLevel): + course, number = self.getCourseAndNumber() + table, row = self.getAssignmentTableAndRow() + updateAssignmentCompletion(course, number, completionLevel) + item = QTableWidgetItem(QString(completionLevel)) + item.setBackground(self.assignment.makeBrush(completionLevel)) + table.setItem(row, 4, item) + table.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + + ## Return the assignment table and its current row + def getAssignmentTableAndRow(self): + table = self.assignment.assignmentTable + row = table.currentRow() + return table, row + + ## Return the course and number of the current assignment + def getCourseAndNumber(self): + table, row = self.getAssignmentTableAndRow() + courseItem = table.item(row, 1) + numberItem = table.item(row, 2) + courseFull = courseItem.text() + number = numberItem.text() + course = getCourseCode(courseFull) + return course, number + + # Reading + + ## Open the Add Reading dialog + def addReading(self): + self.rdlg = ReadingDlg() + self.rdlg.show() + + ## Delete the reading from the database and the table + def deleteReading(self): + week, course, book, chapter = self.getWeekCourseBookAndChapter() + table, row = self.getReadingTableAndRow() + global semester + removeReadingFromDB(week, course, book, chapter) + table.removeRow(row) + table.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + + ## Mark the reading as done + def doneReading(self): + table, row = self.getReadingTableAndRow() + if table.item(row, 6).text().compare(QString(self.trUtf8("Not done"))) == 0: + done = True + item = QTableWidgetItem(self.trUtf8("Done")) + item.setBackground(QBrush(Qt.green, Qt.SolidPattern)) + else: + done = True + item = QTableWidgetItem(self.trUtf8("Not done")) + item.setBackground(QBrush(Qt.red, Qt.SolidPattern)) + table.setItem(row, 6, item) + table.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + week = (table.item(row, 0).text().toInt())[0] + courseFull = table.item(row, 1).text() + courseCode = QString(getCourseCode(courseFull)) + bookTitle = table.item(row, 2).text() + book = getBookWithTitleFromDB(bookTitle) + bookIsbn = book.getIsbn() + chapter = table.item(row, 3).text() + updateReadingDone(week, courseCode, bookIsbn, chapter, done) + print week, courseCode, bookIsbn, chapter, done + + ## Return the reading table and its current row + def getReadingTableAndRow(self): + table = self.reading.readingTable + row = table.currentRow() + return table, row + + ## Return the week, course and book of the current reading + def getWeekCourseBookAndChapter(self): + table, row = self.getReadingTableAndRow() + weekItem = table.item(row, 0) + courseItem = table.item(row, 1) + bookItem = table.item(row, 2) + chapterItem = table.item(row, 3) + week = (weekItem.text().toInt())[0] + courseFull = courseItem.text() + courseCode = getCourseCode(courseFull) + book = getBookWithTitleFromDB(bookItem.text()) + chapter = chapterItem.text() + return week, courseCode, book.getIsbn(), chapter + + # Schedule + + ## Open the Add Lesson dialog + def addLesson(self): + self.sdlg = ScheduleDlg() + self.sdlg.show() + + ## Delete the lesson from the database and the table + def deleteLesson(self): + table, row, column = self.getScheduleTableRowAndColumn() + day, course, time = self.getDayCourseAndTime() + global semester + removeLessonFromDB(day, course, time) + table.setItem(row, column, QTableWidgetItem()) + + ## Return the schedule table and the current row and column + def getScheduleTableRowAndColumn(self): + table = self.schedule.scheduleTable + row = table.currentRow() + column = table.currentColumn() + return table, row, column + + ## Return the day, course and time of the current lesson + def getDayCourseAndTime(self): + table, row, column = self.getScheduleTableRowAndColumn() + item = table.item(row, column) + text = item.text() + textlist = text.split('\n') + courseFull = textlist[0] + coursecode = getCourseCode(courseFull) + day = self.getDayFromTable(column) + time = row + 8 + return day, coursecode, time + + ## Return the day belonging to the specified column + def getDayFromTable(self, column): + return self.days[column] + + # Course + + ## Open the Add Course dialog + def addCourse(self): + self.cdlg = CourseDlg() + self.cdlg.show() + + # Book + + ## Open the Add Book dialog + def addBook(self): + self.bdlg = BookDlg() + self.bdlg.show() + + # Calendar + + ## Show the calendar + def showCalendar(self): + if self.calendarDockWidget.isVisible(): + self.removeDockWidget(self.calendarDockWidget) + else: + self.addDockWidget(Qt.BottomDockWidgetArea, self.calendarDockWidget) + self.calendarDockWidget.setVisible(True) + + # General + + ## Make and open the About dialo + def helpAbout(self): + QMessageBox.about(self, "About %s" % self.title, """ + %s v %s +

Copyright © 2008 Tiril Anette Langfeldt Roedland. No rights reserved. +

You may modify and redistribute the program under the terms of the GPL. The license can be found here: http://www.gnu.org/licenses/gpl.html +

This application is mainly for use by students, and can be used to keep track of assignments, planned readings and the schedule. +

Python %s - Qt %s - PyQt %s on %s +

Developer: Tiril Anette Langfeldt Roedland, tirilane@pvv.ntnu.no + """ % (self.title, __version__, platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR, platform.system())) + + ## Updates the File menu + def updateFileMenu(self): + self.fileMenu.clear() + self.addActions(self.fileMenu, self.fileMenuActions[:-1]) + current = QString(self.filename) if self.filename is not None else None + + ## Create and return the action specified by the input + def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): + action = QAction(text, self) + if icon is not None: + iconfile = ":/%s.png" % icon + action.setIcon(QIcon(iconfile)) + if shortcut is not None: + action.setShortcut(shortcut) + if tip is not None: + action.setToolTip(tip) + action.setStatusTip(tip) + if slot is not None: + self.connect(action, SIGNAL(signal), slot) + if checkable: + action.setCheckable(True) + return action + + ## Add action to the target + def addActions(self, target, actions): + for action in actions: + if action is None: + target.addSeparator() + else: + target.addAction(action) + + ## Set the title on the main window, depending on the chosen semester + def setMainWindowTitle(self): + global semester + if semester: + self.setWindowTitle("%s : %s %i" % (self.title, semester.getTerm(), semester.getYear())) + else: + self.setWindowTitle(self.title) + + ## Load the assignments, readings and schedule from the specified semester + def load(self, semester): + self.assignment.updateTable(semester) + self.reading.updateTable(semester) + self.schedule.updateTable(semester) + + +### The New Semester dialog +class NewSemesterDlg(QDialog): + + ## Initialize the New Semester dialog + def __init__(self, parent=None): + super(NewSemesterDlg, self).__init__(parent) + + # Labels + self.termLabel = QLabel(self.trUtf8("Semester")) + self.yearLabel = QLabel(self.trUtf8("Year")) + + # Widgets + self.termEdit = QComboBox() + self.yearEdit = QSpinBox() + self.termEdit.addItems(getMain().getTermList()) + self.yearEdit.setRange(2000, 2050) + self.yearEdit.setSingleStep(1) + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Layout + layout = QGridLayout() + layout.addWidget(self.termLabel, 0, 0) + layout.addWidget(self.termEdit, 0, 1) + layout.addWidget(self.yearLabel, 1, 0) + layout.addWidget(self.yearEdit, 1, 1) + layout.addWidget(self.buttonBox, 2, 1) + self.setLayout(layout) + + # Connect statements + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("New semester")) + + ## Accept the dialog and add the specified semester + def accept(self): + term = unicode(self.termEdit.currentText()) + year = self.yearEdit.value() + global semester + semester = SemesterModel(term, year) + addNewSemesterToDB(term, year) + getMain().load(semester) + getMain().setMainWindowTitle() + self.close() + + +### The Open Semester dialog +class OpenSemesterDlg(QDialog): + + ## Initialize the Open Semester dialog + def __init__(self, parent=None): + super(OpenSemesterDlg, self).__init__(parent) + + # Widgets + self.semesterList = QListWidget() + semesters = getSemestersFromDB() + semesterlist = QStringList() + for semester in semesters: + semesterlist.append("%s %i" % (semester.getTerm(), semester.getYear())) + self.semesterList.addItems(semesterlist) + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Layout + layout = QVBoxLayout() + layout.addWidget(self.semesterList) + layout.addStretch() + layout.addWidget(self.buttonBox) + self.setLayout(layout) + + # Connect statements + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("Open semester")) + + ## Accept the dialog and open the specified semester + def accept(self): + text = self.semesterList.currentItem().text() + textlist = text.split(' ') + term = textlist[0] + year = textlist[1].toInt()[0] + global semester + semester = SemesterModel(term, year) + semester.setAssignments(getAssignmentsFromDB(semester)) + semester.setReadings(getReadingsFromDB(semester)) + semester.setLessons(getLessonsFromDB(semester)) + getMain().load(semester) + getMain().setMainWindowTitle() + self.close() + + +### The semester model +class SemesterModel(): + + term = "" + year = 0 + assignments = [] + readings = [] + lessons = [] + + ## Initialize the semester model + def __init__(self, term, year): + self.term = term + self.year = year + + ## Return the term of the semester + def getTerm(self): + return self.term + + ## Return the year of the semester + def getYear(self): + return self.year + + ## Add an assignment to the semester's list of assignments + def addAssignment(self, assignment): + self.assignments.append(assignment) + + ## Set the list of assignments + def setAssignments(self, assignments): + self.assignments = assignments + + ## Return the list of assignments + def getAssignments(self): + return self.assignments + + ## Add a reading to the semester's list of readings + def addReading(self, reading): + self.readings.append(reading) + + ## Set the list of readings + def setReadings(self, readings): + self.readings = readings + + ## Return the list of readings + def getReadings(self): + return self.readings + + ## Add a lesson to the semester's list of lessons + def addLesson(self, lesson): + self.lessons.append(lesson) + + ## Set the list of lessons + def setLessons(self, lessons): + self.lessons = lessons + + ## Return the list of lessons + def getLessons(self): + return self.lessons + + +### The contents of the Assignment tab +class AssignmentTab(QWidget): + + ## Initialize the Assignment tab + def __init__(self, parent=None): + super(AssignmentTab, self).__init__(parent) + + # Widgets + self.addAssignmentButton = QPushButton("Add assignment") + self.statusAssignmentBox = QComboBox() + self.deleteAssignmentButton = QPushButton("Delete assignment") + statusTypes = [self.trUtf8("Not available"), self.trUtf8("Available"), self.trUtf8("Begun"), self.trUtf8("Finished"), self.trUtf8("Delivered"), self.trUtf8("Approved"), self.trUtf8("Not approved")] + self.statusAssignmentBox.addItems(statusTypes) + + # Make the table + self.makeTable() + + # Layout + self.vlayout = QVBoxLayout() + self.hlayout = QHBoxLayout() + self.hlayout.addWidget(self.addAssignmentButton) + self.hlayout.addWidget(self.deleteAssignmentButton) + self.hlayout.addWidget(self.statusAssignmentBox) + self.hlayout.addStretch() + self.vlayout.addWidget(self.assignmentTable) + self.vlayout.addLayout(self.hlayout) + self.setLayout(self.vlayout) + + ## Make an empty assignment table + def makeTable(self, current=None): + self.assignmentTable = QTableWidget(0, 5, self) + self.assignmentTable.clear() + + self.assignmentHeaderList = QStringList() + self.assignmentHeaderList.append(self.trUtf8("Date")) + self.assignmentHeaderList.append(self.trUtf8("Course")) + self.assignmentHeaderList.append(self.trUtf8("Number")) + self.assignmentHeaderList.append(self.trUtf8("Description")) + self.assignmentHeaderList.append(self.trUtf8("Status")) + + self.assignmentTable.setHorizontalHeaderLabels(self.assignmentHeaderList) + self.assignmentTable.setAlternatingRowColors(True) + self.assignmentTable.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.assignmentTable.setSelectionBehavior(QAbstractItemView.SelectRows) + self.assignmentTable.setSelectionMode(QAbstractItemView.SingleSelection) + + selected = None + + ## Add the assignments of the semester to the table + def updateTable(self, semester, current=None): + self.assignments = getAssignmentsFromDB(semester) + rows = len(self.assignments) + self.assignmentTable.setRowCount(rows) + + for row in range(rows): + self.addAssignmentToTable(row) + + self.assignmentTable.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + self.assignmentTable.sortItems(0, Qt.AscendingOrder) + + ## Add new assignment to Table + def addAssignmentToTable(self, row, assignment=None): + if assignment == None: + assignment = self.assignments[row] + + status = QString(assignment.getStatus()) + brush = self.makeBrush(status) + + self.assignmentTable.setItem(row, 0, QTableWidgetItem(QString(assignment.getDate().toString("yyyy-MM-dd hh:mm, ddd")))) + self.assignmentTable.setItem(row, 1, QTableWidgetItem(QString(assignment.getCourse().getFull()))) + self.assignmentTable.setItem(row, 2, QTableWidgetItem(QString("%i" % assignment.getNumber()))) + self.assignmentTable.setItem(row, 3, QTableWidgetItem(QString("%s" % assignment.getDescription()))) + + statusItem = QTableWidgetItem(status) + statusItem.setBackground(brush) + self.assignmentTable.setItem(row, 4, statusItem) + + ## Set the right brush and color for the assignment, depending on level of completion + def makeBrush(self, status): + brush = QBrush(Qt.NoBrush) + if status.compare(QString(self.trUtf8("Available"))) == 0: + brush.setStyle(Qt.Dense7Pattern) + brush.setColor(QColor(Qt.cyan)) + elif status.compare(QString(self.trUtf8("Begun"))) == 0: + brush.setStyle(Qt.Dense5Pattern) + brush.setColor(QColor(Qt.cyan)) + elif status.compare(QString(self.trUtf8("Finished"))) == 0: + brush.setStyle(Qt.Dense3Pattern) + brush.setColor(QColor(Qt.cyan)) + elif status.compare(QString(self.trUtf8("Delivered"))) == 0: + brush.setStyle(Qt.Dense1Pattern) + brush.setColor(QColor(Qt.cyan)) + elif status.compare(QString(self.trUtf8("Approved"))) == 0: + brush.setStyle(Qt.SolidPattern) + brush.setColor(QColor(Qt.green)) + elif status.compare(QString(self.trUtf8("Not approved"))) == 0: + brush.setStyle(Qt.SolidPattern) + brush.setColor(QColor(Qt.red)) + else: + brush.setStyle(Qt.NoBrush) + return brush + + +### The Add Assignment dialog +class AssignmentDlg(QDialog): + + ## Initialize the Add Assignment dialog + def __init__(self, parent=None): + super(AssignmentDlg, self).__init__(parent) + + # Labels + self.dateLabel = QLabel(self.trUtf8("&Date")) + self.courseLabel = QLabel(self.trUtf8("&Course")) + self.numberLabel = QLabel(self.trUtf8("&Number")) + self.descriptionLabel = QLabel(self.trUtf8("De&scription")) + self.statusLabel = QLabel(self.trUtf8("&Status")) + + # Widgets + self.dateEdit = QLineEdit("DD.MM.YYYY HH:MM") + self.dateEdit.setSelection(0, 16) + self.courseEdit = QComboBox() + self.courseEdit.addItems(makeCoursesString()) + self.numberEdit = QSpinBox() + self.numberEdit.setRange(1, 20) + self.descriptionEdit = QLineEdit() + self.statusEdit = QComboBox() + statusTypes = [self.trUtf8("Not available"), self.trUtf8("Available"), self.trUtf8("Begun"), self.trUtf8("Finished"), self.trUtf8("Delivered"), self.trUtf8("Approved"), self.trUtf8("Not approved")] + self.statusEdit.addItems(statusTypes) + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Buddies + self.dateLabel.setBuddy(self.dateEdit) + self.courseLabel.setBuddy(self.courseEdit) + self.numberLabel.setBuddy(self.numberEdit) + self.descriptionLabel.setBuddy(self.descriptionEdit) + self.statusLabel.setBuddy(self.statusEdit) + + # Layout + self.layout = QGridLayout() + self.layout.addWidget(self.dateLabel, 0, 0) + self.layout.addWidget(self.courseLabel, 1, 0) + self.layout.addWidget(self.numberLabel, 2, 0) + self.layout.addWidget(self.descriptionLabel, 3, 0) + self.layout.addWidget(self.statusLabel, 4, 0) + self.layout.addWidget(self.dateEdit, 0, 1) + self.layout.addWidget(self.courseEdit, 1, 1) + self.layout.addWidget(self.numberEdit, 2, 1) + self.layout.addWidget(self.descriptionEdit, 3, 1) + self.layout.addWidget(self.statusEdit, 4, 1) + self.layout.addWidget(self.buttonBox, 5, 0, 1, 2) + self.setLayout(self.layout) + + # Connect statements + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("Add new assignment")) + + ## Return an array with the values of the widgets + def getValues(self): + assignment = [] + assignment.append(unicode(self.dateEdit.text())) + assignment.append(unicode(self.courseEdit.currentText())) + assignment.append(self.numberEdit.value()) + assignment.append(unicode(self.descriptionEdit.text())) + assignment.append(unicode(self.statusEdit.currentText())) + return assignment + + ## Accept the dialog and add the specified assignment + def accept(self): + assignmentList = self.getValues() + dateString = assignmentList[0] + courseFull = assignmentList[1] + number = assignmentList[2] + description = assignmentList[3] + status = assignmentList[4] + + regex = QRegExp(r"[01-31].[01-12].[2000-2050] [00-23]:[00-60]") + validator = QRegExpValidator(regex, self) + valid = validator.validate(dateString, 16) + if valid == QValidator.Invalid: + regexMessage = QErrorMessage() + regexMessage.showMessage(QString(self.trUtf8("The date is not in a correct format."))) + + if len(dateString) <= 11: + dateList = dateString.split('.') + timeList = ['00', '00'] + else: + dateTime = dateString.split() + dateList = dateTime[0].split('.') + timeList = dateTime[1].split(':') + + if dateList[1] > '13': + dateMessage = QErrorMessage() + elif dateList[0] > '31': + dateMessage = QErrorMessage() + dateMessage.showMessage(QString(self.trUtf8("The day is not valid. Please enter a valid date."))) + elif timeList[0] > '23': + dateMessage = QErrorMessage() + dateMessage.showMessage(QString(self.trUtf8("The hour is not valid. Please enter a valid time."))) + elif timeList[1] > '59': + dateMessage = QErrorMessage() + dateMessage.showMessage(QString(self.trUtf8("The minutes are not valid. Please enter a valid time."))) + else: + try: + date = QDate(string.atoi(dateList[2]), string.atoi(dateList[1]), string.atoi(dateList[0])) + time = QTime(string.atoi(timeList[0]), string.atoi(timeList[1])) + datetime = QDateTime(date, time) + except ValueError, e: + valueMessage = QErrorMessage() + valueMessage.showMessage(QString(self.trUtf8("The date is not valid. Please enter a date on the DD.MM.YYYY HH:MM form."))) + + course = getCourseFromDB(getCourseCode(courseFull)) + + try: + global semester + addNewAssignmentToDB(semester, datetime, course, number, description, status) + assignment = AssignmentModel(datetime, course, number, description, status) + except UnboundLocalError, e: + pass + + table = getMain().assignment.assignmentTable + row = table.rowCount() + table.insertRow(row) + getMain().assignment.addAssignmentToTable(row, assignment) + table.sortItems(0, Qt.AscendingOrder) + + self.close() + + +### The assignment model +class AssignmentModel(): + + date = None + course = None + number = 0 + description = "" + status = "" + + ## Initialize the assignment model + def __init__(self, date, course, number, description, status): + self.date = date + self.course = course + self.number = number + self.description = description + self.status = status + + ## Return the date the assignment is due + def getDate(self): + return self.date + + ## Return the course the assignment is given in + def getCourse(self): + return self.course + + ## Return the number of the assignment + def getNumber(self): + return self.number + + ## Return the description of the assignment + def getDescription(self): + return self.description + + ## Return the level of completion for the assignment + def getStatus(self): + return self.status + + +### The contents of the Reading tab +class ReadingTab(QWidget): + + ## Initialize the Reading tab + def __init__(self, parent=None): + super(ReadingTab, self).__init__(parent) + + # Widgets + self.addReadingButton = QPushButton(self.trUtf8("Add pages to read")) + self.deleteReadingButton = QPushButton(self.trUtf8("Delete pages")) + self.readingDoneButton = QPushButton(self.trUtf8("Done")) + + # Make the table + self.makeTable() + + # Layout + self.vlayout = QVBoxLayout() + self.hlayout = QHBoxLayout() + self.hlayout.addWidget(self.addReadingButton) + self.hlayout.addWidget(self.deleteReadingButton) + self.hlayout.addWidget(self.readingDoneButton) + self.hlayout.addStretch() + self.vlayout.addWidget(self.readingTable) + self.vlayout.addLayout(self.hlayout) + self.setLayout(self.vlayout) + + ## Make an empty reading table + def makeTable(self, current=None): + self.readingTable = QTableWidget(0, 7, self) + self.readingTable.clear() + self.readingHeaderList = QStringList() + self.readingHeaderList.append(self.trUtf8("Week")) + self.readingHeaderList.append(self.trUtf8("Course")) + self.readingHeaderList.append(self.trUtf8("Book")) + self.readingHeaderList.append(self.trUtf8("Chapter")) + self.readingHeaderList.append(self.trUtf8("Pages")) + self.readingHeaderList.append(self.trUtf8("Number of pages")) + self.readingHeaderList.append(self.trUtf8("Done")) + self.readingTable.setHorizontalHeaderLabels(self.readingHeaderList) + self.readingTable.setAlternatingRowColors(True) + self.readingTable.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.readingTable.setSelectionBehavior(QAbstractItemView.SelectRows) + self.readingTable.setSelectionMode(QAbstractItemView.SingleSelection) + selected = None + + ## Add the readings of the semester to the table + def updateTable(self, semester): + self.readings = getReadingsFromDB(semester) + rows = len(self.readings) + self.readingTable.setRowCount(rows) + for row in range(rows): + self.addReadingToTable(row) + self.readingTable.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + self.readingTable.sortItems(0, Qt.AscendingOrder) + + ## Add a new reading to the table + def addReadingToTable(self, row, reading=None): + if reading == None: + reading = self.readings[row] + + brush = QBrush(Qt.NoBrush) + brush.setStyle(Qt.SolidPattern) + + if reading.getDone(): + doneString = self.trUtf8("Done") + brush.setColor(Qt.green) + else: + doneString = self.trUtf8("Not done") + brush.setColor(Qt.red) + + self.readingTable.setItem(row, 0, QTableWidgetItem(QString("%02s" % reading.getWeek()))) + self.readingTable.setItem(row, 1, QTableWidgetItem(QString(reading.getCourse().getFull()))) + self.readingTable.setItem(row, 2, QTableWidgetItem(QString(reading.getBook().getTitle()))) + self.readingTable.setItem(row, 3, QTableWidgetItem(QString(reading.getChapter()))) + self.readingTable.setItem(row, 4, QTableWidgetItem(QString(reading.getPages()))) + self.readingTable.setItem(row, 5, QTableWidgetItem(QString("%i" % reading.getNumberOfPages()))) + + item = QTableWidgetItem(QString(doneString)) + item.setBackground(brush) + self.readingTable.setItem(row, 6, item) + + +### The Add Reading dialog +class ReadingDlg(QDialog): + + ## Initialize the Add Reading dialog + def __init__(self, parent=None): + super(ReadingDlg, self).__init__(parent) + + # Labels + self.weekLabel = QLabel(self.trUtf8("&Week")) + self.courseLabel = QLabel(self.trUtf8("&Course")) + self.bookLabel = QLabel(self.trUtf8("&Book")) + self.chapterLabel = QLabel(self.trUtf8("Cha&pter")) + self.pagesLabel = QLabel(self.trUtf8("&Pages")) + + # Widgets + self.weekEdit = QSpinBox() + self.weekEdit.setRange(1, 52) + self.courseEdit = QComboBox() + coursesStringList = QStringList() + courses = makeCoursesString() + for course in courses: + coursesStringList.append(course) + self.courseEdit.addItems(coursesStringList) + self.bookEdit = QComboBox() + booksStringList = QStringList() + books = makeBooksString() + for book in books: + booksStringList.append(book) + self.bookEdit.addItems(booksStringList) + self.chapterEdit = QLineEdit() + self.pagesEdit = QLineEdit() + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Buddies + self.weekLabel.setBuddy(self.weekEdit) + self.courseLabel.setBuddy(self.courseEdit) + self.bookLabel.setBuddy(self.bookEdit) + self.chapterLabel.setBuddy(self.chapterEdit) + self.pagesLabel.setBuddy(self.pagesEdit) + + # Layout + self.layout = QGridLayout() + self.layout.addWidget(self.weekLabel, 0, 0) + self.layout.addWidget(self.courseLabel, 1, 0) + self.layout.addWidget(self.bookLabel, 2, 0) + self.layout.addWidget(self.chapterLabel, 3, 0) + self.layout.addWidget(self.pagesLabel, 4, 0) + self.layout.addWidget(self.weekEdit, 0, 1) + self.layout.addWidget(self.courseEdit, 1, 1) + self.layout.addWidget(self.bookEdit, 2, 1) + self.layout.addWidget(self.chapterEdit, 3, 1) + self.layout.addWidget(self.pagesEdit, 4, 1) + self.layout.addWidget(self.buttonBox, 5, 0, 1, 2) + self.setLayout(self.layout) + + # Connect statements + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("Add new reading")) + + ## Accept the dialog and add the specified reading + def accept(self): + week = unicode(self.weekEdit.value()) + courseFull = unicode(self.courseEdit.currentText()) + bookTitle = unicode(self.bookEdit.currentText()) + chapter = unicode(self.chapterEdit.text()) + pages = unicode(self.pagesEdit.text()) + + self.close() + + course = getCourseFromDB(getCourseCode(courseFull)) + book = getBookWithTitleFromDB(bookTitle) + + global semester + addNewReadingToDB(semester, week, course, book, chapter, pages, False) + + reading = ReadingModel(week, course, book, chapter, pages, False) + table = getMain().reading.readingTable + row = table.rowCount() + table.insertRow(row) + getMain().reading.addReadingToTable(row, reading) + table.sortItems(0, Qt.AscendingOrder) + + +### The reading model +class ReadingModel(): + + week = 0 + course = None + book = None + chapter = "" + pages = "" + numberOfPages = 0 + done = False + + ## Initialize the reading model + def __init__(self, week, course, book, chapter, pages, done=False): + self.week = week + self.course = course + self.book = book + self.chapter = chapter + self.pages = pages + self.numberOfPages = self.getNumberOfPages() + self.done = done + + ## Return the week the reading shall be done + def getWeek(self): + return self.week + + ## Return the course the reading is in + def getCourse(self): + return self.course + + ## Return the book to be read in + def getBook(self): + return self.book + + ## Return the chapter to be read + def getChapter(self): + return self.chapter + + ## Return the pages to be read + def getPages(self): + return self.pages + + ## Return the number of pages to be read + def getNumberOfPages(self): + pages = self.getPages() + pagesArray = pages.split(",") + nextArray = [] + sum = 0 + for p in pagesArray: + p.strip() + nextArray.append(p.split("-")) + for n in nextArray: + sum += int(n[1]) - int(n[0]) + 1 + return sum + + ## Return whether the reading has been done or not + def getDone(self): + return self.done + + +### The contents of the Schedule tab +class ScheduleTab(QWidget): + + ## Initialize the Schedule tab + def __init__(self, parent=None): + super(ScheduleTab, self).__init__(parent) + + # Widgets + self.addScheduleButton = QPushButton("Add lesson") + self.deleteScheduleButton = QPushButton("Delete lesson") + + # Make the table + self.makeTable() + + # Layout + self.vlayout = QVBoxLayout() + self.hlayout = QHBoxLayout() + self.hlayout.addWidget(self.addScheduleButton) + self.hlayout.addWidget(self.deleteScheduleButton) + self.hlayout.addStretch() + self.vlayout.addWidget(self.scheduleTable) + self.vlayout.addLayout(self.hlayout) + self.setLayout(self.vlayout) + + ## Make an empty schedule table + def makeTable(self, current=None): + self.scheduleTable = QTableWidget(12, 5, self) + self.scheduleTable.setRowCount(12) + self.scheduleTable.clear() + self.updateHeaders() + self.scheduleTable.setAlternatingRowColors(False) + self.scheduleTable.setEditTriggers(QAbstractItemView.NoEditTriggers) + self.scheduleTable.setSelectionBehavior(QAbstractItemView.SelectItems) + self.scheduleTable.setSelectionMode(QAbstractItemView.SingleSelection) + selected = None + + def updateHeaders(self): + self.scheduleHorizontalHeaderList = QStringList() + self.scheduleHorizontalHeaderList.append(self.trUtf8("Monday")) + self.scheduleHorizontalHeaderList.append(self.trUtf8("Tuesday")) + self.scheduleHorizontalHeaderList.append(self.trUtf8("Wednesday")) + self.scheduleHorizontalHeaderList.append(self.trUtf8("Thursday")) + self.scheduleHorizontalHeaderList.append(self.trUtf8("Friday")) + self.scheduleVerticalHeaderList = QStringList() + for i in range(8, 20): + self.scheduleVerticalHeaderList.append(self.trUtf8("%i" % i)) + self.scheduleTable.setHorizontalHeaderLabels(self.scheduleHorizontalHeaderList) + self.scheduleTable.setVerticalHeaderLabels(self.scheduleVerticalHeaderList) + + + ## Add the lessons of the semester to the table + def updateTable(self, semester): + self.schedule = getLessonsFromDB(semester) + rows = len(self.schedule) + self.scheduleTable.clear() + for l in range(rows): + self.addLessonToTable(self.schedule[l]) + self.scheduleTable.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) + self.scheduleTable.verticalHeader().setResizeMode(QHeaderView.ResizeToContents) + self.updateHeaders() + + ## Add a new lesson to the table + def addLessonToTable(self, lesson): + row = lesson.getTime() - 8 + column = self.getColumn(lesson.getDay()) + code = lesson.getCourse().getCode() + title = lesson.getCourse().getTitle() + type = lesson.getType() + room = lesson.getRoom() + olditem = self.scheduleTable.item(row, column) + newtext = "%s\n%s\n%s\n%s" % (code, title, type, room) + if olditem: + oldtext = olditem.text() + text = QString(oldtext + "\n" + newtext) + collision = True + else: + text = QString(newtext) + collision = False + item = QTableWidgetItem(text) + item.setBackground(self.getBackground(QString("%s" % type), lesson.getCourse(), collision)) + self.scheduleTable.setItem(row, column, item) + + ## Return the column specified by the day + def getColumn(self, dayString): + day = QString("%s" % dayString) + if day.compare(QString(self.trUtf8("Monday"))) == 0: + return 0 + elif day.compare(QString(self.trUtf8("Tuesday"))) == 0: + return 1 + elif day.compare(QString(self.trUtf8("Wednesday"))) == 0: + return 2 + elif day.compare(QString(self.trUtf8("Thursday"))) == 0: + return 3 + elif day.compare(QString(self.trUtf8("Friday"))) == 0: + return 4 + else: + return -1 + + ## Set the right brush and color for the lesson, depending on type of lesson + def getBackground(self, type, course, collision): + if type.compare(QString(self.trUtf8("Lecture"))) == 0: + brush = QBrush(Qt.SolidPattern) + elif type.compare(QString(self.trUtf8("Assignment lecture"))) == 0: + brush = QBrush(Qt.Dense2Pattern) + elif type.compare(QString(self.trUtf8("Assignment help"))) == 0: + brush = QBrush(Qt.Dense3Pattern) + elif type.compare(QString(self.trUtf8("Lab"))) == 0: + brush = QBrush(Qt.Dense1Pattern) + elif type.compare(QString(self.trUtf8("Seminar"))) == 0: + brush = QBrush(Qt.Dense4Pattern) + elif type.compare(QString(self.trUtf8("Other"))) == 0: + brush = QBrush(Qt.Dense5Pattern) + else: + brush = QBrush(Qt.NoBrush) + if not collision: + brush.setColor(course.getColor()) + else: + brush.setColor(Qt.red) + return brush + + +### The Add Lesson dialog +class ScheduleDlg(QDialog): + + ## Initialize the Add Lesson dialog + def __init__(self, parent=None): + super(ScheduleDlg, self).__init__(parent) + + # Labels + self.dayLabel = QLabel(self.trUtf8("&Day")) + self.fromLabel = QLabel(self.trUtf8("&From")) + self.toLabel = QLabel(self.trUtf8("&To")) + self.courseLabel = QLabel(self.trUtf8("&Course")) + self.typeLabel = QLabel(self.trUtf8("Ty&pe")) + self.roomLabel = QLabel(self.trUtf8("&Room")) + + # Widgets + self.dayEdit = QComboBox() + self.dayEdit.addItems(getMain().days) + self.fromEdit = QSpinBox() + self.fromEdit.setRange(08.15, 18.15) + self.fromEdit.setSingleStep(01.00) + self.toEdit = QSpinBox() + self.toEdit.setRange(09.00, 19.00) + self.toEdit.setSingleStep(01.00) + self.courseEdit = QComboBox() + courses = makeCoursesString() + coursesStringList = QStringList() + for course in courses: + coursesStringList.append(course) + self.courseEdit.addItems(coursesStringList) + self.typeEdit = QComboBox() + types = [self.trUtf8("Lecture"), self.trUtf8("Assignment lecture"), self.trUtf8("Assignment help"), self.trUtf8("Lab"), self.trUtf8("Seminar"), self.trUtf8("Other")] + self.typeEdit.addItems(types) + self.roomEdit = QLineEdit() + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Buddies + self.dayLabel.setBuddy(self.dayEdit) + self.fromLabel.setBuddy(self.fromEdit) + self.toLabel.setBuddy(self.toEdit) + self.courseLabel.setBuddy(self.courseEdit) + self.typeLabel.setBuddy(self.typeEdit) + self.roomLabel.setBuddy(self.roomEdit) + + # Layout + self.layout = QGridLayout() + self.layout.addWidget(self.dayLabel, 0, 0) + self.layout.addWidget(self.fromLabel, 1, 0) + self.layout.addWidget(self.toLabel, 2, 0) + self.layout.addWidget(self.courseLabel, 3, 0) + self.layout.addWidget(self.typeLabel, 4, 0) + self.layout.addWidget(self.roomLabel, 5, 0) + self.layout.addWidget(self.dayEdit, 0, 1) + self.layout.addWidget(self.fromEdit, 1, 1) + self.layout.addWidget(self.toEdit, 2, 1) + self.layout.addWidget(self.courseEdit, 3, 1) + self.layout.addWidget(self.typeEdit, 4, 1) + self.layout.addWidget(self.roomEdit, 5, 1) + self.layout.addWidget(self.buttonBox, 6, 0, 1, 2) + self.setLayout(self.layout) + + # Connect statements + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("Add new lesson")) + + ## Accept the dialog and add the specified lesson + def accept(self): + day = unicode(self.dayEdit.currentText()) + fromtime = self.fromEdit.value() + totime = self.toEdit.value() + courseFull = unicode(self.courseEdit.currentText()) + type = unicode(self.typeEdit.currentText()) + room = unicode(self.roomEdit.text()) + + course = getCourseFromDB(getCourseCode(courseFull)) + + global semester + for t in range(fromtime, totime): + addNewLessonToDB(semester, day, t, course, type, room) + getMain().schedule.addLessonToTable(ScheduleModel(day, t, course, type, room)) + + self.close() + + +### The schedule model +class ScheduleModel(): + + day = "" + time = 0 + course = None + type = "" + room = "" + + ## Initialize the schedule model + def __init__(self, day, time, course, type, room): + self.day = day + self.time = time + self.course = course + self.type = type + self.room = room + + ## Return the day of the lesson + def getDay(self): + return self.day + + ## Return the start time of the lesson + def getTime(self): + return self.time + + ## Return the course of the lesson + def getCourse(self): + return self.course + + ## Return the type of the lesson + def getType(self): + return self.type + + ## Return the room the lesson is in + def getRoom(self): + return self.room + + +### The Add Course dialog +class CourseDlg(QDialog): + + ## Initialize the Add Course dialog + def __init__(self, parent=None): + super(CourseDlg, self).__init__(parent) + + # The books + self.books = [] + + # Labels + self.codeLabel = QLabel(self.trUtf8("&Code")) + self.titleLabel = QLabel(self.trUtf8("&Title")) + self.shortLabel = QLabel(self.trUtf8("&Short form")) + self.booksLabel = QLabel(self.trUtf8("&Books")) + + # Widgets + self.codeEdit = QLineEdit() + self.titleEdit = QLineEdit() + self.shortEdit = QLineEdit() + self.booksEdit = QListWidget() + self.updateList() + self.booksEdit.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.newBook = QPushButton("Add new book") + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Buddies + self.codeLabel.setBuddy(self.codeEdit) + self.titleLabel.setBuddy(self.titleEdit) + self.shortLabel.setBuddy(self.shortEdit) + self.booksLabel.setBuddy(self.booksEdit) + + # Layout + self.layout = QGridLayout() + self.layout.addWidget(self.codeLabel, 0, 0) + self.layout.addWidget(self.titleLabel, 1, 0) + self.layout.addWidget(self.shortLabel, 2, 0) + self.layout.addWidget(self.booksLabel, 3, 0) + self.layout.addWidget(self.codeEdit, 0, 1) + self.layout.addWidget(self.titleEdit, 1, 1) + self.layout.addWidget(self.shortEdit, 2, 1) + self.layout.addWidget(self.booksEdit, 3, 1) + self.layout.addWidget(self.newBook, 4, 0) + self.layout.addWidget(self.buttonBox, 4, 1, 1, 2) + self.setLayout(self.layout) + + # Connect statements + self.connect(self.newBook, SIGNAL("pressed()"), getMain().addBook) + self.connect(getMain(), SIGNAL("newBook"), self.updateList) + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("Add new course")) + + ## Update the book list + def updateList(self): + self.booksEdit.clear() + booksDB = getBooksFromDB() + booksStringList = QStringList() + for book in booksDB: + booksStringList.append(book.getAuthor() + u" : " + book.getTitle() + u", " + str(book.getEdition()) + self.trUtf8(". edition")) + self.booksEdit.addItems(booksStringList) + self.booksEdit.sortItems() + + ## Add a new book to the course + def addNewBookToCourse(self): + book = getBookWithTitleFromDB(booktitle) + self.books.append(book) + + ## Accept the dialog and add the specified course + def accept(self): + courseCode = unicode(self.codeEdit.text()) + courseTitle = unicode(self.titleEdit.text()) + courseShort = unicode(self.shortEdit.text()) + courseBooks = self.booksEdit.selectedItems() + color = getRandomColor() + global colors + while color in colors: + color = getRandomColor() + colors.append(color) + books = [] + for book in courseBooks: + books.append(getBookWithTitleFromDB("%s" % book.text())) + course = CourseModel(courseCode, courseTitle, courseShort, color, books) + addNewCourseToDB(courseCode, courseTitle, courseShort, color, books) + self.close() + + +### The course model +class CourseModel(): + + code = "" + title = "" + short = "" + full = "" + books = [] + + ## Initialize the course model + def __init__(self, code, title, short, color, books): + self.code = code + self.title = title + self.short = short + self.setFull(code, title) + self.color = color + self.books = books + + ## Return the code of the course + def getCode(self): + return self.code + + ## Return the title of the course + def getTitle(self): + return self.title + + ## Return the short form of the course + def getShort(self): + return self.short + + ## Set the full form of the course + def setFull(self, code, title): + self.full = code + ' ' + title + + ## Return the full form of the course + def getFull(self): + return self.full + + ## Return the color of the course + def getColor(self): + return self.color + + ## Add a book to the course + def addBook(self, book): + books.append(book) + + ## Return the books of the course + def getBooks(self): + return self.books + + +### The Add Book dialog +class BookDlg(QDialog): + + ## Initialize the Add Book dialog + def __init__(self, parent=None): + super(BookDlg, self).__init__(parent) + + # Labels + self.titleLabel = QLabel(self.trUtf8("&Title")) + self.authorLabel = QLabel(self.trUtf8("&Author")) + self.editionLabel = QLabel(self.trUtf8("&Edition")) + self.isbnLabel = QLabel(self.trUtf8("&ISBN")) + + # Widgets + self.titleEdit = QLineEdit() + self.authorEdit = QLineEdit() + self.editionEdit = QSpinBox() + self.editionEdit.setRange(1, 50) + self.isbnEdit = QLineEdit() + self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel) + + # Buddies + self.titleLabel.setBuddy(self.titleEdit) + self.authorLabel.setBuddy(self.authorEdit) + self.editionLabel.setBuddy(self.editionEdit) + self.isbnLabel.setBuddy(self.isbnEdit) + + # Layout + self.layout = QGridLayout() + self.layout.addWidget(self.titleLabel, 0, 0) + self.layout.addWidget(self.authorLabel, 1, 0) + self.layout.addWidget(self.editionLabel, 2, 0) + self.layout.addWidget(self.isbnLabel, 3, 0) + self.layout.addWidget(self.titleEdit, 0, 1) + self.layout.addWidget(self.authorEdit, 1, 1) + self.layout.addWidget(self.editionEdit, 2, 1) + self.layout.addWidget(self.isbnEdit, 3, 1) + self.layout.addWidget(self.buttonBox, 4, 0, 1, 2) + self.setLayout(self.layout) + + # Connect statements + self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) + self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) + + # Set the title + self.setWindowTitle(self.trUtf8("Add new book")) + + # Accept the dialog and add the specified book + def accept(self): + bookTitle = unicode(self.titleEdit.text()) + bookAuthor = unicode(self.authorEdit.text()) + bookEdition = self.editionEdit.value() + bookIsbn = unicode(self.isbnEdit.text()) + addNewBookToDB(bookIsbn, bookTitle, bookAuthor, bookEdition) + getMain().emit(SIGNAL("newBook")) + self.close() + + +### The book model +class BookModel(): + + title = "" + author = "" + edition = 0 + isbn = "" + + ## Initialize the book model + def __init__(self, isbn, title, author, edition): + self.isbn = isbn + self.title = title + self.author = author + self.edition = edition + + # Return the ISBN number of the book + def getIsbn(self): + return self.isbn + + # Return the title of the book + def getTitle(self): + return self.title + + # Return the author(s) of the book + def getAuthor(self): + return self.author + + # Return the edition of the book + def getEdition(self): + return self.edition + + +# Database + +# Initialization + +## Connect to the database and return the cursor and connection +def initDB(): + if 'HOME' in os.environ: + home = os.environ['HOME'] + else: + home = "" + path = home+"/.egon/egon.db" + conn = sqlite.connect(path) + cursor = conn.cursor() + + cursor.execute(''' + SELECT name FROM sqlite_master + WHERE type='table' + ORDER BY name + ''') + + if cursor.fetchall() == []: + initSemesterDB(cursor) + initAssignmentDB(cursor) + initReadingDB(cursor) + initScheduleDB(cursor) + initBookDB(cursor) + initCourseDB(cursor) + initCourseUsesBook(cursor) + + return cursor, conn + + exitDB(conn) + +# Create the database + +## Create the Semester table +def initSemesterDB(cursor): + cursor.execute(''' + CREATE TABLE Semester ( + term TEXT, + year INT, + PRIMARY KEY (term, year) + ) + ''') + +## Create the Assignment table +def initAssignmentDB(cursor): + cursor.execute(''' + CREATE TABLE Assignment ( + date DATETIME, + course TEXT, + number INT, + description TEXT, + status TEXT, + term TEXT, + year INT, + PRIMARY KEY (course, number) + ) + ''') + +## Create the Reading table +def initReadingDB(cursor): + cursor.execute(''' + CREATE TABLE Reading ( + week INT, + course TEXT, + book INT, + chapter TEXT, + pages TEXT, + done BOOLEAN, + term TEXT, + year INT, + PRIMARY KEY (week, course, book, chapter) + ) + ''') + +## Create the Lesson table +def initScheduleDB(cursor): + cursor.execute(''' + CREATE TABLE Lesson ( + day TEXT, + time INT, + course TEXT, + type TEXT, + room TEXT, + term TEXT, + year INT, + PRIMARY KEY (course, day, time) + ) + ''') + +## Create the Course table +def initCourseDB(cursor): + cursor.execute(''' + CREATE TABLE Course ( + code TEXT PRIMARY KEY, + title TEXT, + short TEXT, + red INT, + green INT, + blue INT + ) + ''') + +## Create the Book table +def initBookDB(cursor): + cursor.execute(''' + CREATE TABLE Book ( + isbn TEXT PRIMARY KEY, + title TEXT, + author TEXT, + edition INTEGER + ) + ''') + +## Create the Course-Book relation +def initCourseUsesBook(cursor): + cursor.execute(''' + CREATE TABLE CourseUsesBook ( + courseCode TEXT, + bookIsbn TEXT, + PRIMARY KEY (courseCode, bookIsbn) + ) + ''') + +# Add things to the database + +## Add new semester to the database +def addNewSemesterToDB(term, year): + cursor, conn = initDB() + + cursor.execute(''' + INSERT INTO Semester (term, year) + VALUES (?, ?) + ''', (term, year)) + + exitDB(conn) + +## Add new assignment to the database +def addNewAssignmentToDB(semester, datetime, course, number, description, status): + cursor, conn = initDB() + + day = datetime.date().day() + month = datetime.date().month() + year = datetime.date().year() + hour = datetime.time().hour() + minute = datetime.time().minute() + timestring = "%02i-%02i-%02i %02i:%02i" % (year, month, day, hour, minute) + term = "%s" % semester.getTerm() + year = semester.getYear() + + cursor.execute(''' + INSERT INTO Assignment (date, course, number, description, status, term, year) + VALUES (datetime(?), ?, ?, ?, ?, ?, ?) + ''', (timestring, course.getCode(), number, description, status, term, year)) + + exitDB(conn) + +## Add new reading to the database +def addNewReadingToDB(semester, week, course, book, chapter, pages, done): + cursor, conn = initDB() + + term = "%s" % semester.getTerm() + year = semester.getYear() + + cursor.execute(''' + INSERT INTO Reading (week, course, book, chapter, pages, done, term, year) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ''', (week, course.getCode(), book.getIsbn(), chapter, pages, done, term, year)) + + exitDB(conn) + +## Add new lesson to the database +def addNewLessonToDB(semester, day, time, course, type, room): + cursor, conn = initDB() + + term = "%s" % semester.getTerm() + year = semester.getYear() + + cursor.execute(''' + INSERT INTO Lesson (day, time, course, type, room, term, year) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', (day, time, course.getCode(), type, room, term, year)) + + exitDB(conn) + +## Add new course to the database +def addNewCourseToDB(code, title, short, color, books): + cursor, conn = initDB() + + red = color.red() + green = color.green() + blue = color.blue() + + cursor.execute(''' + INSERT INTO Course (code, title, short, red, green, blue) + VALUES (?, ?, ?, ?, ?, ?) + ''', (code, title, short, red, green, blue)) + + for book in books: + cursor.execute(''' + INSERT INTO CourseUsesBook (courseCode, bookIsbn) + VALUES (?, ?) + ''', (code, book)) + + exitDB(conn) + +## Add new book to the database +def addNewBookToDB(isbn, title, author, edition): + cursor, conn = initDB() + + cursor.execute(''' + INSERT INTO Book + VALUES (?, ?, ?, ?) + ''', (isbn, title, author, edition)) + + exitDB(conn) + +# Get things from the database + +## Get all the semesters form the database +def getSemestersFromDB(): + cursor, conn = initDB() + + cursor.execute(''' + SELECT * + FROM Semester + ''') + + semesters = [] + for row in cursor.fetchall(): + semesters.append(SemesterModel(row[0], row[1])) + + exitDB(conn) + + return semesters + +## Get the assignments of the specified semester from the database +def getAssignmentsFromDB(semester): + cursor, conn = initDB() + + term = "%s" % semester.getTerm() + year = (semester.getYear()) + + cursor.execute(''' + SELECT * + FROM Assignment + WHERE term = ? + AND year = ? + ''', (term, year)) + + assignments = [] + for row in cursor.fetchall(): + assignments.append(AssignmentModel(getQDateTime(row[0]), getCourseFromDB(row[1]), row[2], row[3], row[4])) + + exitDB(conn) + + return assignments + +## Get the readings of the specified semester from the database +def getReadingsFromDB(semester): + cursor, conn = initDB() + + term = "%s" % semester.getTerm() + year = semester.getYear() + + cursor.execute(''' + SELECT * + FROM Reading + WHERE term = ? + AND year = ? + ''', (term, year)) + + readings = [] + for row in cursor.fetchall(): + readings.append(ReadingModel(row[0], getCourseFromDB(row[1]), getBookFromDB(row[2]), row[3], row[4], row[5])) + + exitDB(conn) + + return readings + +## Get the lessons of the specified semester from the database +def getLessonsFromDB(semester): + cursor, conn = initDB() + + term = "%s" % semester.getTerm() + year = semester.getYear() + + cursor.execute(''' + SELECT * + FROM Lesson + WHERE term = ? + AND year = ? + ''', (term, year)) + + lessons = [] + for row in cursor.fetchall(): + lessons.append(ScheduleModel(row[0], row[1], getCourseFromDB(row[2]), row[3], row[4])) + + exitDB(conn) + + return lessons + +## Get all the courses from the database +def getCoursesFromDB(): + cursor, conn = initDB() + + cursor.execute(''' + SELECT * + FROM Course + ''') + + courses = [] + for row in cursor.fetchall(): + courses.append(CourseModel(row[0], row[1], row[2], QColor(row[3], row[4], row[5]), [])) + + for course in courses: + cursor.execute(''' + SELECT bookIsbn + FROM CourseUsesBook + WHERE courseCode = ? + ''', (course.getCode(),)) + fetched = cursor.fetchall() + for fetchedRow in fetched: + cursor.execute(''' + SELECT * + FROM Book + WHERE isbn = ? + ''', (fetchedRow[0],)) + for row in cursor: + course.addBook(BookModel(row[0], row[1], row[2], row[3])) + + return courses + +## Get the course specified by the course code from the database +def getCourseFromDB(courseCode): + cursor, conn = initDB() + + cursor.execute(''' + SELECT * + FROM Course + WHERE code = ? + ''', (courseCode,)) + + course = None + + for row in cursor.fetchall(): + course = CourseModel(row[0], row[1], row[2], QColor(row[3], row[4], row[5]), []) + + cursor.execute(''' + SELECT bookIsbn + FROM CourseUsesBook + WHERE courseCode = ? + ''', (courseCode,)) + fetched = cursor.fetchall() + for fetchedRow in fetched: + cursor.execute(''' + SELECT * + FROM Book + WHERE isbn = ? + ''', (fetchedRow[0],)) + for row in cursor: + course.addBook(BookModel(row[0], row[1], row[2], row[3])) + + exitDB(conn) + + return course + +## Get all the books from the database +def getBooksFromDB(): + cursor, conn = initDB() + + cursor.execute(''' + SELECT * + FROM Book + ''') + + books = [] + for row in cursor.fetchall(): + books.append(BookModel(row[0], row[1], row[2], row[3])) + + exitDB(conn) + + return books + +## Get the book specified by the ISBN number from the database +def getBookFromDB(isbn): + cursor, conn = initDB() + + cursor.execute(''' + SELECT * + FROM Book + WHERE isbn = ? + ''', (isbn,)) + + for row in cursor.fetchall(): + book = BookModel(row[0], row[1], row[2], row[3]) + + exitDB(conn) + + return book + +## Get the book specified by the title from the database +def getBookWithTitleFromDB(booktitle): + cursor, conn = initDB() + + book = "%s" % booktitle + + cursor.execute(''' + SELECT * + FROM Book + WHERE title = ? + ''', (book,)) + + for row in cursor.fetchall(): + book = BookModel(row[0], row[1], row[2], row[3]) + + exitDB(conn) + + return book + +# Update things in the database + +## Update the completion level of the specified assignment +def updateAssignmentCompletion(coursecode, num, completion): + cursor, conn = initDB() + + status = "%s" % completion + course = "%s" % coursecode + number = (num.toInt())[0] + + cursor.execute(''' + UPDATE Assignment + SET complete = ? + WHERE course = ? + AND number = ? + ''', (status, course, number)) + + exitDB(conn) + +## Update whether the specified reading is done or not +def updateReadingDone(week, coursecode, bookisbn, chapter, done): + cursor, conn = initDB() + + course = "%s" % coursecode + book = "%s" % bookisbn + ch = "%s" % chapter + + cursor.execute(''' + UPDATE Reading + SET done = ? + WHERE week = ? + AND course = ? + AND book = ? + AND chapter = ? + ''', (done, week, course, book, ch)) + + exitDB(conn) + +# Remove things from the database + +## Remove the specified assignment from the database +def removeAssignmentFromDB(coursecode, num): + cursor, conn = initDB() + + course = "%s" % coursecode + number = (num.toInt())[0] + + cursor.execute(''' + DELETE + FROM Assignment + WHERE course = ? + AND number = ? + ''', (course, number)) + + exitDB(conn) + +## Remove the specified reading from the database +def removeReadingFromDB(week, coursecode, bookisbn, chapter): + cursor, conn = initDB() + + course = "%s" % coursecode + book = "%s" % bookisbn + ch = "%s" % chapter + + cursor.execute(''' + DELETE + FROM Reading + WHERE week = ? + AND course = ? + AND book = ? + AND chapter = ? + ''', (week, course, book, ch)) + + exitDB(conn) + +## Remove the specified lesson from the database +def removeLessonFromDB(daystring, coursecode, fromTime): + cursor, conn = initDB() + + day = "%s" % daystring + course = "%s" % coursecode + time = "%s" % fromTime + + cursor.execute(''' + DELETE + FROM Lesson + WHERE day = ? + AND course = ? + AND time = ? + ''', (day, course, time)) + + exitDB(conn) + +# Exit the database + +## Commit and close the connection +def exitDB(conn): + conn.commit() + conn.close() + +# Course + +## Get the course code from the full name of the course +def getCourseCode(courseFull): + course = courseFull.split(' ') + return course[0] + +## Add a new course to the list of courses +def addNewCourse(course): + courses.append(course) + addNewCourseToDB(course.getCode(), course.getTitle(), course.getShort(), course.getColor(), course.getBooks()) + addNewCourseString(course) + +## Return the list of courses +def getCourses(): + global courses + return courses + +## Add the course as a course string +def addNewCourseString(course): + global coursesString + coursesString.append(course.getFull()) + +## Make the courses string list and return it +def makeCoursesString(): + emptyCourses() + cs = getCoursesFromDB() + if cs: + for c in cs: + addNewCourseString(c) + s = getCoursesString() + return s + +## Return the courses string list +def getCoursesString(): + global coursesString + return coursesString + +## Empty the courses list and the courses string list +def emptyCourses(): + global courses + global coursesString + courses = [] + coursesString = [] + +# Book + +## Add a new book to the list of books +def addNewBook(book): + books.append(book) + addNewBookToDB(book.getISBN(), book.getTitle(), book.getAuthor(), book.getEdition(), book.getCourse()) + addNewBookString(book) + +## Return the list of books +def getBooks(): + global books + return books + +## Add the book as a book string +def addNewBookString(book): + global booksString + booksString.append(book.getTitle()) + +## Make the books string list and return it +def makeBooksString(): + emptyBooks() + bs = getBooksFromDB() + if bs: + for b in bs: + addNewBookString(b) + s = getBooksString() + return s + +## Return the books string list +def getBooksString(): + global booksString + return booksString + +## Empty the books list and the books string list +def emptyBooks(): + global books + global booksString + books = [] + booksString = [] + +# Color + +## Return a random color, RGB +def getRandomColor(): + return QColor(getRandomColorPart(), getRandomColorPart(), getRandomColorPart()) + +## Return a random number between 0 and 255 +def getRandomColorPart(): + return random.random()*256 + +# General + +## Convert a SQL timestamp to a QDateTime object +def getQDateTime(timestamp): + if not timestamp: + return None + datetimeList = timestamp.split() + dateString = datetimeList[0] + timeString = datetimeList[1] + dateList = dateString.split('-') + timeList = timeString.split(':') + year = string.atoi(dateList[0]) + month = string.atoi(dateList[1]) + day = string.atoi(dateList[2]) + hour = string.atoi(timeList[0]) + minute = string.atoi(timeList[1]) + date = QDate(year, month, day) + time = QTime(hour, minute) + datetime = QDateTime(date, time) + return datetime + +## Return the main window +def getMain(): + global main + return main + +## Run the program +def main(): + app = QApplication(sys.argv) + locale = QLocale.system().name() + qtTranslator = QTranslator() + if qtTranslator.load("qt_" + locale, ":/"): + app.installTranslator(qtTranslator) + appTranslator = QTranslator() + if appTranslator.load("imagechanger_" + locale, ":/"): + app.installTranslator(appTranslator) + + form = MainWindow() + app.setOrganizationName("PVV") + app.setOrganizationDomain("pvv.ntnu.no") + app.setApplicationName(form.title) + form.show() + app.exec_() + + +main() +#initNewDB() diff --git a/egon_en.py b/egon_en.py index 2aa014b..3c9dfd7 100755 --- a/egon_en.py +++ b/egon_en.py @@ -335,14 +335,14 @@ class MainWindow(QMainWindow): ## Make and open the About dialog def helpAbout(self): - QMessageBox.about(self, "About %s" % self.title, u""" + QMessageBox.about(self, "About %s" % self.title, self.trUtf8(""" %s v %s

Copyright © 2008 Tiril Anette Langfeldt Rødland. No rights reserved.

You may modify and redistribute the program under the terms of the GPL. The license can be found here: http://www.gnu.org/licenses/gpl.html

This application is mainly for use by students, and can be used to keep track of assignments, planned readings and the schedule.

Python %s - Qt %s - PyQt %s on %s

Developer: Tiril Anette Langfeldt Rødland, tirilane@pvv.ntnu.no - """ % (self.title, __version__, platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR, platform.system())) + """ % (self.title, __version__, platform.python_version(), QT_VERSION_STR, PYQT_VERSION_STR, platform.system()))) ## Updates the File menu def updateFileMenu(self): diff --git a/egon_nb.qm b/egon_nb.qm new file mode 100644 index 0000000..c382ffa Binary files /dev/null and b/egon_nb.qm differ diff --git a/egon_nb.ts b/egon_nb.ts new file mode 100644 index 0000000..3f35c1f --- /dev/null +++ b/egon_nb.ts @@ -0,0 +1,657 @@ + + + + + AssignmentDlg + + + &Date + &Dato + + + + &Course + &Fag + + + + &Number + &Nummer + + + + De&scription + &Beskrivelse + + + + &Status + &Status + + + + Not available + Ikke tilgjengelig + + + + Available + Tilgjengelig + + + + Begun + Påbegynt + + + + Finished + Ferdig + + + + Delivered + Levert + + + + Approved + Godkjent + + + + Not approved + Underkjent + + + + Add new assignment + Ny øving + + + + The date is not in a correct format. + Datoen er ikke i et godkjent format. + + + + The day is not valid. Please enter a valid date. + Dagen er ikke gyldig. Vennligst oppgi et gyldig dato. + + + + The hour is not valid. Please enter a valid time. + Timen er ikke gyldig. Vennligst oppgi et gyldig tidspunkt. + + + + The minutes are not valid. Please enter a valid time. + Minuttene er ikke gyldig. Vennligst oppgi et gyldig tidspunkt. + + + + The date is not valid. Please enter a date on the DD.MM.YYYY HH:MM form. + Datoen er ikke gyldig. Vennligst oppgi en dato på formatet DD.MM.YYYY HH:MM. + + + + AssignmentTab + + + Not available + Ikke tilgjengelig + + + + Available + Tilgjengelig + + + + Begun + Påbegynt + + + + Finished + Ferdig + + + + Delivered + Levert + + + + Approved + Godkjent + + + + Not approved + Underkjent + + + + Date + Dato + + + + Course + Fag + + + + Number + Nummer + + + + Description + Beskrivelse + + + + Status + Status + + + + BookDlg + + + &Title + &Tittel + + + + &Author + &Forfatter + + + + &Edition + &Utgave + + + + &ISBN + &ISBN + + + + Add new book + Ny bok + + + + CourseDlg + + + &Code + Fag&kode + + + + &Title + Fag&navn + + + + &Short form + &Kortform + + + + &Books + &Bøker + + + + Add new course + Nytt fag + + + + . edition + . utgave + + + + MainWindow + + + Egon + Egon + + + + Monday + Mandag + + + + Tuesday + Tirsdag + + + + Wednesday + Onsdag + + + + Thursday + Torsdag + + + + Friday + Fredag + + + + &Assignment + &Øving + + + + &Reading List + &Leselekse + + + + &Schedule + &Timeplan + + + + Calendar + Kalender + + + + Spring + Vår + + + + Autumn + Høst + + + + &New + &Ny + + + + Create a new semester plan + Lag ny semesterplan + + + + &Open + &Åpne + + + + Open an existing semester plan + Åpne en eksisterende semesterplan + + + + &Quit + &Avslutt + + + + Quit program + Avslutt program + + + + Add &course + Nytt &fag + + + + Add a new course + Legg til et nytt fag + + + + Add &book + Ny &bok + + + + Add a new book + Legg til en ny bok + + + + Add &assignment + Ny &øving + + + + Add a new assignment + Legg til en ny øving + + + + Add &reading + Ny &leselekse + + + + Add a new reading + Legg til en ny leselekse + + + + Add &lesson + Ny &time + + + + Add a new lesson + Legg til en ny time + + + + Cal&endar + Kal&ender + + + + Show the calendar + Vis kalenderen + + + + &About Egon + Om &Egon + + + + &File + &Fil + + + + &Edit + &Rediger + + + + &Help + &Hjelp + + + + File + Fil + + + + Edit + Rediger + + + + Not done + Ikke ferdig + + + + Done + Ferdig + + + + NewSemesterDlg + + + Semester + Semester + + + + Year + År + + + + New semester + Nytt semester + + + + OpenSemesterDlg + + + Open semester + Åpne en semesterplan + + + + ReadingDlg + + + &Week + &Uke + + + + &Course + &Fag + + + + &Book + &Bok + + + + Cha&pter + &Kapittel + + + + &Pages + &Sider + + + + Add new reading + Ny leselekse + + + + ReadingTab + + + Add pages to read + Legg til leselekse + + + + Delete pages + Slett leselekse + + + + Done + Ferdig + + + + Week + Uke + + + + Course + Fag + + + + Book + Bok + + + + Chapter + Kapittel + + + + Pages + Sider + + + + Number of pages + Antall sider + + + + Not done + Ikke ferdig + + + + ScheduleDlg + + + &Day + &Dag + + + + &From + Fr&a + + + + &To + &Til + + + + &Course + &Fag + + + + Ty&pe + T&ype + + + + &Room + &Rom + + + + Lecture + Forelesning + + + + Assignment lecture + Øvingsforelesning + + + + Assignment help + Øvingshjelp + + + + Lab + Lab + + + + Seminar + Seminar + + + + Other + Annet + + + + Add new lesson + Legg til ny time + + + + ScheduleTab + + + Monday + Mandag + + + + Tuesday + Tirsdag + + + + Wednesday + Onsdag + + + + Thursday + Torsdag + + + + Friday + Fredag + + + + %i + %i + + + + Lecture + Forelesning + + + + Assignment lecture + Øvingsforelesning + + + + Assignment help + Øvingshjelp + + + + Lab + Lab + + + + Seminar + Seminar + + + + Other + Annet + + +